user-0 提问: 钩子是不是都是通过注入来实现的？

回复:
user: user-0
content: 引用 20 楼 _mervyn 的回复:  本来还想加到六七百分的，结果最大只能四百分，只能这样结贴了。非常感谢您的帮助！

user: user-0
content: 引用 20 楼 _mervyn 的回复:  一万个感谢！  恐怕除了您也没人愿意花这么多时间和精力来给我讲这么详细了  我这里积分不够，等我再多攒点积分再结贴

user: user-1
content: 注入和钩子是两回事，钩子只是注入的一种方式 而且看上面的答复，感觉LZ主要是讨论windows的hook  微软的官方文档对Hook的解释 A hook is a point in the system message-handling mechanism where an application can install a subroutine to monitor the message traffic in the system and process certain types of messages before they reach the target window procedure.  翻译过来就是，hook是系统消息机制的一个挂钩点，通过它能够安装一个子程序来管理消息传送和处理关键类型的消息，在消息被发送到目标窗口程序前。也就是可以简单的把hook理解为一个系统消息的拦截过滤器。  windows提供了好几种消息钩子，每种消息钩子有自己的钩子链，安装一个钩子就是把你的回调函数挂入相应类型的钩子链，在消息发送到目标窗口前，这些钩子链的回调函数会先被调用，这样就可以达到对消息的拦截和过滤。  而注入是指在原有的处理流程中插入一段（想要注入的）处理，从而可以改变原有的处理流程。 注入的方式就很多，LS也做了一些回答了，还可以参考以下了解更多的注入方式 https://blog.csdn.net/Sagittarius_Warrior/article/details/52164204 https://www.freebuf.com/articles/system/187239.html  通过上面对hook和注入的解释可以知道，hook能拦截过滤系统消息，在消息到达目标窗口前插入自己的处理，所以它也是一种注入；但是注入不仅仅只有hook方式，只要到达这个注入目的的任何手段都可以称为注入。所以不要搞混着两个概念，要理解它们的联系和区别。

user: user-0
content: 您说的这些中，有两点我还是有疑问：  引用 13 楼 Yofoo 的回复:不过我总觉得有哪里不对，比如系统消息队列，这个应该是独立于所有进程的吧？那么为什么WH_JOURNALRECORD会无效？还是说我测试的方法不对？ ->消息队列, 一般是说窗体的消息队列, 实际实现是在线程中实现, 在内核的线程结构TEB中, 但是实际引擎的循环是在用户层, 是否需要进程加载dll, 跟前面说的一样, 系统是按尽量简单的逻辑, 事件处理在用户层的进程被隔离, 没办法才这样处理, 否则没那么傻, 故意搞这么麻烦 WH_JOURNALRECORD钩子的描述确实是监视系统消息队列呀，不是线程消息队列。  引用 13 楼 Yofoo 的回复:->死循环的dll注入植物大战僵尸来获取无限阳光, 实际实现如:用远线程注入, dllmain创建一个线程, 线程中死循环写内存, 这样没任何问题 钩子是实时的, 是不会故意开个线程去调用, 很多钩子的回调函数异常时, 会导致整个系统卡死, 由于系统有保护才可以卡了一会又恢复 下面是dll的代码和注入的方式，显然是没有用到线程的，总不可能这个注入工具可以在dll中新开线程吧？

user: user-2
content: MSDN的文档中就有说明  Hook Scope  WH_CALLWNDPROC Thread or global  WH_CALLWNDPROCRET Thread or global  WH_CBT Thread or global  WH_DEBUG Thread or global  WH_FOREGROUNDIDLE Thread or global  WH_GETMESSAGE Thread or global  WH_JOURNALPLAYBACK Global only  WH_JOURNALRECORD Global only  WH_KEYBOARD Thread or global  WH_KEYBOARD_LL Global only  WH_MOUSE Thread or global  WH_MOUSE_LL Global only  WH_MSGFILTER Thread or global  WH_SHELL Thread or global  WH_SYSMSGFILTER Global only    For a specified hook type, thread hooks are called first, then global hooks.   The global hooks are a shared resource, and installing one affects all applications in the same desktop as the calling thread. All global hook functions must be in libraries. Global hooks should be restricted to special-purpose applications or to use as a development aid during application debugging. Libraries that no longer need a hook should remove its hook procedure.

user: user-3
content: 引用 7 楼 六道佩恩 的回复:还有个问题就是，还有哪些类型的钩子是不需要dll的？WH_SYSMSGFILTER？这个你是怎么知道的？我看这几个钩子的共同特点是只能用作全局钩子，你是不是根据这点来说的？那么只要是全局钩子就不需要dll，还是只能用作全局的钩子才能不需要dll？  钩子的意思就是，系统提供了一个挂钩，可以将自己的代码钩上去。执行到这个位置的时候，先执行自己的程序，不执行默认的程序。自己的程序从哪里来？从DLL里面提供啊，当然如果你能用其他的方式提供自己的程序也行啊。

