【文章标题】: 第一次PEDIY---让Windows 任务管理器更强,更人性!
【文章作者】: slore
【作者邮箱】: slorelee@yahoo.com.cn
【软件名称】: taskmgr.exe
【下载地址】: 附件
【使用工具】: PEiD,OD,ImportREC,Restorator
【操作平台】: Windows XP SP3
看了stalker前辈的给任务管理器增加显示程序完整路径功能
链 接:
http://bbs.pediy.com/showthread.php?t=82582
感觉很不错,但是不够好(不是技术,是本身的任务管理器不好。)
我手头一直用的任务管理器本身就有这个功能,而且更人性,更方便。
如图所示:
PIC1.PNG
而且右键有直接打开到所选进程所在目录并选中的功能,很方便。
PIC2.PNG
PEiD 0.95查壳:
PECompact 2.5 Retail -> Jeremy Collake
自己不会脱壳,看到壳就先搜索了。
网上的基本都是BP VirtualFree下断和查找 push 8000的方法。
但是这个程序下断,Alt+F9回不到原程序领空,直接白了...很郁闷,
后来找到了
http://bbs.pediy.com/showthread.php?t=94624
这个单步法还挺简单,成功脱壳修复,但是查壳显示:什么都没找到 *
不过OD载入,好像没有问题。(查看了下微软原版的也是什么都没找到 *)
要添加显示程序的命令行参数的功能。本来不添加这个功能这个任务管理
器已经很不错了。但是不DIY下,怎么好意思发呢~哈哈。
本身程序既然能获取路径,一定已经有这个函数,先看函数列表。
GetCommandLine先想到,后来查了原型,没有参数,应该只能的本身运行程序的参数。
一个个觉得不认识和可能会是下断……苦试了3~4个小时发现NtReadVirtualMemory
这个函数,在每次打开个新程序的时候会停下来(7C8021E5),F8跟着走,ret一层来到
调用ReadProcessMemory的过程,继续F8,看最后的ret的位置。
出来是0100CF52,在点下回车键,进到函数,可以看到:
Local Calls from 0100A739, 0100CF52
我们先回0100CF52去,继续走看下堆栈。
0100CF25 |. FF73 08 PUSH DWORD PTR DS:[EBX+8] ; /ProcessId
0100CF28 |. 6A 00 PUSH 0 ; |Inheritable = FALSE
0100CF2A |. 68 10040000 PUSH 410 ; |Access = VM_READ|QUERY_INFORMATION
0100CF2F |. FF15 14110001 CALL DWORD PTR DS:[<&kernel32.OpenProces>; \OpenProcess
0100CF35 |. 85C0 TEST EAX,EAX
0100CF37 |. 8985 F0FDFFFF MOV DWORD PTR SS:[EBP-210],EAX
0100CF3D |. 0F84 A7000000 JE taskmgr.0100CFEA
0100CF43 |. 68 04010000 PUSH 104 ; /Arg4 = 00000104
0100CF48 |. 8D8D F4FDFFFF LEA ECX,DWORD PTR SS:[EBP-20C] ; |
0100CF4E |. 51 PUSH ECX ; |Arg3
0100CF4F |. 6A 00 PUSH 0 ; |Arg2 = 00000000
0100CF51 |. 50 PUSH EAX ; |Arg1
0100CF52 |. E8 9F560000 CALL taskmgr.010125F6 ; \taskmgr.010125F6
0100CF57 |. 85C0 TEST EAX,EAX ;从上面出来继续F8
0100CF59 |. 74 34 JE SHORT taskmgr.0100CF8F
0100CF5B |. 8D3C00 LEA EDI,DWORD PTR DS:[EAX+EAX]
0100CF5E |. 8D47 02 LEA EAX,DWORD PTR DS:[EDI+2]
0100CF61 |. 50 PUSH EAX ; /Size
0100CF62 |. 6A 40 PUSH 40 ; |Flags = LPTR
0100CF64 |. FF15 38110001 CALL DWORD PTR DS:[<&kernel32.LocalAlloc>; \LocalAlloc
0100CF6A |. 85C0 TEST EAX,EAX
0100CF6C |. 8983 A0000000 MOV DWORD PTR DS:[EBX+A0],EAX
0100CF72 |. 74 1B JE SHORT taskmgr.0100CF8F
0100CF74 |. 8BCF MOV ECX,EDI
0100CF76 |. 8BF8 MOV EDI,EAX
0100CF78 |. 8BC1 MOV EAX,ECX
0100CF7A |. C1E9 02 SHR ECX,2
0100CF7D |. 8DB5 F4FDFFFF LEA ESI,DWORD PTR SS:[EBP-20C] ;堆栈看到这里能显示进程的全路径
0100CF83 |. F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
0100CF85 |. 8BC8 MOV ECX,EAX
0100CF87 |. 83E1 03 AND ECX,3
0100CF8A |. F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
0100CF8C |. 8B75 10 MOV ESI,DWORD PTR SS:[EBP+10]
0100CF8F |> 68 04010000 PUSH 104
0100CF94 |. 8D85 F4FDFFFF LEA EAX,DWORD PTR SS:[EBP-20C]
0100CF9A |. 50 PUSH EAX
0100CF9B |. FFB5 F0FDFFFF PUSH DWORD PTR SS:[EBP-210]
0100CFA1 |. E8 A2560000 CALL taskmgr.01012648 ;这个CALL不知道为什么没注释,
0100CFA6 |. 85C0 TEST EAX,EAX ;通过句柄查找映像路径我们补丁要用到。
0100CFA8 |. 74 34 JE SHORT taskmgr.0100CFDE
0100CFAA |. 8D3C00 LEA EDI,DWORD PTR DS:[EAX+EAX]
0100CFAD |. 8D47 02 LEA EAX,DWORD PTR DS:[EDI+2]
0100CFB0 |. 50 PUSH EAX ; /Size
0100CFB1 |. 6A 40 PUSH 40 ; |Flags = LPTR
0100CFB3 |. FF15 38110001 CALL DWORD PTR DS:[<&kernel32.LocalAlloc>; \LocalAlloc
0100CFB9 |. 85C0 TEST EAX,EAX
0100CFBB |. 8983 9C000000 MOV DWORD PTR DS:[EBX+9C],EAX
0100CFC1 |. 74 1B JE SHORT taskmgr.0100CFDE
0100CFC3 |. 8BCF MOV ECX,EDI
0100CFC5 |. 8BF8 MOV EDI,EAX
0100CFC7 |. 8BC1 MOV EAX,ECX
0100CFC9 |. C1E9 02 SHR ECX,2
0100CFCC |. 8DB5 F4FDFFFF LEA ESI,DWORD PTR SS:[EBP-20C] ;到这里我们看到了带运行参数的字符串!兴奋!终于找到了。
0100CFD2 |. F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
0100CFD4 |. 8BC8 MOV ECX,EAX
0100CFD6 |. 83E1 03 AND ECX,3
0100CFD9 |. F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
0100CFDB |. 8B75 10 MOV ESI,DWORD PTR SS:[EBP+10]
0100CFDE |> FFB5 F0FDFFFF PUSH DWORD PTR SS:[EBP-210] ; /hObject
0100CFE4 |. FF15 5C110001 CALL DWORD PTR DS:[<&kernel32.CloseHandl>; \CloseHandle
我们再看下这个0100A739位置:
发现它在从0100A709开始到0100A756是个函数。
流程:
OpenProcess->CALL taskmgr.010125F6->CloseHandle
这里CALL taskmgr.010125F6我们从前面的那个断点进来知道会得到程序的全路径。
它要干什么呢?我们看下函数入口从谁CALL来。
Local Call from 0100A781
转过来,我们看到又是个比较短的函数(上下滚动下就能看全了),很注意到用了2个
shell32下面函数——
DS:[010011D4]=7D5BE074 (shell32.SHParseDisplayName)
DS:[010011D8]=7D692B76 (shell32.SHOpenFolderAndSelectItems)
SHOpenFolderAndSelectItems:
e文字面意思是打开文件夹选择文件
你想到了什么呢?
正是这个任务管理器一个很方便的功能,右键进程打开进程所在目录。
我们验证下,看下这个函数被谁调用——
Local Call from 0100BE59
转过去看:
是Cases...switch...结构的代码
进这里前的比较语句是:
81F9 CFC30000 CMP ECX,0C3CF
用资源查看器打开程序看菜单如下:
111 MENU
{
POPUP "Banana"
{
MENUITEM "打开所在目录(&O)", -15409
MENUITEM SEPARATOR
MENUITEM "结束进程(&E)", -25508
MENUITEM "结束进程树(&T)", -25431
MENUITEM "调试(&D)", -25509
MENUITEM "创建转储文件(&C)", -25405
MENUITEM SEPARATOR
POPUP "设置优先级(&P)"
{
MENUITEM "实时(&R)", -25507
MENUITEM "高(&H)", -25505
MENUITEM "高于标准(&A)", -25506
MENUITEM "标准(&N)", -25504
MENUITEM "低于标准(&B)", -25503
MENUITEM "低(&L)", -25502
}
MENUITEM "关系设置(&A)...", -25481
}
}
我用的Restorator,显示"打开所在目录(&O)"的ID是-15409
用CALC计算65536-15409再转十六进制正好是C3CF
(上面都静态分析,这里F2下,对进程右键...果然过来了)
运气不错!碰巧找到了我们要打补丁的跳转入口。
既然打开了,就顺手把我们的菜单补上来:
MENUITEM "打开所在目录(&O)", -15409
MENUITEM "映像路径(&I)", -15408
MENUITEM SEPARATOR
这个是菜单-15408(C3D0),没什么要求不要和其他的资源ID重复就行。
补丁入口,如何获取带参数的映像路径,我们的菜单
准备工作做好了,下面开工写补丁代码。
思路:
OpenProcess->CALL 01012648->MessageBoxW->CloseHandle
思路是正确的,我的第一次补丁遇到了许多错误,这里给最终修订好的,
后面会说明你和第一个补丁代码的完善部分。
PEiD 查看全0信息。
---------------------------
节 RVA 偏移 大小
.text 00026BDF 00026BDF 00010421
.rsrc0 00047097 00047097 00000F69
.rsrc 000B264C 000B264C 000009B4
.mackt 000B44BF 000B44BF 00000B41
---------------------------
决定补丁写在.text下面的01026F00处:
补丁代码:
01026F00 > 81F9 D0C30000 CMP ECX,0C3D0 ;判断菜单是不是"映像路径(I)"
01026F06 . 0F85 A3000000 JNZ taskmgr.01026FAF ;不是则转原程序指令处
01026F0C . 60 PUSHAD ;保护现场
01026F0D . 8B40 08 MOV EAX,DWORD PTR DS:[EAX+8] ;进程PID
01026F10 . 83F8 04 CMP EAX,4 ;判断是不是SYSTEM.EXE进程
01026F13 . 0F84 95000000 JE taskmgr.01026FAE ;是则转恢复现场
01026F19 . 50 PUSH EAX ; /ProcessId
01026F1A . 6A 00 PUSH 0 ; |Inheritable = FALSE
01026F1C . 68 10040000 PUSH 410 ; |Access = VM_READ|QUERY_INFORMATION
01026F21 . FF15 14110001 CALL DWORD PTR DS:[<&kernel32.OpenProcess>] ; \OpenProcess
01026F27 . 8985 00FAFFFF MOV DWORD PTR SS:[EBP-600],EAX ;将OpenProcess打开的句柄保存
01026F2D . 83A5 F8F9FFFF >AND DWORD PTR SS:[EBP-608],0 ;将字符串保存地址清零
01026F34 . 85C0 TEST EAX,EAX ;判断EAX是否为0即OpenProcess是否成功
01026F36 . 74 76 JE SHORT taskmgr.01026FAE ;不成功跳到恢复现场
01026F38 . 68 04010000 PUSH 104
01026F3D . 8D85 F4FBFFFF LEA EAX,DWORD PTR SS:[EBP-40C]
01026F43 . 50 PUSH EAX ;传入保存结果地址
01026F44 . FFB5 00FAFFFF PUSH DWORD PTR SS:[EBP-600] ;OpenProcess句柄
01026F4A . E8 F9B6FEFF CALL taskmgr.01012648 ;获取进程映像路径的函数
01026F4F . 85C0 TEST EAX,EAX ;EAX保存查询到的字符串WORD数
01026F51 . 74 31 JE SHORT taskmgr.01026F84 ;没有查询到则转CloseHandle
01026F53 . 8D3C00 LEA EDI,DWORD PTR DS:[EAX+EAX] ;转为字节数保存到EDI
01026F56 . 8D47 02 LEA EAX,DWORD PTR DS:[EDI+2] ;字符串结尾0000需要两个字节
01026F59 . 50 PUSH EAX ; /Size
01026F5A . 6A 40 PUSH 40 ; |Flags = LPTR
01026F5C . FF15 38110001 CALL DWORD PTR DS:[<&kernel32.LocalAlloc>] ; \LocalAlloc
01026F62 . 85C0 TEST EAX,EAX ;判断是否申请成功,成功EAX为地址
01026F64 . 8985 F8F9FFFF MOV DWORD PTR SS:[EBP-608],EAX ;将申请到的空间首地址保存
01026F6A . 74 18 JE SHORT taskmgr.01026F84 ;不成功转OpenProcess
01026F6C . 8BCF MOV ECX,EDI
01026F6E . 8BF8 MOV EDI,EAX ;字符串复制目的地址放入EDI
01026F70 . 8BC1 MOV EAX,ECX ;复制字节数保存在EAX
01026F72 . C1E9 02 SHR ECX,2 ;字节数/4=按DWORD复制的次数
01026F75 . 8DB5 F4FBFFFF LEA ESI,DWORD PTR SS:[EBP-40C] ;字符串复制源地址存入ESI
01026F7B . F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI];执行字符串双字复制
01026F7D . 8BC8 MOV ECX,EAX
01026F7F . 83E1 03 AND ECX,3 ;计算剩余字节数,即按字节复制次数
01026F82 . F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] ;执行字符串字节复制
01026F84 > FFB5 00FAFFFF PUSH DWORD PTR SS:[EBP-600] ; /hObject
01026F8A . FF15 5C110001 CALL DWORD PTR DS:[<&kernel32.CloseHandle>] ; \CloseHandle
01026F90 . 8B85 F8F9FFFF MOV EAX,DWORD PTR SS:[EBP-608] ;读取保存的字符串地址
01026F96 . 85C0 TEST EAX,EAX
01026F98 . 74 14 JE SHORT taskmgr.01026FAE ;没有有效字符串地址则不弹消息框
01026F9A . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
01026F9C . 68 C06F0201 PUSH taskmgr.01026FC0 ; |Title = "映像路径"
01026FA1 . 50 PUSH EAX ; |Text
01026FA2 . FF35 645D0101 PUSH DWORD PTR DS:[1015D64] ; |hOwner = NULL
01026FA8 . FF15 94120001 CALL DWORD PTR DS:[<&user32.MessageBoxW>] ; \MessageBoxW
01026FAE > 61 POPAD ;恢复现场
01026FAF > 81F9 CFC30000 CMP ECX,0C3CF ;跳转处替换的汇编指令
01026FB5 .^E9 974EFEFF JMP taskmgr.0100BE51 ;转到原程序执行处
01026FBA 0000 ADD BYTE PTR DS:[EAX],AL
01026FBC 0000 ADD BYTE PTR DS:[EAX],AL
01026FBE 0000 ADD BYTE PTR DS:[EAX],AL
01026FC0 2066 CF50 ;标题(Unicode)
01026FC4 EF8D 845F
01026FC8 0000
入口:
0100BE4B 81F9 CFC30000 CMP ECX,0C3CF
0100BE51 > 75 42 JNZ SHORT taskmgr.0100BE95
改为:
0100BE4B . E9 B0B00100 JMP taskmgr.01026F00
0100BE50 90 NOP
0100BE51 > 75 42 JNZ SHORT taskmgr.0100BE95
补丁代码完善历史:
1.
将贴出来的第一部分代码整段COPY去,精简只保留OpenProcess,
CALL 01012648,CloseHandle的代码,然后直接CALL 01012648后,
将 DWORD PTR SS:[EBP-20C] PUSH给MessageBoxW
调试看堆栈调用的时候的确有路径信息,但是弹出是乱码。
问题原因:SS是在堆栈中,数据被冲掉了……
解决方法:看到那段用了LocalAlloc申请空间了,就加过来了。
2.
成功显示了,但是新问题出现:发现后面的字符老少几字节。
原因:不知道具体代码作用(生搬),只知道大概代码块作用。
简单说对细节不了解——0100CFD之后就将EDI传给MessageBoxW了
(因为看堆栈是自己想要的数据。)
解决方法:将其后的0100CFD2、0100CFD4执行后再传EDI。
(具体原因看最终代码01026F7D附近的注释)
3.
MessageBoxW开始都是PUSH 0只有TEXT是传查到的字符串地址,
能弹是能弹出来,但是有小问题,标题为"错误",任务管理器本身
有在最前面显示的功能,如果开了总在最上功能,弹出来的消息框
看不到。
原因:参数不合理
解决方法:加参数——标题,和Owner句柄
标题简单,在代码区后面加了几个字节,注意这里要Unicode编码。
Owner,我们要Taskmgr的句柄,自己FindWindows可以,但是麻烦,
编程的时候我们对句柄都是直接调用的,程序应该会保存在固定的
位置。找不到再FindWindows这条路……
在SetWindowsPos下断
01005390 |. FF15 38130001 CALL DWORD PTR DS:[<&user32.SetWindowPos>] ; \SetWindowPos
看着堆栈F9,看啥时候标题是"Windows 任务管理器" (断点很多,比较笨下在这里了。。。)
功夫不负有心人,看到了,准备把他存在段地址里面,不知道那安全,数据区拖到最后了。。
后来想,程序应该会存,记下SetWindowPos的hwnd参数,在数据区搜索下XA XB XC,
没有结果好失望,后来想起存储结构高低位要变,搜索 XC XB XA,铛铛铛~终于被我
找着咯~(明星三缺一的台词原来是【碰】)。
仔细一看断点处下面一点的:
01005399 |. FF35 645D0101 PUSH DWORD PTR DS:[1015D64] ; |hWnd = NULL
0100539F |. FF15 88130001 CALL DWORD PTR DS:[<&user32.ShowWindow>] ; \ShowWindow
(自己应该好笨,再ShowWindow设置断点估计F9能少按几十次)
这里就直接用了。我们向上看下来源:
01005353 |. FF15 98120001 CALL DWORD PTR DS:[<&user32.CreateDialogParam>; \CreateDialogParamW
01005359 |. 3BC3 CMP EAX,EBX
0100535B |. A3 645D0101 MOV DWORD PTR DS:[1015D64],EAX
改怎么调用知道了吧?(最终代码MessageBoxW部分也可以看到)
测试,任务管理器置顶不置顶都可以正常显示消息框。
4.
调试的时候消息框完了,调用CloseHandle总是进异常处理部分。
原因:生搬硬套,知道要一个参数照搬别的地方的PUSH EBX,那里EBX也许是句柄这里就……
解决方法:转存OpenProcess的句柄。
01026F27 . 8985 00FAFFFF MOV DWORD PTR SS:[EBP-600],EAX
以前的认识是:把EAX存到EBP-600的地址里面,一点都不知道为什么,
现在知道了。顺便提下开始用的EBP-210,但是会下面的获取映像路径的CALL清空,
所以这里把写远些。
5.
逻辑结构进行了改善。OpenProcess->查询->CloseHandle->MessageBoxW
这个经过前面那些,越来越了解,所以比较简单,就是提下保存地址不要选
太近……这里用了EBP-608
6.
程序OD调试测试都OK……实际运行却……
问题:MessageBoxW显示内容一会全一会不全,有的中间还显示乱码。
原因:保存地址太近,被刷掉了...调试的时候都没错,可能调试的时候
实时性比较高。
解决方法:保存位置从EBP-20C改到EBP-40C。
7.
问题:对SYSTEM.EXE右键会出错。
解决方法:这个进程是PID固定为4,所以做了下简单判断即可。
其他进程没有映像路径的测试通过从01026F51跳转。
顺便改了下颜色,调试的时候看到注释RGB字样。。。
bp CreatePen 下断:
5650h处 设置性能页网格颜色
90C5h处 设置联网页网格颜色
好像还有几个断点,颜色不是常数用的EDI+固定量,所以就没去改。
你要是查出来了记得分享哦。
(记得以前有篇文章叫给Windows任务管理器点颜色看看,里面说那个
地址改成自己的,还有那个地址……给了固定的值,版本不同的任务
管理器发现和他说的不一样一直没机会改,今天终于得偿所愿,更重要
是知道为什么改几个字节就会变了。)
至此自己已经满意了,第一能这样感觉很不错,虽然很累,花了一晚上
+一下午时间。
修改后保存,运行下看看效果:
PIC3.PNG
直接对着弹出对话框Ctrl+C就能复制(不知道多少人知道,很实用的功能)
---------------------------
映像路径
---------------------------
"C:\WINDOWS\notepad.exe" d:\slorelee\桌面\第一次PEDIY.txt
---------------------------
确定
---------------------------
PS:
说stalker前辈用的任务管理器不好,测试的时候发现这个任务管理器在
任务界面,右键菜单和结束任务按钮有些不灵,开始以为和自己DIY的部分
有关,下了断点点结束任务不会执行那里,而且调试状态都能一点就结束
(残念),后来网上重新下载了很多Longhorn任务管理器都有这个问题,反
应不好不太重要,关键如果结束驱动器或文件夹任务居然会重启EXPLORER
(似乎被结束掉了...),所以不是DIY的错~别拍我。最后下了所谓的完美版,
测试了几次都还没发现那个问题,但是还是有结束任务点了没反映的状况,
不过没之前的夸张。
附件列表:
PIC1.PNG
PIC2.PNG
PIC3.PNG
taskmgr修改前.exe
taskmgr脱壳.exe
taskmgr.exe
第一次PEDIY.txt
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界