• 标 题:豪杰V8添加快捷键
  • 作 者:nbw
  • 时 间:004-06-05,21:22
  • 链 接:http://bbs.pediy.com

上次哪位让我做给豪杰添加快捷键的来着,今天发上来,请指正。                           
                          豪杰V8添加快捷键

作者:nbw[NE365]

www.vxer.com

nboy.cnwlt.com

Email:  advice107@sina.com



    这篇文章我讲以下如何给豪杰V8添加快捷键。豪杰的快进快捷键是:Ctrl+PageDown 。我想修改为右方向键。

    在早期,大约是豪杰2000,修改快捷键很方便,当时的处理方式我记得是 cmp   eax, ***** ;  jz ----- ;其中修改**** 为相应的按键消息就完成了快捷键修改。后来,豪杰提供了修改快捷键的功能。修改快捷键更方便。但是到了V8好象这些都没有了。以至于修改快捷键很困难。

    这里要感谢轻描淡写的朋友和NE365的朋友,弟兄们提出不少方法来修改。有的说用RegisterHotKey,有的说用钩子,我这里用钩子来做,因为这种方法很通用,如果设置成全局钩子,就可以作成“一键呼出”那种。不过如果别的方法更方便,还请告诉我。



    首先,我把钩子函数写为动态库Hookdll.dll,把里面的函数先导入到豪杰中,用PEDIT就可以。



导入KeyDll信息到豪杰主程序:



InstallHook     :      RVA==AD228

UninstallHook  :      RVA==AD22C



安装钩子



获得窗口句柄:

       由于钩子函数需要窗口句柄,因此,需从内存中找到句柄存放位置。我在以前写的“豪杰最大化按钮无效”的文章中介绍过寻找方法。这里再粗略说说。

       打开豪杰,用工具(比如TRW的Hwnd命令)查看主窗口句柄,比如说是FD4 H。用WinHex打开豪杰进程的内存空间,以Hex方式搜索 D40F,可以找到:43C9F4 处有这个内容。则,句柄存放位置为:43C9F4 。但是这个地址的内容是程序初始化的时候填入的,下命令bpm  43C9F4  w 。重新打开豪杰,2次中断后,(:410684处), :43C9F4 处被填入正确句柄,具体位置如下:



* Possible Reference to String Resource ID=00001: "Select Directory"

                                  |

:0041067C 6A01                    push 00000001

:0041067E 51                      push ecx

:0041067F E83CF5FFFF              call 0040FBC0

:00410684 83C410                  add esp, 00000010             ;到了这里,窗口句柄被存放到:43C9F4 。所以从这里便可以获得句柄。

:00410687 6870D24300              push 0043D270

:0041068C E8839C0100              call 0042A314

:00410691 8B0D70D24300            mov ecxdword ptr [0043D270]



寻找剩余空间:



用我写的剩余空间查看器分析,部分结果如下:

名称     RVA     OA        尺寸D   可写否

   .text  00030000  00030000      0      可



但是文件地址:30000上面有全零空间。从2FE10 ------ 2FFFF 全为可用空间。且有可读属性。



转华容道(跳转到剩余空间):



:0041067F E83CF5FFFF              call 0040FBC0

:00410684 83C410                  add esp, 00000010

:00410687 6870D24300              push 0043D270



修改为:

:0041067F E83CF5FFFF              call 0040FBC0

:00410684                            jmp  42FE20

                            nop  nop  nop

:0041068C E8839C0100              call 0042A314

少了:00410684   add esp, 00000010  和  :00410687    push 0043D270  记下来,以后补上。



添加自己的代码:



从42FE20开始:

初始化钩子函数

:42FE20         add  esp,10                          ;修补上面占用的代码

              push       43D270



              pushad

              push       000

              push       [43C9F4]

              call  dword     [4AD228]              ;设置钩子函数

              popad