user: user-0
content: 引用 14 楼 _mervyn 的回复: 大佬您太牛了！！！  非常感谢您花了这么多时间回答了这么多详细的干货！  真的太感谢了，我会多花时间多读几遍的！  不过我还是有些疑问，有些也是对您的回应： 1. 关于植物大战僵尸注入的死循环dll，我在16楼里贴了，您看看。我专门确认了一下，这个视频也就十多分钟，没有敲任何创建线程的代码，就是写了这么个函数后，生成dll，然后将dll注入了植物大战僵尸的程序就直接运行了，这就是我觉得匪夷所思的地方。至于“注入是注入，执行是执行”，有时候我也想了解原理，所以这里想问的确实只是注入，还有它成功执行的原理，这里应该不是用的SetWindowHookEx，线程注入的话，它这里又确实没有创建线程，我能想到的方式是，它其实注入了两个dll，一个是这个软件自带的dll，目的就是创建一个新的线程并运行新dll的指定函数，不知道是不是这样。  2. 注入实现的原理，注入的时候会执行dll的DLLMain，按理说被执行的线程只是一个单一的顺序流，执行了DLLMain就意为着顺序流跳到了dll这里，我能想到的实现方式是，由当前执行的指令跳转到指定dll函数，最后再跳转回原地方，但原程序的指令都在内存紧密排列着，不好插入跳转指令吧？那么是由中断实现的吗？还有这种DLL注入的中断？  3. MSDN说的可在自己线程直接用的钩子包括了WH_JOURNAL *，网上的说法也有它，但是我测试却收不到相关消息，或者说钩子没被调用，不知道是什么原因，这是代码（换键盘和鼠标的低级钩子就能正常工作）：  #include "windows.h" LRESULT fun1 ( int code , WPARAM wParam , LPARAM lParam ) { 	printf("按键消息：%x\n", wParam ); 	return CallNextHookEx( NULL, code, wParam, lParam ); }  LRESULT fun2 ( int code , WPARAM wParam , LPARAM lParam ) { 	EVENTMSG *p = (EVENTMSG *)lParam; 	printf("消息：%x\n", p->message );  	return CallNextHookEx( NULL, code, wParam, lParam ); } int main( void ) { 	HHOOK hhook1 = SetWindowsHookEx ( WH_KEYBOARD_LL, fun1, GetModuleHandle(NULL), NULL ); 	HHOOK hhook2 = SetWindowsHookEx ( WH_JOURNALRECORD, fun2, GetModuleHandle(NULL), GetCurrentThreadId() ); 	MSG msg; 	while( GetMessage( &msg , NULL , 0, 0 ) ) 	{     		DispatchMessage(&msg); 	} 	return 0; }   4. 关于您说的系统这个有相关的书籍推荐吗？（希望能通俗易懂点的）       您的我还会仔细阅读的，我说下看了后的一些理解，您看对吗：     每个进程都相当于有系统（核心的管理程序）的备份，当调用系统的API后，顺序流就会进入这些系统核心代码运行，最后再跳回程序代码。系统自己也会运行一些重要的进程来保持系统的正常运作，这样，即便所有普通进程都不调用系统API，通过CPU切换时间片也最终可以调用到系统的这些核心进程，以此保证正常的运作。比如当我们点击鼠标后，由于硬件中断会切换到系统进程（或者直接调用当前进程中的系统映射部分？）来处理。你看这样的说法对吗？  5. 关于系统消息队列那里，消息队列不是有“系统消息队列”和“线程消息队列”之分吗？WH_JOURNALRECORD的说明也明确说的是“the system message queue”？

user: user-4
content: 引用 3 楼 gouyanfen 的回复：exe为什么不能注入？dll可以，exe也可以，只不过地址重定位稍复杂些。 注入为什么执行这个要去看看dll加载消息机制。获得执行权限之后，想做什么就发挥你的想像力了，线程也可以，跳转法也可以+1

