首页
论坛
课程
招聘
半道出家
雪    币: 700
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
5
回帖
206
粉丝
0

[原创]关于NT NATIVE APP

2011-3-12 11:45 5757

[原创]关于NT NATIVE APP

2011-3-12 11:45
5757
昨天无意中看到了有人翻出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。

[公告]看雪论坛2020激励机制上线了!发帖不减雪币了!如何获得积分快速升级?

最新回复 (3)
microdebug
雪    币: 336
活跃值: 活跃值 (10)
能力值: ( LV9,RANK:280 )
在线值:
发帖
23
回帖
212
粉丝
0
microdebug 活跃值 6 2011-3-12 12:26
2
0
1,NtProcessStartup的原型可能发生了改变,但并没有什么关系。
任何类型的程序入口,都是由编译器在编译阶段识别并处理的;只要编译器支持,这个函数的入口可以随便写;

2,以前的NATIVE APP程序范式,现在仍然可以运行在WIN7 32/64位系统,版本更低的系统当然也全部支持了;
半道出家
雪    币: 700
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
5
回帖
206
粉丝
0
半道出家 活跃值 2011-3-12 13:33
3
0
接着讨论

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

2.NT.LIB也是微软自己用的,你看一下autochk.exe就知道了。
半道出家
雪    币: 700
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
5
回帖
206
粉丝
0
半道出家 活跃值 2011-3-15 23:17
4
0
接着研究,不是为了对错,而是想弄清楚到底是怎么回事,而且不想误导看帖子的朋友们。
我最初以为下面这条语句不会得到需要的结果,而且很可能会异常:
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中的堆,而不需要自己创建一个),只是要小心兼容性问题。
游客
登录 | 注册 方可回帖
返回