abstract

IA-32(x86) CPU 의 Basic program execution registers 에 대해 알아봅니다.

* Intel 에서 제공하는 "IA-32 Architectures Software Developer's Manual" 의 설명을 참고 하였으며,
  사용된 모든 이미지 역시 같은 manual 에서 가져온 것입니다.

* 출처 :
http://www.intel.com/products/processor/manuals/index.htm



Goal

어플리케이션의 디버깅을 위해 기본적으로 알아야할 CPU 레지스터들을 이해합니다.



Prologue

리버싱 초급 단계에서 어플리케이션 디버깅을 잘하려면
디버거가 해석(디스어셈)해주는 어셈블리 명령어를 공부해야 합니다.

IA-32(Intel Architecture 32-bit) 에서 제공하는 어셈블리 명령어는
매우 방대한 양이라서 한번에 공부하기 쉽지 않습니다.

저는 디버깅을 해나가면서 모르는 명령어가 나올때마다
Intel 에서 제공한 매뉴얼에서 해당 명령어 설명을 보고 그때 그때 공부해나갔습니다.

잊어버리면 새로 찾아보는 방식으로 자꾸 보게되니 이제는 많은 명령어를 알게 되었습니다.

그 당시 어셈블리를 처음 배울때 가장 궁금했던 내용은 바로 register 였습니다.
어셈블리 명령어의 대부분은 레지스터를 조작하고 그 내용을 검사하는 것들인데,
정작 레지스터를 모르면 명령어 자체도 이해하기 힘들기 때문이지요.

본 문서는 어플리케이션 디버깅을 할때 필수적으로 알아야할 레지스터들에 대해서 설명하고 있습니다.

* 어셈블리 명령어에 대한 자세한 예제는 나중에 'Assembly 기본 명령어' 에서 다루도록 하겠습니다.
* 여기서는 일단 IA-32 만 다루고, 향후 IA-64 관련 내용도 한번 정리해 보도록 하겠습니다.



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 를 알면 됩니다.
중/고급으로 올라가면 Control registers, Memory management registers, Debug registers 등을 더 공부하셔야 합니다.

본 문서에서는 Basic program execution registers 만 다루도록 하겠습니다.

* 나머지 register 들은 CPU 와 OS 에 대해서 깊이 있는 공부를 할 때 필요한 내용입니다.
   나중에 OS 내부구조와 커널 디버깅을 배울 때 자세히 살펴보도록 하겠습니다.



Basic program execution registers

먼저 Intel 에서 제공한 매뉴얼의 Basic program execution registers 설명 그림을 보겠습니다.


<Fig. 1>

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) 가 붙은 것들은 예전 16 bit CPU 부터 존재하던 레지스터들이었는데,
   32 bit CPU 에서 이름 앞에 E 를 붙여서 32 bit 로 확장 되었다는 것을 나타냅니다.

* 같은 이유로 64 bit CPU 에서는 E 대신 R 을 붙여서 64 bit 로 확장 되었음을 나타냅니다.



1. General Purpose Registers

이름 그대로 범용적으로 사용되는 레지스터들입니다.
IA-32 에서 범용 레지스터의 각 크기는 32 bits (4 Bytes) 입니다.

보통은 변수를 저장할 때 주로 사용되며, 특정 Assembly 명령어에서는 특정 레지스터를 조작하기도 합니다.
또한 어떤 레지스터들은 특수한 용도로 사용되기도 합니다.

아래 그림에 범용 레지스터들이 표시되어 있습니다.

<Fig. 2>


16-bit 하위 호환 때문에 <Fig. 2> 가 복잡하게 보이는데요, EAX 를 기준으로 설명드리겠습니다.

EAX : 32-bit
AX : EAX 의 하위 16-bit
AH : AX 의 상위 8-bit
AL : AX 의 하위 8-bit

각 레지스터들의 이름은 아래와 같습니다.

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 내부구조의 '보호모드' 와 '메모리 모델' 의 개념을 이해해야 합니다만,
본 문서에서는 일단 개념적으로만 간단히 설명드리고 넘어간 후 나중에 OS 내부구조를 공부할 때 자세히 설명하도록 하겠습니다.

IA-32 보호모드에서 세그먼트란 메모리를 조각내어
각 조각마다 시작주소, 범위, 접근 권한등을 부여해서 메모리를 보호하는 기법을 말합니다.

또한 세그먼트는 페이징(Paging) 기법과 함께 가상 메모리를 실제 물리적 메모리로 변경할 때 사용됩니다.

세그먼트 메모리는 segment descriptor table 이라고 하는 곳에 기술되어 있는데,
세그먼트 레지스터는 이 segment descriptor table 의 index 를 가지고 있습니다.

