2016. 2. 11. 15:46

Chapter 49 IA-32 Instruction


Instruction이란 쉽게 말해서 CPU가 알아들을 수 있는 '기계어(Machine Language)'이다. IA-32 Instruction은 IA-32(Intel Architecture 32비트) 계열의 CPU에서 사용되는 Instruction을 말하는 것이다.


다음은 리버싱에서 자주 사용되는 용어이다.


C/C++(또는 어셈블리) 언어를 이용하여 PE(Portable Executable) 파일을 생성하면 소스코드는 기계어를 변환된다. 리버서는 이러한 기계어를 해석하여 동작원리를 파악하는 사람이다. 하지만 2진수로 표현된 기계어를 사람이 그냥 보는 것은 너무 힘든 작업이다. 그래서 보통 16진수로 변환해서 본다. 16진수로 보면 가독성은 좋아지지만, 아직 사람이 코드를 인식하기에는 무리가 있다. 그래서 최종적으로 (디버거에 탑재된) Disassembler를 이용하여 기계어를 디스어셈블리 코드로 변환한다.


다음은 여태 익히 보아오던 Ollydbg의 화면이다. Ollydbg에는 IA-32용 Disassembler가 탑재 되어있다.


위 그림을 보면 한줄에 하나의 명령어(Instruction)가 표시되어 있다. (A) 영역이 기계어로 표시된 IA-32 Instruction이다. 그리고 그에 해당하는 각각의 디스어셈블리 코드가 (B)에 나타나있다. 메모리(혹은 파일)에서는 실제로 (C)처럼 되어있다.


이런식으로 디버거에 탑재된 Disassembler는 (C) 영역의 Hex code를 해석하여 Instruction 단위로 쪼개서 표시(A)하고, 각 Instruction별로 디스어셈블리 코드(B)를 표시한다. 사람 눈으로 보기에는 C<A<B 순으로 보기 좋으며, 리버서들은 보통 (B) 형태의 디스어셈블리 코드를 보면서 분석을 수행한다. 


 Decompiler는 기본적으로 Disassembler와 같은 개념이다. 다만 Disassembler는 무조건 디스어셈블리 코드로 변환 해 주는데 반해, Decompiler는 원본 소스코드 형태로 변환 시켜주는 차이가 있다.(해당 언어제 맞는 Decompiler를 사용해야함). 물론 원본 소스코드와는 차이가 있으나 기술이 발전하면서 그 차이가 좁혀지고 있다.


위는 IDA Pro의 Hex-Rays Decompiler 플러그인을 사용하여 기계어 코드를 C언어로 해석한 것이다. 기계어 코드가 C언어 소스코드로 해석 되었음을 알 수 있다.


IA-32 Instruction 포맷은 다음과 같다.


위 그림처럼 총 6개의 항목으로 구성 되어있다. 여기서 Opcode 항목은 반드시 존재해야하며, 나머지 항목은 옵션이다. 이제 위 6가지 항목에 대하여 간단히 알아 볼 것이다.


1.Instruction Prefixes

 Instruction Prefixes는 옵션 항목으로, 뒤에 특정한 Opcode가 나올 때 사용되어 Opcede의 의미를 보조하는 역할을 합니다. 간단한 예는 다음과 같다.


Prefix 항목은 1바이트 크기를 가진다. 


 2.Opcode

 Opcode는 필수 항목이며 실제적인 명령어를 나타낸다.


Opcode는 1~3바이트 크기를 가진다. 일반적인 응용프로그램 디버깅에서 주로 1바이트 크기의 Opcode가 대부분이고 가끔 2바이트 크기의 Opcode들이 있다. 3바이트 크기의 Opcode도 있으나 접하는 경우가 거의 없다. Opcode는 보통 Operand를 갖는 경우가 많다. Operand의 종류는 Register, Memory Address, Constant 이다. 이러한 Opcode의 Operand를 결정하기 위해 보통 ModR/M과 SIB가 뒤따라오는 경우가 있다. Opcode는 그 종류가 매우 많이 때문에 일반적으로는 Intel Manual의 Opcode Map을 보면서 해석을 해야 한다.


 3.ModR/M

 ModR/M은 옵션 항목으로, 주로 Opcode를 도와서 Operand를 설명(Operand의 개수, 종류[Registers, Address, Contant])하는 수단으로 사용된다.


