2014. 1. 14. 16:17 :: 리버싱

안녕하세요~ Seek 입니다.

오늘은 IA-32 레지스터에 대해 공부해보겠습니다.

(여기서 IA-32는 Intel Architecture 32비트를 의미합니다.)

 

1. CPU 레지스터

 

  레지스터란 CPU 내부에 존재하는 다목적 저장 공간입니다. 일반적으로 메모리라고 하는 RAM과는 조금 성격이 다릅니다. CPU가 RAM에 있는 데이터를 접근하기 위해서는 물리적으로 먼 길을 돌아가기 때문에 시간이 오래 걸리니다. 하지만 레지스터는 CPU내부에 있는 저장 공간이기 때문에 고속으로 데이터를 처리할 수 있습니다.

 

  리버싱 초급 단계에서 애플리케이션 디버깅을 잘 하려면 디버거가 해석해주는(디스어셈) 어셈블리 명령어를 공부해야 합니다. 어셈블리 명령어의 대부분은 레지스터를 조작하고 그 내용을 검사하는 것들이기 때문에 정작 레지스터를 모르면 명령어 자체도 이해하기 힘들기 때문에 레지스터를 공부해야합니다. 다들 화이팅 합시다! 저도 화이팅 중입니다 ㅎ

 

2. IA-32 레지스터

 

  IA-32 레지스터는 지원하는 기능도 많고 그만큼 수도 많이 있습니다. 애플리케이션 디버깅의 초급 단계에서는 Basic program execution register에 대해서 알아두어야 합니다. 디버깅할 때 가장 많이 보게 될 레지스터입니다.

 

 2-1. Basic program execution registers

위 그림처럼 크게 4가지의 종류가 있습니다.

   - General Purpose Registers (32비트 - 8개)

   - Segment Registers (16비트 - 6개)

   - Program Status and Control Register (32비트 - 1개)

   - Instruction Pointer (32비트 - 1개)

 

(참고 : 레지스터 이름 앞에 E(Extended)가 붙은 경우는 예전 16비트 CPU인 IA-16 시절부터 존재하던 16비트크기의 레지스터들을 32비트로 확장시켰다는 뜻입니다.)

 

 

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

 

      범용 레지스터는 이름처럼 범용적으로 사용됩니다. '막'쓰는' 막'레지스터들이라고 생각하세요. 32비트(4바이트)이며, 보통 상수/주소 등을 저장할 때 주로 사용되며, 특정 어셈블리 명령어에서는 특정 레지스터를 조작하기도 합니다.

 

위 사진은 범용 레지스터입니다.

 - EAX : 0~31(32비트)

 - AX : 0~15(16비트)

 - AH : 8~15(8비트)

 - AL : 0~7(8비트)

EAX를 기준으로 간단히 설명을 드리면, 4바이트(32비트)를 다 사용하고 있을 때는 EAX를 사용하고, 2바이트(16비트)만 사용할 때는 EAX의 하위 16비트 부분인 AX를 사용하면 됩니다. AX는 다시 상위 1바이트(8비트)인 AH와 하위 1바이트(8비트)인 AL로 나뉘어집니다. 이런식으로 32비트 레지스터를 상황에 맞게 크기에 맞게 나누어 알뜰하게 사용할 수 있습니다.

 

각 레지스터의 이름입니다.

 - 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등) 명령어에서 상수/변수 값의 저장 용도로 많이 사용됩니다. ECX와 EAX는 특수한 용도로도 사용됩니다. ECX는 반복문 명령어(LOOP)에서 반복 카운트(loop count)로 사용됩니다.(루프 돌 때마다 1씩 감소) EAX는 일반적으로 함수 리턴 값에 사용됩니다. 모든 Win32 API함수들은 리턴 값을 EAX에 저장한 후 리턴합니다.

