본문 바로가기
Study/리버싱

Register

by 꼬부기가우는소리 2016. 6. 8.
728x90


참고 도서 : [리버싱 핵심원리 : 악성 코드 분석가의 리버싱 이야기]

저자 : 이승원

출판사 : 인사이트


참고 문헌 : [리버싱 기초]

저자 : 심준보

작성 날짜 : 2014-01-07


참고 사이트 :


디버깅을 잘 하려면 디버거가 해석 (디스어셈)해주는 어셈블리 명령어를 공부해야 한다. 어셈블리 명령어의 대부분은 레지스터를 조작하고 그 내용을 검사하는 것들이기 때문이다.


데이터 단위
(1) BIT
데이터를 표현할 수 있는 가장 작은 단위이다. 
- 표현할 수 있는 값 : 0, 1
(2) BYTE
8개의 비트가 모이면 하나의 바이트이다. 최대 표현할 수 있는 값이다.
- 최대 값 : 0xF (255)
- 8비트와 대응하는 레지스터 : AH, AL, BH, BL, CH, CL, DH, DL
(3) WORD
2개의 바이트가 모이면 워드이다. 16비트로 표현이 가능하다.
- 최대 값 : 0xFF (65535)
- 16비트와 대응하는 레지스터 : AX, BX, CX, DX
(4) DWORD
워드가 2개 모이면 더블워드이다.
- 최대 값 : 0xFFFF (32비트)
- 32비트와 대응하는 레지스터 : EAX, EBX, ECX, EDX, EBP, ESI, EDI, ESP


IA-32

IA-32 (Intel Architecture, 32-bit) 또는 x86-32는 인텔의 32비트 마이크로프로세서에서 사용하는 명령 집합 아키텍처이며, 이전에 사용된던 IA-16 아키텍처의 32비트 확장이다. IA-32를 x86이라는 이름으로 부르기도 하지만 엄밀하게는 x86 아키텍처는 IA-16, IA-32 등을 모두 포함하는 일반적인 이름이다.


처음에 인텔 80386은 32비트 일반 레지스터 8개와 부동 소수점 실수 레지스터 8개를 지원했다. 뒤에 나온 프로세서들은 MMX, 3DNow!, SSE, SSE2, SSE3과 같은 다양한 SIMD 명령 집합들을 위한 레지스터들을 더 지원한다.


응용 프로그램에 의해 사용되지 않고 운영 체제에 의해 빈번하게 사용되는 시스템 레지스터들이 존재한다. Segment, Control, Debug 그리고 Test 레지스터가 이에 속한다. 여섯개의 Segment 레지스터는 주료 메모리 관리에 사용되며, Control, Debug, Test 레지스터는 모델에 따라 다양하다.


이 중 디버깅할 때 가장 많이 보게 될 레지스터는 Basic program execution register이다. 아래는 IA-32 플랫폼에서 쓰이는 어셈블리어에서 많이 사용하는 레지스터에 대한 설명이다.



Register

레지스터 (Register)란 CPU 내부에 존재하는 고속 저장 장소이며 일반 메모리보다 훨씬 빠른 속도로 접근할 수 잇다. 프로그램이 실행되면 실행 파일이 메모리에 상주하면서 동작에 필요한 일부 데이터들이 CPU로 전달되는데, 이 데이터들이 저장되는 곳이 바로 레지스터이다. RAM (Random Access Memory)과는 성격이 조금 다르다. CPU가 RAM에 있는 데이터를 엑세스하기 위해서는 물리적으로 먼 길을 돌아가야 하기 때문에 시간이 오래 걸린다.


기본적인 프로그램 실행 레지스터는 8개의 범용 레지스터와 6개의 세그먼트 레지스터, 프로세서 상태 플래그 레지스터와 명령어 포인터가 있다.




(1) 범용 레지스터 (General Purpose Registers)

8개의 레지스터로 구성되어 있다. 산술/논리 연산, 오퍼랜드 (피연산자)를 저장하거나 포인터의 역할(메모리의 주소를 저장)로 사용된다. 이름 그대로 범용적으로 사용할 수 있지만 보통은 이름의 뜻에 맞게 사용한다.


EAX (Extended Accumlator for operands and results data, AX, AH, AL) : 산술/논리 연산을 수행하여 함수의 반환값이 저장된다.

