Native Application 开发详解(直接在程序中调用 ntdll.dll 中的 Native API,有内存小、速度快、安全、API丰富等8大优点)

文章目录:

1. 引子:

2. Native Application Demo 展示:

3. Native Application 简介:

4. Native Application 有何妙用:

5. MJ0011 关于 Native Application 的文章整理:

6. 互联网上其他关于 Native Application 的文章整理:

7. 小结:

1. 引子:

其实在好久以前就看了 MJ0011 翻译的那个《Native 应用程序详细》系列的文章,

(PS: MJ0011 为 360 的首席技术执行官,技术是没的说,不过貌似有点狂妄之说 ~ )

而且看完后对这一系列文章也很感兴趣的,所以又去 Google 上找了几个小资料学习了一下,

而这篇文章呢,则是将我前阵子的所谓的学习给总结出来也顺道给大伙分享一下。

虽然这里是说的总结 Native Application,但最早出现 Native Application 应该是 06 年的事了,

(当然,Native Application 这个技术是一直存在的,只是在 06 年后有了下面这篇文章后就稍微火了点)

其是 Sysinternals 上的一篇由 Mark Russinovich 发表的文章《Inside Native Applications》,

文章地址如下:http://technet.microsoft.com/en-us/sysinternals/bb897447

而我却在 2011 年才得以来总结这个技术,所以我确是属于研究这些东西的落后者啊 ~

下面我给出一副截图来看一下 Windows 操作系统下的程序的类型:

image

在 Windows 最初设计的时候考虑到了兼容各种系统的应用程序,所以有了环境子系统之说,

其中一开始的时候考虑到了子系统,POSIX 子系统和 OS/2 子系统,

但是随着历史的发展,现在也就剩下个 Windows 子系统了,

我们日常使用的 Windows 操作系统的上层其实也就是指的这个 Windows 子系统了,

至于这里为何要扯到 Windows 子系统的话,就看下文介绍了。

很多朋友都知道有 Windows 应用程序和 Windows 内核驱动程序之说,

却很少有知道在 Windows 中还有 Native Application 一说了,

但是这类程序确实是存在的 ~只不过这类程序应用比较窄,也没有被很好的推广开来,

当然还有一点就是 Microsoft 自然不希望你随随便便的使用 Native API。

2. Native Application Demo 展示:

首先你需要将下载(博文的最后面附上 Demo 的下载地址)的 EXE 文件拷贝一份到 system32 目录下,

(博文的 Demo 只是拿了网上的代码然后自行使用 DDK 编译了而已,Demo 并非笔者原创)

然后再在注册表以下路径中修改 BootExecute,

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager

在其中添加 NativeApp_01 Hello World ! 这个字符串,

image

image

重启电脑,然后就可以看到下面的效果图了(仅在 XP SP3 上进行了测试)

2011-09-18_111934

3. Native Application 简介:

何为 Native API ?

Native API 就是你 system32 目录下的那个 ntdll.dll 中所公开的 API(大部分为 Undocument)~

如果读者看过我前面的《进程隐藏与进程保护(SSDT Hook 实现)》系列文章的话,

相信肯定会知道在 Windows 中 kernel32.dll 中的 API 的调用都会经过转换,

也就是跳转到 ntdll.dll 中,并且在 ntdll.dll 中也有与之相对于的 API 调用,

(比如 Kernel32.dll 中的 CreateProcess 在 ntdll.dll 中有 NtCreateProcess 与之对应)

那么什么称之为 Native 应用程序呢 ?

下面给出一副截图:

image

从上面的截图可以看出,在一开始的 Windows NT 内核中是支持三个环境子系统的,

即 POSIX,WINDOWS,OS/2,这些子系统属于同一层,它们共用了 Windows NT 所提供的 API,

即每一个子系统中的 API 的调用都会转换到下一层的相同调用上,

在 Windows 环境子系统(有 Windows,Posix,OS/2)中的程序,

都会调用其相对于的子系统下的 API,比如 Windows 子系统中的程序有可能会调用 Win32 API CreateProcess,

而 Posix 子系统中的程序也有可能会调用 Posix API CreateProcess(当然有可能在 POSIX 下创建进程不是这个名称),

但是终归来说,这两个 CreateProcess 的调用都会转换到 Ntdll.dll 中的 NtCreateProcess 中,

也就是上面的三个子系统最后的调用都会回归到 ntdll.dll 上,

而我们的 Native Application 则是绕过 Windows 子系统,

直接自己调用 Native API,比如创建进程的话,我就不再通过子系统中的神马 CreateProcess 来完成了,

而是直接在程序中调用 ntdll.dll 中的 Native API NtCreateProcess 来完成,

而这类程序即称之为 Native Application !

Native Application 的运行环境:

上面也说了,Native Application 是只能够访问 ntdll.dll 中的内容的,

而如果是在子系统下运行一个程序的话,必然会加载其他的 DLL,

比如在 Windows 子系统下一个 kernel32.dll 是必不可少的吧,

如果 Native Application 能够运行在 Windows 子系统下的话,必然也会加载到 Kernel32.dll,

这样不就和上面相违背了嘛 ~

总之:Native Application 是不能够运行在任何子系统下的 !

比如在 Windows 子系统下运行 Native Application 会弹出如下错误对话框:

image

Native Application 的启动时机:

对于 Windows 操作系统的引导过程,这里需要带一笔的,Windows 操作系统启动时,

当 Windows 内核的引导完成以后,就会启动会话管理器 smss.exe 进程了,

smss.exe 进程虽然是一个用户模式的进程,但是这个进程相对于其他用户模式进程是具有一定特殊性的,

首先 smss.exe 进程是直接建立在 Windows NT 内核上的,其不依赖于任何一个环境子系统,

至于不依赖于任何一个环境子系统这一说的话,还是可以很好的解释的,

因为当环境子系统进程(Windows 子系统进程为 csrss.exe)就是由 smss.exe 进程启动的,

然后 smss.exe 是 Windows 操作系统启动的第一个用户态进程,

而 Native Application 也属于用户态程序,自然 Native Application 的启动是在 smss.exe 之后,

而后前面也说过,Native Application 运行时,子系统进程还尚未启动,

所以 Native Application 的启动则是在 csrss.exe 之前的,

而话又说回来,csrss.exe 就是由会话管理器(smss.exe)启动的,

所以 Native Application 的启动时机也就只有一种可能了,

即 smss.exe 先启动 Native Application,然后 Native Application 开始执行,

等到 Native Application 都给执行完了后 smss.exe 再启动 csrss.exe 进程。

(事实上,Win32 应用程序环境子系统 csrss.exe 本质上也是一个 Native Application ~)

下面给出一副截图以说明 SMSS.EXE 的在启动过程中所完成的工作:

运行启动时执行的程序即是执行 Native Application

image

4. Native Application 有何妙用:

前面也提到过,Native Application 的应用范围比较窄,这主要受以下几点约束:

首先,Native Application 是直接调用 Native API 来完成任务的,

而在 Windows 中,Native API 绝大部分都是 Undocument 的,这样开发起来自然难度会大很多了。

然后,由于 Native Application 是调用的 Undocument API,

说不准那一天 Microsoft 就在下一代 Windows 中修改了这个 API,这样的话,你程序的可移植性也就完全没了。

最后的话,Native Application 的执行环境并非是在 Windows 子系统中,

事实上,当 Native Application 开始执行的时候,Windows 子系统进程(csrss.exe)进程都还没有启动的 ~

所以 Native Application 的执行是受限制的,其不能够执行子系统中的任何东西,

说白一点的话就是 Native Application 只能够调用 ntdll.dll 中的 API,

其他 DLL 中的 API 一律不得调用,这样也就注定 Native Application 的应用范围不会很广泛了。

应用范围比较窄并不等于说没有应用价值,

也还是有蛮多的软件,包括一些商业软件也都是用了 Native Application,

下面来看一些 Native Application 的应用(我也只是从网上道听途说,不过有些也确实是自己也用过的):

最典型的应用自然是属于 Windows 操作系统自带的磁盘自检程序了;

然后比较典型的商业应用是瑞星的开机杀毒,即实现让病毒在你还没有完全开机之前死掉;

然后就是可以通过 Native Application 来实现接管 Windows 的开机启动界面和密码输入界;

还可以通过 Native Application 来实现开机前的磁盘修复(Windows 自带了这款工具也是这样实现的);

5. MJ0011 关于 Native Application 的文章整理:

这一小节里面主要是整理一下关于 MJ0011 的 Native Application 的博文,

也就是说我这里纯粹是将他的东西给贴出来瞧瞧,关于 MJ0011 博文的原地址为:

《NATIVE应用程序详细之一》:

http://hi.baidu.com/mj0011/blog/item/7ee496d67a4d4d2f07088bc7.html

《NATIVE应用程序详细之2 NATIVE应用程序的优势和劣势》

http://hi.baidu.com/mj0011/blog/item/f8108f2f5890fb381e30896d.html

《native应用程序详细之三 构建native应用程序》:

http://hi.baidu.com/mj0011/blog/item/725b4882042b03a20df4d269.html

《深入Native应用程序》(该文翻译自《Inside Native Application》):

http://hi.baidu.com/mj0011/blog/item/85c0b50f80b1baedab6457de.html

《native app GUI界面的实现》:

http://hi.baidu.com/mj0011/blog/item/6e5a22fa214c8116a9d3115b.html

Native 应用程序简介:

NT 系统被设计成为支持子系统(封装),它可以执行在不同平台上的代码。包括但不限于:POSIX、OS/2和Win32,

为了管理这些子系统,NT内核输出了大量名为 Native API的API函数,子系统服务将这些函数包装为他们自己的函数。

例如:CreateFile和fopen都被映射到NtCreateFile.那么子系统管理程序运行在哪个子系统中?

为了避免先有鸡还是先有蛋的问题,NT系统同样支持原生的Native应用程序。

这些Native应用程序是独立于子系统的。现在,所有的子系统应用程序都需要注册它们自己的子系统服务,

显然,Kernel32是一个Win32应用程序,csrss注册WIN32子系统,然后通知子系统管理器SMSS,

因此,一个Native应用程序是无法调用其中数千种API的,

同时它也无法使用一些基本的DLL中的函数例如kernel32、user32、gdi32等,

其它任何调用了这三者的DLL中的函数的DLL也无法被使用。

事实上,加载者在其入口点没有被加载前,并不允许加载决大多数的Win32 DLL。

