能力值:
( LV9,RANK:280 )
|
-
-
2 楼
1,NtProcessStartup的原型可能发生了改变,但并没有什么关系。
任何类型的程序入口,都是由编译器在编译阶段识别并处理的;只要编译器支持,这个函数的入口可以随便写;
2,以前的NATIVE APP程序范式,现在仍然可以运行在WIN7 32/64位系统,版本更低的系统当然也全部支持了;
|
能力值:
( LV3,RANK:20 )
|
-
-
3 楼
接着讨论
1.程序范式中下面这句话是很诡异,
commandLine = &Argument->Environment->CommandLine;
我原以为它是不会有结果的,但是结果是确实是正确的,应该是微软为了兼容性而特意做的。
只是如果参数正确的话,可以得到更多的信息,如可以使用PEB中的堆,而不用自己创建。
2.NT.LIB也是微软自己用的,你看一下autochk.exe就知道了。
|
能力值:
( LV3,RANK:20 )
|
-
-
4 楼
接着研究,不是为了对错,而是想弄清楚到底是怎么回事,而且不想误导看帖子的朋友们。
我最初以为下面这条语句不会得到需要的结果,而且很可能会异常:
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中的堆,而不需要自己创建一个),只是要小心兼容性问题。
|