逆向 API AuxKlibQueryModuleInformation 枚举内核驱动列表的实现

http://dy.163.com/v2/article/detail/F70H7RS20511CJ6O.htmlhtml


  

  本文,我将介绍对AuxKlibQueryModuleInformation进行逆向工程的解决方案。其中,咱们会提到驱动程序可使用记录的API AuxKlibQueryModuleInformation枚举全部加载的模块。这个API是否保证返回的模块列表老是最新的?为了回答这个问题,咱们要对Windows 8的AuxKlibQueryModuleInformation进行逆向工程,并解释它是如何工做的。当多个线程请求访问加载的模块列表时,它如何处理这种状况?注意:处理此请求和其余请求的内部函数至关大,所以须要一些耐心。或者,你可使用调试器来帮助你跟踪感兴趣的代码。
python

  首先,让咱们将其划分为咱们须要执行的任务:git

  1.这个API (AuxKlibQueryModuleInformation)是否保证返回的模块列表老是最新的?github

  2.对Windows 8的AuxKlibQueryModuleInformation进行逆向工程,并解释其工做原理。json

  3.当多个线程请求访问已加载的模块列表时,它如何处理这种状况?windows

  为了解决这个问题,咱们将使用IDA对函数进行静态反向工程。在本文的示例中,咱们将不使用反编译器来读取汇编代码。使用反编译器能够节省大量时间,可是学习如何在反汇编窗口中导航对于逆向工程颇有价值。安全

  什么是“AuxKlibQueryModuleInformation”?框架

  MSDN: The AuxKlibQueryModuleInformation例程检索有关操做系统已加载的映像模块的信息。函数

  NTSTATUS AuxKlibQueryModuleInformation(工具

  PULONG BufferSize,

  ULONG ElementSize,

  PVOID QueryInfo

  );

  听起来它应该返回一个映像列表,不过,这些只是猜想,这些映像是加载到内存中的映像仍是仅加载到内核中的映像,咱们稍后将对此进行检查。

  所以,要回答第一个问题,咱们的第一个子任务是找到' AuxKlibQueryModuleInformation '是在哪里实现的。

  在搜索ntoskrnl.exe时,咱们没有找到该函数。这意味着它必须在其余地方声明,经过查看该文档,咱们就能够看到这个函数是在aux_klib.h中声明的,所需的库是aux_klib.lib。

  LIB文件是静态库,该静态库就是一个包含目标文件的归档文件,连接器可使用这些目标文件将代码添加到二进制文件中。如今,咱们知道这个函数是在aux_klib.lib中定义的。

  咱们能够用 “dumpbin.exe” 工具来反汇编lib 文件,咱们还能够编译一个使用AuxKlibQueryModuleInformation的驱动程序,而后查看编译后的二进制文件,看看结果如何。使用dumpbin.exe快速检查后,能够看到函数调用以下:=

  AuxKlibQueryModuleInformation:

  .....

  .....

  lea r9,[rsp+20h] ; ReturnLength

  mov r8d,esi ; SystemInformationLength

  mov rdx,rbx ; SystemInformation

  mov ecx,0Bh ; SystemInformationClass

  call qword ptr [__imp_ZwQuerySystemInformation] ; <-------

  .....

  .....

  能够看到,AuxKlibQueryModuleInformation使用ZwQuerySystemInformation来查询模块列表,该函数的定义为:

  NTSTATUS WINAPI ZwQuerySystemInformation(

  _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,

  _Inout_ PVOID SystemInformation,

  _In_ ULONG SystemInformationLength,

  _Out_opt_ PULONG ReturnLength

  );

  SystemInformationClass是咱们想要查询的信息类型,正如你在反汇编中看到的,这个类等于0xb,可是0xb是什么呢?

  一般,我会搜索SYSTEM_INFORMATION_CLASS枚举定义,但咱们将查看ZwQuerySystemInformation的实际实现以找出SystemInformationClass,以了解如何能够作到。

  此函数是在在ntoskrnl.exe中实现的,以下所示:

  

  这个函数是Zw函数,你能够点击这里读取更多关于Zw函数的信息。

  Zw函数调用Nt函数,并将PreviousMode更改成KernelMode。所以,jmp KiServiceInternal在后台调用NtQuerySystemInformation。ntdll中存在相同的名称,但ntoskrnl.exe包含系统调用的实际实现。若是你查看ntdll,会看到相同的ID是在执行“syscall”。

  

  这是由于内核Zw函数和用户模式Nt函数都通过SSDT来提取系统调用处理程序指针,在本文的示例中,将在内核模式中执行相同的函数(NtQuerySystemInformation)。

  如今,让咱们重复一下当前的任务:咱们须要找到SystemInformationClass 0xb指向的位置。检查NtQuerySystemInformation后,咱们看到了经过ExpQuerySystemInformation或返回错误状态的全部方法。SystemInformationClass在rcx中传递,请注意,我没有开始从上到下阅读反汇编,我只想知道当SystemInformationClass = 0xb时会发生什么。

  

  跟踪rcx,咱们看到在调用ExpQuerySystemInformation以前它没有改变。这意味着ExpQuerySystemInformation的第一个参数是SystemInformationClass参数。

  查看ExpQuerySystemInformation能够开始跟踪rcx中的值,在IDA中,咱们能够突出显示rcx寄存器并查看其使用位置。

  

  你能够看到rcx值被移动到rdi,而后它被覆盖,如今咱们须要查看rdi,这是rdi寄存器的下一个用法:

  

  咱们知道0xb小于0x49,因此让咱们观察一下loc_14069F19C:

  

  这是switch语句的示例。该表包含switch语句中不一样状况的处理程序。IDA已经为你找到了案例!要轻松找到案例,你可使用“Search Text” (Alt-T)并搜索如下文本:“case 11”。因此,在搜索这篇文章后,咱们发现了如下内容:

  

  好的!因此咱们找到了查询模块列表的实际代码。从它的名称中,咱们能够理解PsLoadedModuleList列表包含一个已加载模块的列表。

  ExpQueryModuleInformation函数以下所示:

  

  咱们在这个函数中看到一个大循环,看看函数的开头,咱们看到:

  

  如今,咱们假设这个函数枚举这个列表并返回这个列表中的映像,咱们将在须要的时候验证这个假设。最后,咱们完成了第一个任务,查找什么是SystemInformationClass 0xb?

  第一个须要回答的问题是,这个API (AuxKlibQueryModuleInformation)是否保证返回的模块列表老是最新的?

  要回答这个问题,咱们须要了解返回的值是否老是最新的。“最新的”的通常定义能够有不一样的含义。可是通常的答案是:不能保证在调用AuxKlibQueryModuleInformation以后,列表是最新的。看起来PsLoadedModuleList受ERESOURCE同步对象(读写锁)PsLoadedModuleResource保护。在调用ExpQueryModuleInformation以前,咱们会获取锁。但在那以后,咱们调用ExReleaseResourceLite,列表能够再次更新,咱们能够假定仅在得到锁时才更改PsLoadedModuleList。

  若是AuxKlibQueryModuleInformation被设计为获取PsLoadedModuleList的更新版本,那么它将容许调用者本身获取或释放锁。事实上,PsLoadedModuleResource是在windows 10的ntoskrnl中导出的:

  

  由于能够从同一线程两次获取ERESOURCE锁,因此这意味着调用方实际上能够:

  1.获取锁;

  2.调用AuxKlibQueryModuleInformation;

  3.用列表作一些有趣的事情(列表也已导出;);

  4.运行完成后,释放锁;

  这将确保在保持锁定状态时没法更新列表,固然,前提是假设PsLoadedModuleList在没有锁定的状况下不会更新。不过这种方法还不够好,由于实际使用会出现如下问题:

  1.即便咱们检验了咱们的假设并假设它是正确的,微软也能够随时更改此行为,例如,微软2.有时会更改锁的类型(将ERESOURCE更改成其余类型的锁);

  3.在Windows 7/8中不可用,列表和锁没有导出(这多是个问题,具体取决于你的运行状况);

  出于好奇,让咱们使用windows import searcher工具来查找究竟是哪些模块导入这些变量:

  >python.exe windows_imports_searcher.py search -i index.json -f ntoskrnl.exe!PsLoadedModule*

  Reading file index.json

  c:\windows\system32\drivers\ntosext.sys Imports ntoskrnl.exe!PsLoadedModuleResource

  c:\windows\system32\drivers\ntosext.sys Imports ntoskrnl.exe!PsLoadedModuleList

  咱们看到导入这个列表的惟一驱动是ntosext.sys,咱们可能会错过使用MmGetSystemRoutineAddress导入已加载模块列表的其余模块。此外,有些组件(例如调试器)能够很好地利用此列表。这个列表在windows 10中导出多是由于ntosext.sys被移出ntoskrnl.exe。

  如今回答第2个问题:对AuxKlibQueryModuleInformation进行逆向工程,并解释其工做原理。

  咱们必须当心处理这个任务。“解释它是如何工做的”很容易被误解为“理解关于AuxKlibQueryModuleInformation实现的每一个小细节”。有时候,在逆向工程中,咱们想要获得某些东西的概况,这个“东西”能够是一个完整的程序,也能够是一个程序的特定特性。咱们必须当心,不能花太多时间来颠倒AuxKlibQueryModuleInformation,由于这里所须要作的就是了解全局。

  好了,咱们已经从上一个任务的分析中获得了一些映像:

  1.AuxKlibQueryModuleInformation在aux_klib.lib静态库中定义;

  2.它调用ZwQuerySystemInformation来触发ExpQuerySystemInformation;

  3.ExpQuerySystemInformation获取PsLoadedModuleResource并调用ExpQueryModuleInformation

  4.咱们能够估计ExpQueryModuleInformation会获取列表的快照,并将其保存到输出缓冲区。

  5.锁被释放,缓冲区被返回给用户;

  好的,实际上咱们能够在此时中止分析了,由于咱们已经有一个大的框架。可是咱们尚未检查ExpQueryModuleInformation,因此让咱们验证一下它的做用。

  如上所述,这个函数有一个大的循环。咱们能够估计此循环枚举PsLoadedModuleList中的条目,让咱们验证一下。

  咱们能够在循环以前看到如下代码,看起来r14是循环变量,而且已与列表的开头进行了比较,若是它指向列表的开头,则循环结束。验证r14为循环变量的过程以下:

  

  是的,看来估算是正确的。为了大体了解此循环的做用,让咱们看一下循环对象内r14的用法:

  

  看起来来自ListItem结构(表明已加载模块)的值已复制到rsi指向的某些输出结构,经过查看rsi的来源,来验证rsi是否包含调用者的输出缓冲区:

  

  好的,答案是rsi = (SecondParam + 8),接下来,让咱们来看看第二个参数(rdx)是怎么来的:

  (在ExpQuerySystemInformation内部)

  

  咱们来跟踪rbx,可使用Alt-Up来查看rbx的第一次分配发生在什么位置:

  

  rbx包含了ExpQuerySystemInformation的第四个参数,让咱们追踪一下调用者,看看它来自哪里:

  (在NtQuerySystemInformation内部)

  

  它是NtQuerySystemInformation的第二个参数,让咱们看看这个函数的原型:

  

  好极了!咱们是正确的,由于第二个参数是SystemInformation,是调用者的输出参数。

  __kernel_entry NTSTATUS NtQuerySystemInformation(

  IN SYSTEM_INFORMATION_CLASS SystemInformationClass,

  OUT PVOID SystemInformation,

  IN ULONG SystemInformationLength,

  OUT PULONG ReturnLength

  );

  好,让咱们来看最后一个问题

  当多个线程请求访问加载的模块列表时,它如何处理这种状况?

  咱们已经知道答案了!这是使用读写锁来处理的,这种类型的锁容许读取器一块儿读取列表(这是安全的,由于它们不会更改列表),可是若是编写器但愿编辑列表,则只有编写器能够访问列表。奇怪的是,查询函数使用exacquireresourceexclusive velite函数锁定列表,不容许其余人读取列表。这很奇怪,由于这个函数应该读取而不是写入列表。通过验证,我没有在ExpQueryModuleInformation内找到对列表的任何写操做,所以它看起来像是错误的编码,又或许是我没有理解到位。

  最后,咱们想知道函数是否返回用户模式dll。咱们能够尝试找出静态地插入到PsLoadedModuleList中的内容,可是让咱们使用动态分析来解决这个问题。咱们如何解决这个问题?咱们能够编写调用AuxKlibQueryModuleInformation并查看返回值的代码,可是有一种更简单的方法,就是用循环对象自己。

  

  咱们能够假设这是在将映像放入目标缓冲区以前对映像名称的转换,让咱们在调用RtlUnicodeStringToAnsiString时设置一个断点,而后查看源字符串。为了使这个运行有效,咱们必须以某种方式触发ZwQuerySystemInformation,打开process explorer并经过单击View->System Information就能够触发它。

  kd> bp fffff80207203c57 "dS /c 100 rdx; g"

  kd> g

  ffffa78e`7e605f40 "\SystemRoot\system32\ntoskrnl.exe"

  ffffa78e`7e606e90 "\SystemRoot\system32\hal.dll"

  ffffa78e`7e606ef0 "\SystemRoot\system32\kdcom.dll"

  ffffa78e`7e605f40 "\SystemRoot\system32\ntoskrnl.exe"

  ffffa78e`7e606e90 "\SystemRoot\system32\hal.dll"

  ffffa78e`7e606ef0 "\SystemRoot\system32\kdcom.dll"

  ffffa78e`7e606f50 "\SystemRoot\system32\mcupdate_GenuineIntel.dll"

  ffffa78e`7e607d60 "\SystemRoot\System32\drivers\msrpc.sys"

  ffffa78e`7e607dd0 "\SystemRoot\System32\drivers\ksecdd.sys"

  ffffa78e`7e607e40 "\SystemRoot\System32\drivers\werkernel.sys"

  ffffa78e`7e607ec0 "\SystemRoot\System32\drivers\CLFS.SYS"

  ffffa78e`7e607f30 "\SystemRoot\System32\drivers\tm.sys"

  ffffa78e`7e608010 "\SystemRoot\system32\PSHED.dll"

  ffffa78e`7e608070 "\SystemRoot\system32\BOOTVID.dll"

  ffffa78e`7e6080e0 "\SystemRoot\System32\drivers\FLTMGR.SYS"

  ffffa78e`7e608150 "\SystemRoot\System32\drivers\clipsp.sys"

  ffffa78e`7e6081c0 "\SystemRoot\System32\drivers\cmimcext.sys"

  ffffa78e`7e608240 "\SystemRoot\System32\drivers\ntosext.sys"

  ..................

  ...(truncated)....

  ..................

  从输出中能够看到,只有内核映像保存在输出列表中。

  正如你所看到的,逆向工程的大部分工做是跟踪咱们程序中的数据流。这是反编译器能够作得更好的优点所在,由于它们以更高的表示形式显示信息。全部变量在寄存器之间的“临时”移动都不在反编译视图中体现。

  本文参考自:https://repnz.github.io/posts/practical-reverse-engineering/query-module-information/

相关文章
相关标签/搜索