因此,native应用程序被限制只许使用一个DLL:ntdll.dll,这个DLL供应所有Native和运行函数。

但是想想,既然所有Win32函数最后都是去调用Ntdll.dll中的函数来实现的(除了GUI部分),

这些函数就已经足够了,不是吗?

Native应用程序优势:

(一) 内存使用和大小:因为Native应用程序并不需要加载90多个DLL到内存中去,因此其使用的内存是很小的。

(二) 速度:Native API比Win32对应的函数要更快(通常会快很多),

尽管实际上很多时间都被消耗在Win32 API的封装上,这些包括修改、兼容性选项,和其他的代码部分,

这些都会在执行真正的Native调用前执行。如果你知道如何去做而且希望它运行得更快,Native是一个很好的方法。

因为不需要加载那90多个DLL,kernel32不需要到csrss和smss做lpc注册,因此启动也是非常快速和直接的。

(三) 熟悉程度和特性:Native应用程序并没有古怪的入口点或者奇怪的Hack,就象读命令行一样简单使用,

而事实上将在后面介绍,Native应用程序和Console程序一样启动与_main函数,

通过一组简单的char*[]队列来接受命令行参数和环境变量,就象一个典型的Win32 Console程序一样,

一些特性例如缓冲区溢出保护(/GS)、Safe SEH(/SAFESEH)、hotpathing(/HOTPATCH),

以及其他的特性都被支持。

(四) 丰富:Native API非常丰富,它们提供的特性和功能性要远远超越Win32函数所能达到的程度。

这并不是说Win32无法做到Native API那样的更困难和更复杂的工作,而是Win32太过简单而已,

比如Win32函数中,无法远程注入一个Section到一个进程中,因为MapViewOfFileEx不提供进程句柄接口,

而Native API则可以实现这个功能。

(五) 安全:但是Native应用程序有这样一个特点:人们对于Native API不是十分熟悉,

他们需要花费更多的时间才能理解你的代码,而更重要的是,无法使用一个用户模式的debugger来调试Native应用程序,

只有SoftIce和使用内核模式远程连接的WinDbg才可以对其进行调试,这足以让那些废物脚本小子去死了,

再说一遍,这并不意味着Native 应用程序是“不可调试的,安全的”,只是它更模糊更难被破解。

(六) 同内核模式的连接:因为Native应用程序只使用Native API,这些函数在内核模式仍然可用,

这样,一个Native程序只需少量修改就可修改为内核驱动,而Win32程序则需要几乎重写所有代码。

(七) 运行在独立于子系统的环境中:因为Native应用程序并不依赖与任何子系统,

它运行于一个正常应用永远无法再次得到的环境中,比如autochk.exe,它运行与任何子系统加载之前,

并负责显示’press any key to scan your hard disk”信息,并扫描你的硬盘是否有错误。

在这个模式运行允许你显示信息到启动屏幕上,以及很多增强特性。

(八) 标准性:不象Win32函数有一个正常版本,一个”Ex”版本,以及经常有一个”扩展其他功能”的版本,

以及经常返回0,1,-1或一些随机的数值来表示成功,并且要使用SetLastError来设置返回错误,

在Native应用程序中,这些垃圾都是不存在的,所有的Native API都有统一的标准,

所有都返回NTSTAUS(除非明确表示会返回一个特定的值),NTSTAUS是一个标准的错误码定义,

而且使用它们时你也不需要考虑该死的Ex版本。

Native 应用程序劣势:

(一) 和Win32开发的差异较大:如果你以前从来没有进行过Native API或内核驱动的相关开发,

你可能需要学习所有的API相关知识,当然,他们的名字是相似的,但是他们的标志经常是完全不同的,

而且他们的返回值,很可能使你感到迷惑。

(二) 缺少文档:虽然所有的Rtl*函数都是有文档的,数百个其他的Native API仍是无文档的。

(三) 缺少商业价值:虽然Native程序如此美好,但是建议你不要在商业产品中使用 Native API 或着使用Native应用程序,

Native API是可能改变的,虽然它们通常没有改变,但是他们很可能变得不再有用,不要拿你的客户的钱冒险。

(四) 没有GUI、及输入/输出接口:没有”Native控制台程序”,你无法简单地从用户那里接受到输入或显示些什么到屏幕上,

因为那些接口都无法再使用(控制台API都是Win32 API)。

Native 应用程序构建:

你需要这两者或两者之一来创建你的 native 应用程序:

(1)Visual C++ 2005 Express(or higher)

(2)Windows Driver Kit

我们将从基础开始,首先你需要创建你的应用程序的头文件,precomp.h,ntddk.h

1: #include “ntddk.h”
Now that that’s done, create your main initialization file,

which we will call init.c. In this file, add precomp.h like this:

1: #include “precomp.h”
And define your entrypoint:

1: NTSTATUS __cdeclmain(
2:
3: INT argc,
4:
5: PCHAR argv[],
6:
7: PCHAR envp[],
8:
9: ULONG DebugFlag OPTIONAL)
10:
11: {
12:
13: // Entry code here
14:
15: }
Hopefully you are familiar with this entrypoint, it’s the typical one used by C programs,

except with an addon: the “DebugFlag”. Right now, we don’t need to care about this.

We’ll keep this entry simple, and turn this into a “Hello World”:

1: NTSTATUS __cdecl main(
2:
3: INT argc,
4:
5: PCHAR argv[],
6:
7: PCHAR envp[],
8:
9: ULONG DebugFlag OPTIONAL)
10:
11: {
12:
13: UNICODE_STRING HelloMsg = RTL_CONSTANT_STRING(L”Hello World!\n”);
14:
15: //Say hello
16:
17: NtDisplayString(&HelloMsg);
18:
19: }
20:
Now, if you’re wondering what NTSTATUS is,

what NtDisplayString is or what

RTL_CONSTANT_STRING and UNICODE_STRINGs are,

then you’ll need to read all the DDK documentation you can swallow,

as well as Nebett’s Undocumented Native API book.

Although it’s outdated, the information about the APIs is still pretty valid.

I also plan to possibly give a fully-fledged lesson on this if lots of

people are interested.So now that we have our simple program, we need to build it.

I prefer using the WDK myself,

because it’s much simpler and doesn’t require changing 100 of MSVC’s default settings.

Assuming you’ve properly installed the WDK and entered the Windows build environment for

your OS (from the Start Menu),

you’ll need to create two files in the directory where init.c and precomp.h are:

sources and makefile.

Sources should look something like this:

1: TARGETNAME=native
2:
3: TARGETTYPE=PROGRAM
4:
5: UMTYPE=nt
6:
7: INCLUDES=\
8:
9: $(DDK_INC_PATH); \
10:
11: $(NDK_INC_PATH);
12:
13: SOURCES=init.c
14:
15: PRECOMPILED_INCLUDE=precomp.h
16:

that you’ll have to set NDK_INC_PATH as an environment variable yourself,

to where the NDK is installed (DDK_INC_PATH is already setup by the WDK).

Finally, you’ll need a makefile:

1: INCLUDE $(NTMAKEENV)\makefile.def

That’s all you really need for our purposes.

So now you should have init.c, precomp.h, sources and makefile.

The only thing left is to write “build”,

and the WDK should do the magic and create your first native application.

Unforunately, you can’t really test it for now,

unless you do the following:Open registry editor and browse to

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager

Edit the “BootExecute” key and write “native” instead of what’s currently in it,

then copy native.exe to your system32 directory and restart the computer.

You should see the message appear on the screen for a little while.

Make sure that you do NOT boot with /NOGUIBOOT, or else you will never see it.

//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

/////////////////MJ0011 翻译《Inside Native Application》//////////////////////

//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

导言

如果你对Windows Nt结构有一定的了解,你可能会知道,Win32 应用程序所使用的API,

并非是”真正”的NT API,POSIX、OS/2和Win32这些Windows NT 操作系统环境,

使用他们自己的 API 同他们的客户应用程序进行交流,但却使用Windows NT的”Native” API同Windows NT交流,

这些Native API大都是未公开UnDocumented 的。

大约只有25个API(包含250种功能)在Windows NT设备驱动开发工具包(DDK)里有所描述。

尽管绝大多数人都不知道,但”Native”应用程序的确存在与Windows NT上,他们在操作环境上没有任何客户程序,

这些程序交流着Native NT API并且不能使用任何操作环境API比如 Win32,为什么这样一种程序是必须的呢?

因为在Win32子系统启动之前(大约在登陆对话框出现时)只可以运行Native应用程序,

最常见的Native应用程序的例子是”autochk”程序,他在初始化蓝色登陆屏幕前运行chkdsk(程序在屏幕上打印一串”.”)。

当然,Win32应用程序环境服务程序:CSRSS.exe(客户-服务运行时间子系统),也是一个Native应用程序。

在这篇文章里,我将会讲述如何构造一个Native应用程序以及它们是如何工作的,

同时我也会提供一个Native应用程序的示例源代码。

这个示例很容易安装,它会在启动时的蓝色屏幕打印一段你指定的字符串。

Autochk是如何被执行的

Autochk在当内存分页被打开,Windows启动和系统开始驱动被载入之间的时间内运行,

在这个时间点会启动会话管理器(smss.exe)进入Windows NT用户模式,并且没有任何程序被启动。

注册表中:HKLM\System\CurrentControlSet\Control\Session Manager\BootExecute

一个MULTI_SZ类型的键值,这里存放着将被会话管理器所执行的程序名称和参数,通常是Autochk后加*号作为其参数

Autocheck Autochk *
;名称 程序名 参数

会话管理器在\system32目录下查找该值列出的可执行程序,当Autochk运行时,没有任何文件被打开,

所以Autochk可以用raw模式打开任何一个驱动器卷(包括根驱动器),并操作其磁盘数据结构,

之后的任何时间点都无法进行类似这样的操作。

编译Native应用程序

1. 微软没有提供相应的文档,但是NT DDK构建器知道如何去生成一个Native应用程序,

而且它可以被用来编译autochk程序,和编写设备驱动程序一样,你必须指定SOURCE文件中的信息来定义应用程序,

然而和编写驱动不同的时,你在SOURCE文件中要求生成一个Native应用程序需要这样定义:

TARGETTYPE=PROGRAM

2. 构建器使用一个标准的makefile来进行向导:\ddk\inc\makefile.def 在编译Native应用程序时,

