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 来处理。
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 来处理。