user: user-5
content: 引用 17 楼 六道佩恩 的回复:不过我还是有些疑问，有些也是对您的回应： 1. 关于植物大战僵尸注入的死循环dll，我在16楼里贴了，您看看。我专门确认了一下，这个视频也就十多分钟，没有敲任何创建线程的代码，就是写了这么个函数后，生成dll，然后将dll注入了植物大战僵尸的程序就直接运行了，这就是我觉得匪夷所思的地方。至于“注入是注入，执行是执行”，有时候我也想了解原理，所以这里想问的确实只是注入，还有它成功执行的原理，这里应该不是用的SetWindowHookEx，线程注入的话，它这里又确实没有创建线程，我能想到的方式是，它其实注入了两个dll，一个是这个软件自带的dll，目的就是创建一个新的线程并运行新dll的指定函数，不知道是不是这样。 我看了下你16楼贴的了，其实很简单，关键是你的那个注入工具，你看它可以接受一个“ 加载时执行函数” 的参数。 而你在写go（）的时候，注释里也明确说你的dll需要导出这个函数。所以，你猜的基本八九不离十了： 1、这个工具利用某种方法注入自己软件附带的dll（常见的方法应该就是我在14楼说的方法三：使用远程线程来注入）。 2、它的dll内部可以和自己的软件通信，一旦收到自己的软件发来的注入命令（命令带上待注入dll、注入时需要调用的函数、卸载时需要调用的函数） 3、它的dll创建了一个线程来执行自己软件发来的命令，即，调用LoadLibaray加载待注入dll（你的dll），用GetProcAddress获得你的dll的导出函数（go），调用之。 你可以用一些工具（比如Process Explorer）来看PlantsVsZombies.exe是否被注入了他软件自己的dll，来印证自己的猜测。 Process Explorer下载地址： https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer 引用 17 楼 六道佩恩 的回复:2. 注入实现的原理，注入的时候会执行dll的DLLMain，按理说被执行的线程只是一个单一的顺序流，执行了DLLMain就意为着顺序流跳到了dll这里，我能想到的实现方式是，由当前执行的指令跳转到指定dll函数，最后再跳转回原地方，但原程序的指令都在内存紧密排列着，不好插入跳转指令吧？那么是由中断实现的吗？还有这种DLL注入的中断？ 你关键就是不理解到底是谁来执行dll的DllMain吧，其实我之前已经有说过了，谁加载的dll就是谁调用的dll的入口函数。这就跟注入手法有关了。以我在14楼举的那3个最常见的方法为例： 1、使用注册表注入。目标程序启动时，加载系统库User32.dll的那条线程会调用User32.dll的DllMain，在这个DllMain中该线程又会LoadLibaray加载待注入dll，于是该dll的DllMain又会被该线程调用。调用堆栈： ---InjectedDll::DllMain(...) ---.... ---kernel32::LoadLibaray( InjectedDll ...) ---user32::DllMian(...) ---.... ---某个加载user32的线程，一般来说那就是这个进程的主线程，在调用exe的main之前。    （因为一般来说，user32都是隐式依赖的，进程启动后main函数之前，会初始化所有全局变量以及加载所有隐式依赖的dll）     （那么如果user32是该程序LoadLibaray动态加载的，那这里就是调用LoadLibaray的那条线程了）。  2、使用SetWindowsHookEx来注入。这个我后面给答案，你可以以你现在的理解再回过头去看一下那个步骤，想一想是哪个线程去调用的DllMain，再来看我的答案。  3、使用远程线程来注入dll。也是显而易见的，你的程序在调用CreateRemoteThread为目标进程创建了一条线程。这条线程的入口就是LoadLibaray，于是这条线程就会去调用你的dll的DllMain。  然后这里说下第2种方法的答案，其实就是重复说一下SetWindowsHookEx之后，目标线程发生的事情： 1、进程B中的 目标线程C 准备调用 GetMessage 或 PeekMessage 获取一条消息 2、系统（线程C）检查该线程是否已经安装了WH_GETMESSAGE挂钩 3、系统（线程C）检查钩子函数所在的dll是否已经被映射到进程B的地址空间中 4、如果dll尚未被映射，系统（线程C）会强制将该dll映射到进程B的地址空间中。（LoadLibaray（dll）--> call dll.DllMain）所以是目标线程C调用的DllMain 5、系统（线程C）调用钩子函数。 6、线程C从钩子函数中返回到GetMessage 或 PeekMessage ，继续接下来的功能 7、线程C从GetMessage 或 PeekMessage返回  线程C进入GetMessage之后的大致调用堆栈 ---InjectedDll::DllMain(...) ---.... ---kernel32::LoadLibaray( InjectedDll ...)  （类似或就是调用的LoadLibaray） ---.... (系统代码： 当检查到 钩子函数所在dll还未加载  ) ---.... (系统代码： 当检查到 当前线程被挂钩  ) ---user32::GetMessage(...)  当然，以上是 当前线程被挂钩且dll未被加载的情况下会走一次，加载dll完毕后，或者 当再次进入GetMessage后，执行流当然直接就进入钩子函数了：  ---InjectedDll::GetMsgProc (...) （dll中的钩子函数） ---.... (系统代码： 当检查到 钩子函数所在dll已加载  ) ---.... (系统代码： 当检查到 当前线程被挂钩  ) ---user32::GetMessage(...)  [/quote]  引用 17 楼 六道佩恩 的回复:3. MSDN说的可在自己线程直接用的钩子包括了WH_JOURNAL *，网上的说法也有它，但是我测试却收不到相关消息，或者说钩子没被调用，不知道是什么原因，这是代码（换键盘和鼠标的低级钩子就能正常工作）： 我没有用过这个功能，我猜是，你还没有触发这个功能吧。我看了msdn，没看懂它说的WH_JOURNALRECORD和WH_JOURNALPLAYBACK这两个触发条件。msdn应该是说WH_JOURNALRECORD这个钩子在 records input messages posted to the system message queue. 时会被调用。  你明白什么是 records input messages  吗？ 好像是说 系统日志 相关的东西。 有windows系统日志产生的时候才会被调用吧。  引用 17 楼 六道佩恩 的回复:4. 关于您说的系统这个有相关的书籍推荐吗？（希望能通俗易懂点的） 可以看 windows核心编程 这本书。看完后对windows会有一个比较清晰的了解。  引用 17 楼 六道佩恩 的回复:每个进程都相当于有系统（核心的管理程序）的备份，当调用系统的API后，顺序流就会进入这些系统核心代码运行，最后再跳回程序代码。系统自己也会运行一些重要的进程来保持系统的正常运作，这样，即便所有普通进程都不调用系统API，通过CPU切换时间片也最终可以调用到系统的这些核心进程，以此保证正常的运作。比如当我们点击鼠标后，由于硬件中断会切换到系统进程（或者直接调用当前进程中的系统映射部分？）来处理。你看这样的说法对吗？ 应该没错了。 起码我觉得就是这样的。 再细节的东西我也不甚了解了。  引用 17 楼 六道佩恩 的回复:5. 关于系统消息队列那里，消息队列不是有“系统消息队列”和“线程消息队列”之分吗？WH_JOURNALRECORD的说明也明确说的是“the system message queue”？ 我查了一下，你说的应该是对的。 确实有 “系统消息队列” ，且应该是指专门用来处理硬件输入的队列：系统硬件输入队列（System Hardware Input Queue），那么我之前的说处理底层键盘鼠标消息的说法可能有部分错误。那应该是一个系统进程（我猜是System.exe 但我没查到明确的说法）处理并分发到对应的进程中去的。

user: user-6
content: 低级钩子（低级键盘和低级鼠标，不知道还有没有其他的，WH_JOURNALRECORD测试不通过，单独的exe中没被调用）所反应的是硬件的消息，由于是硬件的，不和其他任何进程相关，所以系统单独发给一个进程就没有问题。其他钩子都是要和其他进程沟通的，所以要用独立的dll？ ->基本正确  不过我总觉得有哪里不对，比如系统消息队列，这个应该是独立于所有进程的吧？那么为什么WH_JOURNALRECORD会无效？还是说我测试的方法不对？ ->消息队列, 一般是说窗体的消息队列, 实际实现是在线程中实现, 在内核的线程结构TEB中, 但是实际引擎的循环是在用户层, 是否需要进程加载dll, 跟前面说的一样, 系统是按尽量简单的逻辑, 事件处理在用户层的进程被隔离, 没办法才这样处理, 否则没那么傻, 故意搞这么麻烦  还有个很大的问题是关于有死循环的注入函数的，我详细描述下： 首先我认为dll注入的hook函数是由系统调用的，消息进入进程时先调用对应的钩子过程，然后再交给进程处理（不知道对不对）； ->这个说法对钩子系统来说当然最简单的, 但是实际事件可能产生或处理就在用户层, 根本没有进到内核, 所以这个要看具体事件类型  但是我看到有人写一个死循环的dll注入植物大战僵尸来获取无限阳光（阳光低于一定值时就恢复到某个值），如果按上述的理解，显然是不可行的，我不太明白这类注入和钩子的注入是否是同一类，它又是怎么实现的？另开线程吗？可是这个注入的函数又是在什么时候调用的，是在注入的时候立刻另开线程且立刻运行吗？ ->死循环的dll注入植物大战僵尸来获取无限阳光, 实际实现如:用远线程注入, dllmain创建一个线程, 线程中死循环写内存, 这样没任何问题 钩子是实时的, 是不会故意开个线程去调用, 很多钩子的回调函数异常时, 会导致整个系统卡死, 由于系统有保护才可以卡了一会又恢复

