注册表回调是一个比较监控注册表读写的回调, 它的能量非常大, 一个回调能实现在 SSDT 上 HOOK 十几个 API 的效果。 部分游戏保护还会在注册表回调上做功夫,监控 service 键的子键,实现双层拦截驱动加载(在映像回调那里还有一层)。 而在卡巴斯基等 HIPS 类软件里,则用来监控自启动等键值。
注册表回调在 XP 系统上貌似是一个数组, 但是从 WINDOWS 2003 开始,就变成了一个链表。 这个链表的头称为 CallbackListHead , 可在 CmUnRegisterCallback 中找到:
lkd> uf CmUnRegisterCallback nt!CmUnRegisterCallback: fffff800`01cba790 48894c2408 mov qword ptr [rsp+8],rcx fffff800`01cba795 53 push rbx fffff800`01cba796 56 push rsi fffff800`01cba797 57 push rdi fffff800`01cba798 4154 push r12 fffff800`01cba79a 4155 push r13 fffff800`01cba79c 4156 push r14 fffff800`01cba79e 4157 push r15 fffff800`01cba7a0 4883ec60 sub rsp,60h fffff800`01cba7a4 41bc0d0000c0 mov r12d,0C000000Dh fffff800`01cba7aa 4489a424b0000000 mov dword ptr [rsp+0B0h],r12d fffff800`01cba7b2 33db xor ebx,ebx fffff800`01cba7b4 48895c2448 mov qword ptr [rsp+48h],rbx fffff800`01cba7b9 33c0 xor eax,eax fffff800`01cba7bb 4889442450 mov qword ptr [rsp+50h],rax fffff800`01cba7c0 4889442458 mov qword ptr [rsp+58h],rax fffff800`01cba7c5 448d6b01 lea r13d,[rbx+1] fffff800`01cba7c9 458afd mov r15b,r13b fffff800`01cba7cc 4488ac24a8000000 mov byte ptr [rsp+0A8h],r13b fffff800`01cba7d4 440f20c7 mov rdi,cr8 fffff800`01cba7d8 450f22c5 mov cr8,r13 fffff800`01cba7dc f00fba351b6adcff00 lock btr dword ptr [nt!CallbackUnregisterLock (fffff800`01a81200)],0 fffff800`01cba7e5 720c jb nt!CmUnRegisterCallback+0x63 (fffff800`01cba7f3) //省略中间无关代码 nt!CmUnRegisterCallback+0xc6: fffff800`01cba856 4533c0 xor r8d,r8d fffff800`01cba859 488d542420 lea rdx,[rsp+20h] fffff800`01cba85e 488d0d6b69dcff lea rcx,[nt!CallbackListHead (fffff800`01a811d0)] fffff800`01cba865 e8a261e5ff call nt!CmListGetNextElement (fffff800`01b10a0c) fffff800`01cba86a 488bf8 mov rdi,rax fffff800`01cba86d 4889442428 mov qword ptr [rsp+28h],rax fffff800`01cba872 483bc3 cmp rax,rbx fffff800`01cba875 0f84b8000000 je nt!CmUnRegisterCallback+0x1a3 (fffff800`01cba933)
搜索的过程跟寻找进程、线程、映像的数组类似,根据 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;
}
执行效果类似于:
。 对付注册表回调有三种方法(老三套):
1. 直接使用 CmUnRegisterCallback 把回调注销;
2. 把链表中记录的回调地址修改为自定义的空函数的回调地址;
3. 直接在目标回调地址上写一个 RET , 使其不执行任何代码就返回。
第三种方法没有针对性,可以用于对付任何回调函数。 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);
}
}
以上所述就是小编给大家介绍的《x64驱动基础教程 38》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
从零开始学创业大全集
阳飞扬 / 中国华侨出版社 / 2011-10-1 / 29.80元
为了让每一个怀揣梦想走上创业之路的有志者能在最短的时间内叩开创业的大门,了解创业的流程和方法,从而找到适合自己的创业之路,我们精心编写了这本《从零开始学创业大全集》。阳飞扬编著的《从零开始学创业大全集(超值白金版)》从创业准备、创业团队的组建、创业项目和商业模式的选择、创业计划书的制作、创业资金的筹集、企业的经营策略、资本运作以及产品营销方法、危机应对策略等方面,全面系统地阐述了创业的基本理论与实......一起来看看 《从零开始学创业大全集》 这本书的介绍吧!