Native NT Application 的编写与调试入门

来源:互联网 发布:淘宝儿童模特 编辑:程序博客网 时间:2024/06/04 19:41

文章转自:http://chongyanglee.bokee.com/6726337.html

 

Native NT Application 的编写与调试入门

关键词: Native    NT    Application                                          

 

前言       

      Native NT Application本来我是想写成中文的,但怎么翻译都觉得不太合适,大多文章都写作为Native Application,并翻译为原生应用程序,或者本地应用程序,但自从有了.NET之后,原生应用程序好像更多的用作Win32应用程序了,本地应用程序倒还可以,但怎么听来也不好听,算了,还是简写作 Native 应用程序吧。

        Native 应用程序与 Win32 应用程序的文件结构都是 PE 格式的,甚至也是以.exe为后缀名,但是当你的 Windows 启动到一定阶段后,正常情况下你却再也不能运行 Native 应用程序了,因为此时,Win32子系统已经启动,你已经运行到了 用户模式 下。

        Native 应用程序有哪些呢,当然最出名的就是非法关机后用于磁盘检查的AutoChk,当Windows启动起来后,你再想运行AutoChk.exe等 Native 应用程序,你会得到一个

<应用程序>无法在 Win32 模式下运行

的错误提示。

      Native 能干什么呢,想必你听说过PQMagic,当你挂起动态分区,重新启动电脑时,PQMagic就运行了一个Native应用程序来执行操作,这就是因为系统还没有正常启动起来,所以你可以做好多好多系统启动后不能做的事情。ps: 瑞星杀毒的 BSMain 也是一个 Native 应用程序。

      编写 Native 应用程序是不容易的,甚至你要调用的 API 微软都不愿意公开,我们的入门资料是 Mark Russinovich 写的《Inside Native Application》,网上有中文翻译的文档,可以参考,关于这篇文章,Mark 修改了多次,并且相差很多。关于其 API ,参考资料是多年以前的一本电子书《Windows NT/2000 Native API Reference》。

开始编写

      一般来说,编写 Native 应用程序使用的是 DDK 或者 WDK 的 build,其实,VC 编译器和链接器也知道如何生成此类程序,本文就介绍这种方法。

我们需要的工具

  1. 当然是 VC,本文中使用的是 VS 2008 (盗版的);
  2. DDK 或者 WDK,就算你不使用其 build ,也得有这些头文件和库文件;
  3. winternl.h 或者 NDK,本文中使用的是 NDK,以前使用的是前者,官方的 winternl.h 我们得不到,微软没有提供给我们,只能从 MSDN 中可以窥视到它的存在, 可以从
    http://svn.reactos.org/svn/reactos/trunk/reactos/include/psdk/winternl.h?view=markup
    下载一个非官方版本,我使用的时候做了少许更改,你也可以从
    http://files.cnblogs.com/ChongyangLee/winternl.rar

    下载一个我更改过的版本,更改的地方我都做了注释,搜索 chongyangLee 即可。关于 NDK(Native Development Kit ),就连 ReactOS 都使用了它,放心吧,很全,很强大。 从此处
     http://code.google.com/p/native-nt-toolkit/
    下载。使用时也做了少许更改。

VC的配置

      一般来说,你需要做如下配置:

***********************************************************************************************

General:

不使用MFC (Use Standard Windows Libraries)
使用Unicode字符集 (Use Unicode Character Set)

***********************************************************************************************
C/C++
-----------------------------------------------------------------------------------------------
General:

设置增加的 Include 文件目录,当然还应当包含其它的,用到时自己增加,
注意,此处已经把 NDK 目录拷贝到了 $(DDKROOT)/inc/ndk 下,不使用继承目录
Addtional Include Directories: "$(DDKROOT)/inc/ddk/wxp";"$(DDKROOT)/inc/ndk";$(NOINHERIT)

调试信息格式,如果不是调试版本,此处 Disable
Debug Information Format: Program Database (/Zi)
------------------------------------------------------------------------------------------------
Optimization:

禁止优化,如果不是调试版本,此处 自己看着办
Optimization:   Disabled (/Od)
------------------------------------------------------------------------------------------------
Preprocessor:

预处理器定义,此处必须设置的是 _X86_ 其余的...
如果不是调试版本,DBG=0,以对应 DDK 中的 Free 版本
Preprocessor Definitions: _X86_;DBG=1;_WIN32_WINNT=0x0501
------------------------------------------------------------------------------------------------
Code Generation:

代码生成,基本运行期检查,关闭,不使用。
Basic Runtime Checks:  Default

运行期库,多线程调试
Runtime Library:  Multi-threaded Debug DLL (/MDd)
------------------------------------------------------------------------------------------------
Precomplied Headers:

不使用预编译头(不好意思,习惯而已)
Create/Use Precomplied Header: Not Using Precompiled Headers
------------------------------------------------------------------------------------------------

***********************************************************************************************
Linker
------------------------------------------------------------------------------------------------
Gneral:

启用增量链接,关闭,不使用
Enable Incremental Linking: No (/INCREMENTAL:NO)

增加的 lib 文件目录
Additional Library Directories: "$(DDKROOT)/lib/wxp/i386"
------------------------------------------------------------------------------------------------
Input:

增加的 lib 文件,或者可以使用 #pragma comment(lib, "ntdll.lib)... 代替之
Additional Dependencies: ntdll.lib nt.lib $(NOINHERIT)

忽略所有默认库文件
Ignore All Default Libraries: Yes (/NODEFAULTLIB)
------------------------------------------------------------------------------------------------
Debugging

生成调试信息,即使不是调试版本,此处 也可以生成
Generate Debug Information:  Yes (/DEBUG)
------------------------------------------------------------------------------------------------
System:

子系统为 Native
Subsystem:   Native (/SUBSYSTEM:NATIVE)
------------------------------------------------------------------------------------------------
Advanced:

入口函数,其实 Native 默认的就是这个,可以不设置
Entry Point:   NtProcessStartup

加载基地址,内核部署,默认也是这个,可以不设置
Base Address:    0x10000
------------------------------------------------------------------------------------------------
***********************************************************************************************

 

配置确实挺多的,为此,我用 VS 2008 写了一个 Wizard ,因为本文为了调试,所以没有修改 Release 配置, 

 http://files.cnblogs.com/ChongyangLee/MyWizard.rar

这个压缩文件中包含工程源代码文件,

 http://files.cnblogs.com/ChongyangLee/MyWizard_.rar

这个压缩文件 包含工程源代码文件,正常使用的话可以使用这个方法如下

  1.  解压缩后将其拷贝到vc安装目录下的vcprojects文件夹,例如 E:/Microsoft Visual Studio 9.0/VC/vcprojects/MyWizard
  2.  修改 NativeAppWizard.vsz 中 Param="ABSOLUTE_PATH = G:/Work/SoftWare/MyWizard/NativeAppWizard" 的路径为你的正确路径,如Param="e:/Microsoft Visual Stuidio 9/vc/vcprojects/MyWizard/NativeAppWizard"
  3. 启动 VS 2008 就可以了

 写程序了

      其实,如果你使用我上面提供的向导,一个例子程序已经写好了,正如上面所提到的,用 VC 创建 Native 应用程序时,默认的入口函数为 NtProcessStartup,其参数为 PPEB,其实,使用 DDK 创建 Native 应用程序时,可以创建 main 函数就行了,NtProcessStartup 自动创建好了, 下面是我在向导中提供的例子代码

 

 1 #ifdef __cplusplus

 2 extern "C" {

 3 #endif/*__cplusplus*/

 4 #include <ntndk.h>

 5 #ifdef __cplusplus

 6 }/* extern "C" */

 7 #endif/*__cplusplus*/

 8 

 9 

10 #include <stdio.h>

11 

12 //Handle of Heap

13 HANDLE g_hHeap;

14 

15 

16 void NtProcessStartup(PPEB ppeb)