user: user-5
content: 仔细看了一下你的回复，你有很多误区，你可以先看下我在14楼的回答。  然后，我在这楼回答下你的疑问。 引用 2 楼 六道佩恩 的回复:1. 我没用dll，使用全局钩子也是成功的 你指的是WH_KEYBOARD_LL、WH_MOUSE_LL、WH_JOURNALRECORD、WH_JOURNALPLAYBACK吧？这几个是特例，你可以去看msdn的解释，每个都在remark中有说的，这里摘一下LowLevelMouseProc钩子remark： 摘自https://docs.microsoft.com/zh-cn/previous-versions/windows/desktop/legacy/ms644986(v=vs.85)#remarks中remark的第二段：  This hook is called in the context of the thread that installed it. The call is made by sending a message to the thread that installed the hook. Therefore, the thread that installed the hook must have a message loop. 该挂钩在安装它的线程（调用SetWindowsHookEx的线程）中被调用。通过向安装钩子的线程（调用SetWindowsHookEx的线程）发送消息来进行调用。因此，安装挂钩的线程（调用SetWindowsHookEx的线程）必须具有消息循环。  因此，这几个特殊钩子的实现是 “被安装钩子的线程”在收到对应消息且检查到被安装钩子时会通过SendMessage（或类似实现）向 “调用SetWindowsHookEx安装钩子的线程“  发送该消息使其钩子函数被调用。  引用 2 楼 六道佩恩 的回复:2. 为什么注入的函数能够执行.....钩子是从哪里注入的？ 我在14楼说了，注入有很多种方式，如果你指的是SetWindowsHookEx，又指WH_KEYBOARD_LL、WH_MOUSE_LL、WH_JOURNALRECORD、WH_JOURNALPLAYBACK这几个特例，看上文。  如果是其他消息，看14楼。  引用 2 楼 六道佩恩 的回复:还有就是，我看别人注入的是一个死循环函数，这都能成功，那是不是说，注入的函数其实是新开的线程执行的？ 你这边又似乎再说其他注入方式了。你要明确一点的是，注入是注入，执行是执行，不要混为一谈。 如果只是通过VirtualAllocEx和WriteProcessMemory来仅仅注入一段函数代码。 那么想让它正确的去执行还是需要一些复杂的手段的。 所以一般简单的注入都是注入dll（方法见14楼），因为dll被加载时，DllMain就会被“加载该dll的线程”调用。这时，你就可以在DllMain函数中调用CreateThread（已经在目标进程了）创建自己的线程了，请确认你说的死循环函数是否说的是在这个线程里，如果是，那这是你自己创建的。 如果你说的还是SetWindowsHookEx，那你的说法就是有错误的，我在14楼有说，该方法注入的是整个dll，没有注入函数一说。且同时SetWindowsHookEx提供的也就那几个函数可以被钩，函数形式是被定死的，你指的“死循环函数”又是何意呢？是指在那几个函数中写了死循环，永不退出吗？ 请确认是否真是这样？如果真是这样，我去看了msdn，我只在WH_KEYBOARD_LL和WH_MOUSE_LL的钩子类型中，也就是LowLevelKeyboardProc和LowLevelMouseProc这两个钩子函数的remark中看到有明确写明系统有超时保护，如果你的钩子函数超过了这个时间，那么在Win7以前，系统直接就不会等你了，在win7及以后，系统会直接删除这个钩子。  这里的“系统”是指目标线程，即被挂勾线程。上文说了这几个特殊的钩子是通过消息通信实现的，钩子函数在你自己安装钩子的线程里执行的，如果被挂勾函数长时间得不到回复，是可以直接不理你的，必要时自动删除钩子。 但如果是其他类型的钩子，那势必会导致目标进程卡死，因为其他类型的钩子是被钩线程自己直接执行的。  引用 6 楼 六道佩恩 的回复:处于系统和程序之间的话，那还算是注入吗？本来按我的理解，这些应该都是归系统调用的，如果全都注入，那么系统里这么多运行的程序，如果每新增一个全局hook都要注入一遍，那太麻烦了，所以我以为全局hook不是注入的，SetWindowsHookEx单独指定某条线程时才会注入，我主要就是想确认是否是这样。 是否注入，和全不全局完全没有关系，只和你的钩子类型有关，上面说了就那几个不需要注入。 其他类型，是必须要注入的，详细看我14楼二方法第4步，如果被钩线程所在进程的钩子函数所在的dll还未注入，被钩线程才会去加载该dll（这时才注入了）。另外再说一句，进程自己就是所谓的“系统”的一部分，当然，有些系统功能确实在某些系统进程中，通过进程间通信调用，但大多数功能其实就是进程自己完成的。  引用 7 楼 六道佩恩 的回复:还有个问题就是，还有哪些类型的钩子是不需要dll的？W 详细看msdn官方说明。对应回调函数页的remarks中有说明 https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexw 引用 12 楼 六道佩恩 的回复:低级钩子（低级键盘和低级鼠标，不知道还有没有其他的，WH_JOURNALRECORD测试不通过，单独的exe中没被调用）所反应的是硬件的消息，由于是硬件的，不和其他任何进程相关，所以系统单独发给一个进程就没有问题。其他钩子都是要和其他进程沟通的，所以要用独立的dll？ 键盘鼠标消息，一定是和当前拥有焦点的进程相关的，否则谁来处理这个消息？我的理解是底层键盘鼠标消息是所有进程共用的那一块内核代码中的驱动得到的，并判断了当前进程是否拥有焦点，没有焦点的进程则抛弃了这些消息。拥有焦点的那个进程则再将消息传递到其用户态。又或者是, 只有当进程拥有焦点时，其内核态的代码才会去获取或者监听到底层鼠标和键盘的消息，并将其传递给自己的用户态。  至于挂勾这两个消息为何处理不太一样，我想就是简单的因为，这两个消息产生时机还在内核态，目标进程本身就没什么太大的必要在这一刻转到用户态加载dll什么的，这样效率高不了。所以就采用了直接发送消息给钩子安装者了，消息跨线程通信本身效率也不高，所以这个钩子更加要求钩子安装者尽快返回了。而其他钩子类型又为什么不这么做了呢？还是同理，其他钩子的消息产生就在用户态，完全可以直接加载dll在自己进程内执行，提高效率。而采用消息跨线程通信，效率太低。  引用 12 楼 六道佩恩 的回复:不过我总觉得有哪里不对，比如系统消息队列，这个应该是独立于所有进程的吧？  其他钩子需要用dll我觉得我应该是理解的，也许它应该是不允许消息跨进程传输吧？比如交给这个进程处理后再发给另一个进程，所以需要一个公用的dll。 你所谓的 “系统消息队列” 就是指线程的消息队列，一旦一个线程调用了GetMessage或者PeekMessage，消息队列就会被创建。何来独立于所有进程一说？只要是GUI进程就拥有消息队列。 至于消息哪来的？有自己进程的高位地址内核态代码产生的底层消息，有自己线程其他API的调用产生（如CreateWindow会产生WM_PAINT），有其他线程发送的消息（如其他线程调用了PostThreadMessage）等。  引用 12 楼 六道佩恩 的回复:还有个很大的问题是关于有死循环的注入函数的，我详细描述下： 首先我认为dll注入的hook函数是由系统调用的，消息进入进程时先调用对应的钩子过程，然后再交给进程处理（不知道对不对）； 但是我看到有人写一个死循环的dll注入植物大战僵尸来获取无限阳光（阳光低于一定值时就恢复到某个值），如果按上述的理解，显然是不可行的，我不太明白这类注入和钩子的注入是否是同一类，它又是怎么实现的？另开线程吗？可是这个注入的函数又是在什么时候调用的，是在注入的时候立刻另开线程且立刻运行吗？ 你对“系统”和进程的理解有很大的误区，前面已经详细描述了，就不多说了。 注入是注入，hook是hook，是两个概念。 一般我们注入的都是整个dll，很少只注入一个函数，前面说过，只注入函数的话，并让其运行是很复杂的。起码你讨论的SetWindowsHookExW是做不到的，它会将整个dll都注入，这种方法同时还完成特定功能的hook（其实它的主要目的是hook，注入才是顺带的）。而其他方法（见14楼），就只是单纯的注入dll了，hook要另外单独自己写。不管哪种方法，一旦注入了dll（让目标进程将dll映射进其地址空间），这个dll的DllMain入口函数就会被调用，你可以在这里干任何事情了，比如自己开线程，自己去调用该dll的其他任何函数。

