驱动开发:PE导出函数与RVA转换

打印 上一主题 下一主题

主题 866|帖子 866|积分 2598

在笔者上篇文章《驱动开发:内核扫描SSDT挂钩状态》中简单介绍了如何扫描被挂钩的SSDT函数,并简单介绍了如何解析导出表,本章将继续延申PE导出表的解析,实现一系列灵活的解析如通过传入函数名解析出函数的RVA偏移,ID索引,Index下标等参数,并将其封装为可直接使用的函数,以在后期需要时可以被直接引用,同样为了节约篇幅本章中的LoadKernelFile()内存映射函数如需要使用请去前一篇文章中自行摘取。
首先实现GetRvaFromModuleName()函数,当用户传入参数后自动将函数名解析为对应的RVA偏移或Index下标索引值,该函数接收三个参数传递,分别是wzFileName模块名,FunctionName所在模块内的函数名,Flag标志参数,函数输出ULONG64类型的数据。
  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. // 从指定模块中得到特定函数的RVA或相对序号相对偏移
  6. ULONG64 GetRvaFromModuleName(WCHAR *wzFileName, UCHAR *FunctionName, INT Flag)
  7. {
  8.         // 加载内核模块
  9.         PVOID BaseAddress = LoadKernelFile(wzFileName);
  10.         // 取出导出表
  11.         PIMAGE_DOS_HEADER pDosHeader;
  12.         PIMAGE_NT_HEADERS pNtHeaders;
  13.         PIMAGE_SECTION_HEADER pSectionHeader;
  14.         ULONGLONG FileOffset;
  15.         PIMAGE_EXPORT_DIRECTORY pExportDirectory;
  16.         // DLL内存数据转成DOS头结构
  17.         pDosHeader = (PIMAGE_DOS_HEADER)BaseAddress;
  18.         // 取出PE头结构
  19.         pNtHeaders = (PIMAGE_NT_HEADERS)((ULONGLONG)BaseAddress + pDosHeader->e_lfanew);
  20.         // 判断PE头导出表是否为空
  21.         if (pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0)
  22.         {
  23.                 return 0;
  24.         }
  25.         // 取出导出表偏移
  26.         FileOffset = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
  27.         // 取出节头结构
  28.         pSectionHeader = (PIMAGE_SECTION_HEADER)((ULONGLONG)pNtHeaders + sizeof(IMAGE_NT_HEADERS));
  29.         PIMAGE_SECTION_HEADER pOldSectionHeader = pSectionHeader;
  30.         // 遍历节结构进行地址运算
  31.         for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
  32.         {
  33.                 if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
  34.                 {
  35.                         FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
  36.                 }
  37.         }
  38.         // 导出表地址
  39.         pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONGLONG)BaseAddress + FileOffset);
  40.         // 取出导出表函数地址
  41.         PULONG AddressOfFunctions;
  42.         FileOffset = pExportDirectory->AddressOfFunctions;
  43.         // 遍历节结构进行地址运算
  44.         pSectionHeader = pOldSectionHeader;
  45.         for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
  46.         {
  47.                 if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
  48.                 {
  49.                         FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
  50.                 }
  51.         }
  52.         AddressOfFunctions = (PULONG)((ULONGLONG)BaseAddress + FileOffset);
  53.         // 取出导出表函数名字
  54.         PUSHORT AddressOfNameOrdinals;
  55.         FileOffset = pExportDirectory->AddressOfNameOrdinals;
  56.         // 遍历节结构进行地址运算
  57.         pSectionHeader = pOldSectionHeader;
  58.         for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
  59.         {
  60.                 if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
  61.                 {
  62.                         FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
  63.                 }
  64.         }
  65.         AddressOfNameOrdinals = (PUSHORT)((ULONGLONG)BaseAddress + FileOffset);
  66.         // 取出导出表函数序号
  67.         PULONG AddressOfNames;
  68.         FileOffset = pExportDirectory->AddressOfNames;
  69.         // 遍历节结构进行地址运算
  70.         pSectionHeader = pOldSectionHeader;
  71.         for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
  72.         {
  73.                 if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
  74.                 {
  75.                         FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
  76.                 }
  77.         }
  78.         AddressOfNames = (PULONG)((ULONGLONG)BaseAddress + FileOffset);
  79.         // 分析导出表
  80.         ULONG uOffset;
  81.         LPSTR FunName;
  82.         ULONG uAddressOfNames;
  83.         ULONG TargetOff = 0;
  84.         for (ULONG uIndex = 0; uIndex < pExportDirectory->NumberOfNames; uIndex++, AddressOfNames++, AddressOfNameOrdinals++)
  85.         {
  86.                 uAddressOfNames = *AddressOfNames;
  87.                 pSectionHeader = pOldSectionHeader;
  88.                 for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
  89.                 {
  90.                         if (pSectionHeader->VirtualAddress <= uAddressOfNames && uAddressOfNames <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
  91.                         {
  92.                                 uOffset = uAddressOfNames - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
  93.                         }
  94.                 }
  95.                 FunName = (LPSTR)((ULONGLONG)BaseAddress + uOffset);
  96.                 // 如果找到则返回RVA
  97.                 if (!_stricmp((const char *)FunctionName, FunName))
  98.                 {
  99.                         // 等于1则返回RVA
  100.                         if (Flag == 1)
  101.                         {
  102.                                 TargetOff = (ULONG)AddressOfFunctions[*AddressOfNameOrdinals];
  103.                                 // DbgPrint("索引 [ %p ] 函数名 [ %s ] 相对RVA [ %p ] \n", *AddressOfNameOrdinals, FunName, TargetOff);
  104.                                 return TargetOff;
  105.                         }
  106.                         // 返回索引
  107.                         else if (Flag == 0)
  108.                         {
  109.                                 return *AddressOfNameOrdinals;
  110.                         }
  111.                 }
  112.         }
  113.         // 结束后释放内存
  114.         ExFreePoolWithTag(BaseAddress, (ULONG)"LyShark");
  115.         return 0;
  116. }
