在前面幾篇學過了不少注入技巧,但是他們都有一個缺點 : 痕跡過大,容易被發現。所以今天來介紹代碼注入
( Code Injection
) 這個技術。
它的優點有 :
所以說,如過要注入的代碼比較小,就非常推薦使用這方法。題外話,大部分的病毒、shellcode 都是使用這種方法進行注入操作來規避防毒軟體的查殺。
說道代碼注入最常想到的就是 SQL inject
、PHP inject
等,他們是因為處理了錯誤的數據導致風險發生。而今天的代碼注入可以說完全不一樣,他是透過創建 Thread
來執行要注入的代碼,不講武德的直接讓目標程序執行你想做的事。做個比喻 : 就像是台北車站塞愛心筆給你的那些人,會直接把筆塞到你身上而不是透過話術使你買筆。
本篇使用源碼來自 :
https://github.com/reversecore/book/blob/master/소스코드/03_DLL_Injection/27_Code_Injection/src/CodeInjection/CodeInjection.cpp
個人改編與成果位於 :
https://github.com/Dinlon5566/IT_Reverse_Engineering/tree/main/Dx23
首先我們先看看要做甚麼東西 :
PID
。PID
。可以看到目錄下只有注入器而沒有注入的 DLL。因為已經把要注入的程式放在注入器中了。Thread
產生。開始學習如何製作注入器吧,我們的任務是在目標程序上執行這一行代碼( 實際上通常不止一行 ) :
MessageBoxA(0,"Hello \\OwO/","IT-help",0);
InjectCode( )
是用來注入的函式。int main(int argc,char* argv[])
{
if (argc != 2) {
printf("USEAGE : Injecter.exe <PID>\n");
return 1;
}
DWORD pid = atol(argv[1]);
InjectCode(pid);
return 0;
}
Thread
的代碼 ),這邊稍有複雜。// 定義引用函數與使用資料的儲存結構體
typedef struct _THREAD_PARAM
{
FARPROC pFunc[2]; // LoadLibraryA(), GetProcAddress()
char szBuf[4][128]; // "user32.dll", "MessageBoxA", "www.reversecore.com", "ReverseCore"
} THREAD_PARAM, * PTHREAD_PARAM;
//定義函式行為
//LoadLibrary
typedef HMODULE(WINAPI* PFLOADLIBRARYA)
(
LPCSTR lpLibFileName
);
//GetProcAddress
typedef FARPROC(WINAPI* PFGETPROCADDRESS)
(
HMODULE hModule,
LPCSTR lpProcName
);
//MessageBoxA
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("user32.dll");
hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]);
if (!hMod)
return 1;
// GetProcAddress("MessageBoxA");
pFunc = (FARPROC)((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]);
if (!pFunc)
return 1;
// MessageBoxA(NULL,"hello","IT",NULL);
((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], MB_OK);
return 0;
}
InjectCode( )
,把函數寫入結構中並執行注入的功能。BOOL InjectCode(DWORD dwPID)
{
// 把變數都宣告在這邊
HMODULE hMod = NULL;
THREAD_PARAM param = { 0, };
HANDLE hProcess = NULL;
HANDLE hThread = NULL;
LPVOID pRemoteBuf[2] = { 0, };
DWORD dwSize = 0;
// 取得 kernel32
hMod = GetModuleHandleA("kernel32.dll");
if (hMod == NULL) {
printf("GetModeuleHandle fail : error code [%d]\n",GetLastError());
}
// 設定使用到函式的位置與所引入時的值
// 利用 GetProcAddress 得到函式位置
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], "Hello \\OwO/");
strcpy_s(param.szBuf[3], "IT-help");
OpenProcess(PID)
取得目標程序的 Handle,取得權限後才可以注入目標。
if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, // dwDesiredAccess
FALSE, // bInheritHandle
dwPID))) // dwProcessId
{
printf("OpenProcess() fail : err_code = %d\n", GetLastError());
return FALSE;
}
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;
}
param
中 )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;
}
Thread
所需要的空間。InjectCode - ThreadProc
這樣的位置相減,原因為在編譯時 VisualStudio 會依照函數的順序來進行編譯,所以會以 ThreadProc、InjectCode
的順序來編寫。而相減之後就是函數的空間。// 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;
}
GetProcAddress()
中取得的資料位置寫入上方宣告記憶體中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;
}
CreateRemoteThread
遠端執行線程,透過 PID
開啟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;
}
Handle
。 // wait to thread close
WaitForSingleObject(hThread, INFINITE);
// clode the Handle
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}
那看看能不能執行,並以系統管理員來執行程序 ( 一定要用系統管理員 )。
如果執行後沒有發生行為,請安裝 VC++。