驱动开发:内核特征码搜索函数封装

在前面的系列教程如 《驱动开发:内核枚举DpcTimer定时器》或者 《驱动开发:内核枚举IoTimer定时器》里面 LyShark大量使用了 特征码定位这一方法来寻找符合条件的 汇编指令集,总体来说这种方式只能定位特征较小的指令如果特征值扩展到5位以上那么就需要写很多无用的代码,本章内容中将重点分析,并实现一个 通用特征定位函数。

如下是一段特征码搜索片段,可以看到其实仅仅只是将上章中的搜索方式变成了一个 SearchSpecialCode函数,如下函数,用户传入一个 扫描起始地址以及搜索特征码的字节数组,即可完成搜索工作,具体的参数定义如下。

  • pSearchBeginAddr 扫描的内存(内核)起始地址
  • ulSearchLength 需要扫描的长度
  • pSpecialCode 扫描特征码,传入一个UCHAR类型的字节数组
  • ulSpecialCodeLength 特征码长度,传入字节数组长度
// By: LyShark.com
PVOID SearchSpecialCode(PVOID pSearchBeginAddr, ULONG ulSearchLength, PUCHAR pSpecialCode, ULONG ulSpecialCodeLength)
{
  PVOID pDestAddr = NULL;
  PUCHAR pBeginAddr = (PUCHAR)pSearchBeginAddr;
  PUCHAR pEndAddr = pBeginAddr + ulSearchLength;
  PUCHAR i = NULL;
  ULONG j = 0;

  for (i = pBeginAddr; i = ulSpecialCodeLength)
    {
      pDestAddr = (PVOID)i;
      break;
    }
  }
  return pDestAddr;
}

那么这个简单的特征码扫描函数该如何使用,这里我们就用 《驱动开发:内核枚举IoTimer定时器》中枚举 IopTimerQueueHead链表头部地址为案例进行讲解,如果你忘记了如何寻找链表头部可以去前面的文章中学习,这里只给出实现流程。

驱动开发:内核特征码搜索函数封装

我们首先通过 MmGetSystemRoutineAddress得到 IoInitializeTimer首地址,然后在偏移长度为 0x7e范围内搜索特征码 48 8d 0d特征,其代码可以总结为如下样子。

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint(("hello lyshark.com \n"));

    // 得到基址
    PUCHAR IoInitializeTimer = GetIoInitializeTimerAddress();
    DbgPrint("IoInitializeTimer Address = %p \n", IoInitializeTimer);

    // ---------------------------------------------------
    // LyShark 开始定位特征

    // 设置起始位置
    PUCHAR StartSearchAddress = (PUCHAR)IoInitializeTimer;

    // 设置结束位置
    PUCHAR EndSearchAddress = StartSearchAddress + 0x7e;
    DbgPrint("[LyShark 搜索区间] 起始地址: 0x%X --> 结束地址: 0x%X \n", StartSearchAddress, EndSearchAddress);

    // 设置搜索长度
    LONGLONG size = EndSearchAddress - StartSearchAddress;
    DbgPrint("[LyShark 搜索长度] 长度: %d \n", size);

    PVOID ptr;

    // 指定特征码
    UCHAR pSpecialCode[256] = { 0 };

    // 指定特征码长度
    ULONG ulSpecialCodeLength = 3;

    pSpecialCode[0] = 0x48;
    pSpecialCode[1] = 0x8d;
    pSpecialCode[2] = 0x0d;

    // 开始搜索,找到后返回首地址
    ptr = SearchSpecialCode(StartSearchAddress, size, pSpecialCode, ulSpecialCodeLength);

    DbgPrint("搜索特征码首地址: 0x%p \n", ptr);

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

代码运行后你会发现可以直接定位到我们所需要的位置上,如下图所示:

驱动开发:内核特征码搜索函数封装

如上图可以看到,这个特征码定位函数返回的是内存地址,而我们需要得到地址内的数据,此时就需要提取以下。

例如当指令是:

fffff80206185c00 488d0dd9ddcdff  lea rcx,[nt!IopTimerQueueHead (fffff80205e639e0)]

那么就需要 RtlCopyMemory跳过前三个字节,并在第四个字节开始取数据,并将读入的数据放入到 IopTimerQueueHead_LyShark_Code变量内。

// 署名
// PowerBy: LyShark
// Email: me@lyshark.com

    // 开始搜索,找到后返回首地址
    ptr = SearchSpecialCode(StartSearchAddress, size, pSpecialCode, ulSpecialCodeLength);

    DbgPrint("搜索特征码首地址: 0x%p \n", ptr);

    // 提取特征
    // fffff80206185c00 488d0dd9ddcdff  lea     rcx,[nt!IopTimerQueueHead (fffff80205e639e0)]
    ULONG64 iOffset = 0;
    ULONG64 IopTimerQueueHead_LyShark_Code = 0;

    __try
    {
        // 拷贝内存跳过lea,向后四字节
        RtlCopyMemory(&iOffset, (ULONG64)ptr + 3, 4);

        // 取出后面的IopTimerQueueHead内存地址 LyShark.com
        IopTimerQueueHead_LyShark_Code = iOffset + (ULONG64)ptr + 7;

        DbgPrint("提取数据: 0x%p \n", IopTimerQueueHead_LyShark_Code);
    }
    __except (1)
    {
        DbgPrint("[LySHark] 拷贝内存异常 \n");
    }

这样即可得到我们所需要的地址,如下结果所示:

驱动开发:内核特征码搜索函数封装

Original: https://www.cnblogs.com/LyShark/p/16798318.html
Author: lyshark
Title: 驱动开发:内核特征码搜索函数封装

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/800720/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球