驱动开发:内核枚举Registry注册表回调

[复制链接]
发表于 2022-10-21 12:51:50 | 显示全部楼层 |阅读模式
在笔者上一篇文章《驱动开发:内核枚举LoadImage映像回调》中LyShark教大家实现了枚举系统回调中的LoadImage通知消息,本章将实现对Registry注册表通知消息的枚举,与LoadImage消息不同Registry消息不需要解密只要找到CallbackListHead消息回调链表头并解析为_CM_NOTIFY_ENTRY结构即可实现枚举。
我们来看一款闭源ARK工具是如何实现的:

注册表系统回调的枚举需要通过特征码搜索来实现,首先我们可以定位到uf CmUnRegisterCallback内核函数上,在该内核函数下方存在一个CallbackListHead链表节点,取出这个链表地址。

当得到注册表链表入口0xfffff8063a065bc0直接将其解析为_CM_NOTIFY_ENTRY即可得到数据,如果要遍历下一个链表则只需要ListEntryHead.Flink向下移动指针即可。
  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. // 注册表回调函数结构体定义
  6. typedef struct _CM_NOTIFY_ENTRY
  7. {
  8.   LIST_ENTRY  ListEntryHead;
  9.   ULONG   UnKnown1;
  10.   ULONG   UnKnown2;
  11.   LARGE_INTEGER Cookie;
  12.   PVOID   Context;
  13.   PVOID   Function;
  14. }CM_NOTIFY_ENTRY, *PCM_NOTIFY_ENTRY;
