• 标 题:diy pe教学3 (28千字)
  • 作 者:pll621
  • 时 间:2002-8-14 18:20:01
  • 链 接:http://bbs.pediy.com

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

  • 标 题:忘了一点,重新贴一下,说明可以在哪里改变滚动的速度。 (4千字)
  • 作 者:wscn
  • 时 间:2002-8-17 17:16:26
  • 链 接:http://bbs.pediy.com

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