程序編寫的過程中,往往有些功能需要由其它的進程權限才能夠完成這些工作,如得到其它進程中某個窗口的標題;建立隱藏的守護進程來監測本進程的運行情況;在Win7下,利用其它有高UAC權限的進程來完成一些功能等等。都需要使用到其它進程來完成這些工作。本文就來講解如何獲取其它進程中密碼框中的密碼。
以上代碼實現及理論均為Win32平台為主。
A. 為什麼選擇遠程線程 ?
我們知道,遠程線程就是想讓其它線程執行我們自己的代碼,如果要想使自己的代碼被其它進程執行,首先能想到的就是DLL,因為DLL可以被其它進程附加,並且可以執行所有的功能。但強大的功能也需要一個導火索來給它這個機會爆發,因為在進程運行進來後,LoadLibrary仍然可以使一個動態庫附加映射進某個進程,但當我們執行LoadLibrary時,是在自己進程中執行的,無法使其它進程有機會執行這個代碼,從而也就沒機會使我們的代碼附加到其它進程中,結果就是,強大的代碼永遠沒有得到一個可以執行的機會。
而遠程線程就是系統提供給我們的一個伯樂,它可以在你創建線程時,指定一個起始運行的入口,這樣LoadLibrary就有了執行的機會,從而,DLL裡面的代碼就是會一氣呵成的執行。
所以遠程線程是必須的。
B. 一些必須了解的概念
1. 地址空間
在Win32平台下(Linux及主流平台),每個進程擁有4GB獨立的地址空間,一般叫做線程地址空間,相對於每個進程來說,在自己的空間內訪問一些地址內容,即訪問變 量的值及執行一些代碼都是非常方便的有效的,每個進程完成自己的工作,相互不會產生任何干擾。
2. 遠程線程
一般而言,線程是線程可調試,實際完成一些工作(即執行一些代碼)的最小實體,一個進程可以包含一個及以上的線程,這些線程並行工作,完成實際的功能。而遠 程線程,故名思議,即非本進程的線程。假設有A,B兩個進程,遠程線程就是由A進程創建,但運行於B進程的地址空間中,擁有B進程的進程權限。
3. 代碼注入
代碼注入就是,將精心准備好的代碼(即數據)放進某個進程的地址空間中。
C. 為什麼不選擇DLL的方式 ?
前面已經提到過,DLL裡面就是可以執行的代碼,我們只需要將DLL附加進進程就得以機會去執行這些代碼。但DLL是以文件形式存放在磁盤中,有些功能可能會很 小,或者有些功能需要隱藏一些實現細節,如果附加一個DLL的話,效果並不是那麼的友好,所以如果有機會不用DLL,依然能夠使遠程線程實現強大的功能,那自然 是最佳選擇,但這也使得代碼編寫起來更難,也就需要更強的編程的基本功。
D. 需要使用到的主要API(以Win32平台為例)
OpenProcess:打開一個進程,得到該進程的句柄。
VirtualAllocEx:簡言之為在指定的進程中申請一些空間。
WriteProcessMemory:簡言之為在指定進程中寫入一些數據。
CreateRemoteThread:在指定的進程中創建一個線程。
E. 具體實現 -- Talk is cheap, show me the code! (Linus)
以得到其它進程中某一窗體的標題為例,來簡單的介紹遠程線程的用法。
得到一個窗口的標題,一般會調用 GetWindowText,它的使用方法如下:
The GetWindowText function copies the text of the specified window's titlebar (if it has one) into a buffer. If the specified window is a control, thetext of the control is copied. However, GetWindowText cannot retrievethe text of a control in another application.
上面的使用方法是摘自MSDN上GetWindowText的描述,從描述中可以看到,GetWindowText不能夠得到其它應用程序的標題,即一個進程的程序不能獲得其它進程窗口的句柄。但如果那段代碼是由其它進程來執行,執行後我們再得到結果,是不是就可以做到了呢。
假設我們的進程為A,我們想得到B進程中某一個控制的標題,要想完成以上功能,首先我們要能讓我們的代碼放到B進程的地址空間中,其次讓B執行這些代碼,最後從B進程中得到執行的結果。
代碼實現如下(為突出有效代碼,合法性檢測都沒有添加):
#include <iostream>
#include <windows.h>
using namespace std;
void Test();
void main()
{
Test();
}
__declspec(naked) void __stdcall GetWindowTextSpy(DWORD dwParam)
{
_asm
{
mov eax, [esp + 4] // 得到傳入的參數 dwParam, 此時為窗口句柄
push [eax + 0] // 緩沖區長度
mov ebx, eax
add ebx, 8
push ebx // 緩沖區地址
push [eax + 4] // 目標窗口句柄
call GetWindowText
ret 4
}
}
__declspec(naked) void __stdcall EndLabel(DWORD dwParam)
{
}
void Test()
{
// 得到目標窗口所在進程的ID,篇幅原因假設已經知道窗口句柄
HWND hwnd = (HWND)0x000108F8;
DWORD dwProcessID;
DWORD dwTitleSize = 0x20;
DWORD dwDataLen = 0x30;
GetWindowThreadProcessId(hwnd, &dwProcessID);
HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE |
PROCESS_VM_READ, FALSE, dwProcessID);
// 在B 進程中申請空間以存放返回的窗口標題
LPBYTE pData = (LPBYTE)VirtualAllocEx(hProcess, 0, dwDataLen, MEM_COMMIT, PAGE_READWRITE);
// 填充參數
WriteProcessMemory(hProcess, pData, &dwTitleSize, 4, NULL);
WriteProcessMemory(hProcess, pData + 4, &hwnd, 4, NULL);
// 注入的代碼長度
DWORD dwCodeLen = 0;
#ifdef _DEBUG
const DWORD dwSpyRealAddr = *(LPDWORD)((LPBYTE)(&GetWindowTextSpy)+1) + (DWORD)(&GetWindowTextSpy) + 5;
const DWORD dwEndReadAddr = *(LPDWORD)((LPBYTE)(&EndLabel)+1) + (DWORD)(&EndLabel) + 5;
#else
const DWORD dwSpyRealAddr = (DWORD)GetWindowTextSpy;
const DWORD dwEndReadAddr = (DWORD)EndLabel;
#endif
dwCodeLen = dwEndReadAddr - dwSpyRealAddr;
LPBYTE pCode = (LPBYTE)VirtualAllocEx(hProcess, 0, dwCodeLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
LPBYTE pCodeBuff = (LPBYTE)malloc(dwCodeLen);
memcpy((LPVOID)pCodeBuff, (LPVOID)dwSpyRealAddr, dwCodeLen);
// 調整代碼
LPBYTE p = pCodeBuff;
while(*p != 0xE8){p++;}
*(DWORD*)(p+1) = (DWORD)&GetWindowText - (DWORD)(p - (LPBYTE)pCodeBuff + (LPBYTE)pCode) - 5;
WriteProcessMemory( hProcess, pCode, pCodeBuff, dwCodeLen, NULL);
HANDLE hRThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pCode, pData, 0, 0);
WaitForSingleObject(hRThread, INFINITE);
char szTitle[100] = {0};
DWORD dwReadBytes = 0;
ReadProcessMemory(hProcess, pData + 8, szTitle, dwTitleSize, &dwReadBytes);
cout << szTitle << endl;
CloseHandle(hRThread);
free(pCodeBuff);
VirtualFreeEx(hProcess, pCode, dwCodeLen, MEM_RELEASE);
VirtualFreeEx(hProcess, pData, dwDataLen, MEM_RELEASE);
CloseHandle(hProcess);
}
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-06/103650p2.htm