user: user-0
content: 引用 11 楼 Yofoo 的回复:钩子简单来说就是一个回调函数,  一个进程发生了一个事件, 如果安装有回调函数就会执行这个函数, 系统会查找这个进程是否有这个函数, 如果没这个函数, 就会让进程再加载dll来取得函数  为什么要进程加载dll, 因系统的进程在空间上是隔离的, 在用户空间发生的事件简单来说就是这个方法 如果是在内核传递出来的事件, 那么可以单一进程获取全部事件, 因为这个不存在隔离问题, 如你们说的低级键盘钩子  操作系统也也许可以做到不加载dll的全钩子, 但是系统没有这么做, 这样只是逻辑复杂些: 系统可以把进程的事件传递到内核, 内核再传递给回掉进程  您看下能否这样理解： 低级钩子（低级键盘和低级鼠标，不知道还有没有其他的，WH_JOURNALRECORD测试不通过，单独的exe中没被调用）所反应的是硬件的消息，由于是硬件的，不和其他任何进程相关，所以系统单独发给一个进程就没有问题。其他钩子都是要和其他进程沟通的，所以要用独立的dll？  不过我总觉得有哪里不对，比如系统消息队列，这个应该是独立于所有进程的吧？那么为什么WH_JOURNALRECORD会无效？还是说我测试的方法不对？  其他钩子需要用dll我觉得我应该是理解的，也许它应该是不允许消息跨进程传输吧？比如交给这个进程处理后再发给另一个进程，所以需要一个公用的dll。  还有个很大的问题是关于有死循环的注入函数的，我详细描述下： 首先我认为dll注入的hook函数是由系统调用的，消息进入进程时先调用对应的钩子过程，然后再交给进程处理（不知道对不对）； 但是我看到有人写一个死循环的dll注入植物大战僵尸来获取无限阳光（阳光低于一定值时就恢复到某个值），如果按上述的理解，显然是不可行的，我不太明白这类注入和钩子的注入是否是同一类，它又是怎么实现的？另开线程吗？可是这个注入的函数又是在什么时候调用的，是在注入的时候立刻另开线程且立刻运行吗？