EDX (Extended I/O pointer, DX, DHT, DL) : 입출력 연산에서 반드시 간접 주소 지정에 사용된다.

EBX (Extended Pointer to data in the DS segment, BX, BH, BL) : 특정 메모리 주소를 저장한다. DS 세그먼트의 데이터를 가리킨다.

ECX (Extended Counter for string and loop operations, CX, CH, CL) : 반복 명령어(LOOP)에서 반복 카운트(loop count)로 사용된다. (루프를 돌 때마다 ECX를 1씩 감소시킨다.)


EAX, EDX, EBX, ECX는 주로 산술연산 (ADD, SUB, SOR, OR 등) 명령어에서 상수/변수 값의 저장 용도로 많이 사용된다.


ESI (Extended Source Pointer for String Operations) : 읽기 인덱스, 문자열 전송이나 비교에서 사용된다. 주로 소스 문자열의 오프셋을 가리킨다.

EDI (Extended Destination Pointer for String Operations, DI) : 쓰기 인덱스, 복사 시에 목적지의 주소가 저장된다.

ESP (Extended Stack Point Register, SP) : 스택 포인터로 현재까지 사용된 스택의 위치를 저장한다. 스택 최상부의 오프셋을 가리킨다.

EBP (Extended Pointer to data on the stack (in the SS segment), BP) : 베이스 포인트, 스택 프레임에서 스택의 시작 지점 주소 (스택의 복귀 주소)가 저장된다. 프로그램이나 함수가 시작할 때 기준이 되는 주소다.


ESI, EDI, ESP, EBP는 주로 메모리 주소를 저장하는 포인터로 사용된다.





EAX 레지스터는 AX 레지스터로 반 쪼개지고, AX 레지스터는 또다시 AH와 AL로 나뉘어진다. 이것은 8비트 컴퓨터일 때 8비트 크기의 AH와 AL 레지스터가 있었고, 16비트로 넘어오면서 통합되어 AX 레지스터로 존재하다가, 32비트 시대가 왔을 때 확장되었다는 뜻인 Extention을 맨 앞에 붙여 EAX가 된 것이다. 실제로 위 그림의 레지스터들의 크기를 보면 E가 맨 앞에 붙은 레지스터들은 32비트 크기이지만 그렇지 않은 레지스터들은 16비트이다.



각 레지스터들은 16비트 하위 호환을 위하여 몇 개의 구획으로 나뉘어진다. 즉 4바이트 (32키트)를 다 사용하고 싶을 때는 EAX를 사용하고, 2바이트 (16비트)만 사용할 때는 EAX의 하위 16비트 부분인 AX를 사용하면 된다. AX는 다시 상위 1바이트 (8비트)인 AH와 하위 1바이트 (8비트)인 AL로 나뉘어진다. 이런 식으로 하나의 32비트 레지스터를 상황에 맞게 8비트, 16비트, 32비트로 알뜰하게 사용할 수 있다.

 




(2) 세그먼트 레지스터 (Segment Registers)

메모리를 보호하기 위한 레지스터이다. 프로그램끼리 서로 주소를 침범하는 것을 방지하기 위한 페이징, SDT 등의 기술을 사용하거나 메모리의 값을 참조할 때 사용한다.


IA-32 보호 모드에서 세그먼트란 메모리를 조각내어 각 조각마다 시작 주소, 범위, 접근 권한 등을 부여해서 메모리를 보호하는 기법을 말한다. 세그먼트 메모리는 Segment Descriptor Table (SDT)이라고 하는 곳에 기술되어 있는데, 세그먼트 레지스터는 바로 이 SDT의 index를 가지고 있다.


아래의 그림은 세그먼트 메모리 모델이다. 각 세그먼트 레지스터가 가리키는 세그먼트 디스크립터 (Segment Descriptor)와 가상 메모리가 조합되어 선형 주소 (Linear Address)가 되며, 페이징 기법에 의해서 선형 주소가 최종적으로 물리주소 (Physical Address)로 변환된다. 만약 OS에서 페이징을 사용하지 않는다면 선형주소는 그대로 물리주소가 된다.




CS (Code Segment) : 현재 명령 실행 주소, 다음에 어떤 명령 주소를 실행할 것인지 나타내는 주소이다.

DS (Data Segment) : 데이터 영역의 시작 주소이다.

