昨天无意中看到了有人翻出microDebug去年写的关于Natvie App的贴子,该帖子引用了Mark Russinovich的文章及代码,后来在网上搜了一下,讲NATIVE APP的基本都引是Mark Russinovich的代码,我相信microDebug一定有更好的东西,只是不便写出来,我就来补充一下,其它请参考虑microDebug原贴:http://bbs.pediy.com/showthread.php?t=117694。

Mark Russinovich的这个代码已经是老掉牙了,它已不适用于XP以后的系统了,这样说有两个原因:
1.NtProcessStartup的原型已发生变化,目前的原型为:
    void NtProcessStartup(struct _PEB* Peb);
2.XP/VISTA/WIN7的DDK中还是为NATIVE APP提供了点支持的,DDK中,有一个名为NT.LIB的库文件,此库提供NATIVE APP的最基本的启动代码,然后调用main函数,main函数的原型如下:
int main(int argc, char* argv[], char* envp[], unsigned long DebugParam);
基于此库,最简单的native程序为:
int main()
{
  return 0;
}
从VISTA开始,main函数也有unicode版本,即:
int wmain(int argc, wchar* argv[], wchar_t* envp[], unsigned long DebugParam);
如果要使用unicode版本,则程序入口要指定为:NtProcessStartupW。

  • 标 题:答复
  • 作 者:microdebug
  • 时 间:2011-03-12 12:26:55

1,NtProcessStartup的原型可能发生了改变,但并没有什么关系。
任何类型的程序入口,都是由编译器在编译阶段识别并处理的;只要编译器支持,这个函数的入口可以随便写;

2,以前的NATIVE APP程序范式,现在仍然可以运行在WIN7 32/64位系统,版本更低的系统当然也全部支持了;

  • 标 题:答复
  • 作 者:半道出家
  • 时 间:2011-03-12 13:33:48

引用:
最初由 microdebug发布 查看帖子
1,NtProcessStartup的原型可能发生了改变,但并没有什么关系。
任何类型的程序入口,都是由编译器在编译阶段识别并处理的;只要编译器支持,这个函数的入口可以随便写;

2,以前的NATIVE APP程序范式,现在仍然可以运行在WIN7 32/64位系统,版本更低的系统当然也全部支持了;
接着讨论

1.程序范式中下面这句话是很诡异,
    commandLine = &Argument->Environment->CommandLine;
我原以为它是不会有结果的,但是结果是确实是正确的,应该是微软为了兼容性而特意做的。
只是如果参数正确的话,可以得到更多的信息,如可以使用PEB中的堆,而不用自己创建。

2.NT.LIB也是微软自己用的,你看一下autochk.exe就知道了。

  • 标 题:答复
  • 作 者:半道出家
  • 时 间:2011-03-15 23:17:55

接着研究,不是为了对错,而是想弄清楚到底是怎么回事,而且不想误导看帖子的朋友们。
我最初以为下面这条语句不会得到需要的结果,而且很可能会异常:

引用:
 commandLine = &Argument->Environment->CommandLine;
之所以这样说,是因为XP及以后的系统,NT NATIVE程序的入口函数原型是:
  void __stdcall NtProcessStartup(struct _PEB* Peb);
