1. 程式人生 > >在另一個程序中注入程式碼的方式20171013

在另一個程序中注入程式碼的方式20171013

在另一個程序中注入程式碼,可以實現程式設計師自己編寫的功能,這為很多程式設計師提供了方便,同時也給惡意的黑客(當然黑客範疇很廣,並不是一個貶義詞,也有好的黑客)提供了可乘之機,不管怎樣,注入程式碼很管用,能做到將程式碼注入到一個程序,卻不被防毒軟體查殺到才最牛。

要想另外一個程序能執行我們寫入的程式碼,就是讓另一程序執行我們寫入的一個或多個函式功能,最簡單的方法就是將函式程式碼寫在dll中,然後通過遠端執行緒注入該dll,其次的方法就是通過遠端執行緒直接將我們寫入的函式作為執行緒回撥函式,再其次就是在函式程式碼寫入所要注入的程序中,後通過移動EIP讓程序執行我們寫的程式碼,執行完後再移回來。所以注入程式碼的方式是有很多的,列舉如下:1)遠端注入dll,2)注入dll到另一程序,然後dll中hook那個所注入的程序,3)直接通過彙編或C語言在另一程序中寫程式碼,當做遠端執行緒函式的回撥函式,4)通過移動EIP來執行我們的函式程式碼。後兩種方式需要地址重定位,因此比較複雜。</span>

方法一:遠端注入dll
第一步:利用API函式 FindWindow 對要注入程式碼的程序主視窗名或者類名查詢到視窗控制代碼,然後通過API函式GetWindowThreadProcessId 獲得程序的ID和執行緒的ID,然後通過程序ID開啟程序控制代碼。程式碼如下:

HWND hWnd = FindWindow(NULL, “計算器”);
DWORD dwPid = 0;
DWORD dwTid = GetWindowThreadProcessId(hWnd, &dwPid);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);

第二步:通過程序控制代碼利用API函式 VirtualAllocEx 在另一程序中申請一段記憶體,一般一個分頁就夠了,屬性為可讀可寫可執行,然後通過API函式WriteProcessMemory 將 *.dll 的地址寫入到申請的記憶體分頁的首地址。程式碼如下:
LPVOID pAddr = VirtualAllocEx(hProcess, NULL, 0x1000, MEM_COMMIT,PAGE_EXECUTE_READWRITE);
DWORD dwNumberOfBytesWritten = 0;
BOOL bRet = WriteProcessMemory(hProcess, (TCHAR*)pAddr, DLLPATH, sizeof(DLLPATH) + 1,&dwNumberOfBytesWritten); 


第三步:建立遠端執行緒CreateRemoteThread,這是注入程式碼成功的關鍵,因為執行緒回撥函式有一個引數,而LoadLibrary函式正好也只有一個引數,所以取巧將LoadLibrary作為遠端回撥函式的回撥函式,就可以載入一個 *.dll 到另一程序中。程式碼如下:
HANDLE hThreadLoad = CreateRemoteThread(hProcess,
NULL, 
0, 
(LPTHREAD_START_ROUTINE)LoadLibrary,
(LPVOID)pAddr,
0,
NULL);
DWORD dwRet = WaitForSingleObject(hThreadLoad, INFINITE);


第四步:釋放*.dll , *.dll注入到程序後不會隨著遠端執行緒的退出而被釋放,同樣這裡釋放*.dll用到 FreeLibrary 函式,再建立一個遠端執行緒,以 FreeLibrary  作為回撥函式,然後就可以釋放*.dll,不過得以*.dll 的模組地址做為引數,上一個遠端執行緒的返回碼即為*.dll的模組首地址,通過GetExitCodeThread 獲取即可,程式碼如下:
DWORD dwExitCode = 0;
bRet = GetExitCodeThread(hThreadLoad, &dwExitCode);
DWORD dwTId = 0;
HANDLE hThreadFree = CreateRemoteThread(hProcess,
NULL, 
0, 
(LPTHREAD_START_ROUTINE)FreeLibrary,
(LPVOID)dwExitCode,
0,
&dwTId);
CloseHandle(hProcess);


方法二:hook某一程序
hook的中文意思是鉤子的意思,它的作用是監視程式的執行,在程式執行到某個操作時,將原來程式碼勾過來檢測或者執行我們
自己的程式碼,然後再還回原來的程序。hook 的型別很多,由設定hook的API函式 SetWindowsHookEx 的第一個引數決定hook的型別,
常見的有WH_CALLWNDPROC、WH_KEYBOARD、WH_MOUSE等,執行緒發訊息即呼叫SendMessage函式,鍵盤訊息WM_KEYUP
 和 WM_KEYDOWN 和滑鼠訊息來的時候就能被勾住,即進入到hook的程式中,這是在hook函式的回撥函式中,寫入我們自己的程式碼
即可實現一些自己的功能,不過hook的範圍比較廣,針對性沒那麼強。
實現步驟:
第一步:建立一個*.dll,在其中設定hook,如下:
g_hHook = SetWindowsHookEx(WH_KEYBOARD,( HOOKPROC)KeyboardProc, (HINSTANCE)m_hCurModule, 0);


返回值為hook程式的控制代碼,用全域性變數儲存,是因為後面還需將其解除掉hook,用UnhookWindowsHookEx(g_hHook)。m_hCurModule
為當前dll在程序中的模組地所有址,最後引數為0,說明hook同時執行的所有執行緒。KeyboardProc為回撥函式,只要hook成功了,就會
進入該函式中。
第二步:通過方法一將建立的*.dll檔案匯入到要注入的執行緒,即可以hook程序,執行程式碼,但這樣什麼視窗都會被hook到,有些是不
需要的,這是可以增加條件來實現過濾,或者因為是注入到某一程序,就是為了監控那一程序的執行,因此可以在回撥函式中用
SetWindowLong將回調函式轉換一下,退出時轉換回來。如:
LRESULT CALLBACK MyWindowProc(
     HWND hwnd,      // handle to window
     UINT uMsg,      // message identifier
     WPARAM wParam,  // first message parameter
     LPARAM lParam   // second message parameter
    )
{
if (uMsg == WM_KEYDOWN)
{
AfxMessageBox(TEXT("uMsg == WM_KEYDOWN"));
}
else if (uMsg == WM_KEYUP)
{
AfxMessageBox(TEXT("uMsg == WM_KEYUP"));
}
else if (uMsg == WM_MOUSEMOVE)
{
AfxMessageBox(TEXT("uMsg == WM_MOUSEMOVE"));
}
return CallWindowProc((pFnWindowProc)theApp.m_pfnWindowProc, hwnd, uMsg, wParam, lParam);
}


LRESULT CALLBACK KeyboardProc(int code,       // hook code
                              WPARAM wParam,  // virtual-key code
                              LPARAM lParam   // keystroke-message information
                              )
{
if (code < 0)
{
CallNextHookEx(g_hHook, code, wParam, lParam);
}

g_pfnWindowProc = (pFnWindowProc)SetWindowLong(g_hMainWnd, GWL_WNDPROC, (LONG)MyWindowProc);


return CallNextHookEx(g_hHook, code, wParam, lParam);
}