native app开发小结

Native app 程序开发
      By microDebug
程序类型

关于程序级别的分类,大概可以分为三层:
应用层程序,即普通的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,形如:
void NtProcessStartup( PSTARTUP_ARGUMENT Argument )
其中,参数PSTARTUP_ARGUMENT是一个结构体,用来存放传入参数。
程序退出时,主动调用函数NtProcessTerminate退出,它不会像普通应用程序一样一个返回return就退出了。
基本的结构就是这样了:
void NtProcessStartup( PSTARTUP_ARGUMENT Argument )
{
  // do something else 
  NtProcessTerminate( NtCurrentProcess(), 0 );
}
当然现在还没有包含头文件之类的。
开发语言及第三方的库
  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的重载。 
使用堆函数过程如下:首先创建一个全局堆,然后在这个全局堆上分配和释放局部堆。
HANDLE hGlobalHeap = NULL; // for globle call 
void* __cdecl operator new(size_t size)
{
  if(hGlobalHeap == NULL) 
    return NULL;
  return RtlAllocateHeap(hGlobalHeap,0/*HEAP_ZERO_MEMORY*/,size); 
}
void __cdecl operator delete(void* addr)
{
  if(hGlobalHeap && addr) (void)RtlFreeHeap(hGlobalHeap,0,addr);
}
  
  关于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模板,如:
TARGETNAME=defrag
TARGETPATH=obj
TARGETTYPE=PROGRAM
INCLUDES=$(PUBLIC_ROOT)\inc\ddk
SOURCES=defrag.cpp
  注意:上面说的DLL的编译环境和native app的编译环境要一致,指的是不要用WDK编译的native去尝试链接VS编译的DLL,反之亦然。

Native GUI
在native app执行环境下画界面是不可行的,但是不是说做不到。
前面说了,可以写一个native app来接管windows的启动界面和密码输入界面,那么这个界面是如何画的呢?也有从驱动里实现的。但是,事实上,MS提供了一个native级别的动态库,名为:Bootvid.dll,用来实现GUI启动屏幕的引导视频驱动,这个dll导出了一些函数,可以实现画图,贴图功能,当然,这些函数接口仍然是未公开的。呵呵~
另外,在native app程序,可以利用一个函数向屏幕打印输出字符串,名为:
  //NTSTATUS
  //NTAPI NtDisplayString( PUNICODE_STRING String);
但是,事实上,这个函数已经不推荐使用了,在用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下操作文件,考虑的情况是比较单一的,不会担心文件或目录被锁,从而出现不能访问的情况,也不会考虑过多的并发问题。所以,功能会更加集中,运行效率会更高,操作的空间和权限也更大。
仅限于此,就到这吧~~
              By microdebug



可参考:http://hi.baidu.com/316526334/blog

你可能感兴趣的:(native app开发小结)