标签:
Hook的中文含义是“钩子”,与消息有着非常密切的联系。hook是消息处理中的一个环节,用于监控消息在系统中的传递,并在这些消息到达最终的消息处理过程前,处理某些特定的消息。它将自身的代码融进目标进程里,目标进程的某一调用将优先调用你的hook dll,然后再由hook dll调用目标进程想调用的函数或者方法;
api hook是什么?
在windows系统下编程,应该会接触到api函数的使用,常用的api函数大概有2000个左右。
今天随着控件,stl等高效编程技术的出现,api的使用概率在普通的用户程序上就变得越来越小了。
当诸如控件这些现成的手段不能实现的功能时,我们还需要借助api。
最初有些人对某些api函数的功能不太满意,就产生了如何修改这些api,使之更好的服务于程序的想法,这样api hook就自然而然的出现了。
我们可以通过api hook,改变一个系统api的原有功能。基本的方法就是通过hook“接触”到需要修改的api函数入口点,改变它的地址指向新的自定义的函数。
api hook并不属于msdn上介绍的13类hook中的任何一种。所以说,api hook并不是什么特别不同的hook,它也需要通过基本的hook提高自己的权限,跨越不同进程间访问的限制,达到修改api函数地址的目的。对于自身进程空间下使用到的api函数地址的修改,是不需要用到api hook技术就可以实现的。
Hook的几种方式:
1. 使用注册表HKLM\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs
AppInit_DLLs这个项的值本应该是个空值。注册表的系统设置项“AppInit_DLLs”可以为任一个进程调用一个dll列表,是早期的进程插入式木马的伎俩,通过修改注册表中的KEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs来达到插入进程的目的。缺点是不实时,修改注册表后需要重新启动才能完成进程插入。AppInit_DLLs这个项目,正常情况下它的值应该是空的。这个项目表示每当一个程序启动的时候,AppInit_DLLs中的dll文件就会注入到该程序里面去启动。
比如:如果AppInit_DLLs=qhbpri.dll,那么在登录windows后,系统加载了explorer.exe,那么qhbpri.dll也会出现在其中。dll文件虽然不能自己运行,但等他注入到应用程序中,其中代码还是会被执行,一些qq密码盗窃木马就是用上面的原理工作的。由于dll是寄生在exe中运行,在进程管理器中是看不到它的。
但是此种方法,应该会被当前的大多数杀毒软件警告;
2.调用SetWindowsHookEx(WH_GETMESSAGE, …, 0)
SetWindowsHookEx(WH_GETMESSAGE, …, 0) 是个全局的消息钩子,虽然可能你的程序并不用到消息钩子,但是钩子的一个副作用是会将对应的DLL加载到所有的GUI线程之中(无疑会拖慢程序的运行)。类似的,只有用到GUI的进程才会被挂接。虽然有这种限制,这种方法仍然是最常用的挂接进程的方法。
3.使用CreateRemoteThread函数在目标进程中创建远程线程
这种方法可以在任意的目标进程中创建一个远程线程,远程线程中可以执行任意代码,这样便可以做到把我们的代码Inject到目标进程中。这种方法具有最大的灵活性,但是难度也最高:
a) 远程线程代码必须可以自重定位
b) 要能够监视进程的启动和结束,这样才可以挂接到所有进程
此种方式,着重介绍下:
1. 找到你要挂载的进程;
2.得到要注入的进程的句柄,OpenProcess(...);
3.为后面的dll分配内存,::VirtualAllocEx(...);
4.将dll载入申请的内存中,WriteProcessMemory(...);
5.获得LoadLibraryA在内核中的具体位置,GetProcAddress(...);
6.只要调用了LoadLibraryA都创建远程线程加载dll,CreateRemoteThread(...);
7.等待远程线程终止,WaitForSingleObject(...);
8.关闭句柄,释放申请的远程线程中的分配的空间;
但是此种方法只能是在目标先启动后,然后才能成功的挂载我们的hook程序,那如何检测目标程序是否已经开启了呢?
下面做详细阐述:首先的第一想法,是Hook ZwCreateProcess,结果调试的时候发现,很多创建进程的动作,并没有通过这个API执行,所以自然就是没办法监控进程的创建,于是回到本质,从创建进程的动作过程来分析,创建新的进程,其大致要经历以下步骤:
(1)打开可执行文件,以FILE_EXECUTE权限打开;
(2)将可执行文件加载到内存空间;
(3)进程的活动结构将被创建,如(EPROCESS,KPROCESS和PEB结构);
(4)为新创建的进程分配地址空间;
(5)为进程的主线程创建线程活动结构,如(ETHREAD,KTHREAD和TEB结构);
(6)主线程的栈将会被分配;
(7)进程的主线程的上下文将被创建;
(8)通知windows子系统;
以上总结下来,无非有下面几种办法获取进程创建的消息:
(1)HOOK ZeCreateSection,创建虚拟内存块的时候,根据传入的文件句柄,获取句柄对应的文件名是否为exe可执行文件;
(2)Hook NtReadVirtualMemory,为新创建的进程分配地址空间等操作时,需要读取进程空间,这样捕获,就能够获取进程的创建动作;
(3)通过windows提供的回调函数,注册回调事件;
方法对比:
(1)该方法能够准确的获取进程创建的操作,但是由于此时进程并没有创建完毕,一些进程的基本结构还没有创建,所以进程ID等信息无法获取;
(2)该方法能够获取进程的创建操作,但不准确。因为除了进程的创建会调用此操作外,人为的一些操作,例如某外部应用程序想读取另一个进程的内存空间,也会调用这个函数,这时候也会有事件响应,因此结果不准确;
(3)第三种方法更直观和简单。因为采用的回调事件,并不直接HOOK API,因此更稳定。
重点分析第三种回调方法。
注册回调事件,是通过PsSetCreateProcessNotifyRoutine来实现的,其函数原型如下:
NTSTATUS PsSetCreateProcessNotifyRoutine(
IN PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine,
IN BOOLEAN Remove
);
NotifyRoutine就是注册的回调函数,当有进程创建的时候,就会调用这个NotifyRoutine对应的函数,其函数定义原型如下:
VOID (*PCREATE_PROCESS_NOTIFY_ROUTINE) (
IN HANDLE ParentId,
IN HANDLE ProcessId,
IN BOOLEAN Create
);
其中,ParentId是父进程ID,ProcessId为子进程ID,而Create表示是创建进程还是结束进程,其中True表示创建进程,False表示结束进程。
通过这个函数,我们就能够完成进程创建和退出的监控,首先调用PsSetCreateProcessNotifyRoutine注册进程监控回调函数,然后在回调函数里面,判断Create参数,分别处理进程创建和退出操作。
标签:
原文地址:http://blog.csdn.net/liumaoye/article/details/43953141