(참고 : Win32 API 함수들은 내부에서 ECX와 EDX를 사용합니다. 이런 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는 스택 메모리 주소를 가리킵니다. 스택 메모리 관리는 프로그램에서 매우 중요하기 때문에 ESP를 다른 용도로 사용하지 말아야 합니다. EBP는 함수가 호출되었을 때 그 순간의 ESP를 저장하고 있다가, 함수가 리턴하기 직전에 다시 ESP에 값을 되돌려줘서 스택이 깨지지 않도록 합니다. 이것을 Stack Frame 기법이라고 합니다. ESI와 EDI는 특정 명령어들(LODS, STOS, REP MOVS등)과 함께 주로 메모리 복사에 사용됩니다.

 

 

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

 

    세그먼트 레지스터를 이해하기 위해 세그먼트에 대해 먼저 설명하겠습니다. 세그먼트는 IA-32의 메모리 관리 모델에서 나오는 용어입니다. IA-32보호모드에서 세그먼트메모리를 조각내어 각 조각마다 시작주소, 범위, 접근권한 등을 부여해서 메모리를 보호하는 기법을 말합니다. 또한 세그먼트는 페이징 기법과 함께 가상 메모리를 실제 물리 메모리로 변경할 때 사용됩니다.

 

세그먼트 메모리는 Segment Descriptor Table(SDT)이라는 곳에 기술되어 있습니다. 세그먼트 레지스터는 바로 이 SDT의 index를 가지고 있는것입니다.

 

세그먼트 레지스터는 총 6개(CS, SS, DS, ES, FE, GS)이며 각각의 크기는 16비트(2바이트)입니다.

 - 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 세그먼트는 추가적인 데이터 세그먼트입니다.

 

 

   3) 프로그램 상태와 컨트롤 레지스터(Program Status and Control Register)

 

    - EFLAGS : Flag Register

 

    플래그 레지스터의 이름은 EFLAGS이며 32비트(4바이트) 크기입니다. (이역시 앞에 'E'가 붙어있어서 기존 16비트에서 32비트로 확장된 형태를 의미합니다.)

 

<EFLAGS Register>

 

 

위 그림은 EFLAGS 레지스터이며 각각의 비트마다 의미를 가지고 있습니다. 각 비트는 1 또는 0의 값을 가지는데, 이는 On/Off 혹은 True/False를 의미합니다. 일부 비트는 시스템에서 직접 세팅하고, 일부 비트는 프로그램에서 사용된 명령의 수행 결과에 따라 세팅되기도 합니다.

 

리버싱 입문단계에서는 애플리케이션 디버깅에 필요한 3가지 Flag(ZF, OF, CF)에 대해서만 잘 이해하시면 됩니다.

 

 - Zero Flag(ZF)

   연산 명령 후에 결과 값이 0이 되면 ZF가 1(True)로 세팅됩니다.

 

 - Overflow Flag(OF)

   부호 있는 수(Signed integer)의 오버플로가 발생했을 때 1로 세팅됩니다. 그리고 MSB(Most Significant Bit)가 변경되었을 때도 1로 세팅됩니다.

 

 - Carry Flag(CF)

   부호 없는 수(unsigned integer)의 오버플로가 발생했을 때 1로 세팅됩니다.

 

 

   4) Instruction Pointer

 

    - EIP : Instruction Pointer

 

    Instruction Pointer는 CPU가 처리할 명령어의 주소를 나타내는 레지스터이며, 크기는 32비트(4바이트)입니다. (이역시 16비트에서 32비트로 확장 되었습니다.) CPU는 EIP에 저장된 메모리 주소의 명령어(instruction)를 하나 처리하고 난 후 자동으로 그 명령어 길이만큼 EIP를 증가시킵니다. 이런 식으로 계속 명령어를 처리해 나갑니다.

 

범용레지스터와는 다르게 EIP는 그 값을 직접 변경할 수 없도록 되어 있어서 다른 명령어를 통하여 간접적으로 변경해야 합니다. EIP를 변경하고 싶을 때에는 특정 명령어(JMP, Jcc, CALL, RET)를 사용하거나 인터럽트(interrupt), 예외(exception)를 발생 시켜야 합니다.

 

 

 

이렇게 간단하게 레지스터들에 대해서 간략히 살펴보았습니다. 레지스터는 디버깅에 기초가 되는 어셈블러 명령어에 의해 조작되기 때문에 레지스터를 잘 알면 디버깅을 하는 데 크게 도움이 될 것 입니다.

 

 

참고서적 : 리버싱 핵심 원리 / 저자 : 이승원

':: 리버싱' 카테고리의 다른 글

Red_Seek :: 스택 프레임  (0) 2014.01.29
[Red_Seek] abex' crackme #1 (크랙미 #1)  (0) 2014.01.23
[Red_Seek] 스택  (0) 2014.01.22
[Red_Seek] 엔디언 표기법  (0) 2014.01.09
[Red_Seek] 리버싱(Reversing) 이란?  (0) 2014.01.08
posted by Red_Seek