레지스터

아래 그림은 Ollydbg라는 디버깅 도구를 통해 확인한 레지스터 화면이다

Ollydbg에서 제공하는 레지스터 정보 창

레지스터의 총 개수는 잘 사용되지 않는 디버그(8개)와 제어(8개), FPU(8개) 레지스터를 제외하고 일반적으로 16개라 생각하면 된다. 이 레지스터는 CPU 개발사에 따라 달라지는데 기본적인 범용, 인덱스, 포인터, 세그먼트, 플래그 레지스터는 공통으로 쓰이기에 유지하게 때문이다. 좀 더 설명하자면, 인텔은 MMX, AMD는 3D Now!라는 기술에 이용되는 레지스터를 독립적으로 가지고 있듯이, CPU 제조사에 따라, 개발연도에 따라 레지스터가 달라지게 된다. 그리고 레지스터는 CPU에 있고, CPU의 동작 비트에 따라 크기가 달라지는데, 64비트/32비트 머신을 구분하는 기준이 바로 이 레지스터가 한번에 처리할 수 있는 레지스터 비트 수를 의미한다. 즉 한번에 레지스터 별로 저장 가능한 공간의 크기를 통해 32비트인지 64비트인지를 구분하게 된다(32비트 머신의 메모리가 4기가바이트인 이유도 32비트 레지스터가 한번에 표현할 수 있는 주소공간이 4기가바이트이기 때문이다).

32비트 컴퓨터가 나오기 전에 16비트 컴퓨터는 0~15비트까지 레지스터 공간을 제공했다. 그리고 각 레지스터에 사용 방식에 따라 이름을 통해 구분하였고 32비트로 환경이 변화하면서 ‘확장되었다’는 의미로 ‘E'(Extension)를 각 레지스터의 이름에 추가하였다.

본격적으로 각각의 레지스터에 대해 알아보기 전에 레지스터의 데이터 단위에 대해 간단하게 집고 넘어가보자.

데이터는 다음과 같은 단위를 사용한다.

BIT:

데이터를 표현할 수 있는 제일 작은 단위로 0과 1로 구분한다. 2진수의 표현과 같다.

00000001= 1, 00000010= 2, 00000011= 3

BYTE:

8개의 비트가 모이면 하나의 바이트라 한다. 최대로 표현할 수 있는 값은 0xF(255)까지 표현이 가능하다. 레지스터에서 8비트와 대응하는 레지스터는 AL, AH, BL, BH, CL, CH, DL, DH가 있다.

11111111= 255

WORD:

2개의 바이트가 모이면 워드(WORD)라 하며, 16비트로 표현할 수 있다. 표현할 수 있는 최대값은 0xFF(65535)로, 16비트로 표현할 수 있는 레지스터는 AX, BX, CX, DX, SI, DI, BP, SP, IP가 있다.

11111111 11111111= 65535

DOUBLE WORD:

워드 2개가 모이면 더블워드(DOUBLE WORD)라 한다. 최대 0xFFFFFFFF (4294967295)까지 표현할 수 있으며, 32비트라 한다. 32비트 레지스터는 EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP, EIP가 있다.

11111111 11111111 11111111 11111111 = 4294967295

KILOBYTE:

킬로바이트(KILOBYTE)부터는 32비트의 제곱으로 값을 표현한다. 1024 (32*32) 바이트.

MEGABYTE:

킬로바이트의 제곱으로 값을 표현한다. 1,048,578(1024*1024) 바이트.

이러한 데이터 표현 중 레지스터는 비트 단위로 표현되며, 32비트 시스템에서는 32비트의 레지스터 공간을 가지게 된다. 아래 그림은 각 레지스터 별 비트의 크기이다. 아래 크기는 어셈블리 언어에서 레지스터 저장이나 로드에 사용되므로, 알아두기 바란다(어셈블리 명령 중 MOV AH, AL은 AL(8비트)을 AH로 복사하라는 어셈블리 명령 등이 사용된다)..

레지스터 공간 크기 구분

기본적으로 레지스터들은 아래 표와 같이 용도가 구분되어 있지만 개발자 이용 방식에 따라 다르게 사용 가능하며, 반드시 지킬 필요는 없다.

용도

레지스터

용도

범용

EAX

산술/논리 연산, 처리 결과 리턴값 저장

EBX

간접 주소 연산

ECX

카운터, 반복문 횟수

EDX

산술/논리 연산 보조

인덱스

ESI

문자열 출발지 주소

EDI

문자열 목적지 주소

포인터

ESP

현재 스택 주소

EBP

스택 복귀 주소

EIP

현재 명령 실행 주소

세그먼트

(Segment)

CS

코드(Code) 세그먼트, 코드 영역 시작 주소

DS

데이터(Data) 세그먼트, 데이터 영역 시작 주소

SS

스택(Stack) 세그먼트, 스택 영역 시작 주소

ES

데이터(Data) 세그먼트, 데이터 영역 시작 주소

FS

데이터 세그먼트, 데이터 영역 시작 주소

GS

데이터 세그먼트, 데이터 영역 시작 주소

상태

EFLAGS

CPU 동작 제어, 연산 결과를 1/0으로 반영

FPU 수치연산

ST0~7

부동 소수점 처리

레지스터 별 역활

64비트 레지스터

32비트 프로세서 구조에 대한 선점은 인텔이 하였으나 64비트는 AMD에서 선점하면서 AMD64가 표준이 되었다.