会查找名为nt.lib的运行库。不幸的是,微软并没在DDK上装载这个文件(在Windows Server 2003 DDK里包括了这个文件,

但是我怀疑你用这个版本来连接你的Native应用程序是无法运行在Windows XP或Windows 2000上的)不管怎样,

你可以忽略这个错误,方法是加入一行不考虑nt.lib,而指定Visual C++ 的运行库msvcrt.lib到makefile.lib中。

3. 如果你在DDK的”Checked Build”环境下进行编译,

将会在%BASEDIR%\lib\%CPU%\ Checked(例如c:\ddk\lib\i386\checked\ native.exe)

产生一个包含了全部调试信息的Native应用程序。

4. 如果在”Free Build”环境中编译,你会在%BASEDIR%\lib\%CPU%\Free得到一个释出版本的程序,

这些和构造设备驱动程序放置的位置是一样的。

5. Native应用程序有着”.exe”的扩展名,但是你不能像 Win32的.exe文件那样去运行它,

如果你在Win32环境下运行下,将会得到如下提示:”<应用程序名> 应用程序无法在Win32模式中运行。”

深入学习Native应用程序

1. Native应用程序的入口点是NtProcessStartup,类似WinMain或Main,不同于其他的 Win32入口点的是,

Native应用程序提供一个数据结构来存放它的唯一的参数来定位命令行参数。

2. 大多数的Native应用程序的运行环境是由Windows Nt的Native API输出库 – NTDLL.DLL提供的。

3. Native应用程序必须使用RtlCreateHeap(一个ntdll函数)来创建他们自己的堆来分配存储,

使用RtlAllocateHeap来分配内存以及用RtlFreeHeap来释放内存。

4. Native应用程序需要使用NtDisplayString 函数才可以打印想要的内容到屏幕上(将被输出到初始化时的蓝色屏幕上)。

5. Native应用程序不像Win32程序那样简单地从他们的启动函数返回,你需要调用NtProcessTerminate函数来结束它的进程。

6. NTDLL运行包含了数百个函数允许Native应用程序执行文件I/O,与设备驱动进行相连,并执行进程间通讯。

不幸的是,他们大部分都是未公开的。

Native应用程序实例

1. 我创建一个Native应用程序用来演示Native应用程序是如何构建的以及他们是如何工作的。

运行install.bat来安装Native程序。

2. 批处理程序复制Native.exe到你的\system32目录,

并在注册表中增加一个BootExecute的入口点: native Hello World!

3. 当你重新启动时,会话管理器运行完autochk后就会执行Native,Native分配一些堆,

定位它的命令行参数并打印参数(“Hello world!”)到蓝色屏幕上,它所使用的函数上面已说过了。

如果你想要打印其他的简单内容,可以编辑BootExecute值使用regedit或regedit32,

修改”Hello world”为你想要的信息。

4. 运行uinstall.bat可以卸载这个Native执行程序。它从\system32 目录删除 Native.exe,

并修改BootExecute值为通常的值。

5. 如果你想要构建native程序你必须要用Windows设备驱动工具包(DDK),

复制makefile.def到 \ddk\inc然后你可以运行构建。

Native.H

1: //Environment information, which includes command line and image file name
2: typedef struct
3: {
4: ULONG Unknown[21];
5: UNICODE_STRING CommandLine;
6: UNICODE_STRING ImageFile;
7: } ENVIRONMENT_INFORMATION, *PENVIRONMENT_INFORMATION;
8:
9: // This structure is passed as NtProcessStartup’s parameter
10: typedef struct
11: {
12: ULONG Unknown[3];
13: PENVIRONMENT_INFORMATION Environment;
14: } STARTUP_ARGUMENT, *PSTARTUP_ARGUMENT;
15:
16: // Data structure for heap definition.
17: // This includes various sizing parameters and callback routines,
18: // which, if left NULL, result in default behavior
19: typedef struct
20: {
21: ULONG Length;
22: ULONG Unknown[11];
23: } RTL_HEAP_DEFINITION, *PRTL_HEAP_DEFINITION;
24:
25: // Native NT api function to write something to the boot-time
26: // blue screen
27: NTSTATUS NTAPI NtDisplayString(
28: PUNICODE_STRING String
29: );
30:
31: // Native applications must kill themselves when done –
32: // the job of this native API
33: NTSTATUS NTAPI NtTerminateProcess(
34: HANDLE ProcessHandle,
35: LONG ExitStatus
36: );
37:
38: // Definition to represent current process
39: #define NtCurrentProcess() ( (HANDLE) -1 )
40:
41: // Heap creation routine
42: HANDLE NTAPI RtlCreateHeap(
43: ULONG Flags,
44: PVOID BaseAddress,
45: ULONG SizeToReserve,
46: ULONG SizeToCommit,
47: PVOID Unknown,
48: PRTL_HEAP_DEFINITION Definition
49: );
50:
51: // Heap allocation function (ala “malloc”)
52: PVOID NTAPI RtlAllocateHeap(
53: HANDLE Heap,
54: ULONG Flags,
55: ULONG Size
56: );
57:
58: // Heap free function (ala “free”)
59: BOOLEAN NTAPI RtlFreeHeap(
60: HANDLE Heap,
61: ULONG Flags,
62: PVOID Address
63: );

Native.C

