神奇的记事本

来源:互联网 发布:皇冠娱乐系统源码搭建 编辑:程序博客网 时间:2024/05/22 03:41

最初发表在我的QQ空间,见:http://user.qzone.qq.com/31731705/blog/1317393693

记事本是Windows系统上的老程序了,它的历史几乎和Windows一样久,其实,平凡的它也是一个神奇的程序。在Win7上,将c:\windows\system32\notepad.exe拷贝出来,改个名字,再运行看看?什么都没有!没有记事本的窗口,也没弹出任何错误信息。就和没运行程序一样。

最近很忙,虽然很早就注意到这个现象,不过这几天才开始看。我在自己的笔记本上作了简单的调试,系统是win7 x86 ultimate 中文版本,调试方法是将notepad.exe改名成abc.exe,放到e:\下。

最初的分析挺顺利,失败的原因是下面这个函数失败返回0,使得程序退出了,但并没有报错误窗口。惊讶

USER32!LoadAcceleratorsW( hInstance, "MainACC" ); 

也就是加载某类型(Accelerator)的资源("MainACC")时失败。Windows的资源是独立存放的,会将语言相关的文件都放在MUI文件中,我本来以为继续跟踪下去,能够看到打开这个文件的过程,这样,就能够定位到相关的错误。一开始以为很简单,其实还是挺复杂的。跟了一下,上面这个函数调用了ntdll!LdrpSearchResourceSection_U,后者又调用了ntdll!LdrpLoadResourceFromAlternativeModule,这个时候就跟踪不下去了,因为代码太复杂,跳来跳去,而功力又不够。害羞

看一下大概的栈,WinMain->NPInit->LoadAcceleratorsW...

ChildEBP RetAddr  Args to Child             
0009f9f4 77a017f3 00930000 0009fa20 00000003 ntdll!LdrpLoadResourceFromAlternativeModule(FPO: [Non-Fpo])
0009faa0 77a0e227 00930000 0009fae8 00000003 ntdll!LdrpSearchResourceSection_U+0x62a (FPO: [Non-Fpo])
0009fac0 75cec844 00930000 0009fae8 00000003 ntdll!LdrFindResource_U+0x34 (FPO: [Non-Fpo])
0009fb18 761f9782 00930000 00000009 0093237c KERNELBASE!FindResourceExW+0x70 (FPO: [Non-Fpo])
0009fb30 00931f06 00930000 0093237c 00202602 USER32!LoadAcceleratorsW+0x15 (FPO: [Non-Fpo])
0009fbf4 00931455 00930000 00202616 0000000a notepad!NPInit+0xa0 (FPO: [Non-Fpo])
0009fc30 009316ec 00930000 00000000 00202d22 notepad!WinMain+0x50 (FPO: [Non-Fpo])
0009fcc0 77b3ed6c 7ffdf000 0009fd0c 77a037f5 notepad!_initterm_e+0x1a1 (FPO: [Non-Fpo])
0009fccc 77a037f5 7ffdf000 762b01bc 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
0009fd0c 77a037c8 00933689 7ffdf000 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
0009fd24 00000000 00933689 7ffdf000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])

干脆设置断点CreateFileW,反正总是要打开文件的,不过却没有命中,这个问题确实比想像的复杂。好在还有能够运行的版本,运行一个原来正常的notepad,发现内存中加载了mui文件。

  BaseAddr EndAddr+1 RgnSize     Type       State                 Protect             Usage
-------------------------------------------------------------------------------------------
......
  170000   177000     7000 MEM_MAPPED  MEM_COMMIT  PAGE_WRITECOPY                     MemoryMappedFile "\Device\HarddiskVolume3\Windows\System32\zh-CN\notepad.exe.mui"
  1c0000   227000    67000 MEM_MAPPED  MEM_COMMIT  PAGE_READONLY                      MemoryMappedFile "\Device\HarddiskVolume3\Windows\System32\locale.nls"
  230000   240000    10000 MEM_MAPPED  MEM_COMMIT  PAGE_READONLY                      MemoryMappedFile "PageFile"
......

看来,确实是打开了这个文件,那么到底是何时打开的呢?后来,在断点ntdll!LdrpLoadResourceFromAlternativeModule过程中,发现这个函数在进程启动时就会调用,有没有可能是启动的时候加载MUI文件的?

在初始断点处下断点,这次下得深点,
bp NtCreateFile "r @$t0 = poi(@esp+4+8), @$t1 = poi(@esp+4); .if( @$t0 > 0 ) { dS poi(@$t0+8) }; gu; dd @$t1 l1;"


断点中的命令是打印第3个参数中的ObjectName成员,并且观察函数调用后的返回文件句柄的值,看一下函数原型和相应的数据结构,

