Windows平臺Ring3下DLL注入(HOOK)方法整理彙總
1.dll劫持,粗略整理了下,可以劫持的dll有(持續更新):
lpk.dll、usp10.dll、msimg32.dll、midimap.dll、ksuser.dll、comres.dll、ddraw.dll
以lpk為例,在win7下由於lpk被加入KnownDLLs且該登錄檔值不可修改,使得lpk強制從系統目錄載入,
不過可以將lpk.dll加入ExcludeFromKnownDlls來解決,具體可以建立一個lpk.reg檔案:
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager] "ExcludeFromKnownDlls"=hex(7):6c,00,70,00,6b,00,2e,00,64,00,6c,00,6c,00,00,00,\ 00,00
成功匯入後需要重新啟動電腦才能生效。
另外win7下的lpk在編寫方面需要注意:
WIN7有的程式呼叫LPK.DLL的LpkInitialize輸出函式在LPK的初始化前面. 要在LpkInitialize這個函式中加入一些處理,並且這部分程式碼不能加密.
因此為了相容各個系統,可以在DllMain和LpkInitialize裡均做判斷,如果沒有初始化就進行初始化。下面貼出完整程式碼:
// lpk.cpp : Defines the entry point for the DLL application. // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 標頭檔案#include "stdafx.h" //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 匯出函式#pragmacomment(linker, "/EXPORT:LpkInitialize=_AheadLib_LpkInitialize,@1") #pragma comment(linker, "/EXPORT:LpkTabbedTextOut=_AheadLib_LpkTabbedTextOut,@2") #pragma comment(linker, "/EXPORT:LpkDllInitialize=_AheadLib_LpkDllInitialize,@3") #pragma comment(linker, "/EXPORT:LpkDrawTextEx=_AheadLib_LpkDrawTextEx,@4") //#pragma comment(linker, "/EXPORT:LpkEditControl=_AheadLib_LpkEditControl,@5")#pragma comment(linker, "/EXPORT:LpkExtTextOut=_AheadLib_LpkExtTextOut,@6") #pragma comment(linker, "/EXPORT:LpkGetCharacterPlacement=_AheadLib_LpkGetCharacterPlacement,@7") #pragma comment(linker, "/EXPORT:LpkGetTextExtentExPoint=_AheadLib_LpkGetTextExtentExPoint,@8") #pragma comment(linker, "/EXPORT:LpkPSMTextOut=_AheadLib_LpkPSMTextOut,@9") #pragma comment(linker, "/EXPORT:LpkUseGDIWidthCache=_AheadLib_LpkUseGDIWidthCache,@10") #pragma comment(linker, "/EXPORT:ftsWordBreak=_AheadLib_ftsWordBreak,@11") //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 巨集定義#define EXTERNC extern "C" #define NAKED __declspec(naked) #define EXPORT __declspec(dllexport) #define ALCPP EXPORT NAKED #define ALSTD EXTERNC EXPORT NAKED void __stdcall #define ALCFAST EXTERNC EXPORT NAKED void __fastcall #define ALCDECL EXTERNC NAKED void __cdecl //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //LpkEditControl匯出的是陣列,不是單一的函式(by Backer)EXTERNC void __cdecl AheadLib_LpkEditControl(void); EXTERNC __declspec(dllexport) void (*LpkEditControl[14])() = {AheadLib_LpkEditControl}; //////////////////////////////////////////////////////////////////////////////////////////////////新增全域性變數BOOL g_bInited = FALSE; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // AheadLib 名稱空間namespace AheadLib { HMODULE m_hModule = NULL; // 原始模組控制代碼 // 載入原始模組 BOOL WINAPI Load() { TCHAR tzPath[MAX_PATH]; TCHAR tzTemp[MAX_PATH * 2]; GetSystemDirectory(tzPath, MAX_PATH); lstrcat(tzPath, TEXT("\\lpk.dll")); OutputDebugString(tzPath); m_hModule=LoadLibrary(tzPath); if (m_hModule == NULL) { wsprintf(tzTemp, TEXT("無法載入 %s,程式無法正常執行。"), tzPath); MessageBox(NULL, tzTemp, TEXT("AheadLib"), MB_ICONSTOP); }; return (m_hModule != NULL); } // 釋放原始模組 VOID WINAPI Free() { if (m_hModule) { FreeLibrary(m_hModule); } } // 獲取原始函式地址 FARPROC WINAPI GetAddress(PCSTR pszProcName) { FARPROC fpAddress; CHAR szProcName[16]; TCHAR tzTemp[MAX_PATH]; fpAddress = GetProcAddress(m_hModule, pszProcName); if (fpAddress == NULL) { if (HIWORD(pszProcName) == 0) { wsprintf(szProcName, "%d", pszProcName); pszProcName = szProcName; } wsprintf(tzTemp, TEXT("無法找到函式 %hs,程式無法正常執行。"), pszProcName); MessageBox(NULL, tzTemp, TEXT("AheadLib"), MB_ICONSTOP); ExitProcess(-2); } return fpAddress; } } using namespace AheadLib; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////函式宣告void WINAPIV Init(LPVOID pParam); //////////////////////////////////////////////////////////////////////////////////////////////// void WINAPIV Init(LPVOID pParam) { //在這裡新增DLL載入程式碼 return; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 入口函式BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved) { if (dwReason == DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(hModule); if ( g_bInited==FALSE ){ Load(); g_bInited = TRUE; } //LpkEditControl這個陣列有14個成員,必須將其複製過來 memcpy((LPVOID)(LpkEditControl+1), (LPVOID)((int*)GetAddress("LpkEditControl") + 1),52); _beginthread(Init,NULL,NULL); } else if (dwReason == DLL_PROCESS_DETACH) { Free(); } return TRUE; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 匯出函式ALCDECL AheadLib_LpkInitialize(void) { if ( g_bInited==FALSE ){ Load(); g_bInited = TRUE; } GetAddress("LpkInitialize"); __asm JMP EAX; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 匯出函式ALCDECL AheadLib_LpkTabbedTextOut(void) { GetAddress("LpkTabbedTextOut"); __asm JMP EAX; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 匯出函式ALCDECL AheadLib_LpkDllInitialize(void) { GetAddress("LpkDllInitialize"); __asm JMP EAX; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 匯出函式ALCDECL AheadLib_LpkDrawTextEx(void) { GetAddress("LpkDrawTextEx"); __asm JMP EAX; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 匯出函式ALCDECL AheadLib_LpkEditControl(void) { GetAddress("LpkEditControl"); __asm jmp DWORD ptr [EAX];//這裡的LpkEditControl是陣列,eax存的是函式指標} //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 匯出函式ALCDECL AheadLib_LpkExtTextOut(void) { GetAddress("LpkExtTextOut"); __asm JMP EAX; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 匯出函式ALCDECL AheadLib_LpkGetCharacterPlacement(void) { GetAddress("LpkGetCharacterPlacement"); __asm JMP EAX; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 匯出函式ALCDECL AheadLib_LpkGetTextExtentExPoint(void) { GetAddress("LpkGetTextExtentExPoint"); __asm JMP EAX; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 匯出函式ALCDECL AheadLib_LpkPSMTextOut(void) { GetAddress("LpkPSMTextOut"); __asm JMP EAX; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 匯出函式ALCDECL AheadLib_LpkUseGDIWidthCache(void) { GetAddress("LpkUseGDIWidthCache"); __asm JMP EAX; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 匯出函式ALCDECL AheadLib_ftsWordBreak(void) { GetAddress("ftsWordBreak"); __asm JMP EAX; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2.通過CreateRemoteThread建立遠端執行緒。
XP以下使用程式碼:
BOOL WINAPI RemoteLoadLibrary(LPCTSTR pszDllName, DWORD dwProcessId) { // 開啟目標程序 HANDLE hProcess = ::OpenProcess( PROCESS_VM_WRITE|PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION, FALSE, dwProcessId); if(hProcess == NULL) return FALSE; // 在目標程序申請空間,存放字串pszDllName,作為遠端執行緒的引數 int cbSize = (::lstrlen(pszDllName) + 1); LPVOID lpRemoteDllName = ::VirtualAllocEx(hProcess, NULL, cbSize, MEM_COMMIT, PAGE_READWRITE); ::WriteProcessMemory(hProcess, lpRemoteDllName, pszDllName, cbSize, NULL); // 取得LoadLibraryA函式的地址,我們將以它作為遠端執行緒函式啟動 HMODULE hModule=::GetModuleHandle (_T("kernel32.dll")); LPTHREAD_START_ROUTINE pfnStartRoutine = (LPTHREAD_START_ROUTINE)::GetProcAddress(hModule, "LoadLibraryA"); // 啟動遠端執行緒 HANDLE hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, pfnStartRoutine, lpRemoteDllName, 0, NULL); if(hRemoteThread == NULL) { ::CloseHandle(hProcess); return FALSE; } ::CloseHandle(hRemoteThread); ::CloseHandle(hProcess); return TRUE; }
我參考上面資料和程式碼,稍作整理使之編譯通過並能使用,目標程序開啟時最好使用PROCESS_ALL_ACCESS許可權。
vista的較為簡單些,只要修改一個記憶體裡的數值,這裡不再實現。
typedef struct _CLIENT_ID { HANDLE UniqueProcess; HANDLE UniqueThread; } CLIENT_ID,*PCLIENT_ID; typedef struct _INITIAL_TEB { PVOID PreviousStackBase; PVOID PreviousStackLimit; PVOID StackBase; PVOID StackLimit; PVOID AllocatedStackBase; } INITIAL_TEB, *PINITIAL_TEB; typedef NTSTATUS (NTAPI *TZwAllocateVirtualMemory)( __in HANDLE ProcessHandle, __inout PVOID *BaseAddress, __in ULONG_PTR ZeroBits, __inout PSIZE_T RegionSize, __in ULONG AllocationType, __in ULONG Protect ); static TZwAllocateVirtualMemory ZwAllocateVirtualMemory = (TZwAllocateVirtualMemory)GetProcAddress(GetModuleHandle("ntdll.dll"),"ZwAllocateVirtualMemory"); typedef NTSYSAPI NTSTATUS (NTAPI *TZwWriteVirtualMemory) ( IN HANDLE ProcessHandle, IN PVOID BaseAddress, IN PVOID Buffer, IN SIZE_T NumberOfBytesToWrite, OUT PSIZE_T NumberOfBytesWritten ); static TZwWriteVirtualMemory ZwWriteVirtualMemory = (TZwWriteVirtualMemory)GetProcAddress(GetModuleHandle("ntdll.dll"),"ZwWriteVirtualMemory"); typedef NTSYSAPI NTSTATUS (NTAPI *TZwProtectVirtualMemory) ( IN HANDLE ProcessHandle, IN PVOID * BaseAddress, IN SIZE_T * NumberOfBytesToProtect, IN ULONG NewAccessProtection, OUT PULONG OldAccessProtection ); static TZwProtectVirtualMemory ZwProtectVirtualMemory = (TZwProtectVirtualMemory)GetProcAddress(GetModuleHandle("ntdll.dll"),"ZwProtectVirtualMemory"); typedef NTSYSAPI NTSTATUS (NTAPI *TZwGetContextThread) ( IN HANDLE ThreadHandle, OUT PCONTEXT Context ); static TZwGetContextThread ZwGetContextThread = (TZwGetContextThread)GetProcAddress(GetModuleHandle("ntdll.dll"),"ZwGetContextThread"); typedef NTSYSAPI NTSTATUS (NTAPI *TZwCreateThread) ( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN HANDLE ProcessHandle, OUT PCLIENT_ID ClientId, IN PCONTEXT ThreadContext, IN PINITIAL_TEB UserStack, IN BOOLEAN CreateSuspended ); static TZwCreateThread ZwCreateThread = (TZwCreateThread)GetProcAddress(GetModuleHandle("ntdll.dll"),"ZwCreateThread"); typedef NTSYSAPI NTSTATUS (NTAPI *TZwResumeThread) ( IN HANDLE ThreadHandle, OUT PULONG SuspendCount ); static TZwResumeThread ZwResumeThread = (TZwResumeThread)GetProcAddress(GetModuleHandle("ntdll.dll"),"ZwResumeThread"); HANDLE WINAPI myCreateRemoteThread( HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId) { //by 80695073(QQ) //email [email protected] CONTEXT context = {CONTEXT_FULL}; CLIENT_ID cid={hProcess}; DWORD ret; HANDLE hThread = NULL; DWORD StackReserve; DWORD StackCommit = 0x1000; ULONG_PTR Stack = 0; INITIAL_TEB InitialTeb={}; ULONG x; const CHAR myBaseThreadInitThunk[] = { // 00830000 8BFF mov edi, edi '\x8B','\xFF', // 00830002 55 push ebp '\x55', // 00830003 8BEC mov ebp, esp '\x8B','\xEC', // 00830005 51 push ecx //ntdll.RtlExitUserThread '\x51', // 00830006 53 push ebx //引數 '\x53', // 00830007 FFD0 call eax //函式地址 '\xFF','\xD0', // 00830009 59 pop ecx //恢復結束函式地址 '\x59', // 0083000A 50 push eax //將剛才的結果壓棧 '\x50', // 0083000B FFD1 call ecx //呼叫RtlExitUserThread 結束 '\xFF','\xD1', // 0083000D 90 nop '\x90' }; PVOID pBaseThreadThunk = NULL; //不能釋放 //0、分配非OS的載入函式 StackReserve = 0x1000; ret = ZwAllocateVirtualMemory(hProcess, /*&stack.ExpandableStackBottom*/(PVOID*)&pBaseThreadThunk, 0, &StackReserve, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (ret >= 0x80000000) { //失敗 TRACE("Error IN myCreateRemoteThread ZwAllocateVirtualMemory0 !\n"); goto myCreateRemoteThreadRet; //end } ret = ZwWriteVirtualMemory(hProcess, pBaseThreadThunk, (LPVOID)myBaseThreadInitThunk, sizeof(myBaseThreadInitThunk),&x); if (ret >= 0x80000000) { //失敗 TRACE("Error IN myCreateRemoteThread ZwAllocateVirtualMemory0 !\n"); goto myCreateRemoteThreadRet; //end } //1、準備堆疊 StackReserve = 0x10000; ret = ZwAllocateVirtualMemory(hProcess, /*&stack.ExpandableStackBottom*/(PVOID*)&Stack, 0, &StackReserve, MEM_RESERVE, PAGE_READWRITE); if (ret >= 0x80000000) { //失敗 TRACE("Error IN myCreateRemoteThread ZwAllocateVirtualMemory1!\n"); goto myCreateRemoteThreadRet; //end } TRACE("OK myCreateRemoteThread:ZwAllocateVirtualMemory 0x%08x\n",Stack); InitialTeb.AllocatedStackBase = (PVOID)Stack; InitialTeb.StackBase = (PVOID)(Stack + StackReserve); /* Update the Stack Position */ Stack += StackReserve - StackCommit; Stack -= 0x1000; StackCommit += 0x1000; /* Allocate memory for the stack */ ret = ZwAllocateVirtualMemory(hProcess, (PVOID*)&Stack, 0, &StackCommit, MEM_COMMIT, PAGE_READWRITE); if (ret >= 0x80000000) { //失敗 TRACE("Error IN myCreateRemoteThread ZwAllocateVirtualMemory2!\n"); goto myCreateRemoteThreadRet; //end } TRACE("OK myCreateRemoteThread:ZwAllocateVirtualMemory 2 0x%08x\n",Stack); InitialTeb.StackLimit = (PVOID)Stack; StackReserve = 0x1000; ret = ZwProtectVirtualMemory(hProcess, (PVOID*)&Stack, &StackReserve, PAGE_READWRITE | PAGE_GUARD, &x); if (ret >= 0x80000000) { //失敗 TRACE("Error IN myCreateRemoteThread ZwProtectVirtualMemory!\n"); goto myCreateRemoteThreadRet; //end } /* Update the Stack Limit keeping in mind the Guard Page */ InitialTeb.StackLimit = (PVOID)((ULONG_PTR)InitialTeb.StackLimit - 0x1000); //2、準備CONTEXT // CONTEXT context = {CONTEXT_FULL}; ret = ZwGetContextThread(GetCurrentThread(),&context); if (ret >= 0x80000000) { //失敗 TRACE("Error IN myCreateRemoteThread ZwGetContextThread!\n"); goto myCreateRemoteThreadRet; //end } context.Esp = (DWORD)InitialTeb.StackBase; context.Eip = (DWORD)pBaseThreadThunk; //這裡填寫需要載入的地址,不過需要自己終結自己 context.Ebx = (DWORD)lpParameter; //other init //must context.Eax = (DWORD)lpStartAddress; context.Ecx = (DWORD)GetProcAddress(GetModuleHandle("ntdll.dll"),"RtlExitUserThread");//0x778B0859;/*win7*///0x77AEEC01;/*vista*/ //ntdll.RtlExitUserThread context.Edx = 0x00000000; //nouse ret = ZwCreateThread(&hThread, THREAD_ALL_ACCESS, 0, hProcess, &cid, &context, &InitialTeb, TRUE); if (ret >= 0x80000000) { //失敗 TRACE("Error %d\n",GetLastError()); goto myCreateRemoteThreadRet; //end } if(lpThreadId) { *lpThreadId = (DWORD)cid.UniqueThread; } if (!(dwCreationFlags & CREATE_SUSPENDED)) { ZwResumeThread(hThread, NULL); } myCreateRemoteThreadRet: return hThread; }
最後通用的使用方法是:
// 啟動遠端執行緒 HANDLE hRemoteThread = NULL; OSVERSIONINFO svex = {sizeof(OSVERSIONINFO)}; GetVersionEx(&svex); if( svex.dwMajorVersion<=5 ){ hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, pfnStartRoutine, lpRemoteDllName, 0, NULL); }else{ hRemoteThread = myCreateRemoteThread(hProcess, NULL, 0, pfnStartRoutine, lpRemoteDllName, 0, NULL); }
3.通過SetWindowsHookEx安裝鉤子,如WH_CALLWNDPROC,WH_KEYBOARD,WH_MOUSE,WH_GETMESSAGE鉤子可以實現全域性注入。
SetWindowsHookEx(WH_MOUSE,(HOOKPROC)MouseProc,AfxGetInstanceHandle(),dwThreadId);
4.AppInit_DLLs方式:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs注入到所有載入了user32.dll的程序。
win7下會被對映到:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows,
並且需要設定LoadAppInit_DLLs為1時AppInit_DLLs才會被啟用,預設為0。
例如在xp下建立一個.reg檔案:
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows] "AppInit_DLLs"="c:\\message.dll"
手動匯入後是可以載入指定dll的,但是在win7下面就不行,通過該.reg檔案操作的登錄檔子鍵路徑並沒有被重定向到
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows,
但是通過寫程式碼的方式是成功的:
void LoadLibByAppInit_DLLs(LPCTSTR pszDllName,BOOL bInstall) { HKEY hKey = NULL; DWORD dwRet = 0; //win7下會被對映到:HKEY_LOCAL