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