본문 바로가기

High Level Technique/Reversing

Code Injection

Code Injection



Code Injection은 상대방 프로세스에 독립 실행 코드를 삽인한 후 실행하는 기법을 말한다. CreateRemoteThread() API를 이용하여 원격 쓰렏 형태로 실행하기 때문에 Thread Injection이라고도 한다.


Code Injection을 사용하는 이유는 메모리를 적게 차지하고, DLL Injection과 다르게 흔적을 찾기가 어렵습니다.







Code Injection을 할 때 몇가지 알아 두어야 할 점이 있습니다.


- 프로그램 빌드 시 Release 모드

- 최적화 옵션 /0d

- Data Execution Prevention (DEP) 해제


※ Release 모드로 하는 이유는 Debug 모드로 빌드를 하게되면 코드 중간에 에러를 잡는 코드가 삽입되어 정확한 크기를 알 수 없게 됩니다.

그리고 최적화 옵션을 해제해서 소스코드가 달라지는 것을 막습니다.

또 한가지 DEP를 해제해야 하는데, DEP는 데이터 실행 방지 입니다. 실행 방지 메모리 영역의 실행 코드에서 응용프로그램이나 서비스가 실행되지 못하게 하는 것을 막기 위해 설정되어 있는 것입니다.




DEP 해제


cmd(관리자 권한) - bcdedit.exe /set {current} nx AlwaysOff 입력





소스코드 작성


소스코드는 아래와 같은 흐름으로 작성합니다.







위와 같이 소스코드를 작성합니다. 입력한 PID에 따라서 해당 PID가 존재하면 프로세스에 할당하여 calc.exe를 실행시키는 코드입니다.




중간에 크기를 구하는 부분이 있는데 calc.exe가 실행될 코드가 들어가야 하기 때문에 아무런 기능이 없는 함수를 하나 만들고 해당 함수부터 원래 함수까지의 거리를 구해줍니다.





실행



그러면 이제 실제로 PID를 입력해서 calc.exe가 실행이 되는지 확인해 보도록 하겠습니다.




해당 PID를 입력을 했더니, 메모장이 중지가 되어버렸습니다. 


소스코드는 맞게 작성을 한 것 같은데 왜 이러한 현상이 나타났을까요?



OllyDBG로 직접 찾아보면 0x0C82148 이라는 값을 스택에 저장하고 있습니다. 그리고 해당 주소를 CALL을 하고 있습니다.

하지만, 0xC82148 이라는 주소가 없기 때문에 메모장이 작동 중지가 되어버리게 됩니다.




소스코드 수정


notedpad.exe 내부에 Winexec()를 실행할 주소가 없으므로 필요한 주소들을 담아서 notepad.exe에 넣어줍니다.



#include <stdio.h>
#include <Windows.h>
typedef UINT(WINAPI *WINEXEC)(LPCSTR, UINT);
typedef struct
{
char buf[10];
WINEXEC pFunc;
}INJECT_DATA;
void WINAPI ThreadProc(INJECT_DATA* threadinject)
{
WINEXEC pFunc = threadinject->pFunc;
((WINEXEC)pFunc)(threadinject->buf, SW_SHOWDEFAULT);
}
void AtherFunc(){};
void CodeInjection(DWORD Target_PID)
{
HMODULE hMod;
HANDLE hProcess = NULL;
HANDLE hThread = NULL;
PVOID ThreadProc_Address = NULL;
PVOID INJECT_DATA_Address = NULL;
INJECT_DATA InjectData;
//ThreadProc의 크기를 구한다.
DWORD ThreadProc_Size = (DWORD)AtherFunc - (DWORD)ThreadProc;
hMod = GetModuleHandleA("kernel32.dll");
//TargetPID의 핸들을 얻는다.
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Target_PID);
//Target Process에 ThreadProc_Size만큼 메모리를 할당하고 주소를 반환받는다.
ThreadProc_Address = VirtualAllocEx(hProcess, NULL, ThreadProc_Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
//Target Process의 ThreadProc주소에 ThreadProc위치부터 ThreadProc_size만큼 입력한다.
WriteProcessMemory(hProcess, ThreadProc_Address, (LPVOID)ThreadProc, ThreadProc_Size, NULL);
// Injection되는 INJECT_DATA 구조체의 buf에 실행시킬 calc.exe 문자열을 입력한다.
sprintf(InjectData.buf, "calc.exe\0");
// kernel32.dll 모듈의 WinExec 함수의 주소를 INJECT_DATA 구조체의 함수포인터에 입력한다.
InjectData.pFunc = (WINEXEC)GetProcAddress(GetModuleHandleA("kernel32.dll"), "WinExec");
// Target Process에 INJECT_DATA 구조체 크기 만큼 메모리를 할당한다.
INJECT_DATA_Address = VirtualAllocEx(hProcess, NULL, sizeof(INJECT_DATA), MEM_COMMIT, PAGE_READWRITE);
// Target Process에 INJECT_DATA 주소에 INJECT_DATA 위치부터 INJECT_DATA 크기만큼 입력한다.
WriteProcessMemory(hProcess, INJECT_DATA_Address, (LPVOID)&InjectData, sizeof(INJECT_DATA), NULL);
//Target Process에 Thread를 생성하고 입력한 값을 실행시킨다.
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc_Address, INJECT_DATA_Address, CREATE_SUSPENDED, NULL);
ResumeThread(hThread);
WaitForSingleObject(hThread, INFINITE);
}
int main()
{
int GetPID = 0;
printf("PID 입력: ");
scanf("%d", &GetPID);
CodeInjection((DWORD)GetPID);
return 0;
}














계산기가 실행되는 것을 알 수 있습니다.


'High Level Technique > Reversing' 카테고리의 다른 글

API Hooking - Debug Technique  (0) 2016.07.19
API Hooking  (0) 2016.07.19
DLL Load Using PE Patch  (0) 2016.07.14
DLL Ejection  (0) 2016.07.08
DLL Injection  (0) 2016.07.06