Win64 驱动内核编程-32.枚举与删除注册表回调
枚举与删除注册表回调
注册表回调是一个监控注册表读写的回调,它的效果非常明显,一个回调能实现在SSDT 上 HOOK 十几个 API 的效果。部分游戏保护还会在注册表回调上做功夫,监控 service
键的子键,实现双层拦截驱动加载(在映像回调那里还有一层)。而在卡巴斯基等 HIPS 类软件里,则用来监控自启动等键值。
注册表回调在 XP 系统上貌似是一个数组,但是从 WINDOWS 2003 开始,就变成了一个链表。这个链表的头称为 CallbackListHead,可在 CmUnRegisterCallback 中找到:
搜索的过程跟寻找进程、线程、映像的数组类似,根据 lea REG,XXX 来定位。不过为了更加精准,这次资料中是采用两次 lea REG,XXX 来定位:
ULONG64 FindCmpCallbackAfterXP()
{
ULONG64 uiAddress=0;
PUCHAR pCheckArea=NULL, i=0, j=0, StartAddress=0, EndAddress=0;
ULONG64 dwCheckAddr=0;
UNICODE_STRING unstrFunc;
UCHAR b1=0,b2=0,b3=0;
ULONG templong=0,QuadPart=0xfffff800;
RtlInitUnicodeString(&unstrFunc, L"CmUnRegisterCallback");
pCheckArea = (UCHAR*)MmGetSystemRoutineAddress (&unstrFunc) ;
if (!pCheckArea)
{
KdPrint(("MmGetSystemRoutineAddress failed."));
return 0;
}
StartAddress = (PUCHAR)pCheckArea;
EndAddress = (PUCHAR)pCheckArea + PAGE_SIZE;
for(i=StartAddress;i<EndAddress;i++)
{
if( MmIsAddressValid(i) && MmIsAddressValid(i+1) && MmIsAddressValid(i+2) )
{
b1=*i;
b2=*(i+1);
b3=*(i+2);
if( b1==0x48 && b2==0x8d && b3==0x0d ) //488d0d(lea rcx,)
{
j=i-5;
b1=*j;
b2=*(j+1);
b3=*(j+2);
if( b1==0x48 && b2==0x8d && b3==0x54 ) //488d54(lea rdx,)
{
memcpy(&templong,i+3,4);
uiAddress = MakeLong64ByLong32(templong) + (ULONGLONG)i + 7;
return uiAddress;
}
}
}
}
return 0;
}
定位完毕之后,就是枚举链表了。注册表回调是一个“结构体链表”,类似于 EPROCESS,它的定义如下:
typedef struct _CM_NOTIFY_ENTRY
{
LIST_ENTRY ListEntryHead;
ULONG UnKnown1;
ULONG UnKnown2;
LARGE_INTEGER Cookie;
ULONG64 Context;
ULONG64 Function;
}CM_NOTIFY_ENTRY, *PCM_NOTIFY_ENTRY;
我们只关心两个值,一个是 Cookie,一个是 Function。前者可以理解成注册表回调的“句柄”(用 CmUnRegisterCallback 注销回调传入的就是这个 Cookie),后者是回调函数的地址。代码如下:
ULONG CountCmpCallbackAfterXP(ULONG64* pPspLINotifyRoutine)
{
ULONG sum = 0;
ULONG64 dwNotifyItemAddr;
ULONG64* pNotifyFun;
ULONG64* baseNotifyAddr;
ULONG64 dwNotifyFun;
LARGE_INTEGER cmpCookie;
PLIST_ENTRY notifyList;
PCM_NOTIFY_ENTRY notify;
dwNotifyItemAddr = *pPspLINotifyRoutine;
notifyList = (LIST_ENTRY *)dwNotifyItemAddr;
do
{
notify = (CM_NOTIFY_ENTRY *)notifyList;
if (MmIsAddressValid(notify))
{
if (MmIsAddressValid((PVOID)(notify->Function)) && notify->Function > 0x8000000000000000)
{
DbgPrint("[CmCallback]Function=%p\tCookie=%p", (PVOID)(notify->Function),(PVOID)(notify->Cookie.QuadPart));
//notify->Function=(ULONG64)MyRegistryCallback;
sum ++;
}
}
notifyList = notifyList->Flink;
}while ( notifyList != ((LIST_ENTRY*)(*pPspLINotifyRoutine)) );
return sum;
}
执行效果(在有360的机器上执行的):
对付注册表回调有三种方法:
1.直接使用CmUnRegisterCallback 把回调注销;
2.把链表中记录的回调地址修改为自定义的空函数的回调地址;
3.直接在目标回调地址上写一个 RET,使其不执行任何代码就返回。 第三种 方法 没有针对性,可以用于对付 任何 回调函数。DisableFunctionWithReturnValue 用来对付有返回值的回调函数,DisableFunctionWithoutReturnValue 用于对付无返回值的回调函数。
/*
给函数头写RET废除函数功能,这份代码可以用于对抗任意回调函数,但仅限于WIN64系统。
DisableFunctionWithReturnValue用于有返回值的回调
DisableFunctionWithoutReturnValue用于无返回值的回调
*/
KIRQL WPOFFx64()
{
KIRQL irql=KeRaiseIrqlToDpcLevel();
UINT64 cr0=__readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
_disable();
return irql;
}
void WPONx64(KIRQL irql)
{
UINT64 cr0=__readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
KeLowerIrql(irql);
}
VOID DisableFunctionWithReturnValue(PVOID Address)
{
KIRQL irql;
CHAR patchCode[] = "\x33\xC0\xC3"; //xor eax,eax + ret
if(MmIsAddressValid(Address))
{
irql=WPOFFx64();
memcpy(Address,patchCode,3);
WPONx64(irql);
}
}
VOID DisableFunctionWithoutReturnValue(PVOID Address)
{
KIRQL irql;
if(MmIsAddressValid(Address))
{
irql=WPOFFx64();
RtlFillMemory(Address,1,0xC3);
WPONx64(irql);
}
}
代码备份:win7 X64
typedef struct _CM_NOTIFY_ENTRY
{
LIST_ENTRY ListEntryHead;
ULONG UnKnown1;
ULONG UnKnown2;
LARGE_INTEGER Cookie;
ULONG64 Context;
ULONG64 Function;
}CM_NOTIFY_ENTRY, *PCM_NOTIFY_ENTRY;
LARGE_INTEGER Cookie;
ULONG64 CmCallbackListHead=0;
NTSTATUS MyRegistryCallback(
IN PVOID CallbackContext,
IN PVOID Argument1,
IN PVOID Argument2
)
{
return STATUS_SUCCESS;
}
ULONG CountCmpCallbackAfterXP(ULONG64* pPspLINotifyRoutine)
{
ULONG sum = 0;
ULONG64 dwNotifyItemAddr;
ULONG64* pNotifyFun;
ULONG64* baseNotifyAddr;
ULONG64 dwNotifyFun;
LARGE_INTEGER cmpCookie;
PLIST_ENTRY notifyList;
PCM_NOTIFY_ENTRY notify;
dwNotifyItemAddr = *pPspLINotifyRoutine;
notifyList = (LIST_ENTRY *)dwNotifyItemAddr;
do
{
notify = (CM_NOTIFY_ENTRY *)notifyList;
if (MmIsAddressValid(notify))
{
if (MmIsAddressValid((PVOID)(notify->Function)) && notify->Function > 0x8000000000000000)
{
DbgPrint("[CmCallback]Function=%p\tCookie=%p", (PVOID)(notify->Function),(PVOID)(notify->Cookie.QuadPart));
//notify->Function=(ULONG64)MyRegistryCallback;
sum ++;
}
}
notifyList = notifyList->Flink;
}while ( notifyList != ((LIST_ENTRY*)(*pPspLINotifyRoutine)) );
return sum;
}
LONG64 MakeLong64ByLong32(LONG lng32)
{
LONG64 lng64=0;
if(lng32>0)
{
lng64=(LONG64)lng32;
}
else
{
lng64=0xffffffffffffffff;
memcpy(&lng64,&lng32,4);
}
return lng64;
}
ULONG64 FindCmpCallbackAfterXP()
{
ULONG64 uiAddress=0;
PUCHAR pCheckArea=NULL, i=0, j=0, StartAddress=0, EndAddress=0;
ULONG64 dwCheckAddr=0;
UNICODE_STRING unstrFunc;
UCHAR b1=0,b2=0,b3=0;
ULONG templong=0,QuadPart=0xfffff800;
RtlInitUnicodeString(&unstrFunc, L"CmUnRegisterCallback");
pCheckArea = (UCHAR*)MmGetSystemRoutineAddress (&unstrFunc) ;
if (!pCheckArea)
{
KdPrint(("MmGetSystemRoutineAddress failed."));
return 0;
}
StartAddress = (PUCHAR)pCheckArea;
EndAddress = (PUCHAR)pCheckArea + PAGE_SIZE;
for(i=StartAddress;i<EndAddress;i++)
{
if( MmIsAddressValid(i) && MmIsAddressValid(i+1) && MmIsAddressValid(i+2) )
{
b1=*i;
b2=*(i+1);
b3=*(i+2);
if( b1==0x48 && b2==0x8d && b3==0x0d ) //488d0d(lea rcx,)
{
j=i-5;
b1=*j;
b2=*(j+1);
b3=*(j+2);
if( b1==0x48 && b2==0x8d && b3==0x54 ) //488d54(lea rdx,)
{
memcpy(&templong,i+3,4);
uiAddress = MakeLong64ByLong32(templong) + (ULONGLONG)i + 7;
return uiAddress;
}
}
}
}
return 0;
}
void EnumCmCallback64()
{
//test to add my reg callback
CmRegisterCallback(MyRegistryCallback, NULL, &Cookie);
DbgPrint("[MY FUNCTION]: %p",(PVOID)MyRegistryCallback);
DbgPrint("[MY COOKIE]: %p",(PVOID)Cookie.QuadPart);
//get CmCallbackListHead address
CmCallbackListHead=FindCmpCallbackAfterXP();
DbgPrint("CmCallbackListHead: %p",(PVOID)CmCallbackListHead);
//enum callback address
CountCmpCallbackAfterXP((ULONG64*)CmCallbackListHead);
//unregister my callback
CmUnRegisterCallback(Cookie);
}
/*
给函数头写RET废除函数功能,这份代码可以用于对抗任意回调函数,但仅限于WIN64系统。
DisableFunctionWithReturnValue用于有返回值的回调
DisableFunctionWithoutReturnValue用于无返回值的回调
*/
KIRQL WPOFFx64()
{
KIRQL irql=KeRaiseIrqlToDpcLevel();
UINT64 cr0=__readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
_disable();
return irql;
}
void WPONx64(KIRQL irql)
{
UINT64 cr0=__readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
KeLowerIrql(irql);
}
VOID DisableFunctionWithReturnValue(PVOID Address)
{
KIRQL irql;
CHAR patchCode[] = "\x33\xC0\xC3"; //xor eax,eax + ret
if(MmIsAddressValid(Address))
{
irql=WPOFFx64();
memcpy(Address,patchCode,3);
WPONx64(irql);
}
}
VOID DisableFunctionWithoutReturnValue(PVOID Address)
{
KIRQL irql;
if(MmIsAddressValid(Address))
{
irql=WPOFFx64();
RtlFillMemory(Address,1,0xC3);
WPONx64(irql);
}
}
宋孖健,13
Win64 驱动内核编程-32.枚举与删除注册表回调的更多相关文章
- Win64 驱动内核编程-31.枚举与删除映像回调
枚举与删除映像回调 映像回调可以拦截 RING3 和 RING0 的映像加载.某些游戏保护会用此来拦截黑名单中的驱动加载,比如 XUETR.WIN64AST 的驱动.同理,在反游戏保护的过程中,也可以 ...
- Win64 驱动内核编程-30.枚举与删除线程回调
枚举与删除线程回调 进程回调可以监视进程的创建和退出,这个在前面的章节已经总结过了.某些游戏保护的驱动喜欢用这个函数来监视有没有黑名单中的程序运行,如果运行则阻止运行或者把游戏退出.而线程回调则通常用 ...
- Win64 驱动内核编程-33.枚举与删除对象回调
转载:http://www.voidcn.com/article/p-wulgeluy-bao.html 枚举与删除对象回调 对象回调存储在对应对象结构体里,简单来说,就是存储在 ObjectType ...
- Win64 驱动内核编程-28.枚举消息钩子
枚举消息钩子 简单粘贴点百度的解释,科普下消息钩子: 钩子是WINDOWS中消息处理机制的一个要点,通过安装各种钩子,应用程序能够设置相应的子例程来监视系统里的消息传递以及在这些消息到达目标窗口程序之 ...
- Win64 驱动内核编程-15.回调监控注册表
回调监控注册表 在 WIN32 平台上,监控注册表的手段通常是 SSDT HOOK.不过用 SSDT HOOK 的方式监控注册表实在是太麻烦了,要 HOOK 一大堆函数,还要处理一些 NT6 系统有而 ...
- Win64 驱动内核编程-3.内核里使用内存
内核里使用内存 内存使用,无非就是申请.复制.设置.释放.在 C 语言里,它们对应的函数是:malloc.memcpy.memset.free:在内核编程里,他们分别对应 ExAllocatePool ...
- Win64 驱动内核编程-11.回调监控进线程句柄操作
无HOOK监控进线程句柄操作 在 NT5 平台下,要监控进线程句柄的操作. 通常要挂钩三个API:NtOpenProcess.NtOpenThread.NtDuplicateObject.但是在 VI ...
- Win64 驱动内核编程-8.内核里的其他常用
内核里的其他常用 1.遍历链表.内核里有很多数据结构,但它们并不是孤立的,内核使用双向链表把它们像糖 葫芦一样给串了起来.所以遍历双向链表能获得很多重要的内核数据.举个简单的例子,驱 动对象 Driv ...
- Win64 驱动内核编程-7.内核里操作进程
在内核里操作进程 在内核里操作进程,相信是很多对 WINDOWS 内核编程感兴趣的朋友第一个学习的知识点.但在这里,我要让大家失望了,在内核里操作进程没什么特别的,就标准方法而言,还是调用那几个和进程 ...
随机推荐
- Java流程控制:增强for循环,break&continue,打印99乘法表
增强for循环:java5引入了一种主要用于数组或集合的增强for循环for(声明语句:表达式){//代码句子} 声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配.其作用域限定在循环语 ...
- uniCloud的简单使用 增删改查
新建一个uni-app 项目 启动云开发 选择想要的云服务 在次之前先完成uniCloud 的实名认证 https://unicloud.dcloud.net.cn 有在Web控制台创建过云服务空间就 ...
- 双向链表及有关操作(C语言)
#include <stdio.h> #include <stdlib.h> /** * 含头节点双向链表定义及有关操作 */ //操作函数用到的状态码 #define TRU ...
- 三分钟教你提升应用推送的ROI
推送是App应用性价比最高也是最直接的营销运营手段,其细节颇多,非常考验运营人员的功力,本文将从ROI角度来分析怎么提升营销类推送的收益.(非IM类.系统类等功能服务型推送) 以一个日活100万的应用 ...
- windows回收站无法设置
win+r运行 regedit HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer 修改NoRe ...
- MySQL的安装及使用
安装MySQL 这里建议大家使用压缩版,安装快,方便.不复杂. 1.MySQL软件下载 mysql5.7 64位下载地址: https://dev.mysql.com/get/Downloads/My ...
- OpenGL 绘制你的 github skyline 模型
前言 好久没更新博客了,上一篇文章还是在去年6月份,乍一看居然快要过去一年了,不时还能看到粉丝数和排名在涨,可是却一直没有内容更新,怪不好意思的- -(主要是一直没想好要写些什么,中间也有过一些想法, ...
- 【Spring Cloud & Alibaba全栈开源项目实战】:SpringBoot整合ELK实现分布式登录日志收集和统计
一. 前言 其实早前就想计划出这篇文章,但是最近主要精力在完善微服务.系统权限设计.微信小程序和管理前端的功能,不过好在有群里小伙伴的一起帮忙反馈问题,基础版的功能已经差不多,也在此谢过,希望今后大家 ...
- python函数之有参装饰器
一.为什么要有有参装饰器? 来看之前的无参装饰器 # 无参装饰器 def outter(func): def wrapper(*args,**kwargs): start = time.time() ...
- Android Studio 如何运行单个activity
•写在前面 调试界面运行单个 Activity 可节省编译整个项目的时间提高效率: 本着提高效率的角度,特地上网百度相关知识: •解决方法 首先,在 AndroidManifest.xml 文件中,找 ...