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 -
} -
}
