DLL注入:
1. 使用註冊表注入dll
HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\CurrentVersion\Windows\AppInit_DLLs
AppInit_Dlls中設置待注入的dll絕對路徑
LoadAppInit_Dlls值設爲1
2. 使用Windows掛鉤注入dll
需要使用SetWindowsHookEx函數,MSDN定義如下:
HHOOK WINAPI SetWindowsHookEx(
_In_ int idHook,
_In_ HOOKPROC lpfn,
_In_ HINSTANCE hMod,
_In_ DWORD dwThreadId
);
第一個參數爲掛鉤類型;
第二個參數爲一個函數地址,即掛鉤類型事件發生時,系統應該調用的函數;
第三個參數標識一個dll,這個dll中包含第二個參數表示的函數;
第四個參數表示要給哪個線程掛鉤,0表示所有運行線程
以下給出一個示例:
HOOKPROC hkprcSysMsg;
static HINSTANCE hinstDLL;
static HHOOK hhookSysMsg;
hinstDLL = LoadLibrary(TEXT("c:\\myapp\\sysmsg.dll"));
hkprcSysMsg = (HOOKPROC)GetProcAddress(hinstDLL, "SysMessageProc");
hhookSysMsg = SetWindowsHookEx(
WH_SYSMSGFILTER,
hkprcSysMsg,
hinstDLL,
0);
可以使用UnhookWindowsHookEx取消掛鉤:
BOOL WINAPI UnhookWindowsHookEx(
_In_ HHOOK hhk
);
3. 使用遠程線程注入dll
Dll注入的本質即讓目標進程中的一個線程通過LoadLibrary()加載待注入dll文件。
通常情況下,無法控制目標進程線程,此時可以創建一個線程了實現上述目的,CreateRemoteThread便提供了此功能。
HANDLE WINAPI CreateRemoteThread(
_In_ HANDLE hProcess,
_In_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_ LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_Out_ LPDWORD lpThreadId
);
hProcess表示待注入的目標進程;
lpStartAddress表示線程函數,此處設置爲LoadLibrary();
lpParameter表示傳遞給線程函數的參數,此時設置爲待注入的dll文件絕對路徑。
使用CreateRemoteThread具體注入過程如下:
(1) 使用VirtualAllocEx在遠程進程中申請一塊內存空間;
(2) 使用WriteProcessMemory將Dll路徑名複製到(1)中申請的地址;
(3) 使用GetProcAddress獲取LoadLibraryW或LoadLibraryA的實際地址;
(4) 使用CreateRemoteThread在遠程進程中創建新的線程,新線程調用LoadLibrary,並在參數中傳入(1)中申請的內存地址,此時dll文件已經注入到遠程線程,DllMain將執行我們設計的代碼。
此時遠程線程中保留一塊申請的內存空間,需要對其進行釋放:
(5) 使用VirtualFreeEx釋放(1)中申請的內存;
(6) 使用GetProcAddress獲取FreeLibrary函數實際地址;
(7) 使用CreateRemoteThread創建新的線程,調用FreeLibrary,參數傳入已注入Dll的HMODULE.
4. 使用木馬dll注入dll
將進程需要加載的dll文件替換掉,實現dll劫持。
例如,進程需要使用lpk.dll,通過僞造lpk.dll文件,利用windows應用程序加載dll的順序,讓其優先加載僞造的lpk.dll文件。
5. 把dll作爲調試器注入
(1) DebugActiveProcess(pid)附加進程;
(2) Debug循環函數:WaitForDebugEvent等待調試事件,針對不同類型進行處理;
(3) ReadProcessMemory, WriteProcessMemory操作目標進程,鉤取指定API
6. 使用CreateProcess注入代碼
該方法用於向子進程注入代碼:
(1) 創建子進程並掛起;
(2) 獲取主線程內存地址;
(3) 保存主線程起始地址指令;
(4) 插入指令,調用LoadLibrary加載dll
(5) 子進程恢復運行;
(6) 執行插入的指令;
(7) 恢復原指令,按照原來的邏輯繼續執行。
API 攔截:
1. 通過覆蓋代碼攔截API
(1) 獲取待攔截函數地址;
(2) 保存該函數初始幾個字節指令;
(3) 使用JMP替換這些指令,讓其跳轉到自定義的函數中,需要注意的是,自定義函數需與原函數具有相同的簽名,相同的參數,相同的返回值,相同的調用約定;
(4) 程序調用被攔截函數時,將跳轉到自定義函數中執行;
(5) 執行完畢後,將保存的原指令恢復到起始地址,調用函數讓其按照原來的邏輯執行。
2. 通過修改IAT攔截API
每一個導入的dll文件對應一個IMAGE_IMPORT_DESCRIPTOR結構:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
} DUMMYUNIONNAME;
DWORD TimeDateStamp; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)
DWORD ForwarderChain; // -1 if no forwarders
DWORD Name;
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
其中成員變量FirstThunk指向一個IMAGE_THUNK_DATA結構體數組,數組中每一項對應一個導入函數:
typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString; // PBYTE
DWORD Function; // PDWORD
DWORD Ordinal;
DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 IMAGE_THUNK_DATA;
typedef PIMAGE_THUNK_DATA32 PIMAGE_THUNK_DATA;
因此,IAT HOOK的主要思路爲:
(1) 遍歷IMAGE_IMPORT_DESCRIPTOR數組,找到name爲需要hook的dll;
(2) 遍歷FirstThunk指向的IMAGE_THUNK_DATA,判斷Function是否爲待hook的API,若是,則替換爲新的API.
以下是《Windows核心編程》中作者給出的實現函數:
void ReplaceIATEntryInOneMod(PCSTR pszCalleeModName, //被調模塊
PROC pfnCurrent, //待HOOK原函數
PROC pfnNew, //替換新函數
HMODULE hmodCaller //調用新函數的模塊
) {
// Get the address of the module's import section
ULONG ulSize;
// An exception was triggered by Explorer (when browsing the content of
// a folder) into imagehlp.dll. It looks like one module was unloaded...
// Maybe some threading problem: the list of modules from Toolhelp might
// not be accurate if FreeLibrary is called during the enumeration.
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = NULL;
__try {
pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData(
hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);
}
__except (InvalidReadExceptionFilter(GetExceptionInformation())) {
// Nothing to do in here, thread continues to run normally
// with NULL for pImportDesc
}
if (pImportDesc == NULL)
return; // This module has no import section or is no longer loaded
// Find the import descriptor containing references to callee's functions
for (; pImportDesc->Name; pImportDesc++) {
PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name);
if (lstrcmpiA(pszModName, pszCalleeModName) == 0) {
// Get caller's import address table (IAT) for the callee's functions
PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
((PBYTE) hmodCaller + pImportDesc->FirstThunk);
// Replace current function address with new function address
for (; pThunk->u1.Function; pThunk++) {
// Get the address of the function address
PROC* ppfn = (PROC*) &pThunk->u1.Function;
// Is this the function we're looking for?
BOOL bFound = (*ppfn == pfnCurrent);
if (bFound) {
if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
sizeof(pfnNew), NULL) && (ERROR_NOACCESS == GetLastError())) {
DWORD dwOldProtect;
if (VirtualProtect(ppfn, sizeof(pfnNew), PAGE_WRITECOPY,
&dwOldProtect)) {
WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
sizeof(pfnNew), NULL);
VirtualProtect(ppfn, sizeof(pfnNew), dwOldProtect,
&dwOldProtect);
}
}
return; // We did it, get out
}
}
} // Each import section is parsed until the right entry is found and patched
}
}