@@:              jmp  10689                   ;返回



       到了这里,钩子便被安装上了,以后在使用豪杰时点任何一个按键,就会被我们的钩子函数拦截。下面我们就要在钩子函数中实现快进功能。首先看看我最开始写的钩子函数:

HookProc       proc _dwCode,_wParam,_lParam

              local @szKeyState[256]:byte



              invoke     CallNextHookEx,hHook,_dwCode,_wParam,_lParam

              invoke     GetKeyboardState,addr @szKeyState

              invoke     GetKeyState,VK_SHIFT

              mov @szKeyState + VK_SHIFT,al

              mov ecx,_lParam           ;_lParam的高16位可以标志每个按键。该参数定义可以参考MSDN

              shr   ecx,16     



              and  ecx,0fffh

              cmp ecx,014dh             ;如果是   014D则说明是右方向键

              jnz   @F

              

              call  _forward        ;这里就可以填写用来快进的函数或者代码



       @@:       xor  eax,eax

              ret



HookProc       endp



       上面的函数很简单,首先获得键盘状态,保存在ecx中,处理ecx,如果为014dh,则说明是右方向键。说实话这个014d是我自己实验得来的,或许不准确。代码中的call _forward 是用来快进的函数。毫无疑问这个函数不好编写,事实上我也不会自己写。

       这个用来快进的函数很好寻找。因为程序的控制菜单中有快进项,所以考虑处理这个菜单的地方。利用我以前的文章讲过的方法,很容易可以找到处理菜单消息的函数是::00410E48 E8E30A0000    call 00411930 。打开一部电影,在菜单中选快进项,会被中断在这里。进入这个函数,单步运行,注意播放屏幕的变化,当走过:004127BA FF5218     call [edx+18] 时屏幕发生很大变动,就是向前快进了很多。这个函数就是传说中的快进函数。

       你或许会说直接把上面的call  _forward 改成call [edx+18] 就可以,当然首先需要确定edx值,如果有需要的话再事先传几个参数就可以。但事实往往和人的想法不一样。因为我没有找到那几个函数的正确的参数,因此也不会正确调用call [edx+18] 。不过不用怕,可以把call  _forward 改成跳转,跳到那个函数的地方,也就是jmp  004127BA  (call  [edx+18]所处的位置)。按照正常理解,这样的跳转肯定会出问题的,但下面我讲讲所谓的“模拟跳转”。

       没有人提出来所谓的“模拟跳转”概念,但肯定有人用过。处理菜单的函数是call 00411930,这个函数的入口如下:



:00411930 81EC24090000            sub esp, 00000924                    ;这里是入口

:00411936 A1EC3C4300              mov eaxdword ptr [00433CEC]

:0041193B 53                      push ebx

:0041193C 55                      push ebp

:0041193D 56                      push esi

                            。。。。。。。

                            。。。。。。。

                            。。。。。。。

:004127BA FF5218                  call [edx+18]                           ;这是快进函数

                            。。。。。。。



在这个函数中便有快进函数。我所谓的“跳转”是说跳转到入口:00411930处 ,所谓的“模拟”是指跳转以前把所有需要设置的参数设置好。这些参数包括寄存器,堆栈和其他一些必要数据。下面是我找的一些参数。

              mov eax,01c385H

              mov [esp+4],eax

              mov eax,543a63h

              push       eax

              mov eax,433cech

              pop  eax

              mov ebx,1c385h

              mov ebp,0111h

              xor  esi,esi

              mov edi,0234h

              mov eax,411930h

              push       410e4dh



              jmp  eax         ;模仿按下快进按钮,跳转到处理函数

把上面这些替代call  _forward ,就可以执行快进函数了。我找的这个模拟环境或许有多余的,我懒得测试,一并写上了。由于这个模拟环境并没有实现完全的模拟,就是说环境设置的不够完全,所以虽然可以执行完快进函数,但继续向下运行就会出错。既然如此,就不用继续运行,而是在运行完快进函数后返回到钩子函数,如下:

:004127B3 6810270000              push 00002710