SS (Stack Segment) : 스택 영역의 시작 주소이다. 프로그램이 임시로 저장할 필요가 있거나, 사용자의 '피호출' 서브루틴 (called subroutine)이 사용할 데이터와 주소를 포함한다.

ES (Extra (Data) Segment)

FS (Data Segment)

GS (Data Segment)


ES, FS, GS 세그먼트는 추가적인 데이터 세그먼트이다.


FS레지스터는 애플리케이션 디버깅에도 자주 등장하는데 SEH (Structured Exception Handling), TEB (Thread Environment Block), PEB (Process Environment Block) 등의 주소를 계산할 때 사용되며 이들은 모두 고급 디버깅 주제이다.




Segment

세그먼트는 프로그램에 정의된 특정 영역으로, 코드, 데이터, 그리고 스택(stack)으로 알려져 잇는 것을 포함한다. 한 세그먼트는 패러그래프 경계 (paragraph boundary), 즉 16 또는 hex 10으로 나누어지는 위치에서 시작한다. 명령어가 세그먼트 레지스터에 세그먼트 주소를 적재할 때, 가장 오른쪽에 위치한 네 개의 0비트가 자동으로 오른쪽으로 이동하면서 제거된다.


사용자가 임의 개수의 세그먼트를 정의할 수 있다. 특정 세그먼트에 대해서 주소를 지정하려면 적절한 세그먼트 레지스터의 값을 그 주소로 변경하기만 하면 된다. 실제 모드에서 세 개의 주요 세그먼트는 코드, 데이터, 그래고 스택이다.




(3) 프로그램 상태와 컨트롤 레지스터 (Flag Register, EFLAGS)

32비트 (4바이트) 크기이다. (EFLAGS 레지스터 역시 16비트의 FLAGS 레지스터의 32비트 확장 형태이다) 아래의 그림과 같이 각각의 비트마다 가지고 있으며 프로그램이 실행되고 있는 상황에 연산의 결과나 시스템 제어 등의 상태 정보를 저장한다. 각 비트는 1 또는 0의 값을 가지는데, 이는 On/Off 혹은 True/False를 의미한다.




CF (Carry Flag) : 부호 없는 수의 오버플로우가 발생했을 경우 1로 설정된다.

ZF (Zero Flag) : 연산 결과가 0이면 1로 설정된다.

SF (Sign Flag) : 연산 결과가 1인 경우 1로 설정된다.

OF (Over Flag) : 부호 있는 수의 오버플로우가 발생했을 경우 1로 설정된다.

PF (Parity Flag) : 연산 결과가 짝수이면 1, 홀수이면 0으로 설정된다.

AF (Auxiliary Flag) : 16비트 연산을 할 때 빌림 수가 발생하면 1로 설정된다.

DF (Direction Flag) : 문자열 복사와 관련된 제어 플래그이다. 1로 설정되어 있으면 문자열 복사 시 주소 값이 감소한다.

TF (Trap Flag) : 프로그램 추적 시 1로 설정한다. 명령을 한 행씩 실행한다.


위의 3가지 flag, ZF, OF, CF를 잘 알아두도록 한다. 이는 조건 분기 명령어 (Jcc)에서 이 세개의 Flag 값을 확인하고 동작 수행 여부를 결정하기 때문이다.


(4) EIP 레지스터 (Instruction Pointer Register)

CPU가 처리할 명령어의 주소를 나타내는 레지스터이며, 크기는 32비트 (4바이트)이다. CPU는 EIP에 저장된 메모리 주소의 명령어 (instruction)를 하나 처리하고 난 후 자동으로 그 명령어 길이만큼 EIP를 증가시킨다.


이 값은 직접 변경할 수 없으므로 다른 명령어를 통하여 간접적으로 변경해야 한다. EIP를 변경하고 싶을 때는 특정 명령어 (JMP, Jcc, CALL, RET)를 사용하거나 인터럽트 (interrupt), 예외 (exception)을 발생시켜야 한다..




'Study > 리버싱' 카테고리의 다른 글

PE Structure (1) PE 포맷  (0) 2016.06.19
Stack  (0) 2016.06.12
Endianness  (0) 2016.06.08
Stub Code  (0) 2016.06.08
OllyDbg (1)  (0) 2016.06.05

댓글