아래 그림은 보호모드에서의 세그먼트 메모리 모델을 나타내고 있습니다.

<Fig. 3>


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

<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 이며 32-bit (4 byte) 크기입니다.

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

* Flag 란 단어 그대로 깃발이 올라가면 1(On), 내려가면 0(Off) 으로 이해하시면 됩니다.

<Fig. 4>


32개 bit 의 의미를 전부 이해한다는 것은 매우 힘든 일입니다만,
어플리케이션 디버깅에 가장 중요한 3 가지 flag 를 잘 이해하도록 합니다.

Zero Flag

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

Overflow Flag

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

Carry Flag

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

* 처음에는 OF 와 CF 가 발생하는 조건이 상당히 헷갈리고, 예상대로 되지 않는 경우가 많습니다.
   직접 OllyDbg의 Assemble 명령어[Space]로 이런 저런 경우에 대해서 연습해보시기 바랍니다.



4. Instruction Pointer

EIP : Instruction pointer

CPU 가 처리할 명령어의 주소를 나타내는 레지스터이며, 크기는 32-bit(4 byte) 입니다.
CPU 는 하나의 명령(instruction)을 처리하고 난 후 자동으로 그 명령어 길이 만큼 EIP 를 증가시킵니다.

General Purpose Register 들과는 다르게 EIP 는 그 값을 직접 변경할 수 없도록 되어 있습니다.

EIP 를 변경하고 싶을 때는 특정 명령어(JMP, Jcc, CALL, RET), 인터럽트(interrupt), 예외(exception) 를 발생시켜야 합니다.



공부한 내용

General Purpose Registers : EAX, EBX, ECX, EDX, ESP, ESI, EDI, EBP
Segment Registers : CS, DS, SS, ES, FS, GS
Program Status and Control Register : EFLAGS (ZF, OF, CF)
Instruction Pointer : EIP



앞으로 더 공부할 내용

Control registers
Memory management registers
Debug registers
IA-32 Protected Mode, Segmentation, Paging



Epilogue

어플리케이션 디버깅에 필요한 IA-32 레지스터 들에 대해서 간략히 살펴보았습니다.

디버깅의 기초는 Assembly 명령어의 이해인데 그 명령어의 많은 부분은 레지스터를 조작하는 내용이라서
레지스터를 잘 알면 디버깅 하시는데 훨씬 도움이 되실 것입니다.

향후 실력이 쌓이면 고급 디버깅(커널 디버깅, anti-debugging)을 위해서
IA-32 Architecture 와 다른 레지스터들도 공부하시는 것이 좋습니다.