ModR/M은 1바이트 크기를 가지며 다음 그림과 같이 비트 단위로 분리되어 사용된다.


4.SIB

 SIB(Scale-Index-Base)는 옵션 항목으로, ModR/M을 보조할 때 사용된다. Opcode의 Operand가 Memory Address인 경우 ModR/M과 함께 사용된다.


SIB는 1바이트 크기를 가지며 ModR/M과 같이 비트 단위로 분리되어 사용된다.


 5. Displacement

 Displacement는 옵션 항목으로, Opcode의 Operand가 Memory Address인 경우 Displacement(변위)를 나타낼 때 사용된다. 


Displacement의 크기는 1,2,4바이트로 다양하다.


 6.Immediate

 Immediate는 옵션항목으로, Opcode의 Operand가 Constant인 경우에 그 Constant를 Immediate라고 말한다.

Immediate의 크기는 1,2,4바이트로 다양하다.



이제 본격적으로 바이너리 코드를 어셈블리로 해석하는 실습을 할 것이다. 실습에 앞서 바이너리 코드를 어셈블리로 해석하기 위해서는 IA-32 Instruction에 대한 정확한 이해가 필요하다. IA-32에 대한 설명은 제조사인 Intel에서 제공한 공식 매뉴얼에 자세히 설명되어있다. 


http://www.intel.com/products/processor/manuals


위 링크로 접속하여 아래 두 파일을 다운 받으면 된다.

 Intel@ 64 and IA-32 Architectures Software Developer’ s Manuals Volume 2A.pdf

 Intel@ 64 and IA-32 Architectures Software Developer’ s Manuals Volume 2B.pdf


이제 본격적으로 IA-32 Instruction을 해석 하는 방법에 대해 학습 할 것이다.


1.Opcode Map

첫 실습 예제로 1바이트 Opcode를 골랐다.

 41    INC ECX

앞에서 다운 받으라고 한 Opcode 매뉴얼(혹은 Intel Manual Vol. 2B)의 'Table A-2. One-byte Opcode Map'을 보면 된다.


Instruction을 해석 할 대 가장 먼저 확인해야 할 것이 바로 Table A-2 One-byte Opcode Map이다. 찾으려 하는 Opcode 41을 4와 1로 쪼갠 후 Opcode Map에서 각각 행과 열로 하여 찾으면 된다. 이 경우 명령어는 INC이며, Operand는 ECX로 고정된다.(REX.B는 64비트 전용이므로 무시한다.) 따라서 Instruction 41은 INC ECX 명령어를 의미한다. 이것을 위에서 설명했던 IA-32 Instruction Format으로 표시하면 다음과 같다.


2. Operand

 이번에는 Operand의 형태를 파악하는 연습을 해 볼 것이다.

68 A0B44000 PUSH 0040B4A0

앞에서와 같은 방법으로 Instruction의 첫바이트 68을 Table A-2 Opcode Map에서 찾는다.


 위 그림을 보면 Opcode 68은 PUSH Iz라고 표시되어 있다. 즉 Operand를 하나 가지는 PUSH 명령어이다.

Operand 타입을 나타내는 Iz의 의미를 알아야 정확한 해석이 가능하다. 대문자 I는 Addressing Method를 의미하고 소문자 z는 Operand Type을 의미한다. 이들은 각각 앞에서 만든 Opcode 매뉴얼의 A.2.1 Codes for Addressing Method과 A.2.2 Codes for Operand Type에 설명되어있다. 다음은 자주 사용되는 Addressing Method를 정리한 것이다.

 Addressing Method를 의미하는 대문자 I는 Immediate이다. 상수의 크기를 알려면 Operand Type을 참고해야 한다. 다음은 자주 사용되는 Operand Type을 정리 한 것이다.