NTSTATUS NtCreateFile(  __out     PHANDLE FileHandle,  __in      ACCESS_MASK DesiredAccess,  __in      POBJECT_ATTRIBUTES ObjectAttributes,  __out     PIO_STATUS_BLOCK IoStatusBlock,  __in_opt  PLARGE_INTEGER AllocationSize,  __in      ULONG FileAttributes,  __in      ULONG ShareAccess,  __in      ULONG CreateDisposition,  __in      ULONG CreateOptions,  __in      PVOID EaBuffer,  __in      ULONG EaLength);typedef struct _OBJECT_ATTRIBUTES {  ULONG           Length;  HANDLE          RootDirectory;  PUNICODE_STRING ObjectName;  ULONG           Attributes;  PVOID           SecurityDescriptor;  PVOID           SecurityQualityOfService;}  OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

执行一下,
0:000> g
ModLoad: 76010000 7602f000   C:\Windows\system32\IMM32.DLL
ModLoad: 77230000 772fc000   C:\Windows\system32\MSCTF.dll
00178800  "\??\E:\zh-CN\abc.exe.mui"
0014e284  00000000

eax=c000003a ebx=00000000 ecx=0014e1e4 edx=779e70b4 esi=00178800 edi=00000000
eip=779cee8e esp=0014e218 ebp=0014e220 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!LdrpNtCreateFileUnredirected+0x26:
779cee8e c9              leave


确实是进程启动时试图读取MUI文件,并且读取失败,看看栈,


ChildEBP RetAddr  Args to Child             
0014e220 779ced93 0014e284 0014e240 003e0000 ntdll!LdrpNtCreateFileUnredirected+0x26 (FPO: [Non-Fpo])
0014e288 779ceca1 003e0000 0014e338 00178400 ntdll!LdrpMapResourceFile+0xb4 (FPO: [Non-Fpo])
0014e9dc 77a09470 003e0000 00000804 0014ea18 ntdll!LdrLoadAlternateResourceModuleEx+0x551 (FPO: [Non-Fpo])
0014eac4 77a03e50 003e0000 0014eaf0 00000003 ntdll!LdrpLoadResourceFromAlternativeModule+0x224 (FPO: [Non-Fpo])
0014eb70 77a0c300 003e0000 0014eb4c 00000000 ntdll!LdrpSearchResourceSection_U+0x30f (FPO: [Non-Fpo])
0014eb90 76f214cf 00000008 003e0000 0014ebb4 ntdll!LdrFindResourceEx_U+0x35 (FPO: [Non-Fpo])
0014ebf0 76f2142c 0014ec18 7603a021 00000001 LPK!LpkCheckForMirrorSignature+0x6c (FPO: [Non-Fpo])
0014ebf8 7603a021 00000001 00000000 76f254f3 LPK!LpkInitialize+0x3d (FPO: [Non-Fpo])
0014ec18 76039e36 00000001 00000001 76259240 GDI32!GdiInitializeLanguagePack+0x6d (FPO: [Non-Fpo])
0014ec34 7620d93f 00000000 00000000 00000001 GDI32!GdiProcessSetup+0x14f (FPO: [Non-Fpo])
0014ed6c 7620db45 779e6fee 0014ed88 00000000 USER32!ClientThreadSetup+0x34 (FPO: [Non-Fpo])
0014ed70 779e6fee 0014ed88 00000000 0014f430 USER32!__ClientThreadSetup+0x5 (FPO: [1,0,0])
0014ed84 76039cbf 76039a26 0014eed0 0014f32c ntdll!KiUserCallbackDispatcher+0x2e (FPO: [0,0,0])
0014ed88 76039a26 0014eed0 0014f32c 7620d4ee GDI32!NtGdiInit+0xc (FPO: [0,0,0])
0014ed94 7620d4ee 761f0000 00000001 0014f3fc GDI32!GdiDllInitialize+0x1c (FPO: [Non-Fpo])
0014f32c 779f89d8 761f0000 00000001 0014f618 USER32!_UserClientDllInitialize+0x34b (FPO: [Non-Fpo])
0014f34c 77a05c41 7620d711 761f0000 00000001 ntdll!LdrpCallInitRoutine+0x14
0014f440 77a06175 0014f618 7ffdf000 7ffd9000 ntdll!LdrpRunInitializeRoutines+0x26f (FPO: [Non-Fpo])
0014f5a4 77a06077 0014f618 779a0000 763b46a6 ntdll!LdrpInitializeProcess+0x12da (FPO: [Non-Fpo])
0014f5f4 77a03663 0014f618 779a0000 00000000 ntdll!_LdrpInitialize+0x78 (FPO: [Non-Fpo])
0014f604 00000000 0014f618 779a0000 00000000 ntdll!LdrInitializeThunk+0x10 (FPO: [Non-Fpo])

这时还在进程初始化过程LdrpInitializeProcess中。
大概分析了一下,LdrpLoadResourceFromAlternativeModule函数里有一个循环,使用不同的路径,调用函数LdrpMapResourceFile来依次打开下面的文件,LdrpMapResourceFile会调用到NtCreateFile函数。
"\??\E:\zh-CN\abc.exe.mui"
"\??\E:\zh-Hans\abc.exe.mui"
"\??\E:\zh\abc.exe.mui"
"\??\E:\en-US\abc.exe.mui"
"\??\E:\en\abc.exe.mui"

