【文章标题】: 给任务管理器增加显示程序完整路径功能
【文章作者】: stalker
【软件名称】: taskmgr.exe
【下载地址】: 系统目录
【使用工具】: OllyDbg,LordPE,ResHacker
【操作平台】: Windows XP Sp2
--------------------------------------------------------------------------------
【详细过程】
Windows自带的任务管理器无法显示程序所在的完整路径,我一直觉得这是一个缺陷,今天我们就自己来给它增加这一功能。
获取一个进程完整路径的方法有很多,我们使用CreateToolhelp32Snapshot&Module32First的方法,示例代码如下
代码:
invoke CreateToolhelp32Snapshot,TH32CS_SNAPMODULE,dwTargetProcessID mov MD32.dwSize,sizeof MD32 ;MD32为一个MODULEENTRY32结构体变量 invoke Module32First,eax,offset MD32
下面正式开始对任务管理器的修改,大致可以分为3个步骤
1.增加菜单
2.定位程序响应菜单项点击的代码位置
3.加入对我们新增菜单的响应代码
首先使用资源编辑工具(这里我使用的是ResHacker)给程序增加一个菜单项"显示完整路径(&S)",如图1

我们新增的菜单项与"结束进程(&E)"处于同一组中,我们就通过它来定位任务管理器对菜单项响应代码的位置,从图1我们看
到它的ID为40028,转换为16进制就是9C5C(记下来)
关于定位,首先我想到的是以前使用的“查找所有分支法”,无奈没有成功(看来这办法并不是屡试不爽=_=)
于是另寻他法,通过行为来定位,OD载入任务管理器,对TerminateProcess这个函数下断,运行之,然后随便运行一个程序,
再在被调试的任务管理器中右键结束它,此时OD断下了任务管理器对TerminateProcess函数的调用,如图2

