PE 파일.docx


1. 스택프레임

// ESP가 아닌, EBP레지스터를 사용하여 스택 내의 로컬 변수, 파라미터, 복귀 주소에 접근하는 기법. 

왜?? ESP는 수시로 변경되기 때문에 ESP를 기준으로 프로그램을 만들면 정확한 위치를 참고할 때 CPU가 힘들어. 


예시>

PUSH EBP  // 함수 시작(EBP를 사용하기 전에 기존의 값을 스택에 저장)

MOV EBP, ESP // 현재 ESP(스택 포인터)를 EBP에 저장 

......

......

MOV ESP, EBP // ESP를 정리 (함수 시작했을 때의 값으로 복원시킴)

POP EBP // 리턴되기 전에 저장해 놓았던 원래 EBP 값으로 복원

RETN  // 함수 종료 




call 02_hello.00401000 // add() 함수 



PUSH EBP // EBP값을 스택에 넣어라. EBP가 메인함수에서 베이스 포인터의 역할을 하게 되니깐 EBP가 이전에 갖고 있던 값을 스택에 백업하기 위한 용도
MOV EBP,ESP // ESP의 값을 EBP로 옮겨라. 즉, EBP는 ESP와 같은 값을 갖게됨.
SUB ESP,8 // long은 4byte이며, a,b 2개이므로 8byte의 공간 확보
MOV DWORD PTR SS:[EBP-4],1 // a의 1을 [EBP-4]에 저장.
MOV DWORD PTR SS:[EBP-8],2 // b의 2를 [EBP-8]에 저장.
MOV EAX,DWORD PTR SS:[EBP-8] // [EBP-8]주소에서 4byte 크기의 메모리 내용을 EAX로 이동
PUSH EAX // b에 값인 2를 스택에 넣기
MOV ECX,DWORD PTR SS:[EBP-4] // [EBP-4]주소에서 4byte 크기의 메모리 내용을 ECX로 이동
PUSH ECX // a에 값인 1을 스택에 넣기
CALL 02_hello.00401000 // add()함수
ADD ESP,8
PUSH EAX ; /<%d>
PUSH 02_hello.004020F4 ; |format = "%d
"
CALL DWORD PTR DS:[<&MSVCR90.printf>] ; \printf
ADD ESP,8 // 스택공간 정리 (파라미터 a,b관련 공간)
XOR EAX,EAX // 리턴값을 받는 EAX 초기화
MOV ESP,EBP // 함수의 에필로그
POP EBP
RETN


< CALL 02_hello.00401000 >

PUSH EBP // main() 함수에서 사용되던 EBP값을 스택에 백업
MOV EBP,ESP // add() 함수의 프롤로그
SUB ESP,8 // 스택에 8byte의 공간확보 (로컬변수 a,b의 공간)
MOV EAX,DWORD PTR SS:[EBP+8] // 파라미터 a를 가리킴
MOV DWORD PTR SS:[EBP-8],EAX // a의 값인 1을 [EBP-8]에 넣음
MOV ECX,DWORD PTR SS:[EBP+C] // 파라미터 b를 가리킴
MOV DWORD PTR SS:[EBP-4],ECX // b의 값인 2를 [EBP-4]에 넣음
MOV EAX,DWORD PTR SS:[EBP-8] // 1을 EAX에 넣음
ADD EAX,DWORD PTR SS:[EBP-4] // EAX에 2를 더해서 EAX는 3이 됨
MOV ESP,EBP // ESP를 원래대로 복원 (함수가 시작할때 ESP의 값을 EBP에 넣어두었다가 함수가 종료될 때 ESP를 원래대로 복원시키는 목적)
POP EBP





1. 스택(Stack)

// 높은 주소 -> 낮은 주소 (스택에 값을 입력하면 ESP가 감소하고, 값을 꺼내면 ESP는 증가한다)


- 함수 내의 로컬 변수 임시 저장

- 함수 호출 시 파라미터 전달

- 복귀 주소(return address) 저장




2. abex' crackme #1 분석 



* 어셈블리 명령어 설명

- PUSH : 스택에 값을 입력

- CALL : 지정된 주소의 함수를 호출

- INC : 값을 1 증가

- DEC : 값을 1 감소

- JMP : 지정된 주소로 점프

- CMP : 주어진 두 개의 operand 비교 ( SUB와 비슷하지만, CMP는 operand의 값이 변경되지 않고, 

                                                     EFLAGS 레지스터만 변경됨.  두 operand의 값이 동일 하면 ZF=1이다.)

- JE : 조건 분기(Jump if equal), ZF=1이면 점프 



* 스택에 파라미터를 전달하는 방법

// MessageBoxA() 함수를 호출하기 전에 4번의 PUSH 명령어를 사용하여 필요한 파라미터를 역순으로 입력.



------------------------------ c 언어로 변환 ------------------------------------------------