user: user-5
content: 注入是指把一段代码注入到指定的程序中，即完成了注入，严格的来说，如何让注入的代码得到运行不在概念之内。  但一般来说，在windows中，注入代码最容易的方法就是注入dll，即，让目标进程加载一个dll。它的好处是，注入的代码很容易就可以运行。因为，dll在被任何一个进程加载时，其入口函数DllMain就会被调用（由加载该dll的线程调用），我们一般会在DllMain的DLL_PROCESS_ATTACH通知时，开线程（这样，就相当于为目标进程新开了一个我们自己的线程），然后在新的线程中写一个主循环，处理业务。  windows中，注入有很多种方式，你先明确你想讨论的是哪种? 一、使用注册表注入。windows在注册表的某一路径下有一个AppInit_DLLs键和一个LoadAppInit_DLLs键，当后者为1时，前者即生效。当一个程序需要加载系统库User32.dll时（所有基于GUI的程序都会使用，但那些基于CUI，没有界面的程序则不会），User32.dll会被映射到对应的进程，该进程加载User32.dll的线程会调用User32.dll的DllMain函数。该函数会收到进程加载通知（第2个参数的值为DLL_PROCESS_ATTACH），User32.dll对它进行处理的时候，会取得上述AppInit_DLLs键的值，并调用LoadLibaray来载入这个字符串值中指定的每个DLL。 所以你只要将你写的dll的全路径，写入这个键值中，所有GUI程序在启动时，都将载入你的dll，即完成了注入。同样的，你的dll的DllMain同样得到了执行。  二、使用SetWindowsHookEx来注入。（看你后面的回复，你貌似是在讨论这种，我着重说一说）。它的用法具体可以看msdn，它的功能就像是向系统注册你自己的回调，在系统要做某些功能前有机会来调用你的代码。而一个功能或者说一段代码想要得到执行，必须得有一条线程去执行，所以SetWindowsHookEx是需要指定线程id的（后文我们称之为目标线程）。至于b]系统[/b]中该目标线程做哪些功能前可以调用我们注册的回调，那是有限的（WH_CALLWNDPROC、WH_GETMESSAGE、WH_MOUSE_LL等等），但它们都有一个共同点，就是都需要目标线程拥有消息队列（目标线程调用过GetMessage or PeekMessage ，这样系统就会强制为该线程创建消息队列）。 如果你的目标线程在当前进程，那么你的回调函数（后文我们称之为钩子函数）可以在当前进程的任意模块内。如果你的目标线程在其他进程，那么你的钩子函数必须在dll内。为什么？在了解系统在这种情况下是如何做的，就会明白了。我们以WH_GETMESSAGE为例，假设我们程序的一个线程调用SetWindowsHookEx并传入另一个线程的线程id，通过给这个参数传0，我们告诉系统要给系统中所有GUI线程安装钩子函数（GUI线程一定会调用GetMessage或PeekMessage）。那么接下来会发生什么： 1、进程B中的一个线程准备向一个窗口派送一条消息 2、系统检查该线程是否已经安装了WH_GETMESSAGE挂钩 3、系统检查钩子函数所在的dll是否已经被映射到进程B的地址空间中 4、如果dll尚未被映射，系统会强制将该dll映射到进程B的地址空间中。（我们的dll就在这一刻完成了注入，同时我们dll的DllMain也会得到运行，我们同样可以在这里做些什么） 5、由于dll的HINSTANCE（基址）是在进程B中被映射的，而我们给SetWindowsHookEx的第3个参数HINSTANCE hmod 传入的是dll在进程A中的地址，因此，系统还会对此进行检查，看两者位置是否相同。    如果相同，那么在两个进程的地址空间中，钩子函数位于相同的位置。在这种情况下，系统可以直接在B的地址空间中调用钩子函数。    如果不相同，那么系统必须确定钩子函数在进程B的地址空间中的虚拟内存地址。这个地址不难得出：          钩子函数B = HINSTANCE B + （钩子函数A - HINSTANCE A）        通过把钩子函数A所在地址减去dll在A内的基址可以得到钩子函数在dll内的偏移量，把这个偏移量加上dll在B内的基址就可以得到钩子函数在B内的地址了。 6、系统在B的地址空间中调用钩子函数。  所以一旦系统把钩子函数所在的dll注入或者说映射到地址空间中时，会映射的是整个dll，而不仅仅是钩子函数。这意味着该dll内的所有函数都存在于进程B中，能够为进程B中的任何线程调用。  另外我要说的是，你可能发现了我给上面出现的所有“系统”二字都加粗了，因为我认为你可能不理解的是究竟什么是“系统”？ 我们知道，一段代码想要得到执行，必须有一条线程去跑。 我在上面一直在说“系统”怎么怎么样，“系统”怎么怎么做，“系统”做这些做那些，那“系统”到底是如何有机会做这些做那些的？“系统”不可能在我们不知道的情况下凭空给我们的进程产生线程吧？“系统”或者说“系统”的代码在哪里？“系统”到底是啥？我们回过头仔细想想，最基本的规则：一段代码想要得到执行，必须有一条线程去跑。线程是最小的执行单元。 那么“系统”或者说“系统”的代码其实肯定就在我们自己写的程序里面。   我们知道，windows中有虚拟内存的概念，以32位进程为例，每个进程有4GB虚拟内存，每个进程看到的虚拟内存是不完全相同的，这是因为每个进程的物理内存到该进程虚拟内存地址的映射是不同的。而这4GB虚拟内存分为两部分，虚拟内存地址0~0x7FFFFFFF为用户模式地址，而0x80000000~0xFFFFFFFF为内核模式的地址。啥区别呢？，我们写的“一般”的程序，我们自己的代码，函数也好，运行态申请的空间也好，都在用户模式地址上。而系统写的代码，或者我们写的“不一般”的程序（驱动），就都在内核模式的地址上了。两者结合之后，共同组成了我们的程序。另外要注意的是，它们还有一个最大的区别就是：windows规定所有进程内核模式下的虚拟内存的映射方式是完全一样的，这样也就是说，每个进程中的顶端2GB的内核模式地址的数据是完全一致的。所以每个进程是共用的是同一套系统代码，它就在你的32位程序的高2GB地址空间中。那么它如何得到执行呢？windows为我们提供了很多很多用户态的API来帮助我们利用系统代码。像kernel32.dll user32.dll gdi32.dll 等等。 所以，看到这里，你可以带着这个所谓的系统的概念，再看一遍我上述的（二）方法。相信会更好理解。当然我上面加粗的“系统”，可能一部分还是会包含在用户态的API里做的，里面细节究竟是怎样那就只有微软自己知道了。  三、使用远程线程来注入dll。用上面两个方法注入都对目标有限制（1、目标必须依赖user32.dll,2、目标必须有消息循环），而这个方法的好处是，对目标没有任何限制。其原理就是用了微软提供的几个API： 1、使用VirtualAllocEx为目标进程申请空间A 2、使用WriteProcessMemory为目标进程的地址空间A中写入字符串，内容为我们的dll路径 3、使用CreateRemoteThread为目标进程创建线程，线程地址为LoadLibrary的地址（系统API的地址，在每个进程内的虚拟地址都是一样的），而LoadLibrary的形式正好和线程函数的形式一致。所以给LoadLibrary的参数（即线程函数的参数）传入1种申请的A的地址。 这样我们为目标进程创建出来的线程就会加载我们的dll，同时就会调用我们的dll的DllMain，我们还是可以在这里开始我们的业务。一般来说，如果我们啥都不做，那我们dll内的其他代码就不可能被执行的。这时候需要用hook技术了。 我们得已知目标进程会调用什么函数，该函数在目标进程地址空间中的哪里，我就可以在那里做文章进行跳转hook，当然一般就是系统API的hook了。  就不详细展开了。  四、还有其他很多方法。。。不一一赘述了。 在回到你提的问题，我不知道你是否还有问题。 尤其看了我在（二）中后面的解释。 你要从原理上看，钩子函数代码是谁在跑？是如何开始跑的？什么叫全局？我把我的dll注入到所有进程中，每个进程都会在他们各自的地址空间中用各自的线程跑我的钩子函数，是否就叫全局钩子了呢？ 这都是人为定义的。就比如你说的WH_KEYBOARD_LL和WH_MOUSE_LL，你说不需要dll，直接可以在你安装挂钩的exe里跑，我没用过不知道，如果你实验了确实是这样，那也许就是内核模式下的鼠标键盘驱动在发出消息，在应该处理这个消息的线程处理它之前，先让你的线程处理，再去它应该去的地方。这是否也叫全局钩子呢

