출처 : www.reversecore.com
Register란?
Register란 CPU 내부에 존재하는 저장공간이다.
CPU가 RAM에 있는 데이터를 액세스 하기 위해서는 먼 길을 돌아가야 한다.
하지만 레지스터는 CPU와 한 몸이기 때문에 고속으로 데이터를 처리할 수 있다.
단, 가격이 비싸고 용량이 작으며, CPU 아키텍쳐와 밀접한 연관이 있어서 그 개수와 크기를 변경할 수 없다.
(변경하려면 CPU 아키텍쳐 자체가 변경되어야 한다.)
IA-32 Registers
IA-32는 최신 CPU인 만큼 지원하는 기능도 많다.
따라서 그만큼 레지스터도 많이 가지고 있다.
Basic program execution registers
x87 FPU registers
MMX registers
XMM registers
Control registers
Memory management registers
Debug registers
Memory type range registers
Machine specific registers
Machine check register
…
여기서는 일단 Basic program execution registers에 대해서만 알아보도록 하겠다.
(애플리케이션 디버깅의 초급 단계에서는 이것만 알면 된다.)
Basic program execution registers
Basic program execution registers 들은 크게 4가지 group으로 나눌 수 있다.
- General Purpose Registers (32bit - 8개)
- Segment Registers (16bit - 6개)
- Program Status and Control Register (32bit - 1개)
- Instruction Pointer (32bit - 1개)
※ 레지스터 이름에 E(Extended)가 붙은 것들은 예전 16bit CPU부터 존재하던 레지스터들이었는데,
32bit CPU에서 이름 앞에 E를 붙여서 32bit로 확장되었다는 것을 나타낸다.
※ 같은 이유로 64bit CPU에서는 E대신 R을 붙여서 64bit로 확장되었음을 나타낸다.
#1. General Purpose Registers
이름 그대로 범용적으로 사용되는 레지스터들이다.
IA-32에서 범용 레지스터의 크기는 각 32bits(4bytes)이다.
보통은 변수를 저장할 때 주로 사용되며, 특정 Assembly 명령어에서는 특정 레지스터를 조작하기도 한다.
또한 어떤 레지스터들은 특수한 용도로 사용되기도 한다.
아래 그림에 범용 레지스터들이 표시되어 있다.
- EAX : 32bit
- AX : EAX의 하위 16bit
- AH : AX의 상위 8bit
- AL : AX의 하위 8bit
각 레지스터의 이름은 다음과 같다.
- EAX : Accumulator for operands and results data
- EBX : Pointer to data in the DS segment
- ECX : Counter for string and loop operations
- EDX : I/O pointer
위 4개의 레지스터들은 주로 산술 연산(ADD, SUB, XOR, OR, etc) 명령어에서 변수 저장용도로 많이 사용된다.
특정 Assembly 명령어(MUL, DIV, LODS, etc)에서는 특정 레지스터를 직접 조작하기도 한다.
그리고 ECX와 EAX는 특수한 용도로 사용될 때가 있다.
ECX는 반복문 명령어(Loop)에서 참조 카운트로 사용된다. (루프를 돌 때마다 ECX를 1씩 감소시킨다.)
EAX는 일반적으로 함수 리턴값에 사용된다. (Win32 API 함수들은 모두 리턴값을 EAX에 저장한 후 리턴한다.)
※ Windows Assembly 프로그래밍에서 주의할 점!
Win32 API 함수들은 내부에서 ECX와 EDX를 사용하고 그대로 리턴한다.
따라서 ECX와 EDX에 중요한 값이 저장되어 있다면 API 호출 전에 다른 레지스터나 스택에 백업해야 한다.
나머지 범용 레지스터들의 이름은 다음과 같다.
EBP : Pointer to data on the stack (in the SS segment)
ESI : source pointer for string operations
EDI : destination pointer for string operations
ESP : Stack pointer (in the SS segment)
위 4개의 레지스터들은 주로 메모리 주소를 저장하는데 사용한다.
ESP는 스택 메모리 주소를 가리킨다.
어떤 명령어들(PUSH, POP CALL, RET)은 ESP를 직접 조작하기도 한다.
(스택 메모리 관리는 프로그램에서 매우 중요하기 때문에 ESP를 다른 용도로 사용하지 않는 것이 바람직하다.)
EBP는 함수가 호출되었을 때 그 순간의 ESP를 저장하고 있다가,
함수가 리턴하기 직전에 다시 ESP에 값을 되돌려줘서 스택이 깨지지 않도록 해 준다.
(이 것을 stack frame 기법이라고 한다.)
ESI와 EDI는 특정 명령어(LODS, STOS, REP MOVS, etc)와 함게 주로 메모리 복사에 사용된다.
#2. Segment Register
세그먼트란 IA-32 메모리 모델에서 나오는 용어이다.
따라서 세그먼트 레지스터를 이해하려면 먼저 OS 내부구조의 '보호모드'와 '메모리 모델'의 개념을 이해해야 한다.
일단 이에 대해서는 개념적으로 간단히 알아보도록 하자.
IA-32 보호모드에서 세그먼트란 메모리를 조각내어
각 조각마다 시작주소, 범위, 접근 권한 등을 부여해서 메모리를 보호하는 기법을 말한다.
또한 세그먼트는 페이징(Paging) 기법과 함께 가상 메모리를 실제 물리적 메모리로 변경할 때 사용된다.
세그먼트 메모리는 segment descriptor table이라고 하는 곳에 기술되어 있는데,
세그먼트 레지스터는 이 segment descriptor table의 index를 가지고 있다.
아래 그림은 보호모드에서의 세그먼트 메모리 모델을 나타내고 있다.
세그먼트 레지스터는 총 6개(CS, SS, DS, ES, FS, GS)이며 각각의 크기는 16bit (2byte)이다.
<Fig. 3>에서 각 세그먼트 레지스터가 가리키는 세그먼트 디스크립터와 가상 메모리가 조합되어 선형주소(Linear Address)가 되며,
페이징(Paging) 기법에 의해 선형주소가 최종적으로 물리주소(Physical Address)로 변환된다.
※ 만약 OS에서 Paging을 사용하지 않는다면 선형주소는 그대로 물리주소가 된다.
각 세그먼트 레지스터의 이름은 아래와 같다.
- CS : Code Segment
- SS : Stack Segment
- DS : Data Segment
- ES : Extra(Data) Segment
- FS : Data Segment
- GS : Data Segment
이름 그대로 CS는 프로그램의 코드 세그먼트를 나타내며,
SS는 스택 세그먼트, DS는 데이터 세그먼트를 나타낸다.
ES, FS, GS 세그먼트는 추가적인 데이터 세그먼트이다.
FS 레지스터는 애플리케이션 디버깅에도 자주 등장하는데 SEH(Structured Exception Handling), TEB(Thread Environment Block),
PEB(Process Environment Block) 등을 구할 때 사용된다. (이들은 모두 고급 디버깅 주제이다.)
#3. Program Status and Control Register
플래그 레지스터의 이름은 EFLAGS이며 32bit (4byte) 크기이다.
각 레지스터 bit마다 의미를 가지고 있고 1또는 0의 값을 가지며 On/Off 또는 True/False를 표시한다.
일부 bit는 시스템에서 직접 세팅하고, 일부 bit는 프로그램에서 사용된 명령의 수행결과에 따라 세팅된다.
※ Flag란 단어 그대로 깃발이 올라가면 1(On), 내려가면 0(Off)으로 이해하면 된다.
32개 bit 모두의 의미를 전부 이해한다는 것은 힘든 일이지만,
애플리케이션 디버깅에 가장 중요한 3가지 Flag는 꼭 이해해야 한다.
Zero Flag : 연산 명령 후에 결과값이 0이 되면 ZF가 1로 세팅된다.
Overflow Flag : 부호 있는 수(signed integer)의 오버플로우가 발생했을 때 1로 세팅된다.
MSB(Most Significant Bit)가 변경되었을 때 1로 세팅된다.
Carry Flag : 부호없는 수(unsigned integer)의 오버플로우가 발생했을 때 1로 세팅된다.
#4. Instruction Pointer
EIP : Instruction Pointer
CPU가 처리할 명령어의 주소를 나타내는 레지스터이며, 크기는 32bit (4byte)이다.
CPU는 하나의 명령(instruction)을 처리하고 난 후 자동으로 그 명령어 길이만큼 EIP를 증가시킨다.
General Purpose Register들과는 다르게 EIP는 그 값을 직접 변경할 수 없도록 되어 있다.
EIP를 변경하고 싶을 대는 특정 명령어(JMP, Jcc, CALL, RET), 인터럽트(interrupt), 예외(exception)를 발생시켜야 한다.
'정상을향해 > Program Analysis' 카테고리의 다른 글
PE File Format (3) (0) | 2013.11.10 |
---|---|
PE File Format (2) (0) | 2013.11.10 |
PE File Format (1) (0) | 2013.11.10 |
Calling Convention (0) | 2013.11.10 |
Stack Frame (0) | 2013.11.10 |
[ASM] 어셈블러 : 범용 레지스터 (0) | 2011.05.16 |