DLL注入(英語:DLL injection)是一種計算機編程技術,它能夠強行使另外一個進程加載一個動態連接庫以在其地址空間內運行指定代碼[1]。在Windows操做系統上,每一個進程都有獨立的進程空間,即一個進程是沒法直接操做另外一個進程的數據的(事實上,不只Windows,許多操做系統也是如此)。可是DLL注入是用一種不直接的方式,來實現操做其餘進程的數據。假設咱們有一個DLL文件,裏面有操做目標進程數據的程序代碼邏輯,DLL注入就是使目標進程加載這個DLL,加載後,這個DLL就成爲目標進程的一部分,目標進程的數據也就能夠直接操做了。
git
本文編寫的代碼所作的工做至關於上圖中注入器所作的工做,所說起的DLL均爲C/C++語言生成的DLL。github
(1) 打開目標進程
(2) 在目標進程開闢一段內存空間
(3) 往開闢的內存空間中寫入要注入的DLL的路徑
(4) 給目標建立一個線程, 加載DLL編程
Windows下有個名爲OpenProcess的函數能夠打開一個進程,它的原型以下:安全
HANDLE OpenProcess( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId );
返回值:若是打開成功,返回一個進程句柄,不然返回NULL函數
代碼實現以下:this
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid); if (NULL == hProcess) { OutputDebugString("Cannot open this process.\n"); return -1; }
這段代碼根據進程的pid以PROCESS_ALL_ACCESS權限來打開一個進程,並返回進程句柄,進程pid能夠經過如下方式得到:操作系統
int GetPidByProcessName(const char* ProcessName) { HANDLE Processes = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL); PROCESSENTRY32 ProcessInfo = { 0 }; ProcessInfo.dwSize = sizeof(PROCESSENTRY32); while (Process32Next(Processes, &ProcessInfo)) { if (strcmp(ProcessInfo.szExeFile, ProcessName) == 0) { return ProcessInfo.th32ProcessID; } } return -1; }
該函數經過進程名返回它對應的進程pid線程
VirtualAllocEx函數能夠在目標進程申請一塊內存空間,函數原型以下:指針
LPVOID VirtualAllocEx( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect );
返回值: 若是內存申請成功,返回內存空間的首地址,不然返回NULLcode
代碼實現以下:
LPVOID lpAddr = VirtualAllocEx(hProcess, NULL, strlen(DllPath), MEM_COMMIT, PAGE_READWRITE); if (NULL == lpAddr) { OutputDebugString("Cannot alloc memory.\n"); return -1; }
這段代碼是在目標進程申請開闢一塊內存空間,申請開闢的內存空間大小是DLL完整路徑所佔用的字節數,申請成功將會返回內存空間的起始地址
WriteProcessMemory能夠向指定的內存地址中寫入數據,函數原型以下:
BOOL WriteProcessMemory( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten );
返回值: 返回一個非零值表明寫入成功,返回零則寫入失敗
代碼實現以下:
BOOL isOk = WriteProcessMemory(hProcess , lpAddr, DllPath, strlen(DllPath), NULL); if (!isOk) { OutputDebugString("Cannot write memory.\n"); return -1; }
這段代碼是將DLL的完整路徑寫入到上一步開闢的內存空間中
(1)GetModuleHandle函數根據模塊名稱獲得模塊的句柄,原型以下:
HMODULE GetModuleHandleA( LPCSTR lpModuleName );
返回值:指定模塊的句柄
(2)GetProcAddress函數能夠根據函數名來獲得模塊中的一個導出函數的地址,原型以下:
FARPROC GetProcAddress( HMODULE hModule, LPCSTR lpProcName );
返回值:相應的導出函數的地址
(3)CreateRemoteThread用於在指定進程的虛擬空間中開啓一個線程,原型以下:
HANDLE CreateRemoteThread( HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );
返回值:建立成功返回一個線程句柄,不然返回一個NULL
(4)LoadLibraryA是LoadLibrary函數的ASCII碼版本,它的函數原型以下:
HMODULE LoadLibraryA( LPCSTR lpLibFileName );
返回值:加載成功會返回模塊的句柄,加載失敗返回NULL
代碼實現以下:
HMODULE hKernel32Module = GetModuleHandle("kernel32.dll"); if (NULL == hKernel32Module) { OutputDebugString("Cannot find kernel32.dll.\n"); return -1; } FARPROC hFarProc = GetProcAddress(hKernel32Module, "LoadLibraryA"); if (NULL == hFarProc) { OutputDebugString("Cannot get function address.\n"); return -1; } HANDLE hThread = CreateRemoteThread(hProcess , NULL , 0 , (LPTHREAD_START_ROUTINE)hFarProc , lpAddr , 0 , NULL );
上面的代碼使用GetProcAddress函數得到kernel32.dll模塊中LoadLibraryA函數的地址,而後在目標進程開啓一個線程調用LoadLibraryA函數。lpAddr被寫入DLL的完整路徑,把它傳入CreateRemoteThread函數,至關於就是把DLL的完整路徑傳給LoadLibraryA函數。
DLL注入了目標進程後,若是想要把它從目標進程卸載,須要進行如下步驟:
(1) 打開目標進程
(2) 給目標建立一個線程, 卸載DLL
打開目標進程的操做和注入同樣,再也不詳細展開,代碼以下:
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid); if (NULL == hProcess) { OutputDebugString("Cannot open this process.\n"); return -1; }
這一步代碼實現和注入的最後一步大致同樣,一樣是開啓線程調用一個函數,但這個在線程裏執行的函數是FreeLibrary。
FreeLibrary函數原型以下:
BOOL FreeLibrary( HMODULE hLibModule );
返回值:成功會返回一個非0值
代碼實現以下:
HMODULE hKernel32Module = GetModuleHandle("kernel32.dll"); if (NULL == hKernel32Module) { OutputDebugString("Cannot find kernel32.dll.\n"); return -1; } FARPROC hFarProc = GetProcAddress(hKernel32Module, "FreeLibrary"); if (NULL == hFarProc) { OutputDebugString("Cannot get function address.\n"); return -1; } HMODULE hModule = GetModuleHandleByName(ModuleName,pid); if (NULL == hModule) { OutputDebugString("Cannot find this module.\n"); return -1; } HANDLE hThread = CreateRemoteThread(hProcess ,NULL , 0 , (LPTHREAD_START_ROUTINE)hFarProc , hModule , 0 , NULL );
因爲FreeLibrary函數須要傳入一個模塊的句柄,那麼咱們須要從目標進程中掃描並找到咱們想要卸載的模塊,而後返回它的句柄:
HMODULE GetModuleHandleByName(const char* ModuleName,DWORD pid) { HANDLE Processes = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); MODULEENTRY32 ModuleInfo = { 0 }; ModuleInfo.dwSize = sizeof(MODULEENTRY32); char buf[0x100]; while (Module32Next(Processes, &ModuleInfo)) { if (strcmp(ModuleInfo.szModule, ModuleName) == 0) { return ModuleInfo.hModule; } } return NULL; }