user: user-7
content: 引用 7 楼 六道佩恩 的回复:还有个问题就是，还有哪些类型的钩子是不需要dll的？WH_SYSMSGFILTER？这个你是怎么知道的？我看这几个钩子的共同特点是只能用作全局钩子，你是不是根据这点来说的？那么只要是全局钩子就不需要dll，还是只能用作全局的钩子才能不需要dll？ 这个为啥有些需要dll，有些不需要，没深入看过，直接看的网上的结论。我研究hook也只是为了解决工作上的事情，用到的也都是一些常用的，需要dll的类型（全局和非全局的，都用dll的那种）。

user: user-7
content: 引用 6 楼 六道佩恩 的回复:处于系统和程序之间的话，那还算是注入吗？本来按我的理解，这些应该都是归系统调用的，如果全都注入，那么系统里这么多运行的程序，如果每新增一个全局hook都要注入一遍，那太麻烦了，所以我以为全局hook不是注入的，SetWindowsHookEx单独指定某条线程时才会注入，我主要就是想确认是否是这样。  另外就是，我有点不太理解注入为何能实现，dll注入是加入某个进程的空间是吧？那么执行这个注入的函数的是谁？是系统还是这个进程？按理说应该是这个进程，但即便这个进程只有一条顺序流且注入的函数是个死循环，它都是正常运行的，所以，如果是在原进程的某个命令后加入跳转代码到这个注入函数，最后再跳转回去的这个猜测应该是不对的，毕竟一条顺序流会陷死在这个注入函数的死循环里；所以，它的实现原理是另开了条线程，还是由系统执行？如果系统执行，钩链还好理解，要固定调用的，但这个函数注入的时候应该不是通过hook（我看到的演示没有hook相关字样），所以由系统执行的猜测感觉也不太靠谱，毕竟总不可能记录每个注入的函数然后专门去调用吧（甚至可能要单开线程）；所以，是通过在进程内另开线程的方式执行的吗？这个多开的线程，在任务管理器里能看到吗？  然后，，hook不是api吗？  新增一个全局hook的话，是每个进程都会注入的。进程加载dll的时间不是你调用SetWindowsHookEx的时间，而是消息发生的时间，比如你hook全局的WH_GETMESSAGE，某个进程有窗体消息时，就会加载这个dll。至于怎么加载，我没深入研究过，感觉应该类似loadlibrary的方式（有哪位大侠懂的，可以补充下）。具体的情况，可以通过pchunter来观察 楼主一直在说的hook，其实是在windows的消息链上增加一环。可以理解为，windows有个总的消息循环，然后向指定的进程发送消息，发送这个消息是需要进过windows消息链的。然后hook，就是让消息链多了一环。  API HOOK是修改api的地址，也就是让程序调用某个函数的时候先跳转到自己的函数，然后再跳回到原来的地址。网上比较多的例子是将MessageBox。Hook MessageBox 进阶 跨进程Hook  API HOOK和SetWindowsHookEx还是很不一样的。