Operand Type을 의미하는 소문자 z는 32bit 모드에서 DWORD(32bit) 크기이다. 따라서 이 모든 정보를 종합하면 Opcode 68은 PUSH Iz 명령어 형식이고, Operand 형식을 나타내는 Iz 기호는 4바이트(32비트) 상수를 의미하므로 68 뒤의 4바이트(0040B440)를 더 읽어서 최종적으로 PUSH 0040B4A0으로 해석이 된다. IA-32 Instruction Format으로 표시하면 다음과 같다.








'리버싱핵심원리' 카테고리의 다른 글

Reverse Engineering Chap 48  (0) 2016.02.03
Reverse Engineering Chap 47  (0) 2016.02.03
Reverse Engineering Chap 46  (0) 2016.02.03
Reverse Engineering Chap 50-51  (0) 2016.02.03
Reverse Engineering Chap 21-22  (0) 2016.02.03
Posted by 키흐
2016. 2. 3. 18:40

Chapter 48 SEH

48.1. SEH

EEH Windows 운영체제에서 제공하는 예외 처리 시스템입니다.

48.2. SEH예제 실습

먼저 한 간단한 예제를 통해 SEH의 기본 동작 원리와 안티 디버깅에서 SEH가 어떻에 사용되는지 살펴보겠습니다.

이 예제 파일을 실행해 보고 아래 그림과 같이 메지지 박스를 나왔습니다.

이 프로그램은 정상적으로 실행된 것처럼 보이지만 사실 프로세스 내부에서 예외가 발생했습니다. 그런데 SEH를 통해 예외가 잘 처리되어서 정상 실행될 수 있습니다.

이 파일을 디버깅하고 그대로 실행하면 아래 그림과 같이 예외가 발생하면서 실행이 중지되고 status창에서 경고문이 나타납니다.

401019주소에서 EAX 레지스터의 값은 0입니다. MOV DWORD PTR DS:[EAX], 1 명령어의 의미는 메모리 주소 0에 값1을 입력하는 것입니다. 그런데 주소 0이 할당되지 않은 공간인데 마음대로 접근할 수 없어서 예외가 발생했습니다.

그래서 Shift+f9 단축 키로 계속 진행해 보고 아래의 메시지 박스가 나타납니다.

이 메시지 박스를 보면 알 수 있는데 이와 아까 그냥 실행시켰을 때 나타난 메시지 박스가 다릅니다. 그래서 두 가지 실행 방법은 예외 처리하는 방식이 서로 다릅니다.

 

48.3. OS의 예외 처리 방법

48.3.1. 일반 실행의 경우 예외 처리 방법

프로세스가 실행할 때 예외가 발생하면 OS는 그 프로세스 자신이 예외를 처리하게 만듭니다. 포로세스 코드안에 SEH같은 예외 처리하는 코드가 있으면 예외를 잘 처리한 후 계속 진행할 수 있습니다. 그런데 SEH가 없으면 OS는 기본 예외 처리기를 동작시키고 프로세스를 종료시킵니다.

49.3.2. 디버깅 실행의 경우 예외 처리 방법

디버깅 중에 예외가 발생하면 OS는 먼저 디버거에게 처리를 맡깁니다. 그리고 디버거 실행이 중지되고 다른 방법을 취해야 디버깅을 계속할 수 있습니다. 아래는 자주 쓰는 몇 가지 방법입니다.

1) 예외 직접 수정: 코드, 레지스터, 메모리

이 경우에는 디버거를 통해서 문제가 발생한 코드, 메모리, 레지스터 등을 직접 수정하고 정상적인 진행할 수 있습니다.

2)예외를 디버기에게 넘겨서 처리

디버기 내부에 예외를 처리할 수 있는 SEH가 있으면 예외 통지를 디버기에게 보내서 자체 해결하게 만듭니다. 이와 일반 실행의 경우 똑같습니다.

3)OS 기본 예외 처리기

디버거와 디버기는 발생한 예외를 처리할 수 없으면 OS의 기본 처리기에서 처리하고 디버깅이 중지됩니다.

48.4. 예외