如果打开失败,继续尝试下一个文件,如果打开成功,函数LdrpMapResourceFile会继续调用ZwCreateSection创建内存映射文件,并且检查文件是否匹配当前的locale。这个流程大概是这样,我只是从调试的结果大概去推理,直接从代码分析难度很大。抓狂

也可以在ZwCreateSection下断点,
bp ZwCreateSection "r @$t0 = poi(@esp+4+8); .if( @$t0 > 0 ) { dS poi(@$t0+8) }"


栈如下:
ChildEBP RetAddr  Args to Child             
0028e528 779cedd7 0028e598 000f0005 00000000 ntdll!ZwCreateSection(FPO: [7,0,0])
0028e5a0 779ceca1 00090000 0028e650 0028e700 ntdll!LdrpMapResourceFile+0xf4 (FPO: [Non-Fpo])
0028ecf4 77a09470 00090000 00007804 0028ed30 ntdll!LdrLoadAlternateResourceModuleEx+0x551 (FPO: [Non-Fpo])
0028eddc 77a03e50 00090000 0028ee08 00000003 ntdll!LdrpLoadResourceFromAlternativeModule+0x224 (FPO: [Non-Fpo])
0028ee88 77a0c300 00090000 0028ee64 00000000 ntdll!LdrpSearchResourceSection_U+0x30f (FPO: [Non-Fpo])
0028eea8 76f214cf 00000008 00090000 0028eecc ntdll!LdrFindResourceEx_U+0x35 (FPO: [Non-Fpo])
0028ef08 76f2142c 0028ef30 7603a021 00000001 LPK!LpkCheckForMirrorSignature+0x6c (FPO: [Non-Fpo])
0028ef10 7603a021 00000001 00000000 76f254f3 LPK!LpkInitialize+0x3d (FPO: [Non-Fpo])
0028ef30 76039e36 00000001 00000001 76259240 GDI32!GdiInitializeLanguagePack+0x6d (FPO: [Non-Fpo])
0028ef4c 7620d93f 00000000 00000000 00000001 GDI32!GdiProcessSetup+0x14f (FPO: [Non-Fpo])
0028f084 7620db45 779e6fee 0028f0a0 00000000 USER32!ClientThreadSetup+0x34 (FPO: [Non-Fpo])
0028f088 779e6fee 0028f0a0 00000000 0028f748 USER32!__ClientThreadSetup+0x5 (FPO: [1,0,0])
0028f09c 76039cbf 76039a26 0028f1e8 0028f644 ntdll!KiUserCallbackDispatcher+0x2e (FPO: [0,0,0])
0028f0a0 76039a26 0028f1e8 0028f644 7620d4ee GDI32!NtGdiInit+0xc (FPO: [0,0,0])
0028f0ac 7620d4ee 761f0000 00000001 0028f714 GDI32!GdiDllInitialize+0x1c (FPO: [Non-Fpo])
0028f644 779f89d8 761f0000 00000001 0028f930 USER32!_UserClientDllInitialize+0x34b (FPO: [Non-Fpo])
0028f664 77a05c41 7620d711 761f0000 00000001 ntdll!LdrpCallInitRoutine+0x14
0028f758 77a06175 0028f930 7ffdf000 7ffdb000 ntdll!LdrpRunInitializeRoutines+0x26f (FPO: [Non-Fpo])
0028f8bc 77a06077 0028f930 779a0000 7644286f ntdll!LdrpInitializeProcess+0x12da (FPO: [Non-Fpo])
0028f90c 77a03663 0028f930 779a0000 00000000 ntdll!_LdrpInitialize+0x78 (FPO: [Non-Fpo])
0028f91c 00000000 0028f930 779a0000 00000000 ntdll!LdrInitializeThunk+0x10 (FPO: [Non-Fpo])

函数的原形如下:

NTSTATUS ZwCreateSection(  __out     PHANDLE SectionHandle,  __in      ACCESS_MASK DesiredAccess,  __in_opt  POBJECT_ATTRIBUTES ObjectAttributes,  __in_opt  PLARGE_INTEGER MaximumSize,  __in      ULONG SectionPageProtection,  __in      ULONG AllocationAttributes,  __in_opt  HANDLE FileHandle);

总的来说,abc.exe(其实就是notepad.exe)启动时会加载与语言相关的资源文件,如果失败了当时也不报错,直到程序中代码运行,并且加载所需要的资源时才会将错误体现出来。另外,notepad也没有实现错误提醒机制,仅仅是简单的退出程序了事。知道了原因,修正起来就简单了,将c:\windows\system32\zh-CN\下相关的文件notepad.exe.mui拷贝过来,改名abc.exe.mui,放在e:\zh-CN下,再次运行,这次OK了。

原创粉丝点击