:004127B8 8B11                    mov edxdword ptr [ecx]

:004127BA FF5218                  call [edx+18]             ;快进函数

:004127BD E9451B0000              jmp 00414307            ;把这里修改,返回到钩子函数



修改这个jmp,跳转到@@: xor  eax,eax  就是 call  _forward 下面的地方。但返回后又要恢复原来钩子函数的堆栈,所以可以在模拟跳转以前把堆栈入口保存,返回后再恢复。我把堆栈入口保存在了[42fe10h] 。返回后从这里取就可以了。同时,修改:004127BD jmp 00414307 也必须遵照SMC的标准,不要改变原来程序的可读性。具体的代码请看下文。



标志位:[42FE14]==01  -----> 按右方向键   [42FE14]==00   ------>没有按右方向键



钩子函数:



HookProc       proc _dwCode,_wParam,_lParam

              local @szKeyState[256]:byte



              invoke     CallNextHookEx,hHook,_dwCode,_wParam,_lParam

              invoke     GetKeyboardState,addr @szKeyState

              invoke     GetKeyState,VK_SHIFT

              mov @szKeyState + VK_SHIFT,al

              mov ecx,_lParam                                       ;_lParam的高16位可以标志每个按键。该参数定义可以参考MSDN

              shr   ecx,16                                               ;414D

                                                                      

              and  ecx,0fffh

              cmp ecx,014dh

              jnz   @F                                            ;判断是不是有方向键

              

              pushad



              mov eax,42fe10h

              mov dword ptr [eax],esp                                   ;保存堆栈入口

              mov eax,42fe14h

              mov dword ptr [eax],001h                                 ;设定标志位为1



              mov eax,01c385H                                      ;设定模拟环境

              mov [esp+4],eax

              mov eax,543a63h

              push       eax

              mov eax,433cech

              pop  eax

              mov ebx,1c385h

              mov ebp,0111h

              xor  esi,esi

              mov edi,0234h

              mov eax,41192CH

              push       410e4dh



              jmp  eax                                            ;模仿按下快进按钮,跳转到处理函数

              nop                                                   ;快进函数执行完毕后便跳转到这个地方

              nop                                                   



              mov eax,42fe14h                                       ;va=0e810a7h

              mov dword ptr [eax],000h                                 ;恢复标志位



              mov eax,42fe10h

              mov esp,dword ptr [eax]              ;恢复原来的堆栈地址.一般来说上面有了pushad,下面直接用popad

                                                 ;便可以了.但是我们这里没有很好地恢复堆栈,所以只好手动调整

              popad                                  ;恢复原来保存的环境



       @@:       xor  eax,eax

              ret

HookProc       endp





处理快进的函数调用:



:004127B8 8B11                    mov edxdword ptr [ecx]

                              mov edx,010AF8F8

:004127BA FF5218                  call [edx+18]                    ;快进函数

:004127BD E9451B0000              jmp 00414307                          



改成: 

:004127BA FF5218                  call [edx+18]



:004127BD E9451B0000              jmp 0042FE5A                  ;跳转到剩余空间



剩余空间添加代码:

0042FE5A:

              push       eax

              mov eax,42fe14h

              mov eax,dword ptr [eax]

              dec  eax                              

              test  eax,eax    

              pop  eax

              jnz   00414307                            ;标志位不为1,直接跳转到程序原来设定的地方。

              pop  eax

              jmp  0e810a7                              ;标志位为1,回到钩子函数后面。



   这样整个修改过程就完工了。当按右方向键就会实现快进的功能。当然原来的ctrl+pagedown也可以用。我想我这篇文章肯定给人很凌乱的感觉,但我也没办法了。在技术上,代码写的很有“冗余”度,也没有卸载钩子(UninstallHook),这个可以在程序结束的时候处理,大家有兴趣可以自己加上。同时我只做了一个快进,也没有处理后退功能。处理方法一样,但是我还是想先把毕业论文做完,嘿嘿。