首页
社区
课程
招聘
[原创] cmd.exe 逆向分析
发表于: 2008-5-24 21:14 14813

[原创] cmd.exe 逆向分析

2008-5-24 21:14
14813
作 者: hjjdebug
时 间: 2008-05-24, 21:14z

cmd.exe 在我们卧榻之旁,我们太熟视无睹了。忽然想逆向一下cmd.exe,为了搞清二个问题。

1. cmd.exe 是怎样加载其它进程的。
2. 其它进程的命令行参数是怎样被传递的。
分析完毕,有了四个小小的收获,与大家分享。
菜鸟文章,一目了然,高手飘过。

好,现在开始。
1. ollydbg 加载cmd.exe
  后面一个空的控制台窗口已经创建了。这说明windows 发现cmd.exe 是一个控制台程序。
  就提前为它准备好了一个控制台窗口。
2. f9 运行。
  控制台窗口出现如下提示:
  Microsoft Windows XP [Version 5.1.2600]
  (C) Copyright 1985-2001 Microsoft Corp.
  
  C:\WINDOWS\system32>  
   正等待我们输入命令。
   
3. f12 中断程序运行。我们想看看cmd 会中断在什么地方。
    按ALT-K 查看堆栈。如下:
    调用堆栈:    主线程
地址       堆栈       函数过程 / 参数                       调用来自                      结构
0013FC4C   7C92E3ED   包含ntdll.KiFastSystemCallRet           ntdll.7C92E3EB                0013FC6C
0013FC50   7C9332F8   ntdll.ZwRequestWaitReplyPort          ntdll.7C9332F3                0013FC6C
0013FC70   7C871921   ntdll.CsrClientCallServer             kernel32.7C87191B             0013FC6C
0013FD6C   7C871ABE   ? kernel32.7C871759                   kernel32.7C871AB9             0013FD68
0013FDF8   4AD0BA71   kernel32.ReadConsoleW                 cmd.4AD0BA6B                  0013FDF4
0013FDFC   00000003     hConsole = 00000003
0013FE00   4AD2FAE0     Buffer = cmd.4AD2FAE0
0013FE04   00002000     ToRead = 2000 (8192.)
0013FE08   0013FE84     pRead = 0013FE84
0013FE0C   0013FE34     pReserved = 0013FE34
0013FE60   4AD0BB55   cmd.4AD0B9C4                          cmd.4AD0BB50                  0013FE5C
0013FE8C   4AD021DE   cmd.4AD021EB                          cmd.4AD021D9                  0013FE88
0013FE90   4AD020FA   cmd.4AD021AA                          cmd.4AD020F5                  0013FEA8
0013FEAC   4AD02073   cmd.4AD020B7                          cmd.4AD0206E                  0013FEA8
0013FEBC   4AD02021   cmd.4AD02053                          cmd.4AD0201C                  0013FEB8
0013FECC   4AD01FAC   cmd.4AD01FED                          cmd.4AD01FA7                  0013FEC8
0013FEE0   4AD0BB7E   cmd.4AD01F66                          cmd.4AD0BB79                  0013FEDC
0013FF48   4AD05164   ? cmd.4AD03FF1                        cmd.4AD0515F                  0013FF44
   
  注意用户领空cmd.4ad0ba6b. 
  
