상대방 프로세스에 독립 실행 코드를 삽입한 후 실행하는 기법 --> Thread Injection이라고도 얘기함
DLL Injection
Code Injection
--> Code Injection이 DLL Injection보다 고려할 사항이 좀 더 많다.
ThreadProc()
typedef struct _THREAD_PARAM
{
FARPROC pFunc[2]; // LoadLibraryA(), GetProcAddress()
char szBuf[4][128]; // "user32.dll", "MessageBoxA", "www.reversecore.com", "ReverseCore"
} THREAD_PARAM, *PTHREAD_PARAM;
typedef HMODULE (WINAPI *PFLOADLIBRARYA)
(
LPCSTR lpLibFileName
);
typedef FARPROC (WINAPI *PFGETPROCADDRESS)
(
HMODULE hModule,
LPCSTR lpProcName
);
typedef int (WINAPI *PFMESSAGEBOXA)
(
HWND hWnd,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType
);
DWORD WINAPI ThreadProc(LPVOID lParam)
{
PTHREAD_PARAM pParam = (PTHREAD_PARAM)lParam;
HMODULE hMod = NULL;
FARPROC pFunc = NULL;
// LoadLibrary()
hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]); // "user32.dll"
if( !hMod )
return 1;
// GetProcAddress()
pFunc = (FARPROC)((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]); // "MessageBoxA"
if( !pFunc )
return 1;
// MessageBoxA()
((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], MB_OK);
return 0;
}
ThreadProc() 함수를 보면 직접 API를 호출하지 않는다. 문자열도 직접 정의해서 사용하지 않는다. 전부 스레드 파라미터로 넘어온 THREAD_PARAM 구조체에서 가져다 사용한다.
BOOL InjectCode(DWORD dwPID)
{
HMODULE hMod = NULL;
THREAD_PARAM param = {0,};
HANDLE hProcess = NULL;
HANDLE hThread = NULL;
LPVOID pRemoteBuf[2] = {0,};
DWORD dwSize = 0;
hMod = GetModuleHandleA("kernel32.dll");
// set THREAD_PARAM
param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");
strcpy_s(param.szBuf[0], "user32.dll");
strcpy_s(param.szBuf[1], "MessageBoxA");
strcpy_s(param.szBuf[2], "www.reversecore.com");
strcpy_s(param.szBuf[3], "ReverseCore");
// Open Process
if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, // dwDesiredAccess
FALSE, // bInheritHandle
dwPID)) ) // dwProcessId
{
printf("OpenProcess() fail : err_code = %d\n", GetLastError());
return FALSE;
}
// Allocation for THREAD_PARAM
dwSize = sizeof(THREAD_PARAM);
if( !(pRemoteBuf[0] = VirtualAllocEx(hProcess, // hProcess
NULL, // lpAddress
dwSize, // dwSize
MEM_COMMIT, // flAllocationType
PAGE_READWRITE)) ) // flProtect
{
printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
return FALSE;
}
if( !WriteProcessMemory(hProcess, // hProcess
pRemoteBuf[0], // lpBaseAddress
(LPVOID)¶m, // lpBuffer
dwSize, // nSize
NULL) ) // [out] lpNumberOfBytesWritten
{
printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
return FALSE;
}
// Allocation for ThreadProc()
dwSize = (DWORD)InjectCode - (DWORD)ThreadProc;
if( !(pRemoteBuf[1] = VirtualAllocEx(hProcess, // hProcess
NULL, // lpAddress
dwSize, // dwSize
MEM_COMMIT, // flAllocationType
PAGE_EXECUTE_READWRITE)) ) // flProtect
{
printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
return FALSE;
}
if( !WriteProcessMemory(hProcess, // hProcess
pRemoteBuf[1], // lpBaseAddress
(LPVOID)ThreadProc, // lpBuffer
dwSize, // nSize
NULL) ) // [out] lpNumberOfBytesWritten
{
printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
return FALSE;
}
if( !(hThread = CreateRemoteThread(hProcess, // hProcess
NULL, // lpThreadAttributes
0, // dwStackSize
(LPTHREAD_START_ROUTINE)pRemoteBuf[1], // dwStackSize
pRemoteBuf[0], // lpParameter
0, // dwCreationFlags
NULL)) ) // lpThreadId
{
printf("CreateRemoteThread() fail : err_code = %d\n", GetLastError());
return FALSE;
}
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}
InjectCode() 함수의 앞 부분은 THREAD_PARAM 구조체 변수를 세팅하고 있다.
핵심 흐름은 아래와 같다.
OpenProcess()
// data : THREAD_PARAM
VirtualAllocEx()
WriteProcessMemory()
// code : ThreadProc()
VirtualAllocEx()
WriteProcessMemory()
CreateRemoteThread()
상대방 프로세스에 data와 code를 각각 메모리에 할당하고 인젝션하는 게 Code Injection의 핵심이다.