그 중에 대표적인 예외 다섯 가지가 있습니다.

1) EXCEPTION_ACCESS_VIOLATION(C0000005)

존재하지 않거나 접근 권한이 없는 메모리 영역에 접근할 때 발생하는 예외입니다.

2) EXCEPTION_BREAKPOINT(80000003)

실행 코드에 break point가 설치한 후 CPU가 그 주소를 실행하려 할 때 이 예외가 발생합니다.

3) EXCEPTION_ILLEGAL_INSTRUCTION(C000001D)

CPU가 해석할 수 없는 Instruction을 만날 때 발생하는 예외입니다.

4) EXCEPTION_INT_DIVIDE_BY_ZERO

정수 나눗셈 연산에서 분모가 0이면 이 예외가 발생할 것입니다.

5) EXCEPTION_SINGLE_STEP(80000004)

Flag 레지스터의 TF 비드를 1로 세팅하면 CPU Single Step 모드로 변경할 것입니다. CPU가 이 모드에서 명령어 하나를 샐행 후 이 예외를 발생시켜서 실행을 멈춥니다.

48.5. SEH 상세 설명

48.5.1. SEH체인

SEH는 체인 형태로 구성되어 있습니다. 첫 번쩨 예외 처리기에서 예외를 처리하지 못하면 다음 예외 처리기로 예외를 넘거줍니다.

아래의 코드는 SEH로 구성된 구조체입니다.

Next 멤버는 다음 구조체 포인터입니다.

Handler 멤버가 예외 처리기 함수입니다.

48.5.2. 함수 정의

아래의 코드는 예외 처리기 함수 정의입니다.

4개의 파라미터를 받으며 EXCEPTION_DISPOSITION이라는 열거형을 리턴합니다.

1)    EXCEPTION_RECORD

아래의 코드는 처리기 함수의 첫 번째 파라미터인 EXCEPTION_RECORD 구조체 정의입니다.

ExceptionCode 멤버는 발생한 예외의 종류를 의미합니다.

ExceptionAdress 멤버는 예외가 발생한 코드 주소를 나타냅니다.

1)    CONTEXT

아래의 코드는 처리기 함수의CONTEXT 구조체 정의입니다.

CONTEXT 구조체는 CPU레지스터 값을 백업할 때 사용합니다. CPU가 다른 스레드를 실행하려면 레지스터들의 값을 현재 스레드의 CONTEXT 구조체에 백업해야 합니다. 그 후에 CPU가 다시 예전의 스레드를 실행할 때 CONTEXT구조체에 백업된 레지스터 값을 실제 CPU 레지스터에 덮어씁니다.

48.5.3. SEH설치 방버

C언어에서 __try, __except, __finally  키워드로 구현할 수 있습니다.

어셈블리 언어에서는 아래의 코드로 추가할 수 있습니다.









'리버싱핵심원리' 카테고리의 다른 글

Reverse Engineering Chap 49  (0) 2016.02.11
Reverse Engineering Chap 47  (0) 2016.02.03
Reverse Engineering Chap 46  (0) 2016.02.03
Reverse Engineering Chap 50-51  (0) 2016.02.03
Reverse Engineering Chap 21-22  (0) 2016.02.03
Posted by 알 수 없는 사용자
2016. 2. 3. 17:03

chap 47. PEB


47.1. PEB


<PEB 접근 방법>

TEB에 있는 멤버 중 TEB.ProcessEnvironmentBlock 은 PEB 구조체의 주소를 나타낸다. ProcessEnvironmentBlock 은 TEB 구조체 시작으로부터 30오프셋 만큼 떨어져 있다. TED 구조체는 FS 세그먼트 셀렉터가 가리키고 있다. 따라서 다음과 같이 표현할 수 있다.

위의 공식을 어셈블리어로 표현하면 다음 두가지 방법으로 나타낼 수 있다. (두가지 방법 모두 TEB.ProcessEnvironmentBlock 멤버를 참조하고 있음)




47.2. PEB 중요 멤버 설명