user: user-6
content: 钩子简单来说就是一个回调函数,  一个进程发生了一个事件, 如果安装有回调函数就会执行这个函数, 系统会查找这个进程是否有这个函数, 如果没这个函数, 就会让进程再加载dll来取得函数  为什么要进程加载dll, 因系统的进程在空间上是隔离的, 在用户空间发生的事件简单来说就是这个方法 如果是在内核传递出来的事件, 那么可以单一进程获取全部事件, 因为这个不存在隔离问题, 如你们说的低级键盘钩子  操作系统也也许可以做到不加载dll的全钩子, 但是系统没有这么做, 这样只是逻辑复杂些: 系统可以把进程的事件传递到内核, 内核再传递给回掉进程

user: user-0
content: 引用 1 楼 轻箬笠 的回复: 老哥，知道的话回答下可以吗？我可以再加一两百分。

user: user-7
content: 引用 2 楼 六道佩恩 的回复:问你两个问题： 1. 我没用dll，使用全局钩子也是成功的，总不可能exe也能注入吧？ 2. 为什么注入的函数能够执行，我想不通，原来的程序有自己的执行顺序，钩子是从哪里注入的？是不是从哪里截获了顺序流，然后执行完又让顺序流回到原来的地方？我看有的人用软件注入一个dll，这个软件没看到标明是哪个钩子，估计是线程注入，但问题同上？其实我估计的是，这个注入会不会只是数据共享，从原程序的某个地方跳转执行，然后跳转回来，只是从哪里跳转的，这是个问题。还有就是，我看别人注入的是一个死循环函数，这都能成功，那是不是说，注入的函数其实是新开的线程执行的？  做全局注入的时候，我没用过不使用dll的情形，不过从文件结构上看，exe作为源头似乎也不是不行（个人猜测）。另外，有些钩子是不需要dll的（比如WH_JOURNALPLAYBACK，WH_JOURNALRECORD，WH_KEYBOARD_LL，WH_MOUSE_LL），具体没实现过，就不评论了。 钩子的位置的话，应该在windows系统和程序之间。比如system->hook->process，这里的hook是一个链表，我们调用hook就是往链表里面加一条记录。所以hook不会影响程序内部的顺序流，但是会影响哪些system消息进入这个顺序流。就像河水在流，你往里面扔石头，hook能阻止石头进入河里，但是河水还是会流。 至于“从原程序的某个地方跳转执行，然后跳转回来”，这话听着感觉楼主想表达api hook？？

user: user-0
content: 引用 5 楼 轻箬笠 的回复:有些钩子是不需要dll的（比如WH_JOURNALPLAYBACK，WH_JOURNALRECORD，WH_KEYBOARD_LL，WH_MOUSE_LL）  还有个问题就是，还有哪些类型的钩子是不需要dll的？WH_SYSMSGFILTER？这个你是怎么知道的？我看这几个钩子的共同特点是只能用作全局钩子，你是不是根据这点来说的？那么只要是全局钩子就不需要dll，还是只能用作全局的钩子才能不需要dll？

user: user-0
content: 引用 5 楼 轻箬笠 的回复:[quote=引用 2 楼 六道佩恩 的回复:] 问你两个问题： 1. 我没用dll，使用全局钩子也是成功的，总不可能exe也能注入吧？ 2. 为什么注入的函数能够执行，我想不通，原来的程序有自己的执行顺序，钩子是从哪里注入的？是不是从哪里截获了顺序流，然后执行完又让顺序流回到原来的地方？我看有的人用软件注入一个dll，这个软件没看到标明是哪个钩子，估计是线程注入，但问题同上？其实我估计的是，这个注入会不会只是数据共享，从原程序的某个地方跳转执行，然后跳转回来，只是从哪里跳转的，这是个问题。还有就是，我看别人注入的是一个死循环函数，这都能成功，那是不是说，注入的函数其实是新开的线程执行的？  做全局注入的时候，我没用过不使用dll的情形，不过从文件结构上看，exe作为源头似乎也不是不行（个人猜测）。另外，有些钩子是不需要dll的（比如WH_JOURNALPLAYBACK，WH_JOURNALRECORD，WH_KEYBOARD_LL，WH_MOUSE_LL），具体没实现过，就不评论了。 钩子的位置的话，应该在windows系统和程序之间。比如system->hook->process，这里的hook是一个链表，我们调用hook就是往链表里面加一条记录。所以hook不会影响程序内部的顺序流，但是会影响哪些system消息进入这个顺序流。就像河水在流，你往里面扔石头，hook能阻止石头进入河里，但是河水还是会流。 至于“从原程序的某个地方跳转执行，然后跳转回来”，这话听着感觉楼主想表达api hook？？[/quote]  处于系统和程序之间的话，那还算是注入吗？本来按我的理解，这些应该都是归系统调用的，如果全都注入，那么系统里这么多运行的程序，如果每新增一个全局hook都要注入一遍，那太麻烦了，所以我以为全局hook不是注入的，SetWindowsHookEx单独指定某条线程时才会注入，我主要就是想确认是否是这样。  另外就是，我有点不太理解注入为何能实现，dll注入是加入某个进程的空间是吧？那么执行这个注入的函数的是谁？是系统还是这个进程？按理说应该是这个进程，但即便这个进程只有一条顺序流且注入的函数是个死循环，它都是正常运行的，所以，如果是在原进程的某个命令后加入跳转代码到这个注入函数，最后再跳转回去的这个猜测应该是不对的，毕竟一条顺序流会陷死在这个注入函数的死循环里；所以，它的实现原理是另开了条线程，还是由系统执行？如果系统执行，钩链还好理解，要固定调用的，但这个函数注入的时候应该不是通过hook（我看到的演示没有hook相关字样），所以由系统执行的猜测感觉也不太靠谱，毕竟总不可能记录每个注入的函数然后专门去调用吧（甚至可能要单开线程）；所以，是通过在进程内另开线程的方式执行的吗？这个多开的线程，在任务管理器里能看到吗？  然后，，hook不是api吗？