XP上_PEB的定义如下:
引用:
struct _PEB{
  unsigned char InheritedAddressSpace;          //0x0,0x1
  unsigned char ReadImageFileExecOptions;          //0x1,0x1
  unsigned char BeingDebugged;              //0x2,0x1
  unsigned char SpareBool;                //0x3,0x1
  void* Mutant;                      //0x4,0x4
  void* ImageBaseAddress;                  //0x8,0x4
  struct _PEB_LDR_DATA* Ldr;                //0xc,0x4
  struct _RTL_USER_PROCESS_PARAMETERS* ProcessParameters;  //0x10,0x4
  void* SubSystemData;                  //0x14,0x4
  void* ProcessHeap;                    //0x18,0x4
  struct _RTL_CRITICAL_SECTION* FastPebLock;        //0x1c,0x4
  void* FastPebLockRoutine;                //0x20,0x4
  void* FastPebUnlockRoutine;                //0x24,0x4
  unsigned long EnvironmentUpdateCount;          //0x28,0x4
  void* KernelCallbackTable;                //0x2c,0x4
  unsigned long SystemReserved[0x1];            //0x30,0x4
......后略
};
Argument->Environment->CommandLine取到的会是什么呢?而根据源程序中native.h定义,Environment在Argument中的偏移是0xc, CommmandLine在Environment中的偏移为0x54。对应到_PEB,_PEB偏移0xC处是struct _PEB_LDR_DATA指针Ldr,因此Argument->Environment实际上是Peb->Ldr; Ldr偏移0x54处是什么?这要看struct _PEB_LDR_DATA的定义了,XP上它定义如下:
引用:
struct _PEB_LDR_DATA{
  unsigned long Length;                            //0x0,0x4
  unsigned char Initialized;                          //0x4,0x1
  char _pad_5[0x3];
  void* SsHandle;                                //0x8,0x4
  struct _LIST_ENTRY InLoadOrderModuleList;                  //0xc,0x8
  struct _LIST_ENTRY InMemoryOrderModuleList;                  //0x14,0x8
  struct _LIST_ENTRY InInitializationOrderModuleList;              //0x1c,0x8
  void* EntryInProgress;                            //0x24,0x4
};
可以看到,此结构大小仅为0x28, 偏移0x54在结构定义之外了,因此我写这个帖子时认为:
    commandLine = &Argument->Environment->CommandLine
不会得到正确的结果,甚至很可能异常。但2楼回答让我有些意外,于是实际执行了native程序,确实可以显示参数,说明程序得到了执行,且结果是正确的,至此我以为这是微软故意为兼容而设的。
    但总觉得此事诡异,今天闲来无事,搭个调试环境,实际跟踪一下,有了如下发现:
    &Argument->Environment->CommandLine得到是并不是CommandLine,且看下面WinDbg输出:
引用:
kd> g
Breakpoint 0 hit
native+0x121b:
001b:0100121b 8bff            mov     edi,edi
kd> u
native+0x121b:
001b:0100121b 8bff            mov     edi,edi
001b:0100121d 55              push    ebp
001b:0100121e 8bec            mov     ebp,esp
001b:01001220 e8a2000000      call    native+0x12c7 (010012c7)
001b:01001225 5d              pop     ebp
001b:01001226 e905ffffff      jmp     native+0x1130 (01001130)
001b:0100122b cc              int     3
001b:0100122c cc              int     3
WinDbg断在autochk的入口处,看栈:
引用:
kd> dd esp
0006fff8  00000000 7ffda000 000000c8 0000010d
00070008  eeffeeff 00000002 00000000 0000fe00
......
Argument/Peb=0x7ffda000
引用:
kd> d 7ffda000
7ffda000  00000000 ffffffff 01000000 00171e90
7ffda010  00020000 00000000 00070000 7c99d600
Argument->Environment/Peb->Ldr=0x171e90
引用:
kd> d 171e90
00171e90  00000028 00170101 00000000 00171ec0
00171ea0  00171f18 00171ec8 00171f20 00171f28
00171eb0  00171f28 00000000 0006000b 000801da
00171ec0  00171f18 00171e9c 00171f20 00171ea4
00171ed0  00000000 00000000 01000000 0100121b
00171ee0  00004000 00460044 00020534 00160014
00171ef0  00020564 00005000 0000ffff 00171f54
00171f00  7c99b2c8 4c15a67c 00000000 00000000
Argument->Environment->CommandLine/Ldr偏移0x54处的UNICODE_STRING长度为0x44,指向的字符串为:
引用:
kd> db 20534
00020534  5c 00 3f 00 3f 00 5c 00-43 00 3a 00 5c 00 57 00  \.?.?.\.C.:.\.W.
00020544  49 00 4e 00 44 00 4f 00-57 00 53 00 5c 00 73 00  I.N.D.O.W.S.\.s.
00020554  79 00 73 00 74 00 65 00-6d 00 33 00 32 00 5c 00  y.s.t.e.m.3.2.\.
00020564  6e 00 61 00 74 00 69 00-76 00 65 00 2e 00 65 00  n.a.t.i.v.e...e.
00020574  78 00 65 00 00 00 00 00-6e 00 61 00 74 00 69 00  x.e.....n.a.t.i.
00020584  76 00 65 00 20 00 74 00-65 00 73 00 74 00 00 00  v.e. .t.e.s.t...
00020594  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
000205a4  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
显然,20534指向的并不是CommandLine,它只是程序的路径,紧邻的下一个字符才是命令行。
那native又是如何得到结果的呢?且看native.cpp:
引用:
   ...
    commandLine = &Argument->Environment->CommandLine;
  
    //
    // Locate the argument
    //
    argPtr = commandLine->Buffer;
    while( *argPtr != L' ' ) argPtr++;
    argPtr++;