从0100C226,一直向上找到过程首
代码:
0100C17D /$ 8BFF mov edi, edi 0100C17F |. 55 push ebp 0100C180 |. 8BEC mov ebp, esp 0100C182 |. 83EC 20 sub esp, 20 ......省略中间若干代码 0100C223 |. 6A 01 push 1 ; /ExitCode = 1 0100C225 |. 56 push esi ; |hProcess 0100C226 |. FF15 C0100001 call dword ptr [<&KERNEL32.TerminateP>; \TerminateProcess

有三个地方调用了这个过程,我们要寻找究竟哪个地方对菜单项进行了判断处理,一个一个前往查看下,过程省略,最终找到
下面的地方
代码:
0100CF29 > \BA 5C9C0000 mov edx, 9C5C ;9C5C,你还记得吗? 0100CF2E > 3BCA cmp ecx, edx 0100CF30 .^ 74 AD je short 0100CEDF 0100CF32 .^ 7E BB jle short 0100CEEF 0100CF34 . 81F9 629C0000 cmp ecx, 9C62 0100CF3A . 7E 30 jle short 0100CF6C 0100CF3C . 81F9 779C0000 cmp ecx, 9C77 0100CF42 . 74 18 je short 0100CF5C 0100CF44 . 81F9 A99C0000 cmp ecx, 9CA9 0100CF4A .^ 75 A3 jnz short 0100CEEF 0100CF4C . 85C0 test eax, eax 0100CF4E .^ 74 9F je short 0100CEEF
对菜单项id的处理判断找到了,我们直接从0100CF29这里跳走,加入我们自己的代码,然后再跳回来继续执行
再写代码之前我们应当知道:
1.一个MODULEENTRY32结构的大小为224H
2.结构成员szExePath的相对于结构体起始地址的偏移为120H
3.要成功获取进程的完整路径,我们要获得该进程的PID以及寻找一个内存块来存放一个MODULEENTRY32结构体
关键是第3点,如何获取目标进程的PID,我们知道,通过TerminateProcess来结束一个进程,首先得使用OpenProcess函数
来打开这个进程以获取进程句柄,而OpenProcess又需要进程的PID做为参数。我们回到开始任务管理器结束进程的地方看看
代码:
0100C17D /$ 8BFF mov edi, edi 0100C17F |. 55 push ebp 0100C180 |. 8BEC mov ebp, esp 0100C182 |. 83EC 20 sub esp, 20 ...... 0100C210 |. FF75 08 push dword ptr [ebp+8] ; /ProcessId 0100C213 |. 6A 00 push 0 ; |Inheritable = FALSE 0100C215 |. 6A 01 push 1 ; |Access = TERMINATE 0100C217 |. FF15 B4100001 call dword ptr [<&KERNEL32.OpenProces>; \OpenProcess 0100C21D |. 8BF0 mov esi, eax 0100C21F |. 85F6 test esi, esi 0100C221 |. 74 28 je short 0100C24B 0100C223 |. 6A 01 push 1 ; /ExitCode = 1 0100C225 |. 56 push esi ; |hProcess 0100C226 |. FF15 C0100001 call dword ptr [<&KERNEL32.TerminateP>; \TerminateProcess
可以看到OpenProcess的第三个参数为[ebp+8],即当前过程(0100C17D)的第一个参数,调用此过程的代码如下
代码:
0100CEE3 . 6A 00 push 0 0100CEE5 . FF70 08 push dword ptr [eax+8] ;[eax+8]与上面的[ebp+8]相对应,里面就是目标进程pid 0100CEE8 . 8BCE mov ecx, esi ;调试过程中可以通过命令d eax+8来验证 0100CEEA . E8 8EF2FFFF call 0100C17D
获取目标进程的pid
最后一个问题就是,从哪里找地方来存放我们的MODULEENTRY32结构体
首先使用OD载入运行任务管理器(一定要运行),然后Alt+M找到其.data段的位置,如图4

双击该地址前往,然后会弹出来一个dump窗口,里面显示的是内存数据,一直往下拖,直到出现大片空白为止
然后随便找一个地址作为结构体的起始地址(但要确保其后大小不小于224H),我使用的是01016A00这个地址
到现在,基本所有的问题都解决了,剩下的就是找地方写代码
由于taskmgr.exe并没有导入CreateToolhelp32Snapshot以及Module32First这两个函数,写入代码之前首先用LordPE导入它们,
除此之外,我还导入了MessageBoxA,因为taskmgr.exe只导入了MessageBoxW,而结构体MODULEENTRY32的szExePath成员并不是
一个Unicode String。当然,你也可以先将它转换为Unicode String,然后再调用taskmgr.exe导入的MessageBoxW。不过我想
这样也许会更麻烦,因此我采用了增加导入MessageBoxA的简单方法=_=
写入代码如下
代码:
01014F00 > \60 pushad 01014F01 . BA 989C0000 mov edx, 9C98 01014F06 . 3BCA cmp ecx, edx 01014F08 . 75 37 jnz short 01014F41 01014F0A . FF70 08 push dword ptr [eax+8] ; /ProcessID 01014F0D . 6A 08 push 8 ; |Flags = TH32CS_SNAPMODULE 01014F0F . FF15 38100201 call dword ptr [<&kernel32.CreateTool>; \CreateToolhelp32Snapshot 01014F15 . C705 006A0101>mov dword ptr [1016A00], 224 01014F1F . 68 006A0101 push 01016A00 ; /pModuleentry = taskmgr3.01016A00 01014F24 . 50 push eax ; |hSnapshot 01014F25 . FF15 3C100201 call dword ptr [<&kernel32.Module32Fi>; \Module32First 01014F2B . FF15 7C130001 call dword ptr [<&USER32.GetForegroun>; [GetForegroundWindow 01014F31 . 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL 01014F33 . 6A 00 push 0 ; |Title = NULL 01014F35 . 68 206B0101 push 01016B20 ; |Text = "" 01014F3A . 50 push eax ; |hOwner 01014F3B . FF15 5D100201 call dword ptr [<&user32.MessageBoxA>>; \MessageBoxA 01014F41 > 61 popad 01014F42 . BA 5C9C0000 mov edx, 9C5C 01014F47 .^ E9 E27FFFFF jmp 0100CF2E
修改
代码:
0100CF29 > \BA 5C9C0000 mov edx, 9C5C 0100CF2E > 3BCA cmp ecx, edx
代码:
0100CF29 > \E9 D27F0000 jmp 01014F00 0100CF2E > 3BCA cmp ecx, edx
然后保存所有修改,至此,大功告成
附上胜利截图一张以及修改过的taskmgr.exe

--------------------------------------------------------------------------------
【经验总结】
我的目的基本还算达到了,原来发现不认识的进程时,想找出它来看看,却发现任务管理器居然不支持显示完整路径。
但不知道为什么,获取一些系统进程的路径的时候,没有成功,显示出来是乱码。
该问题已经解决,权限问题(我自己测试的时候使用了一段在其他程序中正常的bug提权代码,于是没有发现)再次感谢 安摧 同学,希望大家写程序的时候一定仔细

看

现在附件中是上传的新的taskmgr.exe
--------------------------------------------------------------------------------
2009年02月22日 下午16:34