【文章标题】: 给任务管理器增加显示程序完整路径功能
【文章作者】: 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
  
  调用上面的代码之后MD32.szExePath中就是目标进程的完整路径了。
  下面正式开始对任务管理器的修改,大致可以分为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
  
  单击0100C17D这一行,可以在信息窗口看到如图3所示信息

  有三个地方调用了这个过程,我们要寻找究竟哪个地方对菜单项进行了判断处理,一个一个前往查看下,过程省略,最终找到
  下面的地方
  
代码:
  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
  
  上面这段调用代码,与开始我们找到的判断菜单id进行处理的代码位于同一过程,因此我们的代码可以直接使用[eax+8]来
  获取目标进程的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
上传的附件 mytaskmgr.rar

  • 标 题:答复
  • 作 者:安摧
  • 时 间:2009-02-22 21:15

权限问题~~~
在打开一个用户名为SYSTEM的进程的时候,因为权限不够,CreateToolhelp32Snapshot返回了-1(INVALID_HANDLE_VALUE)。

引用:
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.CreateToolhelp32Snapshot>]      ; \CreateToolhelp32Snapshot
//这个返回失败!

01014F15   .  C705 006A0101>mov     dword ptr [1016A00], 224
01014F1F   .  68 006A0101   push    01016A00                                              ; /pModuleentry = mytaskmg.01016A00
01014F24   .  50            push    eax                                                   ; |hSnapshot
01014F25   .  FF15 3C100201 call    dword ptr [<&kernel32.Module32First>]                 ; \Module32First
01014F2B   .  FF15 7C130001 call    dword ptr [<&USER32.GetForegroundWindow>]             ; [GetForegroundWindow
01014F31   .  6A 00         push    0                                                     ; /Style = MB_OK|MB_APPLMODAL
01014F33   .  6A 00         push    0                                                     ; |Title = NULL
01014F35   .  68 206B0101   push    01016B20                                              ; |Text = "?",06,"??",06,"?,B6,"",B2,"",92,"??",06,"",B2,"",92,"?",06,"",B2,"",92,"?"
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
提升权限的代码:
引用:
BOOL EnableDebugPrivileges()
{
  /**********************************************************************/
  //提升进程自身权限
  BOOL bRet;
  HANDLE hToken;
  bRet = ::OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken);
  if(!bRet)
  {
    return FALSE;
  }
  TOKEN_PRIVILEGES tp;
  tp.PrivilegeCount = 1;
  ::LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tp.Privileges[0].Luid);
  tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  ::AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(tp),NULL,NULL);
  if(GetLastError() != ERROR_SUCCESS)
  {
    return FALSE;
  }
  return TRUE;
  /**********************************************************************/
}