...    
关键在于while循环上,它并没有检测字符串结束,而是检测空格,从而从20534越到了相邻的下一个字符串,而下一个正好是命令行,因此最终argPtr指向了参数。
如果执行native不加参数执行,则因为找不到空格而导致该程序异常终止。

用Peb如何得到令行参数呢,也很简单,如下:
    Peb->ProcessParameters->CommandLine
ProcessParameters结构内偏移0x10,CommandLine结构内偏移0x40,看一下内存:
引用:
kd> d 7ffda000
7ffda000  00000000 ffffffff 01000000 00171e90
7ffda010  00020000 00000000 00070000 7c99d600
引用:
kd> dd 20000
00020000  00001000 000005a4 00020001 00000000
00020010  00000000 00000000 00000000 00000000
00020020  00000000 02080028 00020290 00000008
00020030  009a0096 00020498 00460044 00020534
00020040  00180016 0002057c 00010000 00000000
引用:
kd> db 2057c
0002057c  6e 00 61 00 74 00 69 00-76 00 65 00 20 00 74 00  n.a.t.i.v.e. .t.
0002058c  65 00 73 00 74 00 00 00-00 00 00 00 00 00 00 00  e.s.t...........
0002059c  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
由于_PEB不同版本不同,可能存在兼容性问题,不过就取CommandLine而言,目前还没有问题,32位的Win7的_PEB定义如下:
引用:
struct _PEB{
  unsigned char InheritedAddressSpace;          //0x0,0x1
  unsigned char ReadImageFileExecOptions;          //0x1,0x1
  unsigned char BeingDebugged;              //0x2,0x1
  union{
    unsigned char BitField;                //0x3,0x1
    struct{
      unsigned char ImageUsesLargePages:0x1;      //0x3:0x0
      unsigned char IsProtectedProcess:0x1;      //0x3:0x1
      unsigned char IsLegacyProcess:0x1;        //0x3:0x2
      unsigned char IsImageDynamicallyRelocated:0x1;  //0x3:0x3
      unsigned char SkipPatchingUser32Forwarders:0x1;  //0x3:0x4
      unsigned char SpareBits:0x3;          //0x3:0x5
    };
  };
  void* Mutant;                      //0x4,0x4
  void* ImageBaseAddress;                  //0x8,0x4
  struct _PEB_LDR_DATA* Ldr;                //0xc,0x4
  struct _RTL_USER_PROCESS_PARAMETERS* ProcessParameters;  //0x10,0x4
  void* SubSystemData;                  //0x14,0x4
  void* ProcessHeap;                    //0x18,0x4
  struct _RTL_CRITICAL_SECTION* FastPebLock;        //0x1c,0x4
  void* AtlThunkSListPtr;                  //0x20,0x4
  void* IFEOKey;                      //0x24,0x4
......后略
};
相应的_RTL_USER_PROCESS_PARAMETERS定义如下:
XP版
引用:
struct _RTL_USER_PROCESS_PARAMETERS{
  unsigned long MaximumLength;          //0x0,0x4
  unsigned long Length;              //0x4,0x4
  unsigned long Flags;              //0x8,0x4
  unsigned long DebugFlags;            //0xc,0x4
  void* ConsoleHandle;              //0x10,0x4
  unsigned long ConsoleFlags;            //0x14,0x4
  void* StandardInput;              //0x18,0x4
  void* StandardOutput;              //0x1c,0x4
  void* StandardError;              //0x20,0x4
  struct _CURDIR CurrentDirectory;        //0x24,0xc
  struct _UNICODE_STRING DllPath;          //0x30,0x8
  struct _UNICODE_STRING ImagePathName;      //0x38,0x8
  struct _UNICODE_STRING CommandLine;        //0x40,0x8
  void* Environment;                //0x48,0x4
  unsigned long StartingX;            //0x4c,0x4
  unsigned long StartingY;            //0x50,0x4
  unsigned long CountX;              //0x54,0x4
  unsigned long CountY;              //0x58,0x4
  unsigned long CountCharsX;            //0x5c,0x4
  unsigned long CountCharsY;            //0x60,0x4
  unsigned long FillAttribute;          //0x64,0x4
  unsigned long WindowFlags;            //0x68,0x4
  unsigned long ShowWindowFlags;          //0x6c,0x4
  struct _UNICODE_STRING WindowTitle;        //0x70,0x8
  struct _UNICODE_STRING DesktopInfo;        //0x78,0x8
  struct _UNICODE_STRING ShellInfo;        //0x80,0x8
  struct _UNICODE_STRING RuntimeData;        //0x88,0x8
  struct _RTL_DRIVE_LETTER_CURDIR CurrentDirectores[0x20];//0x90,0x200
};
Win7版
引用:
struct _RTL_USER_PROCESS_PARAMETERS{
  unsigned long MaximumLength;      //0x0,0x4
  unsigned long Length;          //0x4,0x4
  unsigned long Flags;          //0x8,0x4
  unsigned long DebugFlags;        //0xc,0x4
  void* ConsoleHandle;          //0x10,0x4
  unsigned long ConsoleFlags;        //0x14,0x4
  void* StandardInput;          //0x18,0x4
  void* StandardOutput;          //0x1c,0x4
  void* StandardError;          //0x20,0x4
  struct _CURDIR CurrentDirectory;    //0x24,0xc
  struct _UNICODE_STRING DllPath;      //0x30,0x8
  struct _UNICODE_STRING ImagePathName;  //0x38,0x8
  struct _UNICODE_STRING CommandLine;    //0x40,0x8
  void* Environment;            //0x48,0x4
  unsigned long StartingX;        //0x4c,0x4
  unsigned long StartingY;        //0x50,0x4
  unsigned long CountX;          //0x54,0x4
  unsigned long CountY;          //0x58,0x4
  unsigned long CountCharsX;        //0x5c,0x4
  unsigned long CountCharsY;        //0x60,0x4
  unsigned long FillAttribute;      //0x64,0x4
  unsigned long WindowFlags;        //0x68,0x4
  unsigned long ShowWindowFlags;      //0x6c,0x4
  struct _UNICODE_STRING WindowTitle;    //0x70,0x8
  struct _UNICODE_STRING DesktopInfo;    //0x78,0x8
  struct _UNICODE_STRING ShellInfo;    //0x80,0x8
  struct _UNICODE_STRING RuntimeData;    //0x88,0x8
  struct _RTL_DRIVE_LETTER_CURDIR CurrentDirectores[0x20];//0x90,0x200
  volatile unsigned long EnvironmentSize;  //0x290,0x4
  volatile unsigned long EnvironmentVersion;//0x294,0x4
};
可以看到Peb->ProcessParameters->CommandLine是稳定的。
你在程序还可以引用_PEB中的其它项(比如可以引用_PEB中的堆,而不需要自己创建一个),只是要小心兼容性问题。