이전 강좌에서 이어지는 내용입니다.
# MessageBoxA() 파라미터 입력 1 – MB_OK
002D002C 6A 00 PUSH 0
PUSH 0 은 스택에 0 을 입력하는 명령입니다. 이 0 의 의미는 아래에서 호출될 MessageBoxA() API 의 네 번째 파라미터(uType)로 사용됩니다.
참고로 MessageBoxA() API 는 아래와 같이 4 개의 파라미터를 받습니다.
int WINAPI MessageBox(
__in_opt HWND hWnd,
__in_opt LPCTSTR lpText,
__in_opt LPCTSTR lpCaption,
__in UINT uType
);
* 참고
uType 값이 0 이면 MB_OK 를 의미하며, 단순히 OK(“확인”) 버튼 한 개만 보여주게 됩니다.
# MessageBoxA() 파라미터 입력 2 – "ReverseCore"
002D002E E8 0C000000 CALL 002D003F
002D0033 52 PUSH EDX
002D0034 65:76 65 JBE SHORT 002D009C
002D0037 72 73 JB SHORT 002D00AC
002D0039 65:43 INC EBX
002D003B 6F OUTS DX,DWORD PTR ES:[EDI]
002D003C 72 65 JB SHORT 002D00A3
002D003E 00E8 ADD AL,CH
이번에는 CALL 명령으로 코드 사이에 포함된 문자열 데이터 주소를 스택에 입력하는 기법을 소개하겠습니다. 이 역시 Assembly 프로그래밍 언어에서만 가능한 기법입니다.
위 2D0033 ~ 2D003E 주소 영역은 분명히 프로그램 코드 영역이지만 그 내용은 사실 "ReverseCore" 문자열 데이터 입니다. (붉은색 표시 부분) 즉, "ReverseCore" 문자열의 시작주소는 2D0033 입니다. 그리고 이 문자열은 MessageBoxA() API 의 세 번째 파라미터 (lpCaption)로 사용됩니다.
함수 파라미터로 사용되려면 문자열 주소를 스택에 넣어줘야 하는데 과연 어떤 방식으로 입력할까요?
2D002E 주소의 CALL 002D003F 명령을 디버깅으로 쫓아 들어가 보겠습니다. (StepIn [F7]) 그리고 아래 그림과 같이 스택 주소를 봐주시기 바랍니다.
<Fig. 13>
스택에 "ReverseCore" 문자열 시작 주소인 2D0033 이 입력되었습니다!!! MessageBoxA() 의 세 번째 파라미터가 입력된 셈이죠.
이 트릭은 CALL 명령어의 "동작원리"를 응용한 것입니다.
CALL 002D003F 명령을 수행하면 함수(2D003F) 가 종료된 후 돌아올 리턴 주소(2D0033)를 스택에 입력(PUSH)한 후 해당 함수 주소(2D003F)로 이동(JMP)합니다. 즉, CALL 명령어는 PUSH, JMP 명령어를 합쳐 놓은 것입니다.
사실 2D003F 는 함수 형태가 아닙니다. RETN 명령어로 되돌아가는 형태가 아니란 얘기지요. 여기서의 CALL 002D003F 명령어는 바로 뒤에 이어지는 "ReverseCore" 문자열 주소를 스택에 입력하고 그 다음 코드 명령어로 가기 위해서 사용되고 있는 것입니다.
이해 되시나요? ^^ 재미있는 CALL 명령어 사용법입니다.
# MessageBoxA() 파라미터 입력 3 – "www.reversecore.com"
002D003F E8 14000000 CALL 002D0058
002D0044 77 77 JA SHORT 002D00BD
002D0046 77 2E JA SHORT 002D0076
002D0048 72 65 JB SHORT 002D00AF
002D004A 76 65 JBE SHORT 002D00B1
002D004C 72 73 JB SHORT 002D00C1
002D004E 65:636F 72 ARPL WORD PTR GS:[EDI+72],BP
002D0052 65: PREFIX GS:
002D0053 2E:636F 6D ARPL WORD PTR CS:[EDI+6D],BP
002D0057 006A 00 ADD BYTE PTR DS:[EDX],CH
역시 위 "ReverseCore" 문자열과 마찬가지로 MessageBoxA() API 의 두 번째 파라미터 lpText 문자열("www.reversecore.com")을 입력하는 명령입니다.
위 코드에서 빨간색으로 표시된 부분은 코드 명령어가 아니라 문자열 데이터("www.reversecore.com") 입니다.
2D003F 주소의 CALL 002D0058 명령어는 (앞에서 설명 드린 바와 같이) 바로 뒤에 이어지는 문자열("www.reversecore.com") 데이터의 주소(2D0044)를 스택에 입력하고 그 다음 명령어 주소(2D0058)로 갑니다. (아래 그림 참고)
<Fig. 14>
# MessageBoxA() 파라미터 입력 4 – NULL
002D0058 6A 00 PUSH 0
MessageBoxA() API 의 첫 번째 파라미터인 hWnd 값을 입력합니다. 일반적으로는 메시지 박스가 소속된 윈도우 핸들을 입력하지만, 여기서는 NULL 을 입력하여 무소속(?) 메시지 박스가 출력되도록 만들겠습니다.
# MessageBoxA(NULL, "www.reversecore.com", "ReverseCore", MB_OK) 호출
002D005A FFD0 CALL EAX
드디어 MessageBoxA() API 를 호출하는 CALL 명령어 입니다. 현재 EAX 레지스터에는 위에서 호출한 GetProcAddress() 에 의해서 리턴된 MessageBoxA() API 의 시작 주소(7793EA71)가 저장되어 있습니다. (<Fig. 12> 참고)
2D005A 주소의 CALL EAX 명령어까지 디버깅한 후 레지스터와 스택을 살펴보면 아래 그림과 같습니다.
<Fig. 15>
이 CALL EAX 명령어를 실행하면 메시지 박스가 나타날 것입니다.
<Fig. 16>
# ThreadProc() 리턴값 세팅
002D005C 33C0 XOR EAX,EAX
notepad.exe 프로세스에 인젝션된 코드(ThreadProc() 스레드 함수) 가 종료될 준비를 합니다. 스레드 함수의 리턴값을 0 으로 세팅하기 위해서 XOR EAX, EAX 명령어가 사용됩니다. 함수의 리턴값은 EAX 레지스터를 사용한다는 것을 기억 하시죠?
* 참고
XOR EAX, EAX 명령어는 EAX 레지스터를 0 으로 초기화하는 가장 쉽고 빠른 명령어 입니다. 디버깅하면서 많이 접하게 될 것입니다.
# Stack Frame 해제 및 함수 리턴
002D005E 8BE5 MOV ESP,EBP
002D0060 5D POP EBP
002D0061 C3 RETN
ThreadProc() 함수 시작할 때 생성한 Stack Frame 을 해제 합니다. 그리고 RETN 명령으로 함수가 종료됩니다.
이 ThreadProc() 함수에서 Stack Frame 은 매우 중요합니다. 앞에서 설명 드린 "PUSH 를 이용한 스택에 문자열 넣는 기법" 에서 스택에 입력된 문자열을 일일이 POP 명령으로 힘들게 없앨 필요 없이 Stack Frame 해제 명령어 한방으로 가볍게 초기화 시킬 수 있습니다.
* 참고
Stack Frame 관련 내용은 아래 링크를 참고하세요~
+--+
Assembly 언어를 이용한 Code Injection 에 관한 설명을 마치도록 하겠습니다. C 언어 보다 더 자유로운 Assembly 언어를 사용하여 다양하고 창의적인 코드를 생성해 보시기 바랍니다. Assembly 초보자도 OllyDbg 의 "Assemble" 명령어를 이용하면 좀 더 쉽게 이용하실 수 있습니다.
위 실습을 다 끝내신 분께서는 한번 제 블로그 이미지에 있는 바이트 코드를 입력해서 실행해 보시기 바랍니다. ^^ 어떤 코드가 나타날까요? (OllyDbg 의 편집 기능과 New Origin Here 기능을 사용하면 되겠지요~)
ReverseCore
위 글이 도움이 되셨다면 추천(VIEW ON) 부탁 드려요~
'study' 카테고리의 다른 글
| 어셈블리 언어를 이용한 Code Injection (5) (20) | 2010/07/04 |
|---|---|
| 어셈블리 언어를 이용한 Code Injection (4) (0) | 2010/07/04 |
| 어셈블리 언어를 이용한 Code Injection (3) (2) | 2010/06/30 |
| 어셈블리 언어를 이용한 Code Injection (2) (2) | 2010/06/28 |
| 어셈블리 언어를 이용한 Code Injection (1) (0) | 2010/06/27 |
| Code Injection 기법 (3) (7) | 2010/06/24 |
| Code Injection 기법 (2) (14) | 2010/06/23 |
| Code Injection 기법 (1) (8) | 2010/06/22 |
| Advanced Global API Hooking – IE 접속 제어 (4) (27) | 2010/05/17 |
| Advanced Global API Hooking – IE 접속 제어 (3) (10) | 2010/05/07 |
| Advanced Global API Hooking – IE 접속 제어 (2) (10) | 2010/04/25 |
API,
API Hooking,
assembly,
Code Injection,
CreateRemoteThread,
GetProcAddress,
GetThreadContext,
it,
LoadLibrary,
MessageBox,
OllyDbg,
OpenProcess,
Reverse Code Engineering,
Reverse Engineering,
ReverseCore,
Reversing,
SetThreadContext,
Thread Injection,
VirtualAllocEx,
WriteProcessMemory,
리버스 엔지니어링,
리버싱,
소프트웨어 역공학,
어셈블리,
후킹
CodeInjection2.exe
RVA.txt
CodeInjection2.cpp
CodeInjection.zip