x64驱动基础教程 38

栏目: 数据库 · 发布时间: 6年前

注册表回调是一个比较监控注册表读写的回调, 它的能量非常大, 一个回调能实现在 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;
}

执行效果类似于: x64驱动基础教程 38

不过需要注意的是,干净的 WIN7X64 系统是没有注册表回调的。为了体现枚举效果,可以在测试驱动前运行 WIN64AST

。 对付注册表回调有三种方法(老三套):

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);
    }
}

本文链接地址: https://www.dbgpro.com/archives/4948.html

——版权声明——


以上所述就是小编给大家介绍的《x64驱动基础教程 38》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Refactoring

Refactoring

Martin Fowler、Kent Beck、John Brant、William Opdyke、Don Roberts / Addison-Wesley Professional / 1999-7-8 / USD 64.99

Refactoring is about improving the design of existing code. It is the process of changing a software system in such a way that it does not alter the external behavior of the code, yet improves its int......一起来看看 《Refactoring》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具