分类: WINDOWS
2010-04-06 09:50:25
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 编译器和链接器也知道如何生成此类程序,本文就介绍这种方法。
一般来说,你需要做如下配置:
***********************************************************************************************
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
这个压缩文件 不 包含工程源代码文件,正常使用的话可以使用这个方法如下
其实,如果你使用我上面提供的向导,一个例子程序已经写好了,正如上面所提到的,用 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(¶meter, 0, sizeof(RTL_HEAP_PARAMETERS));
28 parameter.Length = sizeof(RTL_HEAP_PARAMETERS);
29
30 //Create Heap
31 g_hHeap = RtlCreateHeap(HEAP_GROWABLE, NULL, 0x100000, 0x1000, NULL, ¶meter);
32
33 //todo:
34
35 UNICODE_STRING wBuf;
36 wBuf.Buffer = static_cast<wchar_t*>(RtlAllocateHeap(g_hHeap, 0, 255));
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 应用程序调试是困难的,需要进行内核调试,因此你需要一个可以进行内核调试的工具,SoftIce 或者 WinDBG,本文中使用了后者。为了调试,你需要将前面加入的调试代码打开,让Windows运行到你的程序开头时可以中断。下面是不太详细(需要你对 WinDBG 有一定的了解)的详细的调试过程:
待续
【作者: chongyanglee】 【访问统计:60】 【2008年05月30日 星期五 23:58】【】【】