Qt:Windows编程—DLL注入与卸载
前言
這里說的DLL注入 是將我們指定的DLL注入到指定的進(jìn)程中,DLL卸載也就是將指定進(jìn)程中的DLL卸載下來。在Windows提供的API中有 CreateRemoteThread函數(shù) 見名知意 創(chuàng)建遠(yuǎn)程線程函數(shù),這的遠(yuǎn)程指定的垮進(jìn)程,讓遠(yuǎn)程進(jìn)程執(zhí)行我們指定的線程回調(diào)函數(shù)。這就提供操作其他進(jìn)程的契機(jī)。
CreateRemoteThread 函數(shù)原型
實(shí)現(xiàn)思路
注入DLL
我們現(xiàn)在思考下面3個(gè)問題
第一個(gè)問題:如何加載DLL文件?DLL的調(diào)用方式,可以以參考 C/C++:Windows編程—調(diào)用DLL程序的2種方法。將一個(gè)DLL文件注入到目標(biāo)進(jìn)程 使用的是LoadLibrary函數(shù)。該函數(shù)的形式和ThreadProc形式一樣,即可將LoadLibrary函數(shù)地址作為線程回調(diào)函數(shù),這樣目標(biāo)進(jìn)程會(huì)執(zhí)行該函數(shù)將我們指定的DLL文件加載到目的進(jìn)程中。
第二個(gè)問題:DLL的路徑參數(shù)呢?需要將DLL文件的路徑,同樣寫到目標(biāo)進(jìn)程中。這里需要借助WriteProcessMemory函數(shù),
BOOL WINAPI WriteProcessMemory(// 進(jìn)程句柄_In_ HANDLE hProcess,// 指定寫入目標(biāo)進(jìn)程內(nèi)存的起始地址_In_ LPVOID lpBaseAddress,// 寫入的內(nèi)容的緩沖區(qū)起始地址_In_ LPCVOID lpBuffer,// lpBuffer長度_In_ SIZE_T nSize,// 接受實(shí)際寫入內(nèi)容的長度 _Out_ SIZE_T *lpNumberOfBytesWritten );該函數(shù)非常強(qiáng)大,比如在破解方面,可以實(shí)現(xiàn)一個(gè)內(nèi)存補(bǔ)丁,在開發(fā)方面,該函數(shù)可以用于修改目標(biāo)進(jìn)程中指定的值。
這樣目標(biāo)進(jìn)程用LoadLibrary函數(shù)加載指定的DLL文件了。回到第二個(gè)問題 寫入目標(biāo)進(jìn)程的起始地址應(yīng)該是多少?目標(biāo)進(jìn)程中的內(nèi)存塊允許將DLL文件的路徑寫進(jìn)去嗎?這就引出了第3個(gè)問題
第三個(gè)問題:如何確定應(yīng)該將DLL文件的完整路徑寫入目標(biāo)進(jìn)程的哪塊地址呢?對(duì)于目標(biāo)進(jìn)程來說,不會(huì)事先準(zhǔn)備一塊地址讓用戶進(jìn)行寫入,用戶做的是自己在目標(biāo)進(jìn)程中申請(qǐng)一塊內(nèi)存,然后把DLL文件的路徑進(jìn)程寫入。在目標(biāo)進(jìn)程中申請(qǐng)內(nèi)存的函數(shù)是VirtualAllocEx().
// suc,返回值是在目標(biāo)進(jìn)程申請(qǐng)到的內(nèi)存起始地址 // err,NULL LPVOID WINAPI VirtualAllocEx(// 指定進(jìn)程句柄_In_ HANDLE hProcess,// 在目標(biāo)進(jìn)程中申請(qǐng)內(nèi)存的起始地址_In_opt_ LPVOID lpAddress,// 申請(qǐng)內(nèi)存的長度_In_ SIZE_T dwSize,// 申請(qǐng)內(nèi)存的狀態(tài)類型,如MEM_COMMIT _In_ DWORD flAllocationType,// 申請(qǐng)內(nèi)存的屬性,如PAGE_READWRITE _In_ DWORD flProtect ); // 獲取指定模塊的模塊句柄 HMODULE WINAPI GetModuleHandle(_In_opt_ LPCTSTR lpModuleName );那么注入DLL的思路,就是是上面3個(gè)問題的思路。
卸載DLL
卸載DLL庫的API函數(shù)
https://docs.microsoft.com/zh-cn/windows/desktop/api/libloaderapi/nf-libloaderapi-freelibrary
思路還是和注入的一樣,因?yàn)槲覀冊(cè)谀繕?biāo)進(jìn)程使用LoadLibraryA將線程注入了,那么卸載DLL同樣要在目標(biāo)進(jìn)程中執(zhí)行!步驟同注入一樣。由于FreeLibrary參數(shù)為HMODULE 實(shí)際上就是一個(gè)指針值。這個(gè)句柄已經(jīng)加載就已經(jīng)存在。所以并不需要項(xiàng)目標(biāo)進(jìn)程申請(qǐng)空間和寫入數(shù)據(jù)。為什么LoadLibraryA需要在內(nèi)存中申請(qǐng)參數(shù)空間呢?因?yàn)樽址?有空間,事先并沒有,必須自己開辟空間,然后將字符串指針值傳入。
實(shí)現(xiàn)效果
事先準(zhǔn)備了一個(gè)DLLMsgBox.dll,在DLL加載和卸載的時(shí)候 會(huì)有個(gè)彈框效果。這里我們注入到KuGou.exe進(jìn)程中。
我們用上次寫的進(jìn)程管理器 查看KuGou.exe進(jìn)程中的DLL
滑到最下面
已經(jīng)被注入到進(jìn)程中了。
我們進(jìn)行卸載DLL
卸載完畢,再次查看DLL列表
已經(jīng)被成功卸載了。
核心代碼
相關(guān)基礎(chǔ)知識(shí)已經(jīng)有了,下面我們來看核心代碼
注入DLL
// 注入DLL void WorkerThread::injectDLL() {/*注入DLL的思路步驟:1. 在目標(biāo)進(jìn)程中申請(qǐng)一塊內(nèi)存空間(使用VirtualAllocEx函數(shù)) 存放DLL的路徑,方便后續(xù)執(zhí)行LoadLibraryA2. 將DLL路線寫入到目標(biāo)進(jìn)程(使用WriteProcessMemory函數(shù))3. 獲取LoadLibraryA函數(shù)地址(使用GetProcAddress),將其做為線程的回調(diào)函數(shù)4. 在目標(biāo)進(jìn)程 創(chuàng)建線程并執(zhí)行(使用CreateRemoteThread)*/HANDLE targetProc = OpenProcess(PROCESS_ALL_ACCESS,FALSE,m_pId);if( targetProc == NULL ){qDebug() << "OpenProcess error";return;}QString dllPath = m_dllPath;const char* pChar = dllPath.toStdString().c_str();int dllLen = dllPath.length();// 1.目標(biāo)進(jìn)程申請(qǐng)空間LPVOID pDLLPath = VirtualAllocEx(targetProc,NULL,dllLen,MEM_COMMIT,PAGE_READWRITE );if( pDLLPath == NULL ){qDebug() << "VirtualAllocEx error";return;}SIZE_T wLen = 0;// 2.將DLL路徑寫進(jìn)目標(biāo)進(jìn)程內(nèi)存空間int ret = WriteProcessMemory(targetProc,pDLLPath,pChar,dllLen,&wLen);if( ret == 0 ){qDebug() << "WriteProcessMemory error";return;}// 3.獲取LoadLibraryA函數(shù)地址FARPROC myLoadLibrary = GetProcAddress(GetModuleHandleA("kernel32.dll"),"LoadLibraryA");if( myLoadLibrary == NULL ){qDebug() << "GetProcAddress error";return;}// 4.在目標(biāo)進(jìn)程執(zhí)行LoadLibrary 注入指定的線程HANDLE tHandle = CreateRemoteThread(targetProc,NULL,NULL,(LPTHREAD_START_ROUTINE)myLoadLibrary,pDLLPath,NULL,NULL);if(tHandle == NULL){qDebug() << "CreateRemoteThread error";return ;}qDebug() << "注入,wait ..." ;WaitForSingleObject(tHandle,INFINITY);CloseHandle(tHandle);CloseHandle(targetProc);qDebug() << "注入,finish ...";emit doInjectFinish(); }卸載DLL
// 卸載DLL void WorkerThread::uninstallDLL() {/*卸載步驟和注入DLL步驟實(shí)質(zhì)差不多.注入DLL是 在目標(biāo)進(jìn)程中執(zhí)行LoadLibraryA卸載DLL是 在目標(biāo)進(jìn)程中執(zhí)行FreeLibrary函數(shù),不同的是卸載不需要再目標(biāo)進(jìn)程中申請(qǐng)空間,因?yàn)镕reeLibrary參數(shù)為HMODULE 實(shí)際上就是一個(gè)指針值。這個(gè)句柄已經(jīng)加載就已經(jīng)存在。*/HANDLE targetProc = OpenProcess(PROCESS_ALL_ACCESS,FALSE,m_pId);if( targetProc == NULL ){qDebug() << "OpenProcess error";return;}QString dllPath = m_dllPath;// 1. 獲取卸載dll的模塊句柄HANDLE snapHandele = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE ,m_pId);if( INVALID_HANDLE_VALUE == snapHandele){qDebug() << "CreateToolhelp32Snapshot error" ;return;}MODULEENTRY32 entry = {0};entry.dwSize = sizeof(entry);// 長度必須賦值BOOL ret = Module32First(snapHandele,&entry);HMODULE dllHandle = NULL;while (ret) {QString dllName = QString::fromWCharArray(entry.szModule);if(dllPath.endsWith(dllName)){dllHandle = entry.hModule;qDebug() << dllName;break;}ret = Module32Next(snapHandele,&entry);}CloseHandle(snapHandele);if( dllHandle == NULL ){qDebug() << "dll 并未被加載";return;}// 2.獲取FreeLibrary函數(shù)地址FARPROC myLoadLibrary = GetProcAddress(GetModuleHandleA("kernel32.dll"),"FreeLibrary");if( myLoadLibrary == NULL ){qDebug() << "GetProcAddress error";return;}// 3.在目標(biāo)進(jìn)程執(zhí)行FreeLibrary 卸載指定的線程HANDLE tHandle = CreateRemoteThread(targetProc,NULL,NULL,(LPTHREAD_START_ROUTINE)myLoadLibrary,dllHandle,NULL,NULL);if(tHandle == NULL){qDebug() << "CreateRemoteThread error";return ;}qDebug() << "卸載,wait ..." ;WaitForSingleObject(tHandle,INFINITY);CloseHandle(tHandle);CloseHandle(targetProc);qDebug() << "卸載,finish ...";emit doUninstallFinish(); }完整工程
整個(gè)qt工程請(qǐng)?jiān)谶@里下載(含DLLMsgBox.dll)。
總結(jié)
以上是生活随笔為你收集整理的Qt:Windows编程—DLL注入与卸载的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二、内存空间
- 下一篇: java信息管理系统总结_java实现科