64비트 레지스터는 EAX는 RAX, EBX는 RBX로 R로서 구분하여 표시되며, 기존 4개로 사용되던 범용 레지스터에 R8~R15까지 8개의 새로운 레지스터를 추가해 더 많은 레지스터를 사용할 수 있다.

상태 레지스터인 EFLAGS 역시 RFLAGS로 되었으나, 하위 32비트는 기존 EFLAGS와 동일하다.

그리고 64비트에서는 레지스터에 파라미터를 직접 넣어줄 수 있다.

이외에도 몇몇 변경점에 대해서는 아래 링크를 통해 확인해 보기 바란다.

http://msdn.microsoft.com/en-us/library/windows/hardware/ff561499(v=vs.85).aspx

위 언급한 레지스터 외에도 디버깅 레지스터(DR0, DR1, DR2, DR3, DR6, DR7)와 제어 레지스터(CR0, CR2, CR3, CR4, CR8)가 있다. 이 레지스터들은 일반적인 역분석 상황에서는 사용되지 않고, 몇몇 레지스터가 커널 제어와 관련되어 이 책 후반부에 나오는 안티 디버깅과 같은 특수한 상황에서 사용된다. EFLAGS 레지스터(다른 레지스터와 마찬가지로 플래그(FLAGS)의 Extend라는 의미)의 시스템 제어용 플래그와 연동되어 설정되거나, 운영체제의 특정 기능 활성화(PAE와 같은), 이외에 CR0, CR4 레지스터는 차후 커널 후킹과 디버깅 확인 등에 사용되며, 이를 제외하고는 사용할 일이 많지 않다.

레지스터에서 CPU에 가장 많이 이용되는 것은 범용, 인덱스, 포인터 레지스터이다.

이는 CPU에서 자주 사용되는 레지스터로 곧 포스팅 될 어셈블리를 통해 이해할 수 있다. 따라서 여기서는 이러한 레지스터가 있다는 정도로 확인하고 지금은 데이터 위치를 표시하는 세그먼트 레지스터와 연산 결과를 상태로 구분하여 저장하는 EFLAGS 레지스터에 대해 자세히 알아보자.

EFLAGS 레지스터의 32비트의 각 비트 공간별로 의미를 가지는데 약어와 함께 설명하겠다(역분석에 자주 사용되는 플래그에 굵게 표시하였다).

구분

플래그

설명

연산 결과

CF(Carry flag) 덧셈과 뺄셈에서 빌림수(Borrow) 발생시 1로 설정
PF(Parity flag) 연산 결과가 짝수이면 1, 홀수면 0을 설정
AF(Auxiliary carry flag) 16(8)비트 연산시 빌림수(Borrow) 발생시 1로 설정
ZF(Zeor flag) 연산 결과가 0이면 1로 설정
SF(Sign flag) 연산 결과 최상위 비트가 1인 경우 1로 설정
OF(Overflow flag) 연산 결과가 용량을 초과하였을 경우 1로 설정

시스템 제어

TF(Trap flag) 프로그램 추적(Trace)시 1로 설정, 명령을 한 행씩 실행한다
IF(Interrupt enable flag) 외부 인터럽트 요구를 받아들일 때 1로 설정
AC(Alignment check) CR0 레지스터의 AM 비트와 함께 1로 설정하면 메모리 참조 시 정렬 체크를 활성화한다.
IOPL(I/O privilege level) 현재 특권 수준이 IOPL보다 높은 경우에 I/O 주소 접근, 11이 가장 낮음
NT(Nested task) 연결 작업 제어, 1로 설정 시 현재 작업이 기존 실행 작업과 연결됨을 의미함
RF(Resume flag) 디버깅시 프로세서에 일시 중지 예외 발생 제어
VM(Virtual-8086 mode) Virtual-8086 mode 활성화 시 1로 설정
VIF(Virtual interrupt flag) 가상 이미지의 인터럽트 요구를 받아들일 때 1로 설정됨, VIP와 함께 사용된다.
VIP(Virtual interrupt pending) 가상 모드 인터럽트가 지연 시 1로 설정
ID(ID flag) CPUID 명령어 지원 여부로 1로 설정 시 지원

문자열 제어

DF(Direction flag) 문자열 복사 명령과 관련된 제어 플래그, 1로 설정되어 있으면 문자열 복사 시 주소값이 감소

EFLAGS 레지스터의 각 비트별 플래그의 의미

EFLAGS 레지스터는 CPU 연산 결과에 따라 해당 비트에 0혹은 1로 표시하여 처리 결과를 저장하는 곳이다. 즉 이 플래그들은 CPU가 하는 더하기/빼기/나누기/곱하기가 아닌 비교와 같은 조건문 처리시 사용된다. 그 중 조건 문으로 제일 많이 등장하는 플래그는 ZF인데 이 플레그는 참, 거짓의 결과를 보여준다(프로그램 코드에서 IF문의 ==, != 입력시 실제적인 CPU 처리는 어셈블리의 비교 명령을 이용하는데, 이때 ZF 플래그를 참고하여 참, 거짓을 구분하는 것이다. 이 실습은 잠시 후 어셈블리에서 확인할 수 있다).

프로그램 코드 if 명령은 어셈블리 JNE 명령으로 CPU의 ZF 플래그 값을 참고해 분기

레지스터는 어셈블리가 이용하기 때문에 어셈블리와 함께 배워야 이해가 빠르다. 따라서 곧 포스팅 될 어셈블리에서 비교 문을 이해하면 쉽게 저 말들이 조금씩 와 닫게 될 것이다.

Facebook Comments

Leave A Reply

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.