使用自旋锁的各种HOOK
- 格式:doc
- 大小:61.50 KB
- 文档页数:11
⾯试官:你说说互斥锁、⾃旋锁、读写锁、悲观锁、乐观锁的应⽤场景前⾔⽣活中⽤到的锁,⽤途都⽐较简单粗暴,上锁基本是为了防⽌外⼈进来、电动车被偷等等。
但⽣活中也不是没有BUG的,⽐如加锁的电动车在「⼴西-窃·格⽡拉」⾯前,锁就是形同虚设,只要他愿意,他就可以轻轻松松地把你电动车给「顺⾛」,不然打⼯怎么会是他这辈⼦不可能的事情呢?⽜逼之⼈,必有⽜逼之处。
那在编程世界⾥,「锁」更是五花⼋门,多种多样,每种锁的加锁开销以及应⽤场景也可能会不同。
如何⽤好锁,也是程序员的基本素养之⼀了。
⾼并发的场景下,如果选对了合适的锁,则会⼤⼤提⾼系统的性能,否则性能会降低。
所以,知道各种锁的开销,以及应⽤场景是很有必要的。
接下来,就谈⼀谈常见的这⼏种锁:正⽂多线程访问共享资源的时候,避免不了资源竞争⽽导致数据错乱的问题,所以我们通常为了解决这⼀问题,都会在访问共享资源之前加锁。
最常⽤的就是互斥锁,当然还有很多种不同的锁,⽐如⾃旋锁、读写锁、乐观锁等,不同种类的锁⾃然适⽤于不同的场景。
如果选择了错误的锁,那么在⼀些⾼并发的场景下,可能会降低系统的性能,这样⽤户体验就会⾮常差了。
所以,为了选择合适的锁,我们不仅需要清楚知道加锁的成本开销有多⼤,还需要分析业务场景中访问的共享资源的⽅式,再来还要考虑并发访问共享资源时的冲突概率。
对症下药,才能减少锁对⾼并发性能的影响。
那接下来,针对不同的应⽤场景,谈⼀谈「互斥锁、⾃旋锁、读写锁、乐观锁、悲观锁」的选择和使⽤。
最底层的两种就是会「互斥锁和⾃旋锁」,有很多⾼级的锁都是基于它们实现的,你可以认为它们是各种锁的地基,所以我们必须清楚它俩之间的区别和应⽤。
加锁的⽬的就是保证共享资源在任意时间⾥,只有⼀个线程访问,这样就可以避免多线程导致共享数据错乱的问题。
当已经有⼀个线程加锁后,其他线程加锁则就会失败,互斥锁和⾃旋锁对于加锁失败后的处理⽅式是不⼀样的:互斥锁加锁失败后,线程会释放C P U ,给其他线程;⾃旋锁加锁失败后,线程会忙等待,直到它拿到锁;互斥锁是⼀种「独占锁」,⽐如当线程A加锁成功后,此时互斥锁已经被线程A独占了,只要线程A没有释放⼿中的锁,线程 B 加锁就会失败,于是就会释放C P U 让给其他线程,既然线程 B 释放掉了CP U,⾃然线程 B 加锁的代码就会被阻塞。
内核层的三种Hook技术的使⽤1.前⾔本⽂是在Win7 X86系统上进⾏实验,实验的内容是在内核层通过三种不同的Hook⽅法来实现进程保护,让运⾏的程序不被其他的程序关闭。
这⾥⽤来被保护的程序是⼀个简单的HelloWord弹窗程序,程序名是demo.exe。
2.实现原理⼀个程序要想关闭⼀个进程,⾸先就要获取这个进程的句柄,并且获得的这个句柄还需要具备进程关闭的访问掩码。
也就是说,在⽤户层我们想要关闭⼀个进程,⾸先就需要使⽤以下的代码来获得⼀个进程的句柄。
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwPid);其中第⼀个参数PROCESS_TERMINATE就是让我们获得的进程句柄具有关闭进程的访问掩码,它在⽂档中的定义如下:#define PROCESS_TERMINATE (0x0001)那也就是说,如果HOOK了打开进程的函数,就可以在我们编写的函数中判断所打开的进程是否是我们要保护的进程,以及所要获得的进程句柄是否是要具备进程关闭的访问掩码,如果是的话就把它去掉。
这个时候,程序如果使⽤这个被删除掉进程关闭访问掩码的句柄来关闭进程,就会失败,这就完成了对进程的保护作⽤。
⽽在⽤户层所调⽤的OpenProcess到内核层以后,其实在内核中是调⽤ZwOpenProcess,所以只要在内核中监控这个函数就可以完成实验。
该函数在⽂档中的定义如下:NTSTATUSZwOpenProcess(__out PHANDLE ProcessHandle,__in ACCESS_MASK DesiredAccess,__in POBJECT_ATTRIBUTES ObjectAttributes,__in_opt PCLIENT_ID ClientId);参数2所代表的就是要打开的这个进程要具备的访问掩码,通过对它的判断与修改就可以实现进程保护。
C#lock⾃旋锁,互斥锁,混合锁,读写锁介绍c# 并⾏编程、多线程开发中,经常要⽤到线程锁,so, 看了许多⽂章,想总结⼀下,供⾃⼰理解记忆,以及园丁们参考使⽤,理解的不怎么全⾯,勿喷!在多线程环境中,多个线程可能会同时访问同⼀个资源,为了避免访问发⽣冲突,可以根据访问的复杂程度采取不同的措施,原⼦操作适⽤于简单的单个操作,⽆锁算法适⽤于相对简单的⼀连串操作,⽽线程锁适⽤于复杂的⼀连串操作1.lock锁的解释和⽤法官⽅MSDN的说法:lock 关键字可确保当⼀个线程位于代码的临界区时,另⼀个线程不会进⼊该临界区。
如果其他线程尝试进⼊锁定的代码,则它将⼀直等待(即被阻⽌),直到该对象被释放。
lock 关键字在块的开始处调⽤ Enter,⽽在块的结尾处调⽤ Exit。
ThreadInterruptedException 引发,如果 Interrupt 中断等待输⼊ lock 语句的线程。
通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。
1private static readonly object objlock = new object();2lock (objlock )3 {4//要执⾏的代码逻辑5 }1using System;2using System.Collections.Generic;3using System.Linq;4using System.Text;5using System.Threading;6using System.Threading.Tasks;78namespace LockTest9 {10class Program11 {12static void Main(string[] args)13 {14 TestLock testlock = new TestLock();15 Thread th = new Thread(() =>16 {17//模拟死锁:造成死锁,使lock⽆法释放,在i=5时,跳出死循环,释放lock18 testlock.DoWorkWithLock();19 });20 th.Start();21 Thread.Sleep(1000);22 Thread th2 = new Thread(() =>23 {24//这个地⽅你可能会有疑惑,但存在这种情况,⽐如你封装的dll,对其它开发⼈员不是可见的25//开发⼈员很有可能在他的逻辑中,加上⼀个lock保证⽅法同时被⼀个线程调⽤,但这时有其它的线程正在调⽤该⽅法,26//但并没有释放,死锁了,那么在这⾥就不会被执⾏,除⾮上⾯的线程释放了lock锁定的对象。
目录一、驱动开发环境的搭建 (1)1.1 关于DDK (1)1.2 关于驱动程序的编译 (1)1.3关于驱动程序的运行 (2)二、驱动程序的结构 (3)2.1 驱动程序的头文件 (3)2.2 驱动程序的入口点 (3)2.3 创建设备例程 (4)2.4 卸载驱动例程 (5)2.5 派遣例程 (6)三、编写驱动程序的基础知识 (6)3.1 内核模式下的字符串操作 (6)3.2 内核模式下各种开头函数的区别 (8)3.3 一个示例程序 (10)3.4 补充说明 (10)四、在驱动中使用链表 (10)4.1 内存的分配与释放 (10)4.2 使用LIST_ENTRY (12)4.3 使用自旋锁 (12)五、在驱动中读写文件 (15)5.1 使用OBJECT_ATTRIBUTES (15)5.2 创建、打开文件 (16)5.3 读写文件操作 (16)5.4 文件的其它相关操作 (18)六、在驱动中操作注册表 (18)6.1 创建、打开注册表 (19)6.2 读写注册表 (20)6.3 枚举注册表 (21)七、在驱动中获取系统时间 (21)7.1 获取启动毫秒数 (21)7.2 获取系统时间 (22)八、在驱动中创建内核线程 (23)8.1 创建内核线程 (23)8.2 关于线程同步 (24)九、初探IRP (25)9.1 IRP的概念 (25)9.2 IRP的处理 (26)9.3 IRP派遣例程示例 (27)十、驱动程序与应用层的通信 (29)10.1 使用WriteFile通信 (29)10.2 使用DeviceIoControl进行通信 (32)十二、驱动程序开发实例 (33)12.1 NT驱动程序 (33)12.2 WDM驱动程序 (35)十三、参考资料 (41)一、驱动开发环境的搭建1.1 关于DDK开发驱动程序必备的一个东西就是DDK(Device Development Kit,设备驱动开发包),它跟我们在ring3常听到的SDK差不多,只不过它们分别支持开发不同的程序而已。
hook普通方法
在编程中,“Hook普通方法”是指利用某些技术手段,劫持或替换目标方法的执行,从而实现对目标方法的监控或增强。
以下是一些常见的Hook方法:
- DOM操作:通过修改DOM元素的属性和样式,实现对网页的控制和修改。
- 事件监听:通过监听网页上的事件,触发自定义的操作和行为。
- AJAX拦截:通过拦截网页上的AJAX请求,实现对数据的控制和修改。
- 函数替换:通过替换网页上的函数,实现对函数的控制和修改。
使用Hook 方法需要注意其适用场景和安全风险,合理使用可以为编程工作带来很大的便利。
如果你需要更详细的信息或帮助,请提供更具体的问题描述。
react hooks详解React Hooks 是 React 16.8 版本引入的一种新的特性,它能够让我们在不编写类组件的情况下,使用状态和其他 React 特性。
它提供了一种更简洁、更直观、更易于测试和重用的方式来编写 React 组件。
使用 Hooks 可以将状态和副作用(如数据获取、订阅、手动修改 DOM 等)与组件逻辑相关联。
在之前的类组件中,我们需要使用生命周期方法来管理状态和副作用,但是 Hooks 提供了一种更灵活的方式来处理这些逻辑。
在使用 Hooks 之前,我们先来了解一下常用的几个 Hooks。
1. useState: useState 是最常用的 Hook 之一,它允许我们在函数组件中添加状态。
通过调用 useState 方法,我们可以获取一个包含状态值和更新状态值的方法的数组。
在函数组件中,我们可以像使用普通变量一样使用这个状态值,而不再需要使用 this.state。
2. useEffect: useEffect 是用来处理副作用的 Hook。
它可以在组件渲染完成后执行一些额外的操作,比如数据获取、订阅等。
在useEffect 中,我们可以传入一个函数,这个函数将在每次组件渲染完成后执行。
我们还可以通过在函数中返回一个清除函数,来清除副作用。
3. useContext: useContext 是用来访问 React 上下文的 Hook。
它接受一个上下文对象(通过 React.createContext 创建),并返回该上下文的当前值。
使用 useContext 可以避免在多层嵌套的组件中手动传递上下文。
除了上述几个常用的 Hooks,还有一些其他的 Hooks,如 useReducer、useCallback、useMemo 等,它们都可以帮助我们更好地管理状态、处理副作用和优化性能。
使用 Hooks 的好处是可以更好地组织组件的逻辑,使代码更加简洁和可读。
同时,将状态和副作用与组件逻辑相关联,也可以使组件更加易于测试和重用。
C#多线程编程中的锁系统(四):⾃旋锁⽬录⼀:基础⼆:⾃旋锁⽰例三:SpinLock四:继续SpinLock五:总结⼀:基础内核锁:基于内核对象构造的锁机制,就是通常说的内核构造模式。
优点:cpu利⽤最⼤化。
它发现资源被锁住,请求就排队等候。
线程切换到别处⼲活,直到接受到可⽤信号,线程再切回来继续处理请求。
缺点:托管代码->⽤户模式代码->内核代码损耗、线程上下⽂切换损耗。
在锁的时间⽐较短时,系统频繁忙于休眠、切换,是个很⼤的性能损耗。
⾃旋锁:原⼦操作+⾃循环。
通常说的⽤户构造模式。
线程不休眠,⼀直循环尝试对资源访问,直到可⽤。
优点:完美解决内核锁的缺点。
缺点:长时间⼀直循环会导致cpu的⽩⽩浪费,⾼并发竞争下、CPU的消耗特别严重。
混合锁:内核锁+⾃旋锁。
混合锁是先⾃旋锁⼀段时间或⾃旋多少次,再转成内核锁。
优点:内核锁和⾃旋锁的折中⽅案,利⽤前⼆者优点,避免出现极端情况(⾃旋时间过长,内核锁时间过短)。
缺点:⾃旋多少时间、⾃旋多少次,这些策略很难把控。
ps:操作系统或net框架,这块算法策略做的已经⾮常优了,有些API函数也提供了时间及次数可配置项,让开发者根据需求⾃⾏判断。
⼆:⾃旋锁⽰例来看下我们⾃⼰简单实现的⾃旋锁:复制代码代码如下:int signal = 0;var li = new List<int>();Parallel.For(0, 1000 * 10000, r =>{while (Interlocked.Exchange(ref signal, 1) != 0)//加⾃旋锁{//⿊魔法}li.Add(r);Interlocked.Exchange(ref signal, 0); //释放锁});Console.WriteLine(li.Count);//输出:10000000上⾯就是⾃旋锁:Interlocked.Exchange+while1:定义signal 0可⽤,1不可⽤。
unicorn各个hook函数详解在软件开发中,"hook 函数"是一个非常重要的概念。
它允许开发人员在程序的特定点插入自己的代码,以便捕捉和处理特定的事件。
在 Unicorn 框架中,有多个hook 函数可以用来扩展和定制化应用程序的逻辑。
1. `unicorn_before_init_app`该 hook 函数在 Unicorn 应用程序初始化之前被调用。
在这个时候,你可以执行一些预处理任务,例如读取配置文件、验证环境设置等。
这个函数通常用于设置应用程序的一些全局参数或进行必要的初始化操作。
2. `unicorn_after_init_app`与前一个 hook 函数相反,`unicorn_after_init_app`在 Unicorn 应用程序初始化之后被调用。
这时候,你可以执行一些其他的初始化操作,例如建立数据库连接、加载插件或模块等。
该函数的主要作用是确保应用程序在初始化完成后处于正确的状态。
3. `unicorn_before_request`在每个 HTTP 请求到达应用程序之前,`unicorn_before_request`函数被调用。
你可以在这里执行一些前置处理逻辑,例如身份验证、参数解析等。
这个函数经常被用来执行请求预处理任务或者记录相关信息。
4. `unicorn_after_request``unicorn_after_request`函数在应用程序处理完每个 HTTP 请求后被调用。
你可以在这里执行一些后置处理操作,例如记录访问日志、处理异常或者清理资源。
这个函数通常用于添加响应头、处理异常情况或返回统一的格式化响应。
5. `unicorn_teardown_appcontext`在应用程序处理完每个请求后,`unicorn_teardown_appcontext`函数被调用。
你可以在这里进行一些清理操作,例如关闭数据库连接、释放资源等。
这个函数一般用于最后的清理工作,确保应用程序处于正确的终止状态。
本文将试图以下面的顺序讲解HOOK的大部分内容:1、 WINDOWS的消息机制2、 HOOK介绍3、 HOOK链4、 HOOK钩子的作用范围5、 HOOK类型6、回调函数7、 HOOK钩子的安装与卸载8、 HOOK实例演示+++++++++++++++++++WINDOWS的消息机制+++++++++++++++++++Windows系统是以消息处理为其控制机制,系统通过消息为窗口过程(windowsprocedure)传递输入。
系统和应用两者都可以产生消息。
对于每个输入事件,例如用户按下了键盘上的某个键、移动了鼠标、单击了一个控件上的滚动条,等等,系统都将产生一系列消息。
此外,对于应用带给系统的变化,如字体资源的改变、应用本身窗口的改变,系统都将通过消息以响应这种变化。
应用通过产生消息指示应用的窗口完成特定的任务,或与其他应用的窗口进行通信。
每个窗口都有一个处理Windows系统发送消息的处理程序,称为窗口程序。
它是隐含在窗口背后的一段程序脚本,其中包含对事件进行处理的代码。
Windows系统为每条消息指定了一个消息编号,例如当一个窗口变为活动窗口时,它事实上是收到一条来自Windows系统的WM_ACTIVATE消息,该消息的编号为6,它对应于VB窗口的Activate事件。
对于窗口来说,诸如Open、Activate、MouseDown、Resize等事件,实际上对应的是窗口内部的消息处理程序,这些程序对于用户来讲是不可见的。
类似地,命令按钮也有消息处理程序,它的处理程序响应诸如WM_LBUTTONDOWN和WM_RBUTTONDOWN之类的消息,即激活命令按钮的MouseDown事件.WINDOWS的消息处理机制为了能在应用程序中监控系统的各种事件消息,提供了挂接各种回调函数(HOOK)的功能.这种挂钩函数(HOOK)类似扩充中断驱动程序,挂钩上可以挂接多个反调函数构成一个挂接函数链.系统产生的各种消息首先被送到各种挂接函数,挂接函数根据各自的功能对消息进行监视、修改和控制等,然后交还控制权或将消息传递给下一个挂接函数以致最终达到窗口函数。
一、基本概念:钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。
当消息到达后,在目标窗口处理函数之前处理它。
钩子机制允许应用程序截获处理window消息或特定事件。
钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。
每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。
这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。
二、运行机制:1、钩子链表和钩子子程:每一个Hook都有一个与之相关联的指针列表,称之为钩子链表,由系统来维护。
这个列表的指针指向指定的,应用程序定义的,被Hook子程调用的回调函数,也就是该钩子的各个处理子程。
当与指定的Hook类型关联的消息发生时,系统就把这个消息传递到Hook子程。
一些Hook子程可以只监视消息,或者修改消息,或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。
最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。
Windows 并不要求钩子子程的卸载顺序一定得和安装顺序相反。
每当有一个钩子被卸载,Windows 便释放其占用的内存,并更新整个Hook链表。
如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系统会自动为它做卸载钩子的操作。
钩子子程是一个应用程序定义的回调函数(CALLBACK Functio n),不能定义成某个类的成员函数,只能定义为普通的C函数。
用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可以是系统中所有线程的事件。
钩子子程必须按照以下的语法:LRESULT CALLBACK HookProc(int nCode,WPARAM wParam,LPARAM lParamHookProc是应用程序定义的名字。
#include <ntddk.h>#define MEM_TAG 'TMEM'typedef struct _THREAD_BASIC_INFORMA TION{NTSTATUS ExitStatus;PVOID TebBaseAddress;CLIENT_ID ClientId;KAFFINITY AffinityMask;KPRIORITY Priority;KPRIORITY BasePriority;}THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMA TION; typedef BOOL (*NTUSERPOSTTHREADMESSAGE)(DWORD idThread,UINT Msg,WPARAM wParam,LPARAM lParam);typedef NTSTATUS (*NTOPENPROCESS)(OUT PHANDLE ProcessHandle,IN ACCESS_MASK AccessMask,IN POBJECT_A TTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId);typedef NTSTATUS (*NTOPENTHREAD)(OUT PHANDLE ThreadHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_A TTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId);typedef NTSTATUS (*NTDUPLICATEOBJECT)(IN HANDLE SourceProcessHandle,IN HANDLE SourceHandle,IN HANDLE TargetProcessHandle,OUT PHANDLE TargetHandle OPTIONAL,IN ACCESS_MASK DesiredAccess,IN ULONG Attributes,IN ULONG Options);NTKERNELAPI KeAddSystemServiceTable(PVOID, PVOID, PVOID, PVOID, PVOID); NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(IN HANDLE ProcessId,OUT PEPROCESS *Process);NTKERNELAPI NTSTATUS PsLookupThreadByThreadId(IN HANDLE ThreadId,OUT PETHREAD *Thread);NTKERNELAPI PEPROCESS IoThreadToProcess(IN PETHREAD Thread);NTSYSAPI NTSTATUS NTAPI ZwQueryInformationProcess(IN HANDLE ProcessHandle,IN ULONG ProcessInformationClass,OUT PVOID ProcessInformation,IN ULONG ProcessInformationLength,OUT PULONG ReturnLength OPTIONAL);NTSYSAPI NTSTATUS NTAPI ZwQueryInformationThread(IN HANDLE ThreadHandle,IN ULONG ThreadInformationClass,OUT PVOID ThreadInformation,IN ULONG ThreadInformationLength,OUT PULONG ReturnLength OPTIONAL);ULONG idPTM=476; //XP HARD CODEBYTE JmpCode[5]={0xE9,0x00,0x00,0x00,0x00};BYTE OrgCode[5]={0x8B,0x3F,0x8B,0x1C,0x87};BYTE PushRetCode[6]={0x68,0x00,0x00,0x00,0x00,0xc3};KIRQL f_oldirql;KSPIN_LOCK f_spinlock;NTOPENTHREAD OldNtOpenThread;NTOPENPROCESS OldNtOpenProcess;NTDUPLICATEOBJECT OldNtDuplicateObject;NTUSERPOSTTHREADMESSAGE OldNtUserPostThreadMessage;ULONG uKiFastCallEntryAddr=0;ULONG HookAddr=0;ULONG JMPRet=0;ULONG PushRetMem=0;ULONG ppid=0;PEPROCESS ppep=NULL;PSYSTEM_SERVICE_TABLE KeServiceDescriptorTableShadow;extern PSYSTEM_SERVICE_TABLE KeServiceDescriptorTable;ULONG GetShadowTableAddress(){ULONG dwordatbyte,i;PUCHAR p = (PUCHAR) KeAddSystemServiceTable;for(i = 0; i < PAGE_SIZE; i++, p++)// 往下找一页指针递增1{__try{dwordatbyte = *(PULONG)p;}__except(EXCEPTION_EXECUTE_HANDLER){return FALSE;}if(MmIsAddressValid((PVOID)dwordatbyte)){if(memcmp((PVOID)dwordatbyte, KeServiceDescriptorTable, 16) == 0)//对比前16字节相同则找到{if((PVOID)dwordatbyte == KeServiceDescriptorTable)//排除自己{continue;}return dwordatbyte;}}}return FALSE;}ULONG GetSSDTCurAddr(IN ULONG Index,BOOL IsShadow){ULONG ServiceCount,BaseAddr;if (KeServiceDescriptorTableShadow!=NULL){ServiceCount=KeServiceDescriptorTableShadow[IsShadow?1:0].NumberOfServices;BaseAddr = (ULONG)KeServiceDescriptorTableShadow[IsShadow?1:0].ServiceTableBase;if (Index>=ServiceCount) return FALSE;return *(PULONG)(BaseAddr+Index * 4);}return FALSE;}NTSTATUS MyNtOpenThread(OUT PHANDLE ThreadHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId){PETHREAD tcmp;PEPROCESS pcmp;NTSTATUS st,ntotst;if(MmIsAddressValid(ClientId)==TRUE){st=PsLookupThreadByThreadId((HANDLE)ClientId->UniqueThread,&tcmp);if (!NT_SUCCESS(st)){ntotst=STATUS_ACCESS_DENIED;}else{pcmp=IoThreadToProcess(tcmp);if(pcmp==ppep)ntotst=STATUS_ACCESS_DENIED;elsentotst=OldNtOpenThread(ThreadHandle,DesiredAccess,ObjectAttributes,ClientId);}}else{ntotst=STATUS_ACCESS_DENIED;}return ntotst;}NTSTATUS MyNtOpenProcess(OUT PHANDLE ProcessHandle,IN ACCESS_MASK AccessMase,IN POBJECT_A TTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId){PEPROCESS pcmp;NTSTATUS st,ntopst;if(MmIsAddressValid(ClientId)==TRUE){st=PsLookupProcessByProcessId((HANDLE)ClientId->UniqueProcess,&pcmp);if (!NT_SUCCESS(st)){ntopst=STA TUS_ACCESS_DENIED;}else{if(pcmp==ppep)ntopst=STA TUS_ACCESS_DENIED;elsentopst=OldNtOpenProcess(ProcessHandle,AccessMase,ObjectAttributes,ClientId);}}else{ntopst=STA TUS_ACCESS_DENIED;}return ntopst;}NTSTATUS MyNtDuplicateObject(IN HANDLE SourceProcessHandle,IN HANDLE SourceHandle,IN HANDLE TargetProcessHandle,OUT PHANDLE TargetHandle OPTIONAL,IN ACCESS_MASK DesiredAccess,IN ULONG Attributes,IN ULONG Options){NTSTATUS ntStatus,Tmp;PROCESS_BASIC_INFORMATION PBI;THREAD_BASIC_INFORMATION TBI;ULONG NowPid;ntStatus=OldNtDuplicateObject(SourceProcessHandle,SourceHandle,TargetProcessHandle,T argetHandle,DesiredAccess,Attributes,Options);if (NT_SUCCESS(ntStatus) ){Tmp=ZwQueryInformationProcess(*TargetHandle,0,&PBI,sizeof(PBI),NULL);if (NT_SUCCESS(Tmp)){NowPid=PBI.UniqueProcessId;if (NowPid==ppid || NowPid==ppid+1 || NowPid==ppid+2 || NowPid==ppid+3){ZwClose(*TargetHandle);*TargetHandle=0;ntStatus= STATUS_UNSUCCESSFUL;}}Tmp=ZwQueryInformationThread(*TargetHandle,0,&TBI,sizeof(TBI),NULL);if (NT_SUCCESS(Tmp)){NowPid=(ULONG)TBI.ClientId.UniqueProcess;if (NowPid==ppid || NowPid==ppid+1 || NowPid==ppid+2 || NowPid==ppid+3){ZwClose(*TargetHandle);*TargetHandle=0;ntStatus= STATUS_UNSUCCESSFUL;}}}return ntStatus;}BOOL MyNtUserPostThreadMessage(DWORD idThread,UINT Msg,WPARAM wParam,LPARAM lParam){PEPROCESS pcmp=NULL;PETHREAD tcmp=NULL;NTSTATUS st=STATUS_UNSUCCESSFUL;BOOL rt=FALSE;st=PsLookupThreadByThreadId((HANDLE)idThread,&tcmp);if (NT_SUCCESS(st)){pcmp=IoThreadToProcess(tcmp);if(pcmp==ppep)rt=FALSE;elsert=OldNtUserPostThreadMessage(idThread,Msg,wParam,lParam);}else{rt=FALSE;}return rt;}//HOOK KiFastCallEntry过滤函数__declspec(naked)void FakeKiFastCallEntry(){_asm{pushfdpushadmov edi,dword ptr [edi]mov ebx,dword ptr [edi+eax*4]cmp OldNtOpenProcess,ebx; //比较是否为NtOpenProcessje Label1cmp OldNtDuplicateObject,ebx; //比较是否为NtDuplicateObjectje Label2cmp OldNtOpenThread,ebx; //比较是否为NtOpenThreadje Label3cmp OldNtUserPostThreadMessage,ebx; //比较是否为NtOpenThreadje Label4popadpopfdmov edi,dword ptr [edi]mov ebx,dword ptr [edi+eax*4]jmp [JMPRet];Label1:popadpopfdmov ebx,MyNtOpenProcess //修改NtOpenProcess为我们的代理函数jmp [JMPRet];Label2:popadpopfdmov ebx,MyNtDuplicateObject //修改NtDuplicateObject为我们的代理函数jmp [JMPRet];Label3:popadpopfdmov ebx,MyNtOpenThread //修改NtOpenThread为我们的代理函数jmp [JMPRet];Label4:popadpopfdmov ebx,MyNtUserPostThreadMessage //修改NtUserPostThreadMessage为我们的代理函数jmp [JMPRet];}}NTSTATUS LoadKiHooker(){NTSTATUS status = STATUS_UNSUCCESSFUL;KIRQL oldIrql;PMDL pMdl = NULL;UNICODE_STRING ustrFunctionName,ustrFunctionName2;//获得“阴影表”的地址KeServiceDescriptorTableShadow=(PSYSTEM_SERVICE_TABLE)GetShadowTableAddres s();if (!KeServiceDescriptorTableShadow){DbgPrint("Find SSSDT Error!");return STATUS_UNSUCCESSFUL;}//获取NtUserPostThreadMessage地址OldNtUserPostThreadMessage=(NTUSERPOSTTHREADMESSAGE)GetSSDTCurAddr(id PTM,TRUE);DbgPrint("NtUserPostThreadMessaged=0x%08X",OldNtUserPostThreadMessage);//获取NtOpenProcess地址RtlInitUnicodeString(&ustrFunctionName, L"NtOpenProcess");OldNtOpenProcess=(NTOPENPROCESS)MmGetSystemRoutineAddress( &ustrFunctionNa me);DbgPrint("NtOpenProcess=0x%08X",OldNtOpenProcess);//获取NtDuplicateObject地址RtlInitUnicodeString(&ustrFunctionName2, L"NtDuplicateObject");OldNtDuplicateObject=(NTDUPLICATEOBJECT)MmGetSystemRoutineAddress(&ustrFun ctionName2);DbgPrint("NtDuplicateObject=0x%08X",OldNtDuplicateObject);//获取NtOpenThread地址OldNtOpenThread=(NTOPENTHREAD)GetSSDTRealAddr(GetSysCallIndex("NtOpenThre ad"));DbgPrint("NtOpenThread=0x%08X",OldNtOpenThread);//系统调用管理器的地址保存在MSR寄存器里面,标识ID为0x176是SYSENTER_EIP_MSR寄存器,存放着KiFastCallEntry地址!所以在这里用rdmsr读取KiFastCallEntry地址;__asm{pushfdpushadmov ecx,0x176rdmsrmov uKiFastCallEntryAddr,eax //获取KiFastCallEntry地址xor ecx,ecxLabel1:cmp ecx,0x100je Label3mov edx,DWORD ptr [eax]cmp edx,0x1C8B3F8B //搜索特征码,获取要Hook的位置je Label2inc eaxinc ecxjmp Label1Label2:mov HookAddr,eaxLabel3:popadpopfd}if (HookAddr==0){return status;}//申请分配二级跳转内存PushRetMem=(ULONG)ExAllocatePoolWithTag(NonPagedPool,6,MEM_TAG);if ((PVOID)PushRetMem==NULL){return status;}DbgPrint("PushRetMem=0x%08X",PushRetMem);//一级跳转地址*(ULONG*)&JmpCode[1]=(ULONG)(PushRetMem)-(HookAddr+5);//二级跳转地址*(ULONG*)&PushRetCode[1]=(ULONG)FakeKiFastCallEntry;//HOOK返回地址JMPRet=HookAddr+5;//申请MDLpMdl = IoAllocateMdl((PBYTE)HookAddr, 5, FALSE, FALSE, NULL);if (pMdl){//锁定内存页面__try{MmProbeAndLockPages(pMdl, KernelMode, IoWriteAccess);}__except(EXCEPTION_EXECUTE_HANDLER){IoFreeMdl(pMdl);return status;}//申请并使用自旋锁KeInitializeSpinLock(&f_spinlock);KeAcquireSpinLock(&f_spinlock,&f_oldirql);//提升中断请求级oldIrql = KeRaiseIrqlToDpcLevel();//关闭中断_asm{CLIMOV EAX, CR0AND EAX, NOT 10000HMOV CR0, EAX}//进行HOOK操作RtlCopyMemory((PVOID)PushRetMem,PushRetCode,6);RtlCopyMemory((PVOID)HookAddr,JmpCode,5);//开启中断_asm{MOV EAX, CR0OR EAX, 10000HMOV CR0, EAXSTI}//恢复先前中断请求级KeLowerIrql(oldIrql);//释放自旋锁KeReleaseSpinLock(&f_spinlock, f_oldirql);DbgPrint("KiFastCallEntry=0x%08X",uKiFastCallEntryAddr);DbgPrint("HookAddr=0x%08X",HookAddr);status=STATUS_SUCCESS;}return status;}VOID UnloadKiHooker(){PMDL pMdl = NULL;KIRQL oldIrql;if (HookAddr!=0){pMdl = IoAllocateMdl((PBYTE)HookAddr, 5, FALSE, FALSE, NULL);if (pMdl){//锁定内存页面__try{MmProbeAndLockPages(pMdl, KernelMode, IoWriteAccess);}__except(EXCEPTION_EXECUTE_HANDLER){IoFreeMdl(pMdl);return;}//申请并使用自旋锁KeInitializeSpinLock(&f_spinlock);KeAcquireSpinLock(&f_spinlock,&f_oldirql);//提升中断请求级oldIrql = KeRaiseIrqlToDpcLevel();//关闭中断_asm{CLIMOV EAX, CR0AND EAX, NOT 10000HMOV CR0, EAX}//进行还原HOOK操作RtlCopyMemory((PVOID)HookAddr,OrgCode,5);_asm{MOV EAX, CR0OR EAX, 10000HMOV CR0, EAXSTI}//恢复先前中断请求级KeLowerIrql(oldIrql);//释放自旋锁KeReleaseSpinLock(&f_spinlock, f_oldirql);//释放内存ExFreePool((PVOID)PushRetMem);}}}。