PEB의 내용은 OS에 따라서 달라지고 멤버가 많기 때문에 리버싱에 있어서 중요한 멤버들만 살펴본다.


<PEB.BeingDebugged>

Kernel32!IsDebuggerPeresent()라는 API가 있다. 이 API는 현재 프로세스가 디버깅을 당하는지를 판단하여 0 또는 1을 리턴하는 함수이다. 이 함수가 참조하는 PEB의 멤버가 바로 PEB.BeingDebugged이다. (IsDebuggerPresent()는 Windows7에서는 Kernelbase.dll에 구현되어 있고, Windows XP 이하에서는 Kernel32.dll에 구현되어 있음) 이 값은 주로 리버싱 분야에서 안티 디버깅에 쓰인다. 예를 들어 이 값을 조사하여 디버깅중이라고 판단되면 프로세스를 종료시킨다.

Kernel32!IsDebuggerPresent()함수를 Ollydbg로 살펴보면 다음과 같다.

위의 두 줄을 통해 PEB의 주소를 얻고 세번째 줄에서는 +002 만큼 이동하여 PEB.BeingDebugged 멤버를 참조하는 것을 알 수 있다.

실제로 notepad.exe를 실행시켜서 첫 줄을 "MOV EAX,DWORD PTR FS:][30]"으로 바꾼 뒤 한 줄 실행시키면 EAX에 PEB 주소가 들어오며, hex dump에서 PEB를 살펴보면 +002 위치(PEB.BeingDebugged 멤버)에 1이 들어있는 것을 확인할 수 있다.


<PEB.ImageBaseAddress>

* ImageBase : PE파일이 메모리에 로딩되는 시작 주소

* GetModuleHandle() API : 모듈의 이름을 인자로 받아서 해당 모듈의 handle을 리턴하는 함수

Kernel32!GetModuleHandleA() API 내부를 살펴보면 다음과 같다.

PEB를 찾은 뒤 +008에 있는 ImageBase 멤버를 참조하는 것을 알 수 있다.


<PEB.Ldr>

PEB.Ldr 멤버는 _PEB_LDR_DATA 구조체의 포인터이다.

PEB.Ldr을 통해 프로세스에 로딩된 모듈(DLL)의 로딩 베이직 주소를 직접 구할 수 있다.

_PEB_LDR_DATA 구조체는 다음과 같다.

이 구조체에서 InLoadOrderMoudleList, InMemoryOrderModuleList, InInitializationOrderModuleList 세개의 멤버는 _LIST_ENTRY 구조체 타입이다. _LIST_ENTRY 구조체는 프로세스에 로딩된 DLL모듈마다 각각 생성성되며 양방향 연결 리스트로 연결된다. 즉, _PEB_LDR_DATA 구조체에서 알 수 있듯이 모듈들의 연결 방식을 세가지 지원한다.

_LIST_ENTRY 구조체는 다음과 같다.

_LIST_ENTRY 를 통해서 연결 리스트를 제공하고, 저장되는 정보는 _LDR_DATA_TABLE_ENTRY 구조체이다.


<PEB.ProcessHeap & PEB.NtGlobalFlag>

PEB.ProcessHeap 과 PEB.NtGlobalFlag 는 안티 디버깅에서 주로 쓰인다.

PEB.ProcessHeap은 HEAP 구조체를 가리키는 포인터이다.

HEAP 구조체는 다음과 같다.

이 중 Flags와 ForceFlags는 디버깅 중 특정한 값으로 세팅된다.

PEB.NtGlobalFlag는 0x70으로 세팅된다. (윈도우 XP 에서만 효과가 있는 기법이며, 실행중인 프로세스를 attach시킨 경우에도 이 특성이 나타나지 않음)

'리버싱핵심원리' 카테고리의 다른 글

Reverse Engineering Chap 49  (0) 2016.02.11
Reverse Engineering Chap 48  (0) 2016.02.03
Reverse Engineering Chap 46  (0) 2016.02.03
Reverse Engineering Chap 50-51  (0) 2016.02.03
Reverse Engineering Chap 21-22  (0) 2016.02.03
Posted by 알 수 없는 사용자