1: //======================================================================
2: //
3: // This is a demonstration of a Native NT program. These programs
4: // run outside of the Win32 environment and must rely on the raw
5: // services provided by NTDLL.DLL. AUTOCHK (the program that executes
6: // a chkdsk activity during the system boot) is an example of a
7: // native NT application.
8: //
9: // This example is a native ‘hello world’ program. When installed with
10: // the regedit file associated with it, you will see it print
11: // “hello world” on the initialization blue screen during the system
12: // boot. This program cannot be run from inside the Win32 environment.
13: //
14: //======================================================================
15: #include “ntddk.h”
16: #include “stdio.h”
17: #include “native.h”
18:
19: // Our heap
20: HANDLE Heap;
21: //———————————————————————-
22: // NtProcessStartup
23: // Instead of a ‘main’, NT applications are entered via this entry point.
24: //———————————————————————-
25: void NtProcessStartup( PSTARTUP_ARGUMENT Argument )
26: {
27: PUNICODE_STRING commandLine;
28: PWCHAR stringBuffer;
29: PWCHAR argPtr;
30: UNICODE_STRING helloWorld;
31: RTL_HEAP_DEFINITION heapParams;
32:
33: // Initialize some heap
34: memset( &heapParams, 0, sizeof( RTL_HEAP_DEFINITION ));
35: heapParams.Length = sizeof( RTL_HEAP_DEFINITION );
36: Heap = RtlCreateHeap( 2, 0, 0x100000, 0x1000, 0, &heapParams );
37:
38: // Point at command line
39: commandLine = &Argument->Environment->CommandLine;
40: // Locate the argument
41: argPtr = commandLine->Buffer;
42: while( *argPtr != L’ ‘ )
43: {
44: argPtr++;
45: }
46: argPtr++;
47:
48: // Print out the argument
49: stringBuffer = RtlAllocateHeap( Heap, 0, 256 );
50: swprintf( stringBuffer, L”\n%s”, argPtr );
51:
52: helloWorld.Buffer = stringBuffer;
53: helloWorld.Length = wcslen( stringBuffer ) * sizeof(WCHAR);
54: helloWorld.MaximumLength = helloWorld.Length + sizeof(WCHAR);
55:
56: NtDisplayString( &helloWorld );
57:
58: // Free heap
59: RtlFreeHeap( Heap, 0, stringBuffer );
60:
61: // Terminate
62: NtTerminateProcess( NtCurrentProcess(), 0 );
63: }

Install.bat:

1: @echo off
2: copy native.exe %systemroot%\system32\.
3: regedit /s add.reg
4: echo Native Example Installed

UnInstall.bat:

1: @echo off
2: del %systemroot%\system32\native.exe
3: regedit /s remove.reg
4: echo Native Example Uninstalled

6. 互联网上其他关于 Native Application 的文章整理:

下面给出的则是我在互联网上其他地方找到的关于 Native Application 的比较好的资料,

这里也只是将这些资料进行一个整理。

《native app 开发小结(一)》:

http://hi.baidu.com/316526334/blog/item/32823e8a83d87b1fc9fc7a0f.html

《native app 开发小结(二)》:

http://hi.baidu.com/316526334/blog/item/43a21061ad619cd28cb10d08.html

《Native Application 应用之开机杀毒》:

http://www.cublog.cn/u/8754/showart_447592.html

程序类型

关于程序级别的分类,大概可以分为三层:应用层程序,即普通的APP程序;Native App程序,如常见的chkdsk工具,PQ分区工具等,都属于这类,它是在win子系统未启动起来就执行的程序,执行环境比较纯净,只能调用ntdll.dll导出的函数;

Driver 内核驱动程序,因功能不同也分为若干类,如设备驱动程序,内核扩展程序,文件系统驱动,过滤驱动等,

同属于内核态程序;其中,native app在项目开发中用的较少,所用的函数接口MS也均未公开,

开发难度和驱动相当,所以很少有人问津。但是,事实上,在某些应用场景下,用native app来实现是非常完美的,

比如:接管winodws的开机启动界面和密码输入界面,需要native app;

在开机前,执行磁盘修复,需要native app;开机前,执行杀毒,或者磁盘整理,

因为此时环境比较纯净,需要native app。诸如此类~ 最近,开发一个Native App项目,其规模和复杂度也不一般。

期间遇到很多问题,在逐一解决的时候也收获了很多东西。现在略作整理,以备将来查用,二来与大家分享之~

Native app 基本工作原理

这里,只想简单的描述下。

Windows的设计是基于分层模型的,在设计之初,内核NT支持三个子系统,OS/2,posix,win32,

这些子系统同属于一个层面上,它们公用windows nt提供的系统API和例程。其中,在某一个子系统上的API调用,

都会经过NT”native”API同windowsNT进行通信。这些native API就是ntdll.dll导出的函数,

因为它导出的大部分函数都只是起一个从子系统到NT内核的转发传递作用,所以也成为stub函数,

这些函数的原型大多是未公开的,在早期的DDK里会有相关的描述,但是现在没了,

取而代之的是内核实现的zw*,nt*开头的驱动函数。这里表明了MS的一个态度,

不希望第三方在native app上干涉windows的太多工作,比如,接管了开机启动系统,接管了登录密码界面:D.

后来,因为种种历史原因,对OS/2和posix子系统的支持逐渐被淡忘。但是这种分层模型仍然存在,

native app就是工作在子系统未启动之前,此时的系统环境很纯净,权限也相对较高;