4. 在4ad0ba6b 设置断点,CTRL-F2 重新加载程序,F9 执行,程序中断与此处。
注意以下内容:
4AD0BA62   .  50            PUSH EAX                                 ; /pReserved
4AD0BA63   .  53            PUSH EBX                                 ; |pRead
4AD0BA64   .  FF75 10       PUSH DWORD PTR SS:[EBP+10]               ; |ToRead
4AD0BA67   .  56            PUSH ESI                                 ; |Buffer
4AD0BA68   .  FF75 08       PUSH DWORD PTR SS:[EBP+8]                ; |hConsole
4AD0BA6B   .  FF15 9412D04A CALL DWORD PTR DS:[<&KERNEL32.ReadConsol>; \ReadConsoleW
及传来的参数
0013FDFC   00000003  |hConsole = 00000003
0013FE00   4AD2FAE0  |Buffer = cmd.4AD2FAE0
0013FE04   00002000  |ToRead = 2000 (8192.)
0013FE08   0013FE84  |pRead = 0013FE84
0013FE0C   0013FE34  \pReserved = 0013FE34

我们遇到了第一个关键函数,名称为ReadConsoleW. 参数意图很明显,hConsole 为句柄。
Buffer 用来存储我们控制台输入。 ToRead 最多8K 字节(已经足够大了,平时用不了)。
pRead 应为指向实际输入的字符个数。好,验证一下:

5. 按f8 让程序单步运行,在控制台中输入notepad.exe 1.txt <CR>
查buffer 内容,如下:
4AD2FAE0  6E 00 6F 00 74 00 65 00 70 00 61 00 64 00 2E 00  n.o.t.e.p.a.d...
4AD2FAF0  65 00 78 00 65 00 20 00 31 00 2E 00 74 00 78 00  e.x.e. .1...t.x.
4AD2FB00  74 00 0D 00 0A 00 00 00 00 00 00 00 00 00 00 00  t...............
呵,还是双字节形式。
pRead 处内容为。
0013FE08  84 FE 13 00                                      匎.4
再查0013FE84
0013FE84  13 00 00 00                                      ...
看来pRead 还是一个指针的指针。其所指的长度是unicode 字符的长度。并且包含末尾
的回车0d 00, 换行符0a 00.

到这里,也算没有白来,
这是我们的第一个收获,搞清楚了 ReadConsoleW 函数。继续

6. 观堆栈没有什么有意思的东西,按ctrl-f9一路路返回,突然,在下面地址处
4AD0BBB5   .  E8 BB57FFFF   CALL cmd.4AD01375
跳出notepad.exe 程序,并有一个提示框。
  找不到 1.txt
  要创建吗?

7. 在 4AD0BBB5   处按f2 下断点,重新执行程序,被该处断下,按f7 跟进。
呦! 有没有发现,notepad.exe 还活着呢! 而我们的olldby 已经复位,原来的cmd.exe也已经消亡。
这是我们的第二个收获: cmd.exe 一旦启动完进程,该进程就可以独立运行了
它并不会因为父进程cmd.exe 的消亡而消亡。现在让我们关闭这个旧的notepad.exe
言归正转,还是f7 跟进。我们要找cmd 怎样启动的notepad.
这里我们是逆向,并不知道后面要调什么函数,所以F8,配合F7 再配合f2,ctrl-f2,f9,一步步跟将下来。
我们有失败了再重来的机会,这就是我们的胜算,也是crack 的乐趣。
经过无数次回溯,跟进,终于来到了关键的地方。

4AD03080   .  FF15 F812D04A CALL DWORD PTR DS:[<&USER32.GetProcessWi>; [GetProcessWindowStation
4AD03086   .  8985 7CFFFFFF MOV DWORD PTR SS:[EBP-84],EAX
4AD0308C   .  8D4D 98       LEA ECX,DWORD PTR SS:[EBP-68]
4AD0308F   .  51            PUSH ECX                                 ; /pSizeNeeded
4AD03090   .  53            PUSH EBX                                 ; |BufSize => 0
4AD03091   .  53            PUSH EBX                                 ; |Buffer => NULL
4AD03092   .  6A 02         PUSH 2                                   ; |InfoType = UOI_NAME
4AD03094   .  50            PUSH EAX                                 ; |hObject
4AD03095   .  8B35 EC12D04A MOV ESI,DWORD PTR DS:[<&USER32.GetUserOb>; |USER32.GetUserObjectInformationW
4AD0309B   .  FFD6          CALL ESI                                 ; \GetUserObjectInformationW
4AD0309D   .  FF15 1012D04A CALL DWORD PTR DS:[<&KERNEL32.GetCurrent>; [GetCurrentThreadId
4AD030A3   .  50            PUSH EAX                                 ; /ThreadID
4AD030A4   .  FF15 F012D04A CALL DWORD PTR DS:[<&USER32.GetThreadDes>; \GetThreadDesktop
4AD030AA   .  8985 78FFFFFF MOV DWORD PTR SS:[EBP-88],EAX
4AD030B0   .  8D4D 94       LEA ECX,DWORD PTR SS:[EBP-6C]
4AD030B3   .  51            PUSH ECX                                 ; /pSizeNeeded
4AD030B4   .  53            PUSH EBX                                 ; |BufSize => 0
4AD030B5   .  53            PUSH EBX                                 ; |Buffer => NULL
4AD030B6   .  6A 02         PUSH 2                                   ; |InfoType = UOI_NAME
4AD030B8   .  50            PUSH EAX                                 ; |hObject
4AD030B9   .  FFD6          CALL ESI                                 ; \GetUserObjectInformationW
4AD030BB   .  8B45 98       MOV EAX,DWORD PTR SS:[EBP-68]
4AD030BE   .  8B4D 94       MOV ECX,DWORD PTR SS:[EBP-6C]
4AD030C1   .  8D4408 20     LEA EAX,DWORD PTR DS:[EAX+ECX+20]
4AD030C5   .  50            PUSH EAX                                 ; /HeapSize
4AD030C6   .  6A 08         PUSH 8                                   ; |Flags = HEAP_ZERO_MEMORY
4AD030C8   .  FF15 7812D04A CALL DWORD PTR DS:[<&KERNEL32.GetProcess>; |[GetProcessHeap
4AD030CE   .  50            PUSH EAX                                 ; |hHeap
4AD030CF   .  FF15 7412D04A CALL DWORD PTR DS:[<&KERNEL32.HeapAlloc>>; \HeapAlloc
4AD030D5   .  8945 8C       MOV DWORD PTR SS:[EBP-74],EAX
4AD030D8   .  3BC3          CMP EAX,EBX
4AD030DA   .  74 4D         JE SHORT cmd.4AD03129
4AD030DC   .  8BF8          MOV EDI,EAX
4AD030DE   .  8D4D 98       LEA ECX,DWORD PTR SS:[EBP-68]
4AD030E1   .  51            PUSH ECX                                 ; /pSizeNeeded
4AD030E2   .  FF75 98       PUSH DWORD PTR SS:[EBP-68]               ; |BufSize
4AD030E5   .  50            PUSH EAX                                 ; |Buffer
4AD030E6   .  6A 02         PUSH 2                                   ; |InfoType = UOI_NAME
4AD030E8   .  FFB5 7CFFFFFF PUSH DWORD PTR SS:[EBP-84]               ; |hObject
4AD030EE   .  FFD6          CALL ESI                                 ; \GetUserObjectInformationW

感谢ollydbg 给我们这么好的注释,观其含义,取得顶级线程,堆分配,取得用户对象信息。具体含义我也不
清楚,反正是有用也不算特别有用,继续f8走吧!

不远处,我们的关键对象出现: CreateProcessW, 具体如下:
4AD031BD   .  50            PUSH EAX                                 ; /pProcessInfo = 0013FB80
4AD031BE   .  8D85 18FFFFFF LEA EAX,DWORD PTR SS:[EBP-E8]            ; |
4AD031C4   .  50            PUSH EAX                                 ; |pStartupInfo
4AD031C5   .  BE 0044D34A   MOV ESI,cmd.4AD34400                     ; |UNICODE "C:\WINDOWS\system32"
4AD031CA   .  56            PUSH ESI                                 ; |CurrentDir => "C:\WINDOWS\system32"
4AD031CB   .  53            PUSH EBX                                 ; |pEnvironment
4AD031CC   .  53            PUSH EBX                                 ; |CreationFlags
4AD031CD   .  6A 01         PUSH 1                                   ; |InheritHandles = TRUE
4AD031CF   .  53            PUSH EBX                                 ; |pThreadSecurity
4AD031D0   .  53            PUSH EBX                                 ; |pProcessSecurity
4AD031D1   .  FF75 80       PUSH DWORD PTR SS:[EBP-80]               ; |CommandLine
4AD031D4   .  FF75 9C       PUSH DWORD PTR SS:[EBP-64]               ; |ModuleFileName
4AD031D7   .  FF15 1412D04A CALL DWORD PTR DS:[<&KERNEL32.CreateProc>; \CreateProcessW

堆栈内容:
0013FAC8   00158328  |ModuleFileName = "C:\WINDOWS\system32\notepad.exe"
0013FACC   00157328  |CommandLine = "notepad.exe 1.txt"
0013FAD0   00000000  |pProcessSecurity = NULL
0013FAD4   00000000  |pThreadSecurity = NULL
0013FAD8   00000001  |InheritHandles = TRUE
0013FADC   00000000  |CreationFlags = 0
0013FAE0   00000000  |pEnvironment = NULL
0013FAE4   4AD34400  |CurrentDir = "C:\WINDOWS\system32"
0013FAE8   0013FB38  |pStartupInfo = 0013FB38
0013FAEC   0013FB80  \pProcessInfo = 0013FB80

注释的太好了,都不用我解释了。
在4AD031D7处按一下F8, notepad 被立即启动,并给出了一个找不到1.txt 的提示。
这是我们的第三个收获:
cmd.exe 用CreateProcessW 来创建新进程。
还有第四个收获。
notepad.exe 的1.txt 命令行参数不是cmd.exe 解释的,而是传给CreateProcess 来处理。

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (22)
雪    币: 451
活跃值: (78)
能力值: ( LV12,RANK:470 )
在线值:
发帖
回帖
粉丝
2
抢下一个沙发咯
2008-5-24 21:40
0
雪    币: 236
活跃值: (16)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
3
马上停电了。占个位子,明天一定来看完。
2008-5-24 23:50
0
雪    币: 424
活跃值: (3353)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
4
好!谢谢分享。
2008-5-25 09:03
0
雪    币: 224
活跃值: (147)
能力值: ( LV9,RANK:970 )
在线值:
发帖
回帖
粉丝
5
上面的东西不用逆也知道....
2008-5-25 09:25
0
雪    币: 107
活跃值: (12)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
6
上面的东西不用逆也知道....

那您一定是通过别的途径学到的,而这里的逆向,我认为是一种很好的方法,
而且无师自通,鼓励喜欢实践者用之。
2008-5-25 14:50
0
雪    币: 208
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
顶,我喜欢这样子的文章.支持你~
2008-5-25 16:57
0
雪    币: 1324
活跃值: (5179)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
8
谢谢分享。
我也喜欢这样子的文章.支持你~
2008-5-25 21:13
0
雪    币: 228
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
版主应该给这篇加精华
2008-5-25 22:13
0
雪    币: 2067
活跃值: (82)
能力值: ( LV9,RANK:180 )
在线值:
发帖
回帖
粉丝
10


12345
2008-5-26 08:54
0
雪    币: 357
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
呵呵,很有意思,当时怎么没想到去看看CMD呢
2008-5-26 09:52
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
这样风格的文章我喜欢。
2008-5-26 16:16
0
雪    币: 331
活跃值: (57)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
13
CreateProcessW是铁定的,
改天你去试试explorer.exe,(好像OD会挂调,是线程太多了吧/),也是CreateProcessW
2008-5-26 17:34
0
雪    币: 75
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
不管是Ring3 还是Ring0 ,最后都是CPU完成 !哦也
2008-5-27 01:26
0
雪    币: 107
活跃值: (12)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
15
最初由 better 发布,改天你去试试explorer.exe,(好像OD会挂调,是线程太多了吧/),也是CreateProcessW

首先感谢各位朋友的支持,你们的支持是我动力的源泉。
然后回答上面问题:
explore.exe 是系统关键进程,用ollydbg 调试挂起explore.相当于你中断了它的正常功能。
使原explore 的功能受到影响。
调试explore,调试全屏幕程序,调试内核等可以采用双机调试。windows系统下可以用windbg
可惜ollydbg 不支持双机调试,真希望它后续的版本中能加上。
windbg 环境建立我有一篇帖子,搜一下“windbg 用户态双机调试”可找到。只可惜我只是
建立了调试环境,windbg进一步的使用却没有时间进一步研究. 双机调试的经验我是从linux gdb 获得的。
所以才斗胆说上面的话。
2008-5-27 08:43
0
雪    币: 1137
活跃值: (10)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
16
一定会好好看看
   先谢了
2008-5-27 19:02
0
雪    币: 206
活跃值: (53)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
不知cmd.exe 里面怎么复制数据的呢?好像都没有用到有关剪帖板函数如OpenClipboard 等.
2008-5-28 10:04
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
好到是好,有什么用
2008-6-8 23:27
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
这有什么好的
2008-6-8 23:27
0
雪    币: 230
活跃值: (106)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
不错,学习!
2008-6-9 11:28
0
雪    币: 14
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
学习了 感谢作者~
2020-2-11 17:54
0
雪    币: 146
活跃值: (100)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
好好学习
2020-2-14 16:52
0
雪    币: 16009
活跃值: (3411)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
写的很棒,
跟帖.
2020-2-14 17:26
0
游客
登录 | 注册 方可回帖
返回
//