快雪时晴2009年2月8日
问题提出:
下了最新的PEBrowse Professional Interactive, native and managed code debugger. Last update 1/24/2009.
http://www.smidgeonsoft.prohosting.com/
发现在调试某些程序如局域网通讯软件飞秋FeiQ时,界面常被遮盖,影响调试,找来找去没看到有类似OD的窗口置顶(ALWAYS ON TOP)选项。
记起cxlrb的一篇DIY文章,搜了下:
标 题: 【原创】用 OD 给 EXE 文件添加一个对话框初探
作 者: cxlrb
时 间: 2006-06-08,17:16
链 接: http://bbs.pediy.com/showthread.php?t=27054
【文章标题】: 用OD给exe文件添加一个对话框初探
【文章作者】: CxLrb
【作者邮箱】: cxlrb@yahoo.com.cn
【作者主页】: http://unpack.blog.sohu.com/
【作者QQ号】: 21252130
【软件名称】: Pe_optimizer1.4汉化版by.CxLrb
【下载地址】: 自己搜索下载
【使用工具】: OD,HexDecChar,XN Resource Editor
不过Cxlrb给出的是MFC程序的改造方法,先找到资源项ID,再找CMP R,XXXX的形式。
我这里的PEBrowseDbg v8.13.1是DELPHI编写的,查找事件改造方法还不太一样。
PE Explorer199r5 查看资源文件并反编译(Delphi6)非常方便,很容易找到菜单项Tile对应的处理事件proc:
0061230C TMainForm.WindowTile: 0061230C 55 push ebp 0061230D 8BEC mov ebp,esp 0061230F 83C4F8 add esp,FFFFFFF8h 00612312 8955F8 mov [ebp-08h],edx 00612315 8945FC mov [ebp-04h],eax 00612318 8B45FC mov eax,[ebp-04h] 0061231B E8ECFBE6FF call SUB_L00481F0C 00612320 59 pop ecx 00612321 59 pop ecx 00612322 5D pop ebp 00612323 C3 retn
好了,google一下置顶的C语句(很尴尬,没用过VC编写MFC或是GUI程序)
http://www.google.cn/codesearch/p?hl...pmost%20lang:c
void VID_Front_f( void )
{
SetWindowLong( cl_hwnd, GWL_EXSTYLE, WS_EX_TOPMOST );
SetForegroundWindow( cl_hwnd );
}
//时候证明这两句没能完成任务,仅在任务栏图标提示性的闪两下(OD调试时),界面还是被遮盖。
看看常量定义,又是本机搜素一下dwing的那个VC6RC2_lite带*.h:
\VC6RC2\VC98\Include
283,697 WINUSER.H
#define GWL_EXSTYLE (-20)
#define WS_EX_TOPMOST 0x00000008L
现在要找的数据就是cl_hwnd即主窗口hwnd了,在OD中查看窗口:
000F070C G:\DOWNLOAD\n.exe (2700) (STOPP Topmost 000209AD 17CF0000 00010110 主 004619F4 TMainForm
0002099E 000F070C =句柄 0 54000100 主 004619F4 TStatusBar
000209A0 000F070C =句柄 0 5600084E 00010000 主 004619F4 TToolBar
00100704 000F070C =句柄 0 54010017 00000200 主 004619F4 TTreeView
此次进程主窗口为000F070C,在OD中ALT+M,在进程DATA区段查找,找到一处:0x00626130(如此精确,如此幸运)
下硬件访问断点,CTRL+F2重新加载,F9运行,出现PEBr界面后随便选择一个目标程序加载(PEBr加载,非OD),
第三次中断后,该地址处有赋值了(当然每次运行不一样了)
0047BB0D |. 6A EC PUSH -14 ; /Index = GWL_EXSTYLE
0047BB0F |. 53 PUSH EBX ; |hWnd = 000F070C ('G:\DOWNLOAD\n.exe (2700) (STO...',class='TMainForm')
0047BB10 |. E8 5FC4F8FF CALL <JMP.&user32.GetWindowLongA> ; \GetWindowLongA
0047BB15 |. A8 08 TEST AL,8
0047BB17 |. 75 11 JNZ SHORT PEBrowse.0047BB2A
0047BB19 |. 833D 30616200 00 CMP DWORD PTR DS:[626130],0
0047BB20 |. 75 17 JNZ SHORT PEBrowse.0047BB39
0047BB22 |. 891D 30616200 MOV DWORD PTR DS:[626130],EBX ;这里赋值
0047BB28 |. /EB 0F JMP SHORT PEBrowse.0047BB39 ;;停在这里
//回到上一行查看
EBX=0007066A
DS:[00626130]=0007066A
可以看出,该hwnd放在一个全局变量里面,后面可以直接使用了。
好了,现在就重利用Tile菜单项事件,夹带处理一下置顶语句吧,省了再添加一项“Alway On Top”菜单,一切为了省事,简化。
OD中CTRL+N看了下,
SetWindowLong( cl_hwnd, GWL_EXSTYLE, WS_EX_TOPMOST );
SetForegroundWindow( cl_hwnd );
两个API都有,又免去了导入。
考虑到要求能够取消置顶,恢复原窗口参数,需再用到一个api:
LONG GetWindowLong( HWND hWnd, // handle of window
int nIndex // offset of value to retrieve
);
注意,为了跨平台,不要直接用API地址,参照原程序中的方法用CALL JMP地址:
00408174 $- FF25 4CB76500 JMP DWORD PTR DS:[<&user32.SetForeground>; user32.SetForegroundWindow
004081CC $- FF25 20B76500 JMP DWORD PTR DS:[<&user32.SetWindowLong>; user32.SetWindowLongA
00407F74 $- FF25 48B86500 JMP DWORD PTR DS:[<&user32.GetWindowLongA>];user32.GetWindowLongA
修改原WindowTile事件响应PROC头为:
0061230C /E9 8F2C0100 JMP PEBrowse.00624FA0 ;跳到PE空白处写代码
00612311 |90 NOP
找到PE尾部的空白处写如下代码:
00624FA0 60 PUSHAD
00624FA1 9C PUSHFD
00624FA2 6A EC PUSH -14
00624FA4 FF35 30616200 PUSH DWORD PTR DS:[626130]
00624FAA E8 C52FDEFF CALL <JMP.&user32.GetWindowLongA>
00624FAF 83F0 08 XOR EAX,8
00624FB2 50 PUSH EAX
00624FB3 6A EC PUSH -14
00624FB5 FF35 30616200 PUSH DWORD PTR DS:[626130]
00624FBB E8 0C32DEFF CALL <JMP.&user32.SetWindowLongA>
00624FC0 FF35 30616200 PUSH DWORD PTR DS:[626130]
00624FC6 E8 A931DEFF CALL <JMP.&user32.SetForegroundWindow>
00624FCB 9D POPFD
00624FCC 61 POPAD
00624FCD 55 PUSH EBP
00624FCE 8BEC MOV EBP,ESP
00624FD0 83C4 F8 ADD ESP,-8
00624FD3 ^ E9 39D3FEFF JMP PEBrowse.00612311 ;返回原事件响应PROC
00624FD8 90 NOP
奇怪,OD没法保存,提示“在可执行文件中无法定位数据”,现把二进制记录下:
60 9C 6A EC FF 35 30 61 62 00 E8 C5 2F DE FF 83 F0 08 50 6A EC FF 35 30 61 62 00 E8 0C 32 DE FF
FF 35 30 61 62 00 E8 A9 31 DE FF 9D 61 55 8B EC 83 C4 F8 E9 39 D3 FE FF 90
用HEXWORKSHOP打开看,该空白地址处原来有很多数据,似乎是API IMPORT等信息????怎么在OD中又全0了呢?不懂,等专家回答。
只好拿ZEROADD新增一个KXSQ区段400H大小起始地址00733000:
原PROC头:
0061230C - E9 EF0C1200 JMP PEBrowse.00733000
00612311 90 NOP
新区段KXSQ:
00733000 60 PUSHAD
00733001 9C PUSHFD
00733002 6A EC PUSH -14
00733004 FF35 30616200 PUSH DWORD PTR DS:[626130]
0073300A E8 654FCDFF CALL <JMP.&user32.GetWindowLongA>
0073300F 83F0 08 XOR EAX,8
00733012 50 PUSH EAX
00733013 6A EC PUSH -14
00733015 FF35 30616200 PUSH DWORD PTR DS:[626130]
0073301B E8 AC51CDFF CALL <JMP.&user32.SetWindowLongA>
00733020 FF35 30616200 PUSH DWORD PTR DS:[626130]
00733026 E8 4951CDFF CALL <JMP.&user32.SetForegroundWindow>
0073302B 9D POPFD
0073302C 61 POPAD
0073302D 55 PUSH EBP
0073302E 8BEC MOV EBP,ESP
00733030 83C4 F8 ADD ESP,-8
00733033 - E9 D9F2EDFF JMP PEBrowse.00612311
00733038 90 NOP
注意:二进制串复制过来后,3个CALL和1个JMP都要调整到正确的地址数值。
60 9C 6A EC FF 35 30 61 62 00 E8 65 4F CD FF 83 F0 08 50 6A EC FF 35 30 61 62 00 E8 AC 51 CD FF
FF 35 30 61 62 00 E8 49 51 CD FF 9D 61 55 8B EC 83 C4 F8 E9 D9 F2 ED FF 90
还是不起作用,google了下,也许是多窗口的缘故。
由于PEBrDbg包含多个子窗口,光设置主窗口TOPMOST属性不起作用,子窗口还是被被调试窗口遮盖。
而且一开始再应加个检测是否主窗口hwnd已经获取(非0)。
继续改进:
#define GW_CHILD 5
00407EFC $- FF25 80B86500 JMP DWORD PTR DS:[<&user32.GetWindow>] ; user32.GetWindow
最终要改成如此形式:
if(hWndParent != NULL)
{
HWND hWndChild ;
while((hWndChild = GetWindow(hWndParent,GW_CHILD)) != NULL)//不知道如何遍历子窗口,暂时这样表述一下
{
SetWindowPos(hWndChild,HWND_TOPMOST,0,0,800,480,SWP_SHOWWINDOW);
}
SetWindowPos(hWndParent,HWND_TOPMOST,0,0,800,480,SWP_SHOWWINDOW);
}
}
==================
00733000 60 PUSHAD
00733001 9C PUSHFD
00733002 833D 30616200 0>CMP DWORD PTR DS:[626130],0
00733009 75 15 JNZ SHORT PEBrowse.00733020
0073300B 90 NOP
0073300C 90 NOP
0073300D 90 NOP
0073300E 90 NOP
0073300F 9D POPFD
00733010 61 POPAD
00733011 55 PUSH EBP
00733012 8BEC MOV EBP,ESP
00733014 83C4 F8 ADD ESP,-8
00733017 - E9 F5F2EDFF JMP PEBrowse.00612311
0073301C 90 NOP
0073301D 90 NOP
0073301E 90 NOP
0073301F 90 NOP
00733020 6A 05 PUSH 5
00733022 FF35 30616200 PUSH DWORD PTR DS:[626130]
00733028 E8 CF4ECDFF CALL <JMP.&user32.GetWindow>
0073302D 85C0 TEST EAX,EAX
0073302F 74 26 JE SHORT PEBrowse.00733057
00733031 90 NOP
00733032 90 NOP
00733033 90 NOP
00733034 90 NOP
00733035 50 PUSH EAX
00733036 6A EC PUSH -14
00733038 50 PUSH EAX
00733039 E8 364FCDFF CALL <JMP.&user32.GetWindowLongA>
0073303E 8BD8 MOV EBX,EAX
00733040 83F3 08 XOR EBX,8
00733043 58 POP EAX ; kernel32.7C816FD7
00733044 50 PUSH EAX
00733045 53 PUSH EBX
00733046 6A EC PUSH -14
00733048 50 PUSH EAX
00733049 E8 7E51CDFF CALL <JMP.&user32.SetWindowLongA>
0073304E E8 2151CDFF CALL <JMP.&user32.SetForegroundWindow>
00733053 ^ EB CB JMP SHORT PEBrowse.00733020
00733055 90 NOP
00733056 90 NOP
00733057 6A EC PUSH -14
00733059 FF35 30616200 PUSH DWORD PTR DS:[626130]
0073305F E8 104FCDFF CALL <JMP.&user32.GetWindowLongA>
00733064 83F0 08 XOR EAX,8
00733067 50 PUSH EAX
00733068 6A EC PUSH -14
0073306A FF35 30616200 PUSH DWORD PTR DS:[626130]
00733070 E8 5751CDFF CALL <JMP.&user32.SetWindowLongA>
00733075 FF35 30616200 PUSH DWORD PTR DS:[626130]
0073307B E8 F450CDFF CALL <JMP.&user32.SetForegroundWindow>
00733080 ^ EB 8D JMP SHORT PEBrowse.0073300F
00733082 90 NOP
==============
60 9C 83 3D 30 61 62 00 00 75 15 90 90 90 90 9D 61 55 8B EC 83 C4 F8 E9 F5 F2 ED FF 90 90 90 90
6A 05 FF 35 30 61 62 00 E8 CF 4E CD FF 85 C0 74 26 90 90 90 90 50 6A EC 50 E8 36 4F CD FF 8B D8
83 F3 08 58 50 53 6A EC 50 E8 7E 51 CD FF E8 21 51 CD FF EB CB 90 90 6A EC FF 35 30 61 62 00 E8
10 4F CD FF 83 F0 08 50 6A EC FF 35 30 61 62 00 E8 57 51 CD FF FF 35 30 61 62 00 E8 F4 50 CD FF
EB 8D 90
循环子窗口错!
当然错,因为没有正确遍历,死循环嘛。
有问题,找Google,提示用下面这个函数:
BOOL SetWindowPos(
HWND hwnd, // handle of window
HWND hwndInsertAfter, // placement-order handle
int x, // horizontal position
int y, // vertical position
int cx, // width
int cy, // height
UINT fuFlags // window-positioning flags
);
#define SWP_NOSIZE 0x0001
#define SWP_NOMOVE 0x0002
#define HWND_TOP ((HWND)0)
#define HWND_BOTTOM ((HWND)1)
#define HWND_TOPMOST ((HWND)-1)
#define HWND_NOTOPMOST ((HWND)-2)
再次用OD在程序中找到:
004081DC $- FF25 18B76500 JMP DWORD PTR DS:[<&user32.SetWindowPos>>; user32.SetWindowPos
好了,最终修改如下:
原PROC头:
0061230C - E9 EF0C1200 JMP PEBrowse.00733000 00612311 90 NOP
00733000 60 PUSHAD 00733001 9C PUSHFD 00733002 833D 30616200 0>CMP DWORD PTR DS:[626130],0 00733009 75 15 JNZ SHORT PEBrowse.00733020 0073300B 90 NOP 0073300C 90 NOP 0073300D 90 NOP 0073300E 90 NOP 0073300F 9D POPFD 00733010 61 POPAD 00733011 55 PUSH EBP 00733012 8BEC MOV EBP,ESP 00733014 83C4 F8 ADD ESP,-8 00733017 - E9 F5F2EDFF JMP PEBrowse.00612311 0073301C 90 NOP 0073301D 90 NOP 0073301E 90 NOP 0073301F 90 NOP 00733020 90 NOP 00733021 6A EC PUSH -14 00733023 FF35 30616200 PUSH DWORD PTR DS:[626130] 00733029 E8 464FCDFF CALL <JMP.&user32.GetWindowLongA> 0073302E 83E0 08 AND EAX,8 00733031 83F8 08 CMP EAX,8 00733034 75 07 JNZ SHORT PEBrowse.0073303D 00733036 B8 FEFFFFFF MOV EAX,-2 0073303B EB 05 JMP SHORT PEBrowse.00733042 0073303D B8 FFFFFFFF MOV EAX,-1 00733042 6A 03 PUSH 3 00733044 6A 00 PUSH 0 00733046 6A 00 PUSH 0 00733048 6A 00 PUSH 0 0073304A 6A 00 PUSH 0 0073304C 50 PUSH EAX 0073304D FF35 30616200 PUSH DWORD PTR DS:[626130] 00733053 E8 8451CDFF CALL <JMP.&user32.SetWindowPos> 00733058 ^ EB B5 JMP SHORT PEBrowse.0073300F 0073305A 90 NOP 0073305B 90 NOP
60 9C 83 3D 30 61 62 00 00 75 15 90 90 90 90 9D 61 55 8B EC 83 C4 F8 E9 F5 F2 ED FF 90 90 90 90
90 6A EC FF 35 30 61 62 00 E8 46 4F CD FF 83 E0 08 83 F8 08 75 07 B8 FE FF FF FF EB 05 B8 FF FF
FF FF 6A 03 6A 00 6A 00 6A 00 6A 00 50 FF 35 30 61 62 00 E8 84 51 CD FF EB B5 90
测试通过。
最后用PE Explorer199r5把菜单项&Tile改为“&Tile and Top switch”,保存,收工。