另外,对操作系统来说,支持native app也是一种必须,因为在子系统启动之前,

很多功能的程序只能以native app来呈现,比如登录界面,CSRSS~

具体的启动时机:

Native app由启动会话管理器(smss.exe)来启动,如果想通知smss来执行一个native app程序,

只需要修改一个注册表项,smss在每次启动的时候会去检查该项,确保该项下面的每个native app程序依次执行。

注册表项为:HKLM\System\CurrentControlSet\Control\Session Manager\BootExecute ,

其类型为MULTI_SZ, 又是MS玩的一种字符串类型—多字符串类型,也就是说,

这个MULTI_SZ字符串包含多个以\0结尾的子字符串,而整个字符串以\0\0结尾。

在该注册表项后面添加要注册的native 程序名和参数就可以了。

关于native app的更多详细介绍,可以参考Mark Russinovich的一篇关于native的文章。

Native app程序结构

Native app程序结构很简单,就好像我们写hello world,需要写一个main函数入口一样,

native app的入口函数是NtProcessStartup,形如:

1: void NtProcessStartup( PSTARTUP_ARGUMENT Argument )
其中,参数PSTARTUP_ARGUMENT是一个结构体,用来存放传入参数。

程序退出时,主动调用函数NtProcessTerminate退出,它不会像普通应用程序一样一个返回return就退出了。

基本的结构就是这样了:

1: void NtProcessStartup( PSTARTUP_ARGUMENT Argument )
2:
3: {
4:
5: // do something else
6:
7: NtProcessTerminate( NtCurrentProcess(), 0 );
8:
9: }
10:
当然现在还没有包含头文件之类的。

开发语言及第三方的库

Native app支持的开发语言有C/ASM/C++,并且完美的支持C++的类特性,不过要像编写驱动一样,

需要重载new,delete等内存分配函数。在内存使用上,可以使用两套接口:堆函数接口,以及虚拟内存函数接口,

但是根据我的经验,使用堆函数接口,更高效并且内存bug出现的频率会大大降低。

举个例子:我在调试native app的时候,一切正常,且全部通过,但是双机调试的时候,

功能代码都运行完了,在子系统起来的时候,提示memory_corruption错误,

这个问题整整找了好几天都没有找到,到最后,无意间屏蔽掉了系统的DbgPrint函数,memory错误才解决。

其原因是,native gui图形模块的debug消息打印的太快太频繁,导致调试缓冲被溢出,

当屏蔽了系统DbgPrint的时候,也就是在windbg 下bp DbgPrint,然后a eip,ret后,就正常了。

虽然这个现象和程序无关,但是,用了虚拟内存的话,这个问题会更加容易重现。关于这个问题的重现很容易,

就是在native 环境下双机调试,target机器一直打印消息,当打印到10W条以上时,调试缓冲就“爆”了。

这真不知道是ms的bug,还是自己~~

关于 new, delete 的重载。

使用堆函数过程如下:首先创建一个全局堆,然后在这个全局堆上分配和释放局部堆。

1: HANDLE hGlobalHeap = NULL; // for globle call
2:
3: void* __cdecl operator new(size_t size)
4:
5: {
6: if(hGlobalHeap == NULL)
7: return NULL;
8: return RtlAllocateHeap(hGlobalHeap,0/*HEAP_ZERO_MEMORY*/,size);
9: }
10:
11: void __cdecl operator delete(void* addr)
12:
13: {
14: if(hGlobalHeap && addr) (void)RtlFreeHeap(hGlobalHeap,0,addr);
15: }
16:

关于 NDK:前面说到,native app用的是ntdll.dll的导出函数,而这些函数MS并没有公开接口声明,

那么我们使用的时候,首先必须要自己定义函数声明。

NDK就是这样的一个类库,它几乎定义了ntdll.dll导出的全部函数的声明以及一些常用的数据结构的定义,

我们只需要包含相应的头文件,导入ntdll.lib库,就可以像使用普通的API函数一样开发native app了。

关于DLL:native app可以调用同一级别的DLL,使大型的项目开发更加容易,更加容易划分模块。

注意,DLL的编译环境要和native app一致。

关于native app的编译:可以选择用vs环境,也可以用DDK/WDK,但是推荐使用WDK。

用VS环境的话,需要简单的设置下,随意创建一个类型的工程,然后修改Linker->system->Native,

就可以了。如果用WDK编译,需要写一个SOURCE模板,如:

1: TARGETNAME=defrag
2:
3: TARGETPATH=obj
4:
5: TARGETTYPE=PROGRAM
6:
7: INCLUDES=$(PUBLIC_ROOT)\inc\ddk
8:
9: SOURCES=defrag.cpp
10:
注意:上面说的DLL的编译环境和native app的编译环境要一致,

指的是不要用WDK编译的native去尝试链接VS编译的DLL,反之亦然。

1: //NTSTATUS NTAPI NtDisplayString( PUNICODE_STRING String);
Native GUI

在native app执行环境下画界面是不可行的,但是不是说做不到。

前面说了,可以写一个native app来接管windows的启动界面和密码输入界面,那么这个界面是如何画的呢?

也有从驱动里实现的。但是,事实上,MS提供了一个native级别的动态库,名为:Bootvid.dll,