复制代码
要想得到此处的链表地址,需要先通过MmGetSystemRoutineAddress()获取到CmUnRegisterCallback函数基址,然后在该函数起始位置向下搜索,找到这个链表节点,并将其后面的基地址取出来,在上一篇《驱动开发:内核枚举LoadImage映像回调》文章中已经介绍了定位方式此处跳过介绍,具体实现代码如下。
  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. #include <ntifs.h>
  6. #include <windef.h>
  7. // 指定内存区域的特征码扫描
  8. // PowerBy: LyShark.com
  9. PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize)
  10. {
  11.         PVOID pAddress = NULL;
  12.         PUCHAR i = NULL;
  13.         ULONG m = 0;
  14.         // 扫描内存
  15.         for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++)
  16.         {
  17.                 // 判断特征码
  18.                 for (m = 0; m < ulMemoryDataSize; m++)
  19.                 {
  20.                         if (*(PUCHAR)(i + m) != pMemoryData[m])
  21.                         {
  22.                                 break;
  23.                         }
  24.                 }
  25.                 // 判断是否找到符合特征码的地址
  26.                 if (m >= ulMemoryDataSize)
  27.                 {
  28.                         // 找到特征码位置, 获取紧接着特征码的下一地址
  29.                         pAddress = (PVOID)(i + ulMemoryDataSize);
  30.                         break;
  31.                 }
  32.         }
  33.         return pAddress;
  34. }
  35. // 根据特征码获取 CallbackListHead 链表地址
  36. // PowerBy: LyShark.com
  37. PVOID SearchCallbackListHead(PUCHAR pSpecialData, ULONG ulSpecialDataSize, LONG lSpecialOffset)
  38. {
  39.         UNICODE_STRING ustrFuncName;
  40.         PVOID pAddress = NULL;
  41.         LONG lOffset = 0;
  42.         PVOID pCmUnRegisterCallback = NULL;
  43.         PVOID pCallbackListHead = NULL;
  44.         // 先获取 CmUnRegisterCallback 函数地址
  45.         RtlInitUnicodeString(&ustrFuncName, L"CmUnRegisterCallback");
  46.         pCmUnRegisterCallback = MmGetSystemRoutineAddress(&ustrFuncName);
  47.         if (NULL == pCmUnRegisterCallback)
  48.         {
  49.                 return pCallbackListHead;
  50.         }
  51.         // 查找 fffff806`3a4271b3 488d0d06eac3ff  lea     rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
  52.         /*
  53.         lyshark.com>
  54.                 nt!CmUnRegisterCallback+0x6b:
  55.                 fffff806`3a4271ab 4533c0          xor     r8d,r8d
  56.                 fffff806`3a4271ae 488d542438      lea     rdx,[rsp+38h]
  57.                 fffff806`3a4271b3 488d0d06eac3ff  lea     rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
  58.                 fffff806`3a4271ba e855e2e2ff      call    nt!CmListGetNextElement (fffff806`3a255414)
  59.                 fffff806`3a4271bf 488bf8          mov     rdi,rax
  60.                 fffff806`3a4271c2 4889442440      mov     qword ptr [rsp+40h],rax
  61.                 fffff806`3a4271c7 4885c0          test    rax,rax
  62.                 fffff806`3a4271ca 0f84c7000000    je      nt!CmUnRegisterCallback+0x157 (fffff806`3a427297)  Branch
  63.         */
  64.         pAddress = SearchMemory(pCmUnRegisterCallback, (PVOID)((PUCHAR)pCmUnRegisterCallback + 0xFF), pSpecialData, ulSpecialDataSize);
  65.         if (NULL == pAddress)
  66.         {
  67.                 return pCallbackListHead;
  68.         }
  69.         // 先获取偏移再计算地址
  70.         lOffset = *(PLONG)((PUCHAR)pAddress + lSpecialOffset);
  71.         pCallbackListHead = (PVOID)((PUCHAR)pAddress + lSpecialOffset + sizeof(LONG) + lOffset);
  72.         return pCallbackListHead;
  73. }
  74. VOID UnDriver(PDRIVER_OBJECT Driver)
  75. {
  76. }
  77. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  78. {
  79.         PVOID pCallbackListHeadAddress = NULL;
  80.         RTL_OSVERSIONINFOW osInfo = { 0 };
  81.         UCHAR pSpecialData[50] = { 0 };
  82.         ULONG ulSpecialDataSize = 0;
  83.         LONG lSpecialOffset = 0;
  84.         DbgPrint("hello lyshark.com \n");
  85.         // 查找 fffff806`3a4271b3 488d0d06eac3ff  lea     rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
  86.         /*
  87.         lyshark.com>
  88.         nt!CmUnRegisterCallback+0x6b:
  89.         fffff806`3a4271ab 4533c0          xor     r8d,r8d
  90.         fffff806`3a4271ae 488d542438      lea     rdx,[rsp+38h]
  91.         fffff806`3a4271b3 488d0d06eac3ff  lea     rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
  92.         fffff806`3a4271ba e855e2e2ff      call    nt!CmListGetNextElement (fffff806`3a255414)
  93.         fffff806`3a4271bf 488bf8          mov     rdi,rax
  94.         fffff806`3a4271c2 4889442440      mov     qword ptr [rsp+40h],rax
  95.         fffff806`3a4271c7 4885c0          test    rax,rax
  96.         fffff806`3a4271ca 0f84c7000000    je      nt!CmUnRegisterCallback+0x157 (fffff806`3a427297)  Branch
  97.         */
  98.         pSpecialData[0] = 0x48;
  99.         pSpecialData[1] = 0x8D;
  100.         pSpecialData[2] = 0x0D;
  101.         ulSpecialDataSize = 3;
  102.         // 根据特征码获取地址
  103.         pCallbackListHeadAddress = SearchCallbackListHead(pSpecialData, ulSpecialDataSize, lSpecialOffset);
  104.         DbgPrint("[LyShark.com] CallbackListHead => %p \n", pCallbackListHeadAddress);
  105.         Driver->DriverUnload = UnDriver;
  106.         return STATUS_SUCCESS;
  107. }
复制代码
运行这段代码,并可得到注册表回调入口地址,输出效果如下所示:

得到了注册表回调入口地址,接着直接循环遍历输出这个链表即可得到所有的注册表回调。
  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. #include <ntifs.h>
  6. #include <windef.h>
  7. // 指定内存区域的特征码扫描
  8. // PowerBy: LyShark.com
  9. PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize)
  10. {
  11.         PVOID pAddress = NULL;
  12.         PUCHAR i = NULL;
  13.         ULONG m = 0;
  14.         // 扫描内存
  15.         for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++)
  16.         {
  17.                 // 判断特征码
  18.                 for (m = 0; m < ulMemoryDataSize; m++)
  19.                 {
  20.                         if (*(PUCHAR)(i + m) != pMemoryData[m])
  21.                         {
  22.                                 break;
  23.                         }
  24.                 }
  25.                 // 判断是否找到符合特征码的地址
  26.                 if (m >= ulMemoryDataSize)
  27.                 {
  28.                         // 找到特征码位置, 获取紧接着特征码的下一地址
  29.                         pAddress = (PVOID)(i + ulMemoryDataSize);
  30.                         break;
  31.                 }
  32.         }
  33.         return pAddress;
  34. }
  35. // 根据特征码获取 CallbackListHead 链表地址
  36. // PowerBy: LyShark.com
  37. PVOID SearchCallbackListHead(PUCHAR pSpecialData, ULONG ulSpecialDataSize, LONG lSpecialOffset)
  38. {
  39.         UNICODE_STRING ustrFuncName;
  40.         PVOID pAddress = NULL;
  41.         LONG lOffset = 0;
  42.         PVOID pCmUnRegisterCallback = NULL;
  43.         PVOID pCallbackListHead = NULL;
  44.         // 先获取 CmUnRegisterCallback 函数地址
  45.         RtlInitUnicodeString(&ustrFuncName, L"CmUnRegisterCallback");
  46.         pCmUnRegisterCallback = MmGetSystemRoutineAddress(&ustrFuncName);
  47.         if (NULL == pCmUnRegisterCallback)
  48.         {
  49.                 return pCallbackListHead;
  50.         }
  51.         // 查找 fffff806`3a4271b3 488d0d06eac3ff  lea     rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
  52.         /*
  53.         lyshark.com>
  54.                 nt!CmUnRegisterCallback+0x6b:
  55.                 fffff806`3a4271ab 4533c0          xor     r8d,r8d
  56.                 fffff806`3a4271ae 488d542438      lea     rdx,[rsp+38h]
  57.                 fffff806`3a4271b3 488d0d06eac3ff  lea     rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
  58.                 fffff806`3a4271ba e855e2e2ff      call    nt!CmListGetNextElement (fffff806`3a255414)
  59.                 fffff806`3a4271bf 488bf8          mov     rdi,rax
  60.                 fffff806`3a4271c2 4889442440      mov     qword ptr [rsp+40h],rax
  61.                 fffff806`3a4271c7 4885c0          test    rax,rax
  62.                 fffff806`3a4271ca 0f84c7000000    je      nt!CmUnRegisterCallback+0x157 (fffff806`3a427297)  Branch
  63.         */
  64.         pAddress = SearchMemory(pCmUnRegisterCallback, (PVOID)((PUCHAR)pCmUnRegisterCallback + 0xFF), pSpecialData, ulSpecialDataSize);
  65.         if (NULL == pAddress)
  66.         {
  67.                 return pCallbackListHead;
  68.         }
  69.         // 先获取偏移再计算地址
  70.         lOffset = *(PLONG)((PUCHAR)pAddress + lSpecialOffset);
  71.         pCallbackListHead = (PVOID)((PUCHAR)pAddress + lSpecialOffset + sizeof(LONG) + lOffset);
  72.         return pCallbackListHead;
  73. }
  74. // 注册表回调函数结构体定义
  75. typedef struct _CM_NOTIFY_ENTRY
  76. {
  77.         LIST_ENTRY  ListEntryHead;
  78.         ULONG   UnKnown1;
  79.         ULONG   UnKnown2;
  80.         LARGE_INTEGER Cookie;
  81.         PVOID   Context;
  82.         PVOID   Function;
  83. }CM_NOTIFY_ENTRY, *PCM_NOTIFY_ENTRY;
  84. VOID UnDriver(PDRIVER_OBJECT Driver)
  85. {
  86. }
  87. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  88. {
  89.         PVOID pCallbackListHeadAddress = NULL;
  90.         RTL_OSVERSIONINFOW osInfo = { 0 };
  91.         UCHAR pSpecialData[50] = { 0 };
  92.         ULONG ulSpecialDataSize = 0;
  93.         LONG lSpecialOffset = 0;
  94.         DbgPrint("hello lyshark.com \n");
  95.         // 查找 fffff806`3a4271b3 488d0d06eac3ff  lea     rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
  96.         /*
  97.         lyshark.com>
  98.         nt!CmUnRegisterCallback+0x6b:
  99.         fffff806`3a4271ab 4533c0          xor     r8d,r8d
  100.         fffff806`3a4271ae 488d542438      lea     rdx,[rsp+38h]
  101.         fffff806`3a4271b3 488d0d06eac3ff  lea     rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
  102.         fffff806`3a4271ba e855e2e2ff      call    nt!CmListGetNextElement (fffff806`3a255414)
  103.         fffff806`3a4271bf 488bf8          mov     rdi,rax
  104.         fffff806`3a4271c2 4889442440      mov     qword ptr [rsp+40h],rax
  105.         fffff806`3a4271c7 4885c0          test    rax,rax
  106.         fffff806`3a4271ca 0f84c7000000    je      nt!CmUnRegisterCallback+0x157 (fffff806`3a427297)  Branch
  107.         */
  108.         pSpecialData[0] = 0x48;
  109.         pSpecialData[1] = 0x8D;
  110.         pSpecialData[2] = 0x0D;
  111.         ulSpecialDataSize = 3;
  112.         // 根据特征码获取地址
  113.         pCallbackListHeadAddress = SearchCallbackListHead(pSpecialData, ulSpecialDataSize, lSpecialOffset);
  114.         DbgPrint("[LyShark.com] CallbackListHead => %p \n", pCallbackListHeadAddress);
  115.         // 遍历链表结构
  116.         ULONG i = 0;
  117.         PCM_NOTIFY_ENTRY pNotifyEntry = NULL;
  118.         if (NULL == pCallbackListHeadAddress)
  119.         {
  120.                 return FALSE;
  121.         }
  122.         // 开始遍历双向链表
  123.         pNotifyEntry = (PCM_NOTIFY_ENTRY)pCallbackListHeadAddress;
  124.         do
  125.         {
  126.                 // 判断pNotifyEntry地址是否有效
  127.                 if (FALSE == MmIsAddressValid(pNotifyEntry))
  128.                 {
  129.                         break;
  130.                 }
  131.                 // 判断回调函数地址是否有效
  132.                 if (MmIsAddressValid(pNotifyEntry->Function))
  133.                 {
  134.                         DbgPrint("[LyShark.com] 回调函数地址: 0x%p | 回调函数Cookie: 0x%I64X \n", pNotifyEntry->Function, pNotifyEntry->Cookie.QuadPart);
  135.                 }
  136.                 // 获取下一链表
  137.                 pNotifyEntry = (PCM_NOTIFY_ENTRY)pNotifyEntry->ListEntryHead.Flink;
  138.         } while (pCallbackListHeadAddress != (PVOID)pNotifyEntry);
  139.         Driver->DriverUnload = UnDriver;
  140.         return STATUS_SUCCESS;
  141. }
复制代码
最终运行这个驱动程序,输出如下效果:

目前系统中有两个回调函数,这一点在第一张图片中也可以得到,枚举是正确的。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
继续阅读请点击广告

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

×
登录参与点评抽奖,加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表