Trackback Address :: http://www.reversecore.com/trackback/7 관련글 쓰기

  1. 못다한꿈 2009/10/12 15:21 댓글주소 | 수정 | 삭제 | 댓글

    정말 잘읽고갑니다 많은 도움이 되었습니다! 고급 디버깅주제에관한 글도 올려주시면 고맙겠습니다!

  2. 헤헷 2009/11/02 03:30 댓글주소 | 수정 | 삭제 | 댓글

    정말 좋은글이네요. 잘읽고 갑니다<

  3. 베리굿 2009/11/07 22:12 댓글주소 | 수정 | 삭제 | 댓글

    좀 어렵지만 감사합니다.^^

  4. 극한의고독 2010/03/20 16:28 댓글주소 | 수정 | 삭제 | 댓글

    좋은 글 감사합니다. 자주 방문할것 같네요.....건강조심하시고....좋은 글 많이 부탁드립니다..

  5. Nillgun 2010/03/21 03:30 댓글주소 | 수정 | 삭제 | 댓글

    머리 정리가 잘 되지 않았었는데,
    글 보고 나서 산뜻하게 정리가 되는군요.
    자주 들려서 지식 많이 얻어갑니다. (;
    자주 들릴께요 ~

  6. hack4 2010/04/10 13:09 댓글주소 | 수정 | 삭제 | 댓글

    지금 몇권의 책과 ppt문서를 참조해가며 공부를 하는데도 머릿속이 복잡햇는데
    꽐라꽐라 어렵게 써놓은 책들에 비해 정말 이해하기 편하게 되어있네요 ^^
    덕분에 이해하고갑니다 ^^ 좋은글 너무 감사해요~~

  7. seso 2010/04/14 13:06 댓글주소 | 수정 | 삭제 | 댓글

    깔끔한 정리 감사합니다~ 잘 일고갑니다~

  8. 보름♬달★콤 2010/05/17 14:06 댓글주소 | 수정 | 삭제 | 댓글

    열심히 배우고 저도 이렇게 나누도록 할게요.
    감사합니다.

  9. Acaicat 2010/08/05 23:02 댓글주소 | 수정 | 삭제 | 댓글

    질문이 있는데요.
    세그먼트 레지스터에서 보면요.
    위에 전체적인 그림에서는 ss가 ds 다음에 있는데,
    아래의 그림에서 보면 왜 cs와 ds 사이에 있나요?

    • reversecore 2010/08/06 07:13 댓글주소 | 수정 | 삭제

      안녕하세요.

      위 그림들은 모두 Intel 에서 제공한 IA32 매뉴얼에서 발췌한 것입니다. 레지스터의 순서는 중요하지 않기 때문에 설명의 편의상 저렇게 그려놓은 것으로 생각됩니다.

      감사합니다.

  10. KR 2010/08/11 11:29 댓글주소 | 수정 | 삭제 | 댓글

    정리도 잘하신 것 같고 읽었을 때 이해가 잘 되네요
    감사히 보고 갑니다
    수고하세요 ^^

  11. Zero 2010/08/19 21:45 댓글주소 | 수정 | 삭제 | 댓글

    아주 잘 정리된 노트를 보는 기분이었습니다 .

    최고에요 !

  12. PRIDE 2010/08/20 09:39 댓글주소 | 수정 | 삭제 | 댓글

    정말도움이 많이됬습니다. 감사합니다.ㅎㅎ

  13. reversecore 2010/08/20 15:16 댓글주소 | 수정 | 삭제 | 댓글

    KR님, Zero님, PRIDE님 모두 감사합니다. ^^
    궁금하신점은 댓글 달아주세요~

  14. 분석팎 2010/10/13 14:55 댓글주소 | 수정 | 삭제 | 댓글

    요즘 이 블로그로 리버싱공부 하고있습니다.
    모랄까 그동안 리버싱을 하긴했지만 먼가 정립되지 않았던것을 다듬어 가는 기분이네요
    제일뒤에 어셈블리를이용한 code injetion 부터 보다가 뒤로뒤로 오다가 여기까지 왔네요
    ㅋㅋ

    • reversecore 2010/10/14 20:55 댓글주소 | 수정 | 삭제

      안녕하세요.

      네, 최신 글보다는 가장 오래된 글부터 차례로 읽어 오시는 것이 도움이 되실 것입니다. 의도적으로 그렇게 구성하였거든요.

      방문 감사합니다.

  15. checker 2010/12/30 00:07 댓글주소 | 수정 | 삭제 | 댓글

    역시 레지스터 어렵네요 ㅜㅜ

    • reversecore 2010/12/31 00:03 댓글주소 | 수정 | 삭제

      안녕하세요.

      처음에는 일단 general purpose register 8 개만 눈에 익혀두셔도 됩니다. 디버깅 하시다 보면 차츰 쓰임새를 알게 되실겁니다. ^^~

      감사합니다.

  16. Reverser_H 2011/01/19 23:17 댓글주소 | 수정 | 삭제 | 댓글

    정말 이해하기 쉽게 적어주셨습니다.
    보통 컴퓨터 서적들은 어려운 용어와 함께
    어렵게 설명해 놓았더군요;;

    간단한 개념인데도 말이죠 ㅎㅎ
    암튼 즐겁게 공부하다 갑니다 ㅎ

  17. mjxaone 2011/01/20 17:31 댓글주소 | 수정 | 삭제 | 댓글

    예전부터 리버싱에 관심을 갖고 있다가,
    reversecore님의 강좌로 리버싱의 세계에 뛰어들었습니다.

    정말 잘 정리해 주셨네요.
    저도 열심히 배워서 멋진 리버서가 되보겠습니다.!

    • reversecore 2011/01/25 23:06 댓글주소 | 수정 | 삭제

      안녕하세요~

      와~ 반갑습니다.

      벌써부터 동질감이 느껴집니다.

      우리나라의 리버서들은 모두 한 식구처럼 친했으면 좋겠어요~ ^^

      열심히 공부하셔서 훌륭한 리버서가 되시기 바랍니다.

      감사합니다.

  18. chimei 2011/01/24 19:33 댓글주소 | 수정 | 삭제 | 댓글

    잘 보고갑니다 레지스터 공부 중 우연히 들어오게됬는데 좋은 글이 많네요 .

  19. Fire 2011/03/03 11:59 댓글주소 | 수정 | 삭제 | 댓글

    학부때 컴퓨터 구조 시간에 배웠던건데
    그 땐 이게 뭔소리야!!! 하고 지나갔다가
    이렇게 잘 정리해주신 거 보니 머릿 속에 정리가 확 되네요 ㅎㅎㅎ
    정말 감사합니다ㅜㅜ
    저도 요즘 리버싱 공부중인데 자주 들리겠습니다 :)

  20. Rony 2011/03/14 11:31 댓글주소 | 수정 | 삭제 | 댓글

    글 잘읽었습니다! ^^

    근데 궁굼한것이 있습니다.

    함수내의 로컬변수를 할당하거나, 인자값을 임시로 저장할때도 스택을 사용하는것은 맞는데

    함수내의 로컬변수를 스택에 저장할때는
    mov dword ptr ss:[ebp-4] , 1 과 같이 push를 사용하지 않고 이렇게 직접주소지정방식으로 스택에 접근을 하는 반면

    인자값을 임시로 저장할땐

    mov eax, dword prt ss:[ebp-4]
    push eax
    와 같은 방식으로 사용해서 스택에 저장하는지 궁굼합니다.

    일반적으로, 원래 이렇게 쓰는건지

    아니면 특별한 이유가 있는건지 말이죠! ^^

    • reversecore 2011/03/16 22:30 댓글주소 | 수정 | 삭제

      안녕하세요.

      음... 로컬 변수와 함수 파라미터에 대해 개념을 잡아가는 중이신것 같습니다.

      가령 C 언어에서 int i = 5; 라는 코드를 어셈으로 표현하면 mov [esp-4], 5 이런 식의 코드가 될 것입니다. (대입 명령어 mov 사용)
      즉 이미 스택에 잡힌 공간에 특정 값을 '대입' 하기 때문에 mov 를 사용하는 것이지요.

      그리고 함수 호출시 파라미터 전달은 스택을 통해서 이루어 집니다. 그말은 값을 스택에 집어넣는다는 뜻이지요. (PUSH 명령어 사용)
      따라서 push 명령어로 스택에 값을 입력하는게 의미상으로 맞습니다.

      제 설명이 좀 어색한데요... 잘 이해안가시면 다시 질문 올려주시기 바랍니다.

      감사합니다.

  21. 금빛 star 2011/04/27 10:30 댓글주소 | 수정 | 삭제 | 댓글

    구글링하다가 들렸는데 좋은 글 너무 감사합니다.
    근데 한가지 질문입니다.
    cs ds ss등의 세그먼트 레지스터에는 언제 값이 설정되는건가요?

    • reversecore 2011/05/03 00:47 댓글주소 | 수정 | 삭제

      안녕하세요.

      커널에서 제어를 유저 프로세스의 EP 코드에 넘겨주기 전에 세팅하는 것이죠. 32 bit 에서는 이 값들이 모두 0 인거 아시죠?

      감사합니다.

    • 금빛star 2011/05/06 20:00 댓글주소 | 수정 | 삭제

      안녕하세요. 리버스코어님..
      한밤중에 답변주셨군요.
      다시 한번 감사드립니다.
      근데 워낙 무식한넘이라...
      cs , ds가 32bit에서 0이라면 쓰이지 않는다는 의미인가요? 필요없는건가요?

    • reversecore 2011/05/11 00:32 댓글주소 | 수정 | 삭제

      네, 32bit 에서는 쓰이지 않습니다.

  22. Noblesse Oblige 2011/06/01 00:37 댓글주소 | 수정 | 삭제 | 댓글

    정말 많은 검색과 책을 찾다가 왔는데,
    감사합니다ㅜㅜ 정리가 정말 잘되어있네요!!

  23. Marc 2011/06/26 00:38 댓글주소 | 수정 | 삭제 | 댓글

    정말 대단합니다.
    정리도 잘되있고
    공부하기에 전혀 부족함 없는...
    부럽습니다.
    저도 열심히 공부해서.....!

  24. STIH 2011/07/22 11:09 댓글주소 | 수정 | 삭제 | 댓글

    좋은글 감사합니다 . ㅎ

  25. CARBIN 2012/01/25 17:42 댓글주소 | 수정 | 삭제 | 댓글

    세그먼트 : 페이징을 사용하지 않는다면 선형주소는 그대로 XXXX가 됩니다.

    위 본문내용 확인좀 부탁드립니다.

  26. 쿠쿠님 2012/02/01 18:05 댓글주소 | 수정 | 삭제 | 댓글

    32비트 에서 64비트로 확장되면서 레지스터 이름앞에 R이 추가되었다고 하셨는데요.
    R의 약자는 무엇인가요??? 그리고 책 언제쯤 나오시나요???

  27. Synaps 2012/05/02 00:15 댓글주소 | 수정 | 삭제 | 댓글

    이제서야 이런글을 발견하다니.. ㅜ.ㅜ
    이제 리버싱을 막 접했는데 레지스터라니 ㅋㅋㅋ
    리버싱이 분석 같은건가요?


◀ PREV : [1] : ... [84] : [85] : [86] : [87] : [88] : [89] : [90] : [91] : [92] : ... [93] : NEXT ▶