0
Posted on 上午12:23:00 by Fan Zhang and filed under ,

  一般我们实现系统功能都是通过调用微软提供的公用 Win32 API 函数来实现。但今天我们采用的方法是通过本机系统服务(Native API)来探测本机系统信息。

  Native API 是 Windows 用户模式中为上层 Win32 API 提供接口的本机系统服务。微软没有为我们提供关于本机系统服务的文档 (Undocumented),也就是不会为对它的使用提供任何的保证,所以不提倡使用 Native API 来开发软件。

  不过由于通过 Native API 可以实现很多功能,所以相关的参考也可以找到很多。 比如 Gary Nebbett, Windows NT/2000 Native API Reference. New Riders Publishing, 2000. 是一本详细的参考书(@Amazon ),内容可能不是最新的,但非常有价值。 而 The Undocumented Functions 是一个内容充足的在线参考手册。

  为了探测系统信息,我们要使用一个函数:NtQuerySystemInformation(与ZwQuerySystemInformation相同,只是入口不同),这是一个属于 ntdll.dll 的函数。

  在 MSDN 上对这个函数定义如下:

NTSTATUS WINAPI NtQuerySystemInformation(
  __in       SYSTEM_INFORMATION_CLASS SystemInformationClass,
  __inout    PVOID SystemInformation,
  __in       ULONG SystemInformationLength,
  __out_opt  PULONG ReturnLength
);

  难点在于各个数据结构是如何定义的。事实上,在不同版本的 Windows 中,这些数据结构的定义是有区别的,这也是微软不建议使用这些 API 的一个原因。

  通过我上面给出的资料倒是可以查到这些数据是如何定义的。比如 SYSTEM_INFORMATION_CLASS 是一个集合,如果我想要查看有关系统进程的信息,则要取 5 即 SystemProcessInformation。这个在 winternl.h 也有定义,但是这个头文件中的定义比较保守。更多的信息还需要自己 Google 才行。(比如上文提到的 The Undocumented Functions 中的 SYSTEM_PROCESS_INFORMATION

  对这个函数更详细的说明可以参见:NtQuerySystemInformation(ZwQuerySystemInformation)函数说明,这篇文章也有很全的 SYSTEM_INFORMATION_CLASS 是如何定义的。以及我最早参考的NtQuerySystemInformation,也讲了大部分数据结构如何定义。

  至于如果调用这个函数在分析完数据结构后就比较简单了。下面是一个简单的示例,里面还有很多没有说明的类型,不影响理解,进一步查看相关资料就能清楚了。

// 定义类型 
typedef NTSTATUS (WINAPI *PROCNTQSI)(UINT,PVOID,ULONG,PULONG); 
PROCNTQSI NtQuerySystemInformation;

// 调用 
NtQuerySystemInformation = (PROCNTQSI)GetProcAddress( 
GetModuleHandle("ntdll"), 
"NtQuerySystemInformation" 
);

  网上有很多例子可以参考,第一个是获取每个进程和线程:Processes and Threads Sample,讲解详细(网页上有一个小笔误),提供代码下载,用到了 Windows NT DDK,但是如果用上面我说的方法调用的话就不需要了 DDK 了。研究明白了这个,要点就全掌握了。

  还有一个是 Windows NT/2000系统中如何获取系统的启动时间,也有代码下载。

  上面提到的 GetProcAddress,GetModuleHandle,要涉及到动态链接库的使用。针对性的,可以参考:3个脱壳相关的重要函数介绍。简单地说就是:

  • 如果文件已经被映射进了调用进程的地址空间,则函数 GetModuleHandle 返回指定模块的句柄。注意,GetModuleHandle 是在不增加引用次数的情况下返回已映射模块的句柄。
  • LoadLibrary 把模块映射进调用进程的地址空间内。映射进地址空间后,如果必要,则增加模块的引用次数。最后用 FreeLibrary 把它从进程地址空间释放掉。
  • 使用 GetProcAddress 函数返回指定的输出动态链接库内的函数地址。
0
评论 : 使用 Native API 探测本机系统信息

发表评论