어셈블리(Assembly) 언어로 생성한 코드를 가지고 Injector 를 만들어 보도록 하겠습니다.
이전 강좌의 내용은 아래 링크를 참조하시기 바랍니다.
ThreadProc() 함수의 Binary 코드 얻기
지난 강좌에서 생성한 asmtest_patch.exe 파일을 OllyDbg 로 열어 보겠습니다. 우리가 프로그래밍한 ThreadProc() 의 주소는 401000 입니다. 메모리 윈도우에서 401000 주소로 갑니다.
<Fig. 1>
ThreadProc() 함수는 401000 ~ 401061 의 주소 영역입니다. 위 그림과 같이 이 영역을 선택하신 후 마우스 우측 메뉴의 "Copy - To file" 항목을 선택하시기 바랍니다. (아래 그림 참고)
<Fig. 2>
<Fig. 3>
위 텍스트 파일의 내용은 Hex 값으로 표현된 ThreadProc() 함수로써, IA-32 OpCode (Operation Code) 명령어 입니다. 이 내용은 곧 상대방 프로세스에 인젝션 시킬 코드가 되는 것입니다.
위 텍스트 파일을 아래와 같이 편집합니다. 불필요한 부분을 제거하고 모든 바이트마다 "0x" 표시를 붙여주고 ',' 로 연결합니다. 텍스트 에디터의 편집 기능(열 선택, 문자열 변경)을 적절히 사용하시면 편리합니다.
<Fig. 4>
위 그림에서 편집된 텍스트 내용을 보면 마치 C 언어의 BYTE 배열처럼 보이지 않습니까? 이게 바로 인젝션 시킬 코드 버퍼입니다. (아래 설명되는 CodeInjection2.cpp 파일에서 사용됩니다.)
CodeInjection2.cpp
Injector 프로그램의 소스 코드입니다. 위에서 텍스트 에디터로 만든 코드 버퍼는 아래 소스에서 g_InjectionCode 배열에 사용되었습니다.
* 참고!
아래 소스코드는 MS Visual C++ 2008 Express Edition 에서 개발되었으며, Windows 7 32bit 환경에서 테스트 되었습니다. 또한 설명의 편의를 위하여 리턴 값 체크와 에러 처리 코드는 생략되었습니다. 원본 소스 코드는 위에 첨부된 CodeInjection2.cpp 파일을 참고하시기 바랍니다.
typedef struct _THREAD_PARAM
{
FARPROC pFunc[2]; // LoadLibraryA(), GetProcAddress()
} THREAD_PARAM, *PTHREAD_PARAM;
// ThreadProc()
BYTE g_InjectionCode[] =
{
0x55, 0x8B, 0xEC, 0x8B, 0x75, 0x08, 0x68, 0x6C, 0x6C, 0x00,
0x00, 0x68, 0x33, 0x32, 0x2E, 0x64, 0x68, 0x75, 0x73, 0x65,
0x72, 0x54, 0xFF, 0x16, 0x68, 0x6F, 0x78, 0x41, 0x00, 0x68,
0x61, 0x67, 0x65, 0x42, 0x68, 0x4D, 0x65, 0x73, 0x73, 0x54,
0x50, 0xFF, 0x56, 0x04, 0x6A, 0x00, 0xE8, 0x0C, 0x00, 0x00,
0x00, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x43, 0x6F,
0x72, 0x65, 0x00, 0xE8, 0x14, 0x00, 0x00, 0x00, 0x77, 0x77,
0x77, 0x2E, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x63,
0x6F, 0x72, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x00, 0x6A, 0x00,
0xFF, 0xD0, 0x33, 0xC0, 0x8B, 0xE5, 0x5D, 0xC3
};
/*
// ThreadProc()
004010ED 55 PUSH EBP
004010EE 8BEC MOV EBP,ESP
004010F0 8B75 08 MOV ESI,DWORD PTR SS:[EBP+8]
004010F3 68 6C6C0000 PUSH 6C6C
004010F8 68 33322E64 PUSH 642E3233
004010FD 68 75736572 PUSH 72657375
00401102 54 PUSH ESP
00401103 FF16 CALL DWORD PTR DS:[ESI]
00401105 68 6F784100 PUSH 41786F
0040110A 68 61676542 PUSH 42656761
0040110F 68 4D657373 PUSH 7373654D
00401114 54 PUSH ESP
00401115 50 PUSH EAX
00401116 FF56 04 CALL DWORD PTR DS:[ESI+4]
00401119 6A 00 PUSH 0
0040111B E8 0C000000 CALL 0040112C
00401120 <ASCII>
0040112C E8 14000000 CALL 00401145
00401131 <ASCII>
00401145 6A 00 PUSH 0
00401147 FFD0 CALL EAX
00401149 33C0 XOR EAX,EAX
0040114B 8BE5 MOV ESP,EBP
0040114D 5D POP EBP
0040114E C3 RETN
*/
BOOL InjectCode(DWORD dwPID)
{
HMODULE hMod = NULL;
THREAD_PARAM param = {0,};
HANDLE hProcess = NULL;
HANDLE hThread = NULL;
LPVOID pRemoteBuf[2] = {0,};
hMod = GetModuleHandleA("kernel32.dll");
// set THREAD_PARAM
param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");
// Open Process
hProcess = OpenProcess(PROCESS_ALL_ACCESS,
FALSE,
dwPID);
// Allocation for THREAD_PARAM
pRemoteBuf[0] = VirtualAllocEx(hProcess,
NULL,
sizeof(THREAD_PARAM),
MEM_COMMIT,
PAGE_READWRITE);
WriteProcessMemory(hProcess,
pRemoteBuf[0],
(LPVOID)¶m,
sizeof(THREAD_PARAM),
NULL);
// Allocation for g_InjectionCode
pRemoteBuf[1] = VirtualAllocEx(hProcess,
NULL,
sizeof(g_InjectionCode),
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess,
pRemoteBuf[1],
(LPVOID)&g_InjectionCode,
sizeof(g_InjectionCode),
NULL);
hThread = CreateRemoteThread(hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)pRemoteBuf[1],
pRemoteBuf[0],
0,
NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}
<코드 1>
위의 코드와 지난번 CodeInjection.cpp 의 코드와의 가장 큰 차이점은 인젝션 시키는 코드 내에 필요한 문자열 데이터를 같이 포함시킨 것입니다.
따라서 _THREAD_PARAM 구조체에서 문자열 멤버가 사라졌습니다. 그리고 기존 C로 된 ThreadProc() 함수 대신 <Fig. 4> 의 OpCode 버퍼(g_InjectionCode)가 사용됩니다. (이 OpCode 버퍼를 생성하기 위해 OllyDbg 의 “Assembly” 명령을 사용했던 것을 기억하시죠?)
조금 더 정교하게 프로그래밍을 했다면 _THREAD_PARAM 구조체조차 필요 없도록 만들 수도 있습니다. 세부적인 구현 방법은 어디까지나 구현하는 사람의 마음입니다.
중요한 것은 어셈블리 프로그래밍을 통해서 생성된 OpCode 버퍼를 Injector 소스코드에 사용하여 상대방 프로세스에 인젝션 시킨다는 것입니다.
예전에 설명 드린 CodeInjection.cpp 소스 코드와 비교해서 보시면 차이점을 더 명확히 이해할 수 있을 것입니다. (위 <코드 1> 의 세부 설명은 생략합니다. Code Injection 의 구현 방법에 대해서는 밑의 링크된 설명을 참고하시기 바랍니다.)
☞ 참고 : Code Injection 기법 (2)
다음 강좌에서 실제로 상대방 프로세스에 인젝션 시킨 후 디버깅을 하면서 저 어셈블리 코드가 어떤 의미를 가지고 있는지 알아보도록 하겠습니다.
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,
리버스 엔지니어링,
리버싱,
소프트웨어 역공학,
어셈블리,
후킹
asmtest_patch.exe
RVA.txt
CodeInjection2.cpp
