HHOOK WINAPI SetWindowsHookEx(
\\1,钩子类型
__in int idHook,
\\2,函数地址,即挂钩类型事件发生时,系统应该调用的函数;
__in HOOKPROC lpfn,
\\3,标识一个dll,这个dll中包含第二个参数表示的函数;实例句柄
__in HINSTANCE hMod,
\\4,线程ID,0表示系统钩子,0表示给所有GUI线程安装
__in DWORD dwThreadId);
//如果函数成功,则返回挂钩线程的句柄.如果函数失败,返回值为NULL.
//要获取扩展错误信息,请调用GetLastError
钩子类型:
第一个参数为钩子的类型,钩子的类型一共还有14种,表示在什么时机调用钩子。
常用钩子类型有以下几种:
(1)键盘钩子和低级键盘钩子可以监视各种键盘消息。
(2)鼠标钩子和低级鼠标钩子可以监视各种鼠标消息。
(3)外壳钩子可以监视各种Shell事件消息。比如启动和关闭应用程序。
(4)日志钩子可以记录从系统消息队列中取出的各种事件消息。
(5)窗口过程钩子监视所有从系统消息队列发往目标窗口的消息。
例子:
#include "stdafx.h"
#include
int _tmain(int argc, _TCHAR* argv[])
{
/*
* Load library in which we'll be hooking our functions.
*/
HMODULE dll = LoadLibrary(L"test_dll.dll");
if(dll == NULL) {
DWORD err = GetLastError();
printf("The DLL could not be found.\n");
getchar();
return -1;
}
//__asm int 3;
/*
* Get the address of the function inside the DLL.
*/
HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "meconnect");
if(addr == NULL) {
printf("The function was not found.\n");
getchar();
return -1;
}
/*
* Hook the function.
*/
HHOOK handle = SetWindowsHookEx(WH_KEYBOARD, addr, dll, 0);
if(handle == NULL) {
printf("The KEYBOARD could not be hooked.\n");
}
/*
* Unhook the function.
*/
printf("Program successfully hooked,
\nPress enter to unhook the function and stop the program.\n");
getchar();
UnhookWindowsHookEx(handle);
return 0;
}
BOOL WINAPI UnhookWindowsHookEx(
_In_ HHOOK hhk
);
略
1、如果对于同一事件(如鼠标消息)既安装了线程钩子又安装了系统钩子,那么系统会自动先调用线程钩子,然后调用系统钩子。
2、对同一事件消息可安装多个钩子处理过程,这些钩子处理过程形成了钩子链。当前钩子处理结束后应把钩子信息传递给下一个钩子函数。
3、特别是系统钩子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装钩子,在使用完毕后要及时卸载。
4、32位DLL不能被注入到64位进程中,64位DLL不能被注入到32位进程中。
5、调用CallNextHookEx函数链接到下一个钩子过程是可选的,但强烈建议; 否则,已安装钩子的其他应用程序将不会收到钩子通知,因此可能会出现错误的行为。应该调用CallNextHookEx。
6、所有全局钩子函数都必须在库中。
7、目标进程B必须先运行,安装钩子必须在目标进程运行之后,否则钩子会失效。安装全局钩子时,会将钩子和当前系统的所有线程关联起来。后面建立的线程当然就不在其列了。
8、A用LoadLibrary加载val.dll时,两者必须是同一种版本,即同为Debug版或Release版,否则会出现找不到目标文件错误。
实际中使用最为广泛的一种注入方式,它即可以精确指定需要注入的进程,又可以注入到非GUI程序中。
DLL注入指的是向运行中的其他进程强制插入特定的DLL文件,从而使之运行特定代码。
DLL注入基本过程:运行程序使其他进程调用LoadLibrary()API,调用用户指定的DLL文件,从而在LoadLibrary()完成后,调用DLL文件中的DllMain()函数。
DLL加载到进程后会自动运行DllMain()函数,用户可以把想要执行的代码放到DllMain()函数里,每当该DLL被加载时,添加的代码就会被执行。利用该过程可以修复程序bug,编写恶意DLL等
WINBASEAPI
_Ret_maybenull_
HANDLE
WINAPI
CreateRemoteThread(
//线程所属进程的进程句柄.
_In_ HANDLE hProcess,
//一个指向 SECURITY_ATTRIBUTES 结构的指针, 该结指定了线程的安全属性.
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
//线程初始大小,以字节为单位,如果该值设为0,那么使用系统默认大小.
_In_ SIZE_T dwStackSize,
//在远程进程的地址空间中,该线程的线程函数的起始地址.
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
//传给线程函数的参数.
_In_opt_ LPVOID lpParameter,
线程的创建标志.
_In_ DWORD dwCreationFlags,
//指向所创建线程ID的指针,如果创建失败,该参数为NULL.
_Out_opt_ LPDWORD lpThreadId
);
//如果调用成功,返回新线程句柄
//如果失败,返回NULL
第一个参数指定新创建的线程归哪个进程所有,其他参数和CreateThread相同。
WINBASEAPI
_Ret_maybenull_ _Post_writable_byte_size_(dwSize)
LPVOID
WINAPI
VirtualAllocEx(
//申请内存所在的进程句柄。
_In_ HANDLE hProcess,
//保留页面的内存地址;一般用NULL自动分配。
_In_opt_ LPVOID lpAddress,
//欲分配的内存大小,字节单位;
//注意实际分配的内存大小是页内存大小的整数倍
_In_ SIZE_T dwSize,
//指定内存分配的方式,预定还是要提交
_In_ DWORD flAllocationType,
//指定应用程序读写的权限,内存的保护属性
_In_ DWORD flProtect
);
函数执行成功就返回分配内存的首地址,不成功就是NULL。
远程注入DLL路径:
hTargeProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID); // dwProcessID为被注入目标进程的进程ID
if (!hTargeProcess) {
return;
}
SIZE_T dllPathSize = strlen(pszDllPath); // pszDllPath存储了DLL的路径
pVM4DllPath = VirtualAllocEx(hTargeProcess, NULL, dllPathSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!pVM4DllPath) {
return;
}
if (!WriteProcessMemory(hTargeProcess, pVM4DllPath, pszDllPath, dllPathSize, NULL)) {
return;
}
1、用VirtualAllocEx函数在远程进程申请空间。
2、用WriteProcessMemory函数把DLL的路径复制到申请的内存。
3、用GetProcessAddress函数得到LoadLibraryW(LoadLibraryA)函数(Kernel32.dll)中实际地址。
4、用CreateRenoteThread函数在远程线程创建一个线程,让新的线程调用LoadLibrary函数并在参数中传入第一步分配给的内存地址。这时DLL就被注入到了远程进程地址空间,DLL函数DLLMain会就收触发事件,然后执行我们需要执行的代码,DLLMain返回时远程线程会从LoadLibraryW(LoadLibraryA)调用返回到BaseThreadStart函数。BaseThreadStart然后调用ExitThread,使远程线程终止。
5、需要清理申请的空间,远程线程还在目标进程地址空间。
6、用VirtualFreeEx释放申请的内存。
7、用GetProcessAddress得到FreeLibrary函数在Kernel32.dll中的实际地址。
取消注入就是将DLL从目标进程卸载,我们知道,卸载DLL所用的API是FreeLibrary,但我们不能直接调用这个函数,因为直接调用的话是在我们的进程中卸载DLL,而不是目标进程中卸载,很显然这样达不到卸载的目的。我们需要和加载DLL时一样,将FreeLibrary的地址作为第4个参数传给CreateRemoteThread函数,但同样需要通过GetProcAddress来得到FreeLibrary的确切地址:
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "FreeLibrary");
if(pfnThreadRtn) {
hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn, me.modBaseAddr, 0, NULL);
}
1、在代码注入的时候,你所需要的任何变量最好自己以内存拷贝的方式传过去,比如我直接在线程函数里开了一个变量 char[512] = “00000”,当你把这个函数地址拷贝到指定进程里面去的时候,个"00000"所占的内容并没有被拷贝过去,这样你在调用的时候内存就会出错。
2、代码注入限制非常多,被注入的线程函数里面内容非常有讲究,大小有限制,存储方式有限制,不能随便调用API函数,比如直接在里面来了一个Sleep(1000),你觉得这个函数是系统的,在那都能调用,然而并不是,直接就内存错误。
3、请保证要被注入的程序与注入程序和注入DLL是同位的,不要尝试使用32位程序注入64位(或64位注入32位),建议以管理员权限运行。