老文章档案 · 2003 年

diy pe教学3

破解教程 · 破解杂谈 · 2003-09-16 · 作者:pll621 人气: · 出处:赢政天下整理收藏)

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所以我就不做了,就当留给大家的作业吧

-----------------------------------------

又对滚轮功能改动了一番。这时滚动的速度趋于正常化,不再那么慢了。你只要更改其中的一个字节,就能改变滚动速度。不过程序代码比原来大了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)==》跳回原程序

============================================================================(出处:赢政天下整理收藏)

相关文章:

相关软件:

→ 特别推荐

→ 热点TOP10

我为人人《《==》》人人为我

关于我们 |

帮助(?)

| 广告服务 |

版权声明 |

商业合作 |

发展历程 |

加盟我们 |

联系我们

Copyright © 1999-2003

winzheng.com All Rights Reserved.

繁体版:由华琦数码技术提供

Powered

by:MeSky.Net

2003-2004 年,宽带刚刚普及,下载站的文章区是许多人学电脑的第一课堂。
🕰 → 🤖 从那时到现在

当年一篇教程能让一台电脑焕然一新;今天的效率工具换成了 AI。 想知道当下最可靠的 AI 模型怎么选?看 赢政指数——我们用了 28 年的选软件严谨,现在用来评测大模型。

档案性质声明:本页为赢政天下(1998-2011)老文章的历史档案复原, 内容反映当年发表时的原貌——包括当年的技术表述、软件版本与观点,部分方法在今天已不适用或不合规,仅作互联网历史记录与研究用途, 不提供任何形式的下载。原始数据经由公共网络存档(Internet Archive)复原。发现问题请联系我们
← 返回老文章档案 · 软件史档案馆 · 赢政天下站史