复制代码
调用该函数很容易,传入模块路径以及该模块内的函数名,解析出RVA地址或Index下标。
  1. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  2. {
  3.         // 函数分别传入 [模块路径,函数名,标志=1] 返回该导出函数的RVA
  4.         ULONG64 get_rva = GetRvaFromModuleName(L"\\SystemRoot\\system32\\ntoskrnl.exe", "NtReadFile", 1);
  5.         DbgPrint("NtReadFile RVA = %p \n", get_rva);
  6.         // 函数分别传入 [模块路径,函数名,标志=0] 返回该导出函数的ID下标
  7.         ULONG64 get_id = GetRvaFromModuleName(L"\\SystemRoot\\system32\\ntoskrnl.exe", "NtReadFile", 0);
  8.         DbgPrint("NtReadFile ID = %d \n", get_id);
  9.         Driver->DriverUnload = UnDriver;
  10.         return STATUS_SUCCESS;
  11. }
复制代码
编译并运行程序,分别获取到ntoskrnl.exe模块内NtReadFile函数的RVA,Index索引,调用效果如下;

第二个函数GetModuleNameFromRVA()则实现传入RVA或者函数Index序号,解析出函数名,具体实现方法与如上函数基本一致,仅仅只是在过滤时做了调整。
  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. // 根据传入的函数RVA或Index下标,获取该函数的函数名
  6. PCHAR GetModuleNameFromRVA(WCHAR *wzFileName, ULONG64 uRVA, INT Flag)
  7. {
  8.         // 加载内核模块
  9.         PVOID BaseAddress = LoadKernelFile(wzFileName);
  10.         // 取出导出表
  11.         PIMAGE_DOS_HEADER pDosHeader;
  12.         PIMAGE_NT_HEADERS pNtHeaders;
  13.         PIMAGE_SECTION_HEADER pSectionHeader;
  14.         ULONGLONG FileOffset;
  15.         PIMAGE_EXPORT_DIRECTORY pExportDirectory;
  16.         // DLL内存数据转成DOS头结构
  17.         pDosHeader = (PIMAGE_DOS_HEADER)BaseAddress;
  18.         // 取出PE头结构
  19.         pNtHeaders = (PIMAGE_NT_HEADERS)((ULONGLONG)BaseAddress + pDosHeader->e_lfanew);
  20.         // 判断PE头导出表是否为空
  21.         if (pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0)
  22.         {
  23.                 return 0;
  24.         }
  25.         // 取出导出表偏移
  26.         FileOffset = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
  27.         // 取出节头结构
  28.         pSectionHeader = (PIMAGE_SECTION_HEADER)((ULONGLONG)pNtHeaders + sizeof(IMAGE_NT_HEADERS));
  29.         PIMAGE_SECTION_HEADER pOldSectionHeader = pSectionHeader;
  30.         // 遍历节结构进行地址运算
  31.         for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
  32.         {
  33.                 if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
  34.                 {
  35.                         FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
  36.                 }
  37.         }
  38.         // 导出表地址
  39.         pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONGLONG)BaseAddress + FileOffset);
  40.         // 取出导出表函数地址
  41.         PULONG AddressOfFunctions;
  42.         FileOffset = pExportDirectory->AddressOfFunctions;
  43.         // 遍历节结构进行地址运算
  44.         pSectionHeader = pOldSectionHeader;
  45.         for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
  46.         {
  47.                 if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
  48.                 {
  49.                         FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
  50.                 }
  51.         }
  52.         AddressOfFunctions = (PULONG)((ULONGLONG)BaseAddress + FileOffset);
  53.         // 取出导出表函数名字
  54.         PUSHORT AddressOfNameOrdinals;
  55.         FileOffset = pExportDirectory->AddressOfNameOrdinals;
  56.         // 遍历节结构进行地址运算
  57.         pSectionHeader = pOldSectionHeader;
  58.         for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
  59.         {
  60.                 if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
  61.                 {
  62.                         FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
  63.                 }
  64.         }
  65.         AddressOfNameOrdinals = (PUSHORT)((ULONGLONG)BaseAddress + FileOffset);
  66.         // 取出导出表函数序号
  67.         PULONG AddressOfNames;
  68.         FileOffset = pExportDirectory->AddressOfNames;
  69.         // 遍历节结构进行地址运算
  70.         pSectionHeader = pOldSectionHeader;
  71.         for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
  72.         {
  73.                 if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
  74.                 {
  75.                         FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
  76.                 }
  77.         }
  78.         AddressOfNames = (PULONG)((ULONGLONG)BaseAddress + FileOffset);
  79.         // 分析导出表
  80.         ULONG uOffset;
  81.         LPSTR FunName;
  82.         ULONG uAddressOfNames;
  83.         ULONG TargetOff = 0;
  84.         for (ULONG uIndex = 0; uIndex < pExportDirectory->NumberOfNames; uIndex++, AddressOfNames++, AddressOfNameOrdinals++)
  85.         {
  86.                 uAddressOfNames = *AddressOfNames;
  87.                 pSectionHeader = pOldSectionHeader;
  88.                 for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
  89.                 {
  90.                         if (pSectionHeader->VirtualAddress <= uAddressOfNames && uAddressOfNames <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
  91.                         {
  92.                                 uOffset = uAddressOfNames - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
  93.                         }
  94.                 }
  95.                 FunName = (LPSTR)((ULONGLONG)BaseAddress + uOffset);
  96.                 TargetOff = (ULONG)AddressOfFunctions[*AddressOfNameOrdinals];
  97.                 // 等于1则通过RVA返回函数名
  98.                 if (Flag == 1)
  99.                 {
  100.                         if (uRVA == TargetOff)
  101.                         {
  102.                                 return FunName;
  103.                         }
  104.                 }
  105.                 // 返回索引
  106.                 else if (Flag == 0)
  107.                 {
  108.                         if (uRVA == *AddressOfNameOrdinals)
  109.                         {
  110.                                 return FunName;
  111.                         }
  112.                 }
  113.         }
  114.         // 结束后释放内存
  115.         ExFreePoolWithTag(BaseAddress, (ULONG)"LyShark");
  116.         return "None";
  117. }
复制代码
调用GetModuleNameFromRVA()并传入相应的RVA偏移或Index下标。
  1. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  2. {
  3.         DbgPrint("hello lyshark.com \n");
  4.         PCHAR function_name;
  5.         // 传入函数RVA得到函数名
  6.         function_name = GetModuleNameFromRVA(L"\\SystemRoot\\system32\\ntoskrnl.exe", 0x5e5220, 1);
  7.         DbgPrint("根据RVA得到函数名 = %s \n", function_name);
  8.         // 传入函数下标得到函数名
  9.         function_name = GetModuleNameFromRVA(L"\\SystemRoot\\system32\\ntoskrnl.exe", 1472, 0);
  10.         DbgPrint("根据Index得到函数名 = %s \n", function_name);
  11.         Driver->DriverUnload = UnDriver;
  12.         return STATUS_SUCCESS;
  13. }
复制代码
编译并运行程序,调用后分别获取到RVA=0x5e5220或Index=1472的函数名;


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

本帖子中包含更多资源

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

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

梦见你的名字

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表