Win64 驱动内核编程-30.枚举与删除线程回调
枚举与删除线程回调
进程回调可以监视进程的创建和退出,这个在前面的章节已经总结过了。某些游戏保护的驱动喜欢用这个函数来监视有没有黑名单中的程序运行,如果运行则阻止运行或者把游戏退出。而线程回调则通常用来监控远程线程的建立,如果发现有远程线程注入到了游戏进程里,则马上把游戏退出。现在来详细讲解如何绕过这个两个监控。
我们注册的进程回调,会存储在一个名为 PspCreateProcessNotifyRoutine 的数组里。
PspCreateProcessNotifyRoutine 可以理解成一个 PVOID 数组,它记录了系统里所有进程回调的地址。这个数组最大长度是 64*sizeof(PVOID)。所以枚举进程回调的思路如下:找到这个数组的地址,然后解密数组的数据,得到所有回调的地址(这个数组记录的数据并不是回调的 地 址 , 而 是 经 过 加 密 地 址 , 需 要 解 密 才 行 )。 枚 举 线 程 回 调 同 理 , 要 找 到PspCreateThreadNotifyRoutine 的地址(这个数组最大长度也是 64*sizeof(PVOID)),然后解密数据,并把解密后的地址打印出来。
至于怎么处理这些回调就简单了。可以使用标准函数(PsSetCreateProcessNotifyRoutine、PsRemoveCreateThreadNotifyRoutine)将其摘掉,也可以直接在回调函数首地址写入 RET 把回调函数废掉。
首先要获得 PspCreateProcessNotifyRoutine 的地址。PspCreateProcessNotifyRoutine 在PspSetCreateProcessNotifyRoutine 函数里出现了。而 PspSetCreateProcessNotifyRoutine 则在PsSetCreateProcessNotifyRoutine 中被调用(注意前一个是 PspXXX,后一个是 PsXXX)。找到PspSetCreateProcessNotifyRoutine 之后,再匹配特征码:
于是我们根据特征码写出了以下代码(仅在 WIN7X64 上有效,WIN8、8.1 需要自己重新
定义特征码):
ULONG64 FindPspCreateProcessNotifyRoutine()
{
LONG OffsetAddr=0;
ULONG64 i=0,pCheckArea=0;
UNICODE_STRING unstrFunc;
//获得PsSetCreateProcessNotifyRoutine的地址
RtlInitUnicodeString(&unstrFunc, L"PsSetCreateProcessNotifyRoutine");
pCheckArea = (ULONG64)MmGetSystemRoutineAddress (&unstrFunc);
//获得PspSetCreateProcessNotifyRoutine的地址
memcpy(&OffsetAddr,(PUCHAR)pCheckArea+4,4);
pCheckArea=(pCheckArea+3)+5+OffsetAddr;
DbgPrint("PspSetCreateProcessNotifyRoutine: %llx",pCheckArea);
//获得PspCreateProcessNotifyRoutine的地址
for(i=pCheckArea;i<pCheckArea+0xff;i++)
{
if(*(PUCHAR)i==0x4c && *(PUCHAR)(i+1)==0x8d && *(PUCHAR)(i+2)==0x35) //lea r14,xxxx
{
LONG OffsetAddr=0;
memcpy(&OffsetAddr,(PUCHAR)(i+3),4);
return OffsetAddr+7+i;
}
}
return 0;
}
找到了 PspCreateProcessNotifyRoutine,枚举操作就好办了。需要说明的是,在PspCreateProcessNotifyRoutine 里的数据竟然被加密了,需要把数组的值和 0xfffffffffffffff8进行“与”位运算才行:
找线程的同理:
PspCreateThreadNotifyRoutine ->
PsSetCreateThreadNotifyRoutine ->
PspCreateThreadNotifyRoutine
执行结果如下(干净win7系统没有线程回调,特意注册了两个):
void EnumCreateProcessNotify()
{
int i=0;
BOOLEAN b;
ULONG64 NotifyAddr=0,MagicPtr=0;
ULONG64 PspCreateProcessNotifyRoutine=FindPspCreateProcessNotifyRoutine();
DbgPrint("PspCreateProcessNotifyRoutine: %llx",PspCreateProcessNotifyRoutine);
if(!PspCreateProcessNotifyRoutine)
return;
for(i=0;i<64;i++)
{
MagicPtr=PspCreateProcessNotifyRoutine+i*8;
NotifyAddr=*(PULONG64)(MagicPtr);
if(MmIsAddressValid((PVOID)NotifyAddr) && NotifyAddr!=0)
{
NotifyAddr=*(PULONG64)(NotifyAddr & 0xfffffffffffffff8);
DbgPrint("[CreateProcess]%llx",NotifyAddr);
}
}
}
代码整理: 进程
ULONG64 FindPspCreateProcessNotifyRoutine()
{
LONG OffsetAddr=0;
ULONG64 i=0,pCheckArea=0;
UNICODE_STRING unstrFunc;
//获得PsSetCreateProcessNotifyRoutine的地址
RtlInitUnicodeString(&unstrFunc, L"PsSetCreateProcessNotifyRoutine");
pCheckArea = (ULONG64)MmGetSystemRoutineAddress (&unstrFunc);
//获得PspSetCreateProcessNotifyRoutine的地址
memcpy(&OffsetAddr,(PUCHAR)pCheckArea+4,4);
pCheckArea=(pCheckArea+3)+5+OffsetAddr;
DbgPrint("PspSetCreateProcessNotifyRoutine: %llx",pCheckArea);
//获得PspCreateProcessNotifyRoutine的地址
for(i=pCheckArea;i<pCheckArea+0xff;i++)
{
if(*(PUCHAR)i==0x4c && *(PUCHAR)(i+1)==0x8d && *(PUCHAR)(i+2)==0x35) //lea r14,xxxx
{
LONG OffsetAddr=0;
memcpy(&OffsetAddr,(PUCHAR)(i+3),4);
return OffsetAddr+7+i;
}
}
return 0;
}
void EnumCreateProcessNotify()
{
int i=0;
BOOLEAN b;
ULONG64 NotifyAddr=0,MagicPtr=0;
ULONG64 PspCreateProcessNotifyRoutine=FindPspCreateProcessNotifyRoutine();
DbgPrint("PspCreateProcessNotifyRoutine: %llx",PspCreateProcessNotifyRoutine);
if(!PspCreateProcessNotifyRoutine)
return;
for(i=0;i<64;i++)
{
MagicPtr=PspCreateProcessNotifyRoutine+i*8;
NotifyAddr=*(PULONG64)(MagicPtr);
if(MmIsAddressValid((PVOID)NotifyAddr) && NotifyAddr!=0)
{
NotifyAddr=*(PULONG64)(NotifyAddr & 0xfffffffffffffff8);
DbgPrint("[CreateProcess]%llx",NotifyAddr);
}
}
}
线程(包括创建线程回调测试代码):
void CreateThreadNotify1
(
IN HANDLE ProcessId,
IN HANDLE ThreadId,
IN BOOLEAN Create
)
{
DbgPrint("CreateThreadNotify1\n");
}
void CreateThreadNotify2
(
IN HANDLE ProcessId,
IN HANDLE ThreadId,
IN BOOLEAN Create
)
{
DbgPrint("CreateThreadNotify2\n");
}
void CreateThreadNotifyTest(BOOLEAN Remove)
{
if(!Remove)
{
PsSetCreateThreadNotifyRoutine(CreateThreadNotify1);
PsSetCreateThreadNotifyRoutine(CreateThreadNotify2);
}
else
{
PsRemoveCreateThreadNotifyRoutine(CreateThreadNotify1);
PsRemoveCreateThreadNotifyRoutine(CreateThreadNotify2);
}
}
ULONG64 FindPspCreateThreadNotifyRoutine()
{
ULONG64 i=0,pCheckArea=0;
UNICODE_STRING unstrFunc;
RtlInitUnicodeString(&unstrFunc, L"PsSetCreateThreadNotifyRoutine");
pCheckArea = (ULONG64)MmGetSystemRoutineAddress (&unstrFunc);
DbgPrint("PsSetCreateThreadNotifyRoutine: %llx",pCheckArea);
for(i=pCheckArea;i<pCheckArea+0xff;i++)
{
if(*(PUCHAR)i==0x48 && *(PUCHAR)(i+1)==0x8d && *(PUCHAR)(i+2)==0x0d) //lea rcx,xxxx
{
LONG OffsetAddr=0;
memcpy(&OffsetAddr,(PUCHAR)(i+3),4);
return OffsetAddr+7+i;
}
}
return 0;
}
void EnumCreateThreadNotify()
{
int i=0;
BOOLEAN b;
ULONG64 NotifyAddr=0,MagicPtr=0;
ULONG64 PspCreateThreadNotifyRoutine=FindPspCreateThreadNotifyRoutine();
DbgPrint("PspCreateThreadNotifyRoutine: %llx",PspCreateThreadNotifyRoutine);
if(!PspCreateThreadNotifyRoutine)
return;
for(i=0;i<64;i++)
{
MagicPtr=PspCreateThreadNotifyRoutine+i*8;
NotifyAddr=*(PULONG64)(MagicPtr);
if(MmIsAddressValid((PVOID)NotifyAddr) && NotifyAddr!=0)
{
NotifyAddr=*(PULONG64)(NotifyAddr & 0xfffffffffffffff8);
DbgPrint("[CreateThread]%llx",NotifyAddr);
PsRemoveCreateThreadNotifyRoutine(NotifyAddr);
}
}
}
宋孖健,13
Win64 驱动内核编程-30.枚举与删除线程回调的更多相关文章
- Win64 驱动内核编程-33.枚举与删除对象回调
转载:http://www.voidcn.com/article/p-wulgeluy-bao.html 枚举与删除对象回调 对象回调存储在对应对象结构体里,简单来说,就是存储在 ObjectType ...
- Win64 驱动内核编程-31.枚举与删除映像回调
枚举与删除映像回调 映像回调可以拦截 RING3 和 RING0 的映像加载.某些游戏保护会用此来拦截黑名单中的驱动加载,比如 XUETR.WIN64AST 的驱动.同理,在反游戏保护的过程中,也可以 ...
- Win64 驱动内核编程-32.枚举与删除注册表回调
枚举与删除注册表回调 注册表回调是一个监控注册表读写的回调,它的效果非常明显,一个回调能实现在SSDT 上 HOOK 十几个 API 的效果.部分游戏保护还会在注册表回调上做功夫,监控 service ...
- Win64 驱动内核编程-28.枚举消息钩子
枚举消息钩子 简单粘贴点百度的解释,科普下消息钩子: 钩子是WINDOWS中消息处理机制的一个要点,通过安装各种钩子,应用程序能够设置相应的子例程来监视系统里的消息传递以及在这些消息到达目标窗口程序之 ...
- Win64 驱动内核编程-3.内核里使用内存
内核里使用内存 内存使用,无非就是申请.复制.设置.释放.在 C 语言里,它们对应的函数是:malloc.memcpy.memset.free:在内核编程里,他们分别对应 ExAllocatePool ...
- Win64 驱动内核编程-8.内核里的其他常用
内核里的其他常用 1.遍历链表.内核里有很多数据结构,但它们并不是孤立的,内核使用双向链表把它们像糖 葫芦一样给串了起来.所以遍历双向链表能获得很多重要的内核数据.举个简单的例子,驱 动对象 Driv ...
- Win64 驱动内核编程-7.内核里操作进程
在内核里操作进程 在内核里操作进程,相信是很多对 WINDOWS 内核编程感兴趣的朋友第一个学习的知识点.但在这里,我要让大家失望了,在内核里操作进程没什么特别的,就标准方法而言,还是调用那几个和进程 ...
- Win64 驱动内核编程-2.基本框架(安装.通讯.HelloWorld)
驱动安装,通讯,Hello World 开发驱动的简单流程是这样,开发驱动安装程序,开发驱动程序,然后安装程序(或者其他程序)通过通讯给驱动传命令,驱动接到之后进行解析并且执行,然后把执行结果返回. ...
- Win64 驱动内核编程-11.回调监控进线程句柄操作
无HOOK监控进线程句柄操作 在 NT5 平台下,要监控进线程句柄的操作. 通常要挂钩三个API:NtOpenProcess.NtOpenThread.NtDuplicateObject.但是在 VI ...
随机推荐
- C#的foreach遍历循环和隐式类型变量
C#的foreach遍历循环和隐式类型变量 foreach遍历循环 foreach (<baseType> <name> in <array>>) { //c ...
- ubantu16与windows下——redis的安装与使用
(1) 打开ubantu16,使用如下命令下载安装包 wget http://download.redis.io/releases/redis-2.8.3.tar.gz (2)解压缩命令: tar ...
- java将一个list转换成一个String,中间用分隔符隔开
List sn=[123,1231,1231,231] sn.toString();//[123,1231,1231,231] sn.join(',').toString();//123,1231,1 ...
- 如何使用excel制作查分系统
在工作学习中,我们经常会遇到使用excel制作查分系统这样的问题.培根说过:读书足以恬情,足以博采,足以长才.因此,面对使用excel制作查分系统我们应该有努力探索的精神.书到用时方恨少,事非经过不知 ...
- Java BasicNameValuePair怎么传数组类型的参数?
BasicNameValuePair 传数组的话可以这样传 map.put("ids[]", 1); map.put("ids[]", 2);
- 图像匹配 | NCC 归一化互相关损失 | 代码 + 讲解
文章转载自:微信公众号「机器学习炼丹术」 作者:炼丹兄(已授权) 作者联系方式:微信cyx645016617(欢迎交流共同进步) 本次的内容主要讲解NCCNormalized cross-correl ...
- Elasticsearch 结构化搜索、keyword、Term查询
前言 Elasticsearch 中的结构化搜索,即面向数值.日期.时间.布尔等类型数据的搜索,这些数据类型格式精确,通常使用基于词项的term精确匹配或者prefix前缀匹配.本文还将新版本的&qu ...
- Git 在解决冲突的时候文件覆盖
更新代码导致被还原或覆盖的场景:1.触发冲突的必要条件是修改同一个文件且修改的位置非常近,否则Git会自动合并其内容避免更新代码导致被还原或覆盖的解决方案1.少修改的地方(生产环境.公网测试环境):推 ...
- 运维干货|交换机不同VLAN之间及相同VLAN之内进行隔离
文中所展示的内容为VLAN与VLAN之间分隔关系,如相同VLAN用户之间进行分隔,相同VLAN一组用户之间允许通信并与其它一组用户之间进行分隔,属于VLAN的高级应用范畴.本文来源于智象运维某大神的日 ...
- [图论]剑鱼行动:prim
剑鱼行动 目录 剑鱼行动 Description Input Output Sample Input Sample Output 解析 难点 代码 Description 给出N个点的坐标,对它们建立 ...