17 {

18     //for Debug

19     __asm 

20     {

21         int 3;//DbgBreakpoint()

22     }    

23     DbgPrint("************START************");

24 

25 

26     RTL_HEAP_PARAMETERS parameter;

27     memset(&parameter, 0sizeof(RTL_HEAP_PARAMETERS));

28     parameter.Length = sizeof(RTL_HEAP_PARAMETERS);

29 

30     //Create Heap

31     g_hHeap = RtlCreateHeap(HEAP_GROWABLE, NULL, 0x1000000x1000, NULL, &parameter);

32 

33     //todo:

34 

35     UNICODE_STRING wBuf;

36     wBuf.Buffer = static_cast<wchar_t*>(RtlAllocateHeap(g_hHeap, 0255));

37 

38     RtlInitUnicodeString(&wBuf, ppeb->ProcessParameters->CommandLine.Buffer);

39 

40     NtDisplayString(&wBuf);

41     RtlFreeHeap(g_hHeap, 0, wBuf.Buffer);

42 

43 

44     UNICODE_STRING HelloMsg = RTL_CONSTANT_STRING(L"/nOne World, One Dream!/nBeiJing China");

45     NtDisplayString(&HelloMsg);

46 

47 

48     HANDLE hFile = NULL;

49     OBJECT_ATTRIBUTES objAttr = {0};

50     IO_STATUS_BLOCK IoStatusBlock = {0};

51     UNICODE_STRING wszPath = {0};

52 

53     RtlInitUnicodeString(&wszPath, L"//??//c://abcd.txt");

54     InitializeObjectAttributes(&objAttr, &wszPath, 0, NULL, NULL);

55     NTSTATUS statusFile = NtCreateFile(&hFile, GENERIC_READ, &objAttr, &IoStatusBlock, 

56         NULL, 0, FILE_SHARE_READ, FILE_OPEN_IF, 0, NULL, 0);

57 

58 

59 

60     if(hFile != NULL)

61     {

62         NtClose(hFile);

63     }

64 

65     //Terminate Manual

66     NtTerminateProcess(NtCurrentProcess(), 0);

67 }

68 

69 

上面的代码完成了什么功能,
首先,在 Native 应用程序中,堆是自己来创建并维护的;
第二,显示了两串字符,其一是从 PPEB 中取得的命令行信息,其二是一串固定的字符串;
第三,在 C 盘的根目录下打开或者创建了一个文本文件,没有操作,直接关闭了;
第四,终止该程序(return 是终止不了的)。

演示一下效果吧,
如果你去掉为了调试加的代码(__asm{int 3;} DbgPrint("************START************");)编译生成的程序,拷贝到你的System32目录下,修改注册表HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Session Manager
键:BootExecute
值:autocheck autochk *
<你的应用程序名称,此处假定为try3> xixihaha
重新启动 Windows (不带  /NOGUIBOOT 参数),你会在登录前看到如下画面

 

 此处使用的是Session Manager 启动的 Native Application,当然还有其他的方法,自己google吧。

调试 Native 应用程序 

      编写程序不可能离开调试,但 Native 应用程序调试是困难的,需要进行内核调试,因此你需要一个可以进行内核调试的工具,SoftIce 或者 WinDBG,本文中使用了后者。为了调试,你需要将前面加入的调试代码打开,让Windows运行到你的程序开头时可以中断。下面是不太详细(需要你对 WinDBG 有一定的了解)的详细的调试过程:

  1. 修改 boot.ini 文件(Vista 下不同),加入  /debug 调试选项 和 调试口的配置;
  2. 启动目标操作系统,在选项前按上下键暂停止启动;
  3. 启动 WinDBG,按 Ctrl + K,启动内核调试窗口,按照你的调试口配置启动内核调试;
  4. 回到目标操作系统,启动Widnows;
  5. 在WinDBG下配置 .sympath 增加你的 Native 应用程序的 pdb 文件所在的路径;
  6. 同样配置 源文件 路径;
  7. 当执行到 int 3 时,WinDBG 会自行中止;
  8. 输入命令 .reload -user 装入用户符号表;
  9. 打开源文件,恭喜,你可以调试了。

 待续