用来实现GUI启动屏幕的引导视频驱动,这个dll导出了一些函数,可以实现画图,贴图功能,

当然,这些函数接口仍然是未公开的。呵呵~

另外,在native app程序,可以利用一个函数向屏幕打印输出字符串,名为:

但是,事实上,这个函数已经不推荐使用了,

在用WDK编译native app的时候,会提示一个警告信息,deprecated~~

Native app的灵活性

native app由于受到调用函数接口未公开,以及开发难度和调试难度不小的原因,很少有项目问津,

但是,事实上,它仍然是一种工作于ring3的用户态程序,只是在子系统启动之前运行,

所以,native app程序一般不会引起系统蓝屏的问题。

也正是如此,native 程序工作在一个相对纯净的环境下,可以访问任何文件,甚至搬移MFT,系统元文件等。

Ntdll.dll为native app导出的函数虽少,但是可以完成很多的功能。

这些导出函数,基本上和内核的系统例程都是一一对应的。结合其他的模块,driver,应用程app,

在加之native独特的运行环境和执行能力,往往会达到一个良好的效果。在这里,仅仅是一个普及~

具体应用及实例

在开发native app之前,我想,最好有开发驱动的基础。因为native app程序的编写规范基本上和驱动一样,

除了入口函数,调试方法也一样,必须要双机调试。当然,只是说编写规则一样,驱动特有的函数例程以及工作原理,

二者是毫不相干的。举个例子,操作注册表,文件IO,线程创建,内存使用,二者基本上是一致的,具体的在使用中自己体会吧。

作为入门例子

作为入门我想还是用Mark Russinovich的例子吧,就好像Hello World的作用一样,

让你真切的感受下native app程序。该程序在系统启动时,蓝屏界面上输出一行字符串信息。

程序在附件,没什么过多的需要解释的。

实际开发

实际开发中,有不少的应用例子。比如,影子系统的启动界面,PQ分区工具,

win系统自带的chkdsk工具,都属于这类程序。下面说下自己的一些经历吧:

项目中有个需要,对特定文件进行磁盘整理。我们知道,文件系统导出了一组函数接口,

用于对磁盘上的文件进行搬移操作,使文件内碎片和外碎片减少,提高IO吞吐率和磁盘访问率。

唯一的限制是,pagefile.sys和日志文件不能整理,其他的文件,如MFT,系统元文件都可以整理。

对于文件整理,更重要的是算法和稳定法,当然,这是另外一个话题了。

根据prefreth原理,一个应用程序的工作集页面在运行时基本上是趋于稳定的,

那么这些稳定的页面如果位于不同的文件(可能是链接库之类),且这些文件在磁盘上比较分散,

那么就会影响程序的启动时间了,虽然prefretch做了改善,会自动的激发系统的磁盘整理来对“相关”的文件紧密排放,

但仍然是不够的。所以,关于这种性能的改善看起来微乎其微,但是做好了,价值是不可估量的。

这仅仅是一个方面,当然,项目中的主要目的并不是这个,虽然也是为了提高性能。

在native app下操作文件,考虑的情况是比较单一的,不会担心文件或目录被锁,

从而出现不能访问的情况,也不会考虑过多的并发问题。

所以,功能会更加集中,运行效率会更高,操作的空间和权限也更大。

仅限于此,就到这吧 ~

7. 小结:

上面的文字呢就大体的把 Native Application 给介绍的差不多了,

主要是总结了一下自己关于 Native Application 的理解,

然后再将 MJ0011 和互联网上的几位博主的文章给整理了一下(原博文排版不怎么好),

这里对 MJ0011 以及所整理的文章的博主表示感谢 ~

博文中的 Demo 我是采用的 DDK 2600.1106 编译的,至于 WDK 7 的话,我没有去试哦 ~

有兴趣的可以直接下载了 Demo 进行测试的,代码什么的都在里面,

source 和 makefile 我也都整理好了放在 Demo 中 ~

最后还是和以往一样,给出些我近来的一些疑问:

我曾经从一台电脑 A 上下载了卡巴斯基的试用版,然后我将这个试用版的安装文件拷贝到了电脑 B 上,

这时我发现卡巴斯基的这个安装文件在电脑 B 上是无法运行的 ~ 直接报错,

当然,这个安装文件在电脑 A 上是可以运行安装的,

我想应该是卡巴斯基的这个安装文件在拷贝的过程中还是下载的过程中记录下了我的电脑 A,

然后在安装的时候,其自动判断是否是在电脑 A 上安装运行,

如果是的话,则可以成功安装,如果不是,则直接报错 ~

我想知道上面是怎么来实现的呢 ? 我只是下载和拷贝了一下哦 ~

还有就是貌似要考个微软的 MCTS 证书,

应该是 Windows 方向的 71-511 或者 Web 方向的 71-515 这两门考试,

不晓得大伙有什么好建议或者好的资料没有哦 ~ 可否提供一些呢 ~ 谢谢咯 ~

然后再问一下哦,有没有湖南娄底的啊 ? 国庆组队回去否 ?(我在深圳 ~)

下载 Demo Source Code

版权所有,欢迎转载,但转载请注明: 转载自 Zachary.XiaoZhen – 梦想的天空

http://www.cnblogs.com/BoyXiao/archive/2011/09/21/2183059.html

Leave a Reply

Your email address will not be published. Required fields are marked *