原文:https://www.pediy.com/kssd/pediy05/pediy50239.htm
diy 你的pe 完结篇
上两篇我教给大家如何diy pe,由于种种原因,都是没有太实际的例子。今天我就讲述一个实际的例子。
我想看雪论坛的人没有不用w32asm的吧,这个实用的工具大家都爱它。但是w32asm也有些不完善的地方。
首先它不支持滚轮鼠标的滚动(当然是在你没有用辅助鼠标软件的情况下),然后是不支持文件的拖放
你打开w32asm后拖个文件进去,鼠标是个禁止拖放的图标。想实现这些功能么,那就带上你的老虎钳(trw)
,扳手(hiew)我们出发修理机床(w32asm,我修理的w32asm是killer
修改过的w32asm10,原文件用pecompack压缩过,
自己脱壳修改),(声明:我的系统是98,2000下我没有测试。看完我这篇文章的有兴趣的人,可以在2000下试试)
好,我们先做第一个功能吧加入鼠标滚轮功能(这时候我的想法是假如我有w32asm的源程序的话多好啊,没有?那只有从反编译的pe文件干活了,
感觉像在修里煤气管道漏气)
第一步:分析问题(每个diy者都应该养成这个习惯,不要上来就bpx 断点乱下一通)
不支持滚轮是什么原因造成的?
windows是个消息系统,w32asm不支持滚轮是因为它接受到滚轮的消息但是根本不处理它,
我们的目标就是找到w32asm处理windows消息的地方,然后加入处理滚轮消息,如果你问我怎么处理滚轮消息,很简单,
我们把滚轮的消息转化成按键的消息,w32asm不是可以按上下键来滚动么,我们把滚轮的上下滚动的消息转化为上下按键
的消息,然后其他的事情交给w32asm自己去处理就ok,好了修理思路定好了,开始动工
第二步:找到win32asm处理消息的地方
怎么找处理消息,我在上一篇文章已经讲过了,这里我就不在重复了。
找到处理消息的地方如下
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E471(U)
|
:0045261E 8B4604
mov eax, dword ptr [esi+04]===》【这里是函数参数的传递,esi+04就是msg 的unit】
:00452621 3D21010000 cmp
eax, 00000121======>这里eax就是windows消息的代码
:00452626 7F41
jg 00452669
:00452628 0F84A30D0000
je 004533D1
:0045262E 3D11010000
cmp eax, 00000111===>看过我上篇文章的人就熟悉这个吧,就是wm_command
:00452633 7F1B
jg 00452650 *处理消息的地方很长,因为w32asm有很多消息要处理*
:00452635 7463
je 0045269A *限于篇幅,我只贴关键地方,有兴趣的朋友可以自己*
:00452637 2DA0000000 sub
eax, 000000A0 *反汇编原文件观察
*
:0045263C 0F84530D0000
je 00453395
*
*
:00452642 83E860
sub eax, 00000060
:00452645 0F8493000000
je 004526DE
:0045264B E92F120000
jmp 0045387F
好我们下断点bpx 45261e
这时候你会发现不断的中断停在这个地方,这很正常,这里是w32asm处理消息的核心部分,每一个给w32asm的消息都经过这里过滤。
频繁的windows消息当然就会频繁的中断了:)。这个没有关系,每次中断我们就f5,然后在按f5的时候的间隔就滚动鼠标的滚轮,看看
滚轮的消息是多少(注:这个在windows编程手册里可以查到wm_mousewhell消息的代码是20A,我这样来的目的是因为我不知道其waram的
子参数,也就是如何判断滚轮向上滚动和向下滚动的子参数是如何定义的,而且手头上没有类似的滚轮程序,我只有自己测试了,这个方法
是比较笨了一点,但是我们想人家郭靖都是大智若愚,安慰一下自己),当看到esi+04的值为20a的时候我们d
esi+08(一般子参量都是这个
位置,windows 消息本身就是这么定义的),比较向上滚动和向下滚动esi+08地址有什么不同,我这里看到的是当向上滚动的时候esi+0a是7800
当向下滚动的时候esi+0a是88ff,我就初步确定向上滚动和向下滚动的判断了。知道了滚轮的滚动,我们还要测试一下按键的子参数,按键的消息
是wm_keydown 100,wm_keyup 101,wm_char 102这里w32asm只处理wm_keydown的消息(看w32asm消息处理的地方,我没有贴出来,有兴趣可以自己反
编译自己往45264B下面看),好既然只处理wm_keydown的消息,我们还是老办法,f5加上在间隔的时候按下向上,向下的按键,然后观察esi+08的地址
的子参量,我这里当按向上键esi+08是26,向下键esi+08是28,好现在我们知道了所有要知道的东西了,开始用扳手去修理机床吧
首先找到在程序里空出来的地方
把原来的
:00452621 3D21010000
cmp eax, 00000121
改为
00452621: E90ACD0500
jmp .0004AF330 ==》跳到我们自己的消息处理补丁上
|
自己的消息处理补丁:
004AF330: 3D0A020000
cmp eax,00000020A ;"
==》比较消息是否是滚轮
"
004AF335: 7415
je .0004AF34C
----- (1)==是的话转到滚轮处理
004AF337: 3D33020000
cmp eax,000000233 ;"
3"==》比较消息是否是拖放文件(这个是补丁第二个功能用的,在下面有阐述)
004AF33C: 7400
je
.0004AF33E ----- (2)==》是的话就转到(拖放文件的处理)
004AF33E: 90
nop=====》空出这么多nop是因为我怕等一下补丁 je .0004AF33E以后会变成长跳转所以预留这些字节
004AF33F: 90
nop
004AF340: 90
nop
004AF341: 90
nop
004AF342: 3D21010000
cmp eax,000000121 ;" !"==》恢复原程序动作
004AF347: E9DA32FAFF
jmp .000452626 ----- (3)==》跳回原程序地方
004AF34C: 668B460A
mov ax,[esi][0A]==》取出滚轮的子参数
004AF350: 663DFF00
cmp
ax,000FF ;" �"==》比较是向上滚还是向下滚
004AF354: 720A
jb
.0004AF360 ----- (4)
004AF356: B828000000
mov
eax,000000028 ;" ("==》向下滚动,改动消息子参数为按键下
004AF35B: 894608
mov
[esi][08],eax
004AF35E: EB08
jmps .0004AF368
----- (5)
004AF360: B826000000
mov eax,000000026 ;" &"==》向上滚动,改动消息子参数为按键上
004AF365: 894608
mov [esi][08],eax
004AF368: B800010000
mov
eax,000000100 ;" "==》改动消息为按键消息
004AF36D: 894604
mov
[esi][04],eax
004AF370: EBD0
jmps .0004AF342
----- (6)==》跳回原程序
好到现在为止,我们测试看看,哈哈,果然滚动了,成功!什么?你说滚动的太慢!)◎)◎※¥)※)(◎,如果嫌慢的话,自己把按键消息子参数改成pgdn。和pgup一次滚动一页,快了吧。什么?你说能不能不要这么快,一次滚动3行,或者5行,最好加个定义窗口想滚动几行就几行?我要晕倒了,
大哥,我是改动pe文件,不是改源程序。如果你想这样的话,我告诉你思路,你自己做.首先自己在w32asm的menu里面加个定义滚轮滚动的子选项,我的第二篇
文章有说怎么做的,然后加入点击这个子选项的消息处理,让点击这个子选项的时候弹出一个dialog,可以用CreateDialog的api函数,当然你的先做好这个dialog的资源,然后加入这个dialog的的消息函数,在dialog里面做个edit的控件和一个button控件当点击button的时候就把edit里面的数值保存到一个地址
,然后你的滚轮判断的地方读入这个地址的值,根据这个值判断用sendmessage函数向w32asm发送多少个按键消息,1就发1个,50就发50个,这样你乐意滚动多少行就多少行,还可以自定义:)。我是很累,不做了,那个大哥不满意我做的就自己就做一个想滚动几行就几行的吧,记得做好了发一个给我用用就行了!好了
到现在为止我们的滚轮版已经做好了。休息一下眼睛,我们要开始大动干戈,开始做拖放版了(如果是初学者就不要往下看了,讲述的专业比较多)
现在继续我们的diy之旅
打开w32asm试试拖放一个文件,呵呵,鼠标是禁止拖放的图标,证明是w32asm是根本不支持拖放,说白了也就是不处理拖放的消息。
好,既然我们要是这个w32asm支持拖放,首先我们先了解一下拖放的知识,windows是个图形界面系统,各种各样的程序都是基于图形界面的,
这个界面是我们也可以称为窗口,窗口很多属性,比如大家都了解的enablewindow就是设置窗口的属性的,不过这个函数是设置窗口是否可用的
属性。现在我们需要的是窗口是否接受拖放的属性,我告诉大家,这个函数就是DragAcceptFiles,它是shell32.dll的函数,与拖放有关的函数还有DragFinish和DragQueryFile,我们要补丁也就要用到这个三个函数,
首先我们看看DragAcceptFiles这个函数是设置窗口是否能接受拖放的消息,也就是windows是否发送WM_DROPFILES消息给这个窗口。使用的方法是api手册上查到为:
VOID DragAcceptFiles(
HWND hWnd, //
handle to the registering window 注册窗口的hwnd
BOOL fAccept
// acceptance option 是否接受拖放的消息
);
知道了DragAcceptFiles的用法后.我的思路是把w32asm的所有的窗口都加上一个
invoke DragAcceptFiles,hwnd,TRUE
当然这个是在汇编里实现,但是在已经连接好的pe文件里如何实现
invoke DragAcceptFiles,hDlg,TRUE呢?
要达到这个目的
第一:首先我们需要pe文件的import表(注:如果不懂import表的可以自己先学习一下pe文件的格式,不难,只是有点烦而已)有DragAcceptFiles这个引入函数
第二:然后就是我们需要在pe文件中的到窗口的hwnd
达到以上条件以后
只要在pe空白空间用以下代码就可实现
push
1
push hwnd
call DragAcceptFiles
我们现在先来达到第一个条件
拿出我脱壳的w32asm(注:我自己脱壳的有两个版本,第一个是在入口点dump完全的pe,然后手动修复import,这个版本很遗憾,只能在98下运行,不能在2000下运行,原因是需要修补的import太多可能有遗漏,或者我也不知道,第二个版本,是情狼大哥在入口点用trw2000的pedump命令dump出来版本,这个版本能跨平台,但是也遗憾,用pe编辑器看不到任何的import表)
为了能跨平台,我决定用第二个版本开始改造,由于没有任何的import表,我们根本不知道是否包含有DragAcceptFiles这个函数,用stud_pe打开自己做的第一个版本(也就是修复了import表的那个版本),点击function按钮,观察import的函数信息发现其函数有kernell32、gdi32.dll
等,没有包含shell32.dll,也就是说,不可能有DragAcceptFiles这个我们需要的函数了,没有函数,我们又想用,那就只有自己手动构造了,三个相关的函数构造起来,还是比较容易的,不过我还是教大家怎么使用lordpe构造吧(用stud_pe和peeditor都差不多,手动也行,如果来个二三十个函数的话,我看手动的可就吃亏了)
构造函数篇:
用lordpe打开第二个脱壳版本,点击directories按钮,然后点击importtabl按钮,只能看到一个kernell32.dll,在上面点击右键,选择
add import,弹出窗口,在dll填入SHELL32.dll,api填入DragAcceptFiles点击那个加号的按钮看到DragAcceptFiles已经添加进去了然后用这个方法添加DragFinish和DragQueryFile函数添加完成后点击ok,看到import表多了一个shell32的dll了吧,里面有我们添加的三个函数,记下这个三个函数的thunkrva值(以后要用到),我这里如下:
ThunkRva ThunkOffset ThunkValue
Hint ApiName
0015803B 0015803B
0015800C 0000 DragAcceptFiles
0015803F 0015803F 0015801E 0000
DragQueryFile
00158043 00158043 0015802E
0000 DragFinish
其实你也可以自己用winhex构造这个三个函数,不难,看看相关的pe格式文档就能做到,自己构造的有个好处就是结构比较分明,看起来比较舒服,软件构造的是重新定位过import所以有点混乱,不过你想偷懒就用软件构造吧,这个方便
构造完了函数,我们的给它一个firshthunk,否则在程序里面怎么call那里呢?这里就用到了thunkrva的值了,现在工具换成了hiew,用hiew打开脱壳后的w32asm我们来到004AF29C
如下:
004AF29C: FF25244B4D00
jmp d,[004D4B24]
004AF2A2: FF252C4B4D00
jmp
d,[004D4B2C]
004AF2A8: FF25304B4D00
jmp d,[004D4B30]
004AF2AE: FF25344B4D00
jmp
d,[004D4B34]==>这些jmp都是定位api函数的,每个jmp代表程序要用到api函数地址
004AF2B4: 0000
add
[eax],al *************************************************************
004AF2B6: 0000
add [eax],al *你问,怎么找到这个地方的,很简单,随便下个程序要用到的api函*
004AF2B8: 0000
add [eax],al *数,看看其call的是那个地址,就能找到这个地方
*
004AF2BA: 0000
add
[eax],al *************************************************************
我们现在就要加入DragAcceptFiles、DragQueryFile、DragFinish三个函数的jmp,
jmp到那里,其实就是jmp那个ThunkRva+imagebase的值,imagebase值是4000000
所以我们要在004AF2B4处填入:
004AF2B4:jmp d,[0055803B] *55803b=15803B(ThunkRva)+4000000(imagebase)以下一样计算
jmp d,[0055803f]
jmp d,[00558043]
添完后如下:
004AF29C: FF25244B4D00
jmp d,[004D4B24]
004AF2A2:
FF252C4B4D00 jmp
d,[004D4B2C]
004AF2A8: FF25304B4D00
jmp d,[004D4B30]
004AF2AE: FF25344B4D00
jmp d,[004D4B34]
004AF2B4: FF253B805500
jmp
DragAcceptFiles ;SHELL32.dl==>看到我们构造的函数了么,真是happy啊
004AF2BA: FF253F805500
jmp
DragQueryFile ;SHELL32.dll
004AF2C0: FF2543805500
jmp DragFinish ;SHELL32.dll
004AF2C6: 0000
add [eax],al
004AF2C8: 0000
add
[eax],al
现在记下004AF2B4、004AF2BA、004AF2C0这三个值.以后call 004AF2B4就是call
DragAcceptFiles了,调用DragQueryFile 也就是call 004AF2BA了,以此类推
函数构造完成第一个条件也就满足了,开始我们第二个条件hwnd的寻找吧
寻找hwnd打补丁篇:
如何找到hwnd值呢,对windows编程熟悉的人(你不熟悉就慢慢学,总有一天会熟悉的)知道GetWindowRect函数也需要一个hwnd值,我们截获
getwindowsrect函数,保存这个hwnd,再使用这个hwnd来DragAcceptFiles 不就ok,好确定思路,打开trw然后bpx
getwindowsrect
启动w32asm发现其在这里调用getwindowsrect:
0167:00492941 52
PUSH EDX=========>这个是一个rect的struct的point,也就是一个矩阵结构的指针
0167:00492942 FF700C PUSH
DWORD [EAX+0C]===>这里就是我们需要的hwnd
0167:00492945 E810C90100
CALL `USER32!GetWindowRect===>调用这个函数了也就是call
.0004AF25A
0167:0049294A EB1B JMP
SHORT 00492967
0167:0049294C
8B582C MOV EBX,[EAX+2C]
程序启动调用了很多次GetWindowRect函数,每次hwnd都不同,这是因为w32asm窗口分为好几个,edit窗口,menu窗口,toolbar,状态栏等等
我们不管它,把每个窗口的DragAcceptFiles 值都设置为ture就行了,管它有多少个呢,有多少个,就ture多少个,个个都能接受拖放,想往
那里拖就往那里拖
把这句:
0167:00492945 E810C90100 CALL
`USER32!GetWindowRect
改为:
00492945: E996C90100
jmp .0004AF2E0
-----这里是我们自己的补丁程序了
自己补丁的地方:
004AF2E0: 58
pop
eax----》取出压入堆栈的hwnd
004AF2E1: A3C6F24A00
mov [004AF2C6],eax---》保存到一个地址,我选择的是004AF2C6
004AF2E6: 50
push eax---》压hwnd入堆栈
004AF2E7: E86EFFFFFF
call .0004AF25A -----》 CALL `USER32!GetWindowRect
004AF2EC: 6A01
push 001===传递ture
004AF2EE:
FF35C6F24A00 push
d,[004AF2C6]==》传递hwnd
004AF2F4: E8BBFFFFFF
call
DragAcceptFiles ;SHELL32.dll===》也就是call 004AF2B4我们在上面构造的地方
004AF2F9: E96936FEFF
jmp
.000492967 -----》回到程序原来的地方
现在打开补丁完的程序,拖个文件试试,是不是图标已经变了,你说,变是变了,怎么没有反应啊,还没有加入事件怎么会有反应?真是的,路要
一步一步的走。不要太急
现在开始我们加入事件的过程了,首先找到接受消息的地方,加入判断拖放消息的代码,上面已经说过了在
:0045261E 8B4604
mov eax, dword ptr [esi+04]===》【这里是函数参数的传递,esi+04就是msg 的unit】
:00452621 3D21010000
cmp eax, 00000121======>这里eax就是windows消息的代码
而且
:00452621 3D21010000
cmp eax, 00000121
已经改为:
00452621: E90ACD0500
jmp .0004AF330
也就是消息处理已经在我们自己的补丁地方了
004AF330: 3D0A020000
cmp eax,00000020A
;" ==》比较消息是否是滚轮
"
004AF335: 7415
je
.0004AF34C ----- (1)==是的话转到滚轮处理
004AF337: 3D33020000
cmp
eax,000000233 ;" 3"==》比较消息是否是拖放文件
004AF33C: 7400
je
.0004AF33E ----- (2)==》是的话就转到(拖放文件的处理)
好现在我们可以把004AF33C:
7400
je .0004AF33E转到我们自己拖放处理的地方了
现在程序也能判断拖放消息,我们就需要根据这个消息做相关的动作了,也就是需要做打开这个拖放文件并且反汇编它。
学习到这里,大家是不是已经比较有思路了,首先我们必须得到拖放文件的文件名,这就需要DragQueryFile 函数了
The DragQueryFile
function retrieves the filenames of dropped files.
UINT DragQueryFile(
HDROP hDrop, // handle to structure
for dropped files===》拖放的hdrop
UINT iFile, //
index of file to query==》为0就可以了
LPTSTR lpszFile, //
buffer for returned filename==》放文件名的地址
UINT cch //
size of buffer for filename==》buffer的尺寸
);
我们看看这个函数需要什么参数,现在需要找的就是hdrop这个类似于windows窗口的hwnd用来唯一标识拖放的,这个也就是msg的子参数
:0045261E 8B4604
mov eax, dword ptr [esi+04]===》【这里是函数参数的传递,esi+04就是msg 的unit】
esi+04是消息unit,那么这个子参数就在esi+08处
好了相关的参数都搞定了,下一步就是看看w32asm怎么打开一个根据文件名打开文件的动作的;从那里下手呢?从disassembler的open file下手?这个会弹出一个框子出来,不好,那就从recent
files下手,rencent files可以打开你最近打开的文件,好我们看看它是怎么打开的
(跟踪这个动作很复杂,我要详细的说,可能打一天的字也打不完,我打字到现在已经很累了,大家就将就一下,我仅仅讲跟踪的关键地方)
在消息判断(消息是111 wm_command)的地方下断点,然后点击recent files的按钮,发现其子参量为bd02然后又调用自己,这时候的子参量变成
0c5f,当你点击open file时候发现子参量也是0c5f,看来是在点击recent files文件的时候会post一个0c5f消息回来,那么rencent里面的最近打开的文件是存在那个地方呢?经过跟踪可以发现是存在c:\windows\w32dasm8.ini文件里面,
windows用来打开ini文件的api是GetPrivateProfileString,下bpx GetPrivateProfileString这个断点,然后点击recent
里面的最近打开的文件程序中断在这里:(我们就来分析这里是什么意思)
|:100411F0
|
:1004310C
55
push ebp
:1004310D 8BEC
mov ebp, esp===》这是vc编程传递参数的形式,用vc都是这样,和编译器有关系
:1004310F 53
push ebx==》这里传递的参数是窗口的hwnd
:10043110 56
push esi
:10043111 8B450C
mov eax, dword ptr [ebp+0C]==》这里是消息的子参数,这里是最近五个文件的id
:10043114 2DBD020000 sub
eax, 000002BD
:10043119 D1E0
shl eax, 1
* Possible StringData Ref from Data
Obj ->"12345"
|
:1004311B BB3B5A0410
mov ebx, 10045A3B
:10043120 03D8
add ebx, eax===》把eax做转换变成了table表,就知道是点击了那个文件,[ebx]会变成1-5之间
* Possible StringData Ref from Data Obj ->"W32dasm8.ini"
|
:10043122 684A5A0410
push 10045A4A==>参数,文件名
:10043127 6880000000
push 00000080==>buffer size
:1004312C
68575A0410 push 10045A57==>读入内容地址指针
* Possible StringData Ref from Data Obj ->"NULL"
|
:10043131 68455A0410
push 10045A45==》缺省字符串的指针
:10043136 53
push ebx==>key name
* Possible StringData Ref from Data Obj ->"RECENT"
|
:10043137 68345A0410
push 10045A34===>段名
* Reference To: KERNEL32.GetPrivateProfileStringA,
Ord:0125h
|
:1004313C E8AB0B0000
Call 10043CEC==>得到key name下的内容
:10043141 A3305A0410 mov dword
ptr [10045A30], eax==>返回的到字节
:10043146 68575A0410
push 10045A57
* Possible StringData Ref from Data
Obj ->"NULL"
|
:1004314B 68455A0410
push 10045A45
* Reference
To: KERNEL32.lstrcmpiA, Ord:02D9h
|
:10043150 E8F70B0000 Call
10043D4C==>比较上面两个字符串(这个比较的作用是看是不是要调整最近打开的文件的顺序)
:10043155 0BC0
or eax, eax
:10043157
745E je 100431B7
:10043159 BF575A0410 mov
edi, 10045A57
:1004315E 8B0D305A0410
mov ecx, dword ptr [10045A30]
:10043164 8BF7
mov esi, edi
:10043166 03F9
add edi, ecx
:10043168
FD
std
:10043169 B05C
mov al, 5C
:1004316B F2
repnz
:1004316C AE
scasb
:1004316D 85C9
test ecx, ecx
:1004316F 741C
je 1004318D
:10043171 83C702
add edi, 00000002
:10043174 C647FF00
mov [edi-01], 00
:10043178 3BFE
cmp edi, esi
:1004317A
7411 je 1004318D===》这一段的作用是把d:\1\hiew.exe变成d:\1的形式,为SetCurrentDirectoryA做准备
:1004317C 56
push esi==>esi的地址就是X:\XXX\的形式了
* Reference To: KERNEL32.SetCurrentDirectoryA,
Ord:023Eh==》设置现在的路径为X:\XXX\
|
:1004317D
E8A60B0000 Call 10043D28
:10043182 57
push edi
:10043183 68575A0410
push 10045A57
* Reference To: KERNEL32.lstrcpyA, Ord:02DCh
|
:10043188 E8C50B0000
Call 10043D52
* Referenced by a (U)nconditional
or (C)onditional Jump at Addresses:
|:1004316F(C), :1004317A(C)
|
:1004318D 68575A0410 push 10045A57
:10043192 68515E0410 push
10045E51
* Reference To: KERNEL32.lstrcpyA, Ord:02DCh
|
:10043197 E8B60B0000
Call 10043D52===》上面一段的作用是调整最近文件打开的顺序
:1004319C C605505E041002
mov byte ptr [10045E50], 02==》这个可是标志位哦,如果为零的话,那就会弹出那个打开文件的窗口
:100431A3 6A00
push 00000000
* Possible Ref to Menu: MenuID_00CB, Item: "Open
File to Disassemble.."
|
:100431A5
680C5F0000 push 00005F0C
:100431AA 6811010000 push 00000111
:100431AF FF7508
push [ebp+08]==>窗口的hwnd
* Reference To: USER32.PostMessageA, Ord:01DBh
|
:100431B2 E8E70A0000
Call 10043C9E==》我果然没有猜错,就是post一个点击open file消息给自己
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10043157(C)
|
:100431B7 5E
pop esi
:100431B8 5B
pop ebx
:100431B9
C9
leave
:100431BA C20800
ret 0008
这段代码我已经分析完毕,现在我们要做的事情就是在的到DragQueryFile 的消息后,做一样的处理动作,然后post一个open
file消息给自己
,就达到目的了,关键的地方是如何得到PostMessageA的hwnd呢?这个hwnd是w32asm mune的id,windows程序员都知道setmenu的函数吧,我们下断点bpx
setmenu,看到原程序是在这里setmenu
:0049CD86 8BF7
mov esi, edi
:0049CD88 56
push esi===》旧hwnd
:0049CD89 8B0B
mov ecx, dword ptr [ebx]
:0049CD8B FF710C
push [ecx+0C]==》新hwnd
* Reference
To: user32.SetMenu, Ord:0221h
|
:0049CD8E
E865230100 Call 004AF0F8
:0049CD93 85C0
test eax, eax
我们要做的事情就是保存这个新的hwnd,以变PostMessageA的时候用到
:0049CD8E
E865230100 Call 004AF0F8
改为:
0049CD8E: E97D250100
jmp .0004AF310 -----到我们自己保存hwnd的代码
自己保存hwnd的代码:
004AF310: 58
pop
eax==》取出hwnd
004AF311: A3CAF24A00
mov [004AF2CA],eax==》保存在[004AF2CA],以便用到的时候在这里取回
004AF316: 50
push eax
004AF317:
E8DCFDFFFF call
.0004AF0F8 -----call setmenu
004AF31C: E972DAFEFF
jmp
.00049CD93 -----回到程序原来地方
004AF321: 0000
add
[eax],al
004AF323: 0000
add [eax],al
004AF325: 0000
add [eax],al
现在hwnd也有了,而且DragQueryFile会返回打开文件的字节,完全和GetPrivateProfileStringA返回的一样,也就是在原来GetPrivateProfileStringA处改为DragQueryFile就ok,那么其他的PostMessageA、lstrcpyA、SetCurrentDirectoryA、lstrcmpiA的call到那里去找呢,如果能看到import表就直接从import表找,但是这个脱壳版的无import表真是麻烦,没有办法,下断点一个一个看,先来bpx
postmessagea
原程序中断在
:00453402 E85DBD0500
Call 004AF164==》这个就是call postmessagea
其他几个函数如下:
postmessagea :00453402 E85DBD0500
Call 004AF164
lstrcpyA
:00451C49 E80CD00500
Call 004AEC5A
SetCurrentDirectoryA :0043E679 E8B8050700
Call 004AEC36
lstrcmpiA
:0043E468 E815070700
Call 004AEB82
好了现在需要的函数地址也有了,我们开始补丁
004AF330: 3D0A020000
cmp
eax,00000020A ;" ==》比较消息是否是滚轮
"
004AF335: 7415
je
.0004AF34C ----- (1)==是的话转到滚轮处理
004AF337: 3D33020000
cmp
eax,000000233 ;" 3"==》比较消息是否是拖放文件
004AF33C: 7400
je
.0004AF33E ----- (2)==》是的话就转到(拖放文件的处理)
先把
004AF33C: 7400
je .0004AF33E ----- (2)==》是的话就转到(拖放文件的处理)
改成:
004AF33C: 7442
je .0004AF380 -----调到处理拖放的子程序中
处理拖放的子程序:
*这段程序就是参照原来处理程序写的
004AF380: FF7608
push
d,[esi][08]
004AF383: 8F05CEF24A00
pop d,[004AF2CE]==>保存拖放的hDrop到[004AF2CE]
004AF389: 6800010000
push 000000100 ;" "==>缓冲区为100所以不要拖动路径长度大于256的文件哦,可能要出错
004AF38E: 68575A0410
push 010045A57 ;"ZW"==》保存文件名称的地址
004AF393:
6A00
push 000==》UINT iFile
004AF395: FF35CEF24A00
push
d,[004AF2CE]==》push hDrop
004AF39B: E81AFFFFFF
call DragQueryFile
;SHELL32.dll==》得到拖动的文件名
004AF3A0: A3305A0410
mov [10045A30],eax==》返回的字节数保存,以下都是模仿原来程序的动作,上面已经有分析,我就不详细的说明了
004AF3A5: 68575A0410
push 010045A57 ;"ZW"
004AF3AA: 68455A0410
push
010045A45 ;"ZE"
004AF3AF: E8CEF7FFFF
call .0004AEB82 -----
(2)==》call lstrcmpiA
004AF3B4: 0BC0
or
eax,eax
004AF3B6: 0F846A32FAFF
je .000452626 ----- (3)==》如果为零就直接退出,原动作是直接ret,我们就直接返回
004AF3BC: 6660
pusha==》保存所有的寄存器,因为下面要用到esi等,所以现在先保存之
004AF3BE: BF575A0410
mov
edi,010045A57 ;"ZW"
004AF3C3: 8B0D305A0410
mov ecx,[10045A30]
004AF3C9: 8BF7
mov esi,edi
004AF3CB: 03F9
add
edi,ecx
004AF3CD: FD
std
004AF3CE: B05C
mov
al,05C ;"\"
004AF3D0: F2AE
repne
scasb
004AF3D2: 85C9
test ecx,ecx
004AF3D4:
741D
je .0004AF3F3 -----
(4)
004AF3D6: 83C702
add edi,002 ;""
004AF3D9:
C647FF00
mov b,[edi][-01],000 ;" "
004AF3DD: 3BFE
cmp
edi,esi
004AF3DF: 7412
je
.0004AF3F3 ----- (1)
004AF3E1: 56
push
esi
004AF3E2: E84FF8FFFF
call .0004AEC36 ----- (2)就是call
SetCurrentDirectoryA
004AF3E7: 57
push
edi
004AF3E8: 68575A0410
push 010045A57 ;"ZW"
004AF3ED: 90
nop
004AF3EE: E867F8FFFF
call .0004AEC5A
----- (3)就是 call lstrcpyA
004AF3F3: 68575A0410
push 010045A57 ;"ZW"
004AF3F8: 68515E0410
push 010045E51 ;"^Q"
004AF3FD: E858F8FFFF
call
.0004AEC5A ----- (4)就是 call lstrcpyA
004AF402: C605505E041002
mov b,[10045E50],002
;""
004AF409: 6A00
push 000
004AF40B:
680C5F0000 push
000005F0C ;" _
004AF410: 6811010000
push
000000111 ;" "
004AF415: A1CAF24A00
mov eax,[004AF2CA]==》这个就是我们在setmenu保存的hwnd
004AF41A: 50
push eax
004AF41B:
E844FDFFFF call
.0004AF164 ----- (5)就是 call postmessagea
004AF420: FF35CEF24A00
push
d,[004AF2CE]==》这个是保存的 hDrop,
004AF426: E895FEFFFF
call DragFinish
;SHELL32.dll==》拖放处理完后要释放hDrop,调用DragFinish
004AF42B: 6661
popa 恢复堆栈
004AF42D: E9F431FAFF
jmp .000452626 ----- (7)跳回原处理地方
好了到现在为止我们diy加强版的w32asm已经出来了,大家享受一下劳动成果吧,如果有兴趣的话,还可给w32asm做很多改造,使之更加适合与
我们自己的工作习惯。我已经把方法和原理都教给大家了,有谁做出更加power的版本,记得给我一份哦,
此程序在98下测试成功,2000下我想应该不会成功,因为2000下的拖放函数不同,有兴趣的人可以在里面加上getversion函数判断是98还是2000
如果是2000下用另外一套函数实现。我自己很少在2000下使用w32asm所以我就不做了:(,就当留给大家的作业吧
后记:写到这里已经是两手发酸,希望这片文章成为diy pe的经典教程,如果看雪出一本diy pe的书是不是考虑选上我这篇文章:)
你的朋友:pll621
2002.8.24
又对滚轮功能改动了一番。这时滚动的速度趋于正常化,不再那么慢了。你只要更改其中的一个字节,就能改变滚动速度。不过程序代码比原来大了2个字节,呵呵。
基本思路:
将原来老大的按向下箭头的消息重定向改为了实实在在的WM_MOUSEWHEEL,对窗口发送了WM_VSCROLL消息,即纵向滚动条滚动的消息。查MSDN,找到WM_VSCROLL和WM_MOUSEWHEEL的相关参数:
1、WM_VSCROLL:
WM_VSCROLL nScrollCode = (int)LOWORD(wParam);
nPos = (short int)HIWORD(wParam);
hwndScrollBar = (HWND) lParam;
第一个参数与我们的程序关系密切,作一下说明:
nScrollCode:
SB_BOTTOM Scrolls to the lower right
SB_ENDSCROLL Ends scroll
SB_LINEDOWN Scrolls one line down
SB_LINEUP
Scrolls one line up
SB_PAGEDOWN Scrolls one page down
SB_PAGEUP Scrolls
one page up
SB_THUMBPOSITION The user has dragged the scroll box (thumb)
and released the mouse button. The nPos parameter indicates the position of the
scroll box at the end of the drag operation.
SB_THUMBTRACK The user is dragging
the scroll box. This message is sent repeatedly until the user releases the mouse
button. The nPos parameter indicates the position that the scroll box has been
dragged to.
SB_TOP Scrolls to the upper left
其中与我们相关的常量的值是这样定义的,在windows.inc里面有:
WM_VSCROLL
equ 115h
SB_LINEUP
equ 0
SB_LINEDOWN
equ 1
2、WM_MOUSEWHEEL
fwKeys = LOWORD(wParam);
zDelta = HIWORD(wParam);
xPos = LOWORD(lParam);
yPos = HIWORD(lParam);
其中我们最关心的是第二个参数,即zDelta,它是一个DWORD参数的高16位,在C程序里面是word ptr [esi+0Ah],它表示了鼠标滚动的距离。这个参数是这样说明的:
zDelta
Indicates that the mouse wheel was pressed, expressed in multiples
or divisions of WHEEL_DELTA, which is 120. (笔者注:120即78H,如果正向滚动为78H,反向滚动为-78H,在机器里面以补码表示为10000h-78h=0ff88h,这就是老大跟踪得到的两个值。)
:004AF34C 60
pushad
;保存所有的寄存器
:004AF34D 668B460A
mov ax, word ptr [esi+0A] ;取出zDelta值
:004AF351
98
cwde
;符号位扩展ax到eax
:004AF352 99
cdq
;再将符号扩展eax到edx,这里及xor eax,edx sub eax,edx三句是为了求zDelta的绝对值。具体原理自己想吧。
:004AF353 33C2
xor eax, edx
:004AF355 2BC2
sub eax, edx
:004AF357 F7DA
neg edx
;edx作为WM_VSCROLL里面的nScrollCode+10000h*nPos,这里nPos可为0,实际就是nScrollCode。如果滚动方向为正,edx为0,即SB_LINEUP,否则为not
ffffffff=1,即SB_LINEDOWN。
;以下的循环按zDelta对78h的倍数值,重复向窗口发送WM_VSCROLL消息:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004AF36F(C)
|
:004AF359 52
push edx
;先保存nScrollCode值,以免调用PostMessage时把edx破坏。
:004AF35A
50
push eax ;同样道理,保存zDelta值。
:004AF35B 6A00
push 00000000 ;hwndScrollBar,为0
:004AF35D 52
push edx
;nScrollCode
:004AF35E 6815010000
push 00000115 ;WM_VSCROLL消息
:004AF363 FF36
push dword ptr [esi] ;窗口的Handle。在C里是这样的,不要问为什么:)
:004AF365 E83449B90F call 10043C9E
;PostMessage函数。
:004AF36A 58
pop eax
;以下两句弹出保存的两个重要值
:004AF36B 5A
pop edx
:004AF36C 83E878
sub eax, 00000078
;减去一个标准位移量。如果你嫌它还是太慢,改78h为更小的一个数值。不过最好是78h的子因数。这样舒服些。
:004AF36F 77E8
ja 004AF359
;没减完则重复发送下一个WM_VSCROLL消息。
:004AF371
61
popad
:004AF372 EBCE
jmp 004AF342 ;转入原程序流程。
=================================================================================================
老大原文:
004AF34C: 668B460A
mov ax,[esi][0A]==》取出滚轮的子参数
004AF350: 663DFF00
cmp ax,000FF ;" �"==》比较是向上滚还是向下滚
004AF354:
720A
jb .0004AF360 ----- (4)
004AF356:
B828000000 mov
eax,000000028 ;" ("==》向下滚动,改动消息子参数为按键下
004AF35B:
894608
mov [esi][08],eax
004AF35E: EB08
jmps
.0004AF368 ----- (5)
004AF360: B826000000
mov
eax,000000026 ;" &"==》向上滚动,改动消息子参数为按键上
004AF365: 894608
mov
[esi][08],eax
004AF368: B800010000
mov eax,000000100
;" "==》改动消息为按键消息
004AF36D: 894604
mov [esi][04],eax
004AF370: EBD0
jmps .0004AF342 ----- (6)==》跳回原程序
=================================================================================================