MessageBoxA(NULL, "Make me think your HD is CD-ROM.", "abex' 1st crackme", MB_OK | MB_APPLMODAL ); 


하지만, 받는입장에서 생각하면 Stack은 FIFO이기때문에 역순이 아닌 제대로 받는 것. 


 

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

PE구조 헤더  (0) 2019.03.20
Reversing - 스택프레임  (0) 2018.12.26
Reversing - 바이트 오더링, IA-32 Register 기본 설명  (0) 2018.12.13
Reversing - Hello World!  (0) 2018.12.08
Reversing - 시작<기본>  (0) 2018.12.08

1. 바이트 오더링(Byte Ordering) 

// 데이터를 저장하는 방식.



1) 빅 엔디언(Big Endian) 

// 사람이 보는 것과 동일, 앞부터 순차적으로 저장.

ex) 대형 UNIX서버(RISC계열의 CPU)



2) 리틀 엔디언(Little Endian)

// 데이터를 역순으로 저장.  

- 리틀 엔디언에서 2byte(WORD), 4byte(DWORD)와 같은 멀티 바이트인 경우 역순으로 저장

- 리틀 엔디언에서 2byte 미만 , 문자열(str은 곧 char이기에)은 빅 엔디언처럼 앞부터 순차적으로 저장.  

ex) Windows 계열 Intel x86 CPU 


2. IA-32 Register 기본 설명

* 레지스터 (Register)
// CPU내부에 존재하는 다목적 저장 공간. 

* Basic program execution registers
1) General Purpose Registers(32bit - 8개)
// 범용레지스터

- EAX (Extended Accumulator Register)
// 산술, 논리 연산 수행시 사용되는 레지스터 , 함수 리턴 값에 사용(리턴 값은 EAX에 저장한 후 리턴)
   EAX - 32bit, 반 쪼개면 AX - 16bit , 또 다시 반 쪼개면 AH, AL(각 8bit)

- EBX (Extended Base Register)
// 메모리 주소를 저장하기 위한 용도의 레지스터 

- ECX (Extended Count Register)  
// 반복 명령어(for, while 등) 사용시 카운터로 사용되는 레지스터 

- EDX (Extended Data Register)
// EAX와 함께 쓰이며, 부호 확장 명령이나 큰수의 곱셉/나눗셈 연산에 사용되는 레지스터

-------------------------- 산술 연산 명령어에서 상수/변수 값의 저장 용도로 사용 -------------------------

 - ESI (Extended Source) / EDI (Extended Destination Index)
// ESI는 데이터 조작/복사 시 데이터의 주소가 저장.
   EDI는 복사할 때 목적지의 주소가 저장. 

----------------------------------- 메모리 복사에 사용 -----------------------------------------------------------

- EBP ( Extended Base Pointer) / ESP (Extended Stack Pointer)
 // ESP는 Stack의 끝 지점 주소를 가리킴, 명령어(PUSH, POP, CALL, RET)들을 직접 조작
    EBP는 Stack의 첫 시작 주소를 가리킴, 함수가 호출되었을 때 그 순간의 ESP를 저장하고 있다가 함수가 
             리턴하기 직전에 다시 ESP에 값을 되돌려줘서 스택이 깨지지 않도록 함.(Stack Frame 기법) 

----------------------------- 메모리 주소를 저장하는 포인터로 사용 ----------------------------------------


2) Segment Registers(16bit - 6개)
// CS, DS, SS, ES, FS, GS


3) Program Status and Control Register (32bit - 1개)
// EFLAGS, 종류가 많지만 기초적인 단계에서는 3가지만 알기
   Flag는 단어 그대로 깃발이 올라가면 1(TRUE/On), 내려가면 0(False/Off) 

- Zero Flag(ZF)
// 연산 명령 후에 결과 값이 0이면 -> ZF = 1 세팅

- Overflow Flag(OF)
// 부호 있는 수의 오버플로우 발생/ MSB(Most Significant Bit) 변경 시  -> 1로 세팅  

- Carry Flag(CF)
// 부호 없는 수의 오버플로가 발생했을 때 -> 1로 세팅 


4) Instruction Pointer(32bit - 1개)
// EIP 

- EIP (Instruction pointer)
// cpu가 처리할 명령어의 주소를 나타내는 레지스터,  명령어를처리 후 자동으로 그 명령어 길이만큼 EIP를 증가시킴. 







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

PE구조 헤더  (0) 2019.03.20
Reversing - 스택프레임  (0) 2018.12.26
Reversing - 스택(Stack), abex' crackme #1 분석  (0) 2018.12.13
Reversing - Hello World!  (0) 2018.12.08
Reversing - 시작<기본>  (0) 2018.12.08

1. Hello World! 프로그램


#include "windows.h"
#include "tchar.h"
int _tmain(int argc, TCHAR *argv[])
{
MessageBox(NULL,
L"Hello World!",
L"www.reversecore.com",
MB_OK);
return 0;
}

// 릴리즈 모드로 빌드하기.

c언어(HelloWorld.cpp) ---- <빌드> -----> 실행파일(HelloWorld.exe)// 기계어 ------ <디버거> ----> 어셈블리어



2. 디버깅 시작
// Immunity Debugger 사용

 Code Window 

Register Window 

Dump Window 

Stack Window 


- 사용법

// Restart (ctrl+F2)

Step Into (F7) : CALL함수를 만나면, 함수 코드 내부로 따라 들어감.

Step Over (F8) : CALL함수를 만나면, 따라 들어가지 않고, 함수 자체를 실행.

Execute till Return (Ctrl+F9) : 함수 코드 내에서 RETN 명령어까지 실행(함수 탈출 목적)

Go to(Ctrl+G) : 원하는 주소로 이동

Comment (;) : comment 추가

Set/Reset Break Point (F2) : BP 설정/해제

Run (F9) : 실행(BP가 걸려있으면 그곳에서 실행이 정지됨)

Show the previous Cursor (-) : 직전 커서 위치를 다시 보여줌.



* EP(Entry Point)

// Windows 실행 파일(EXE, DLL, SYS)의 코드 시작점을 의미.

address Instruction      Disassembled code                           comment


- address : 가상 메모리내의 주소

- instruction : cpu 명령어

- Disassembled code : OP code를 보기 쉽게 어셈블리로 변환한 코드 (OP code란 Operation Code)

- comment : 디버거에서 추가한 주석



* BP(Break Point)

- view -> Breakpoints // BP목록 보기(Alt+B)



3. main() 찾기

- 경험과 노하우를 통한,,

// push , push, call...~

call에는 메인함수의 주소가 들어있다.



- 문자열 검색 방법

// code window 부분에서 우클릭 -> Search for -> All referenced text strings -> 이동하고자 하는 문자열을 더블클릭




// 제대로 간지 확인법> dump window 에 ctrl+G -> push 뒤에 있는 주소 입력 (0040211c)

-> dump window보면 아스키에 helloworld!가 찍혀 있음.




- API 검색

// 우리가 사용한 MessageBox()를 이용해서 찾는 것.

// Search for -> All intermodular calls -> 원하는 것 더블클릭하면 이동



- 문자열 패치1 <원본문자열의 크기와 수정문자열의 크기가 같거나 작은 것>

만약,,, 크다면,,, 이거는 뒤에 있는 함수에 영향을 주기때문에 프로그램 에러가 발생.

// 바꾸고자 하는 문자열을 드래그 후 ctrl+E (문자열 패치)


// 문자열이 바뀐 것을 확인할 수 있다. 하지만 이건 임시적으로 바꾼 것이다.

따라서, Restart를 하면 처음 상태 그대로 문자열이 helloWorld가 나온다.



// 임시적인게 아니라 저장하고싶다??

저장하고자 하는 문자열 드래그 -> 우클릭 -> Copy to executable file



// hex파일에 드래그 된 부분 우클릭 -> save file



- 문자열패치2 <원본문자열보다 수정문자열이 크기가 더 큰 경우>


// 맨 밑에 널패딩으로 가득한 곳을 찾는다. 그 주소 00401810을 dump window에서 ctrl+G로 검색

// 검색해서 쓰고싶은 만큼 드래그 -> ctrl+E -> 문자열 입력.



// 아까 위에있던 원본 문자열 push하는 부분에서 주소값을 널패딩으로 가득했던..

내가 새로 입력한 문자열 주소로 바꾼다. 주소를 바꾸기 위해서는 바꾸고자하는 줄을 더블클릭해준다.


// 바꾸고, 그 부분에 BP를 추가하여 Run한다. 그럼 내가 바꾼 문자열로 실행되는 것을 볼 수 있다.



* 깨알용어정리

- VA(Virtual Address) : 프로세스의 가상 메모리

- OP code (Operation code) : cpu 명령어(바이트 code)

- PE(Portable Executable) : Windows 실행파일(EXE, DLL, SYS 등)




1. RCE

// Reversing Code Engineering(역공학)이라고 하며, 시스템의 구조, 기능, 동작 등을 분석/ 원리 이해 


2. RCE 방법

1) 정적 분석 <파일을 실행 X>

//  파일의 종류, 크기, 헤더정보, 내부 문자열, 실행 압축 여부, 등록 정보, 디버깅 정보 등의 다양한 내용을 확인하는 것.


2) 동적 분석 <파일을 실행>

// 디버깅을 통하여 코드 흐름과 메모리 상태보기, 파일/레지스트리/네트워크 행위 분석, 

    디버거를 이용하여 프로그램 내부 구조와 동작 원리 분석.



 * Immunity Debugger //어셈블리어 관찰 


* HxD // HEX(16진수)로 보기  


* vitual studio 2008 // c 및 c++로 코딩 


이 외에도 레지스트리를 관찰하는 Process Moniter

              dll 분석을 위한 BinText 등이 있다. 


+ Recent posts