相信很多人都知道用vb不能做出全局的钩子,导致这个不能的原因有这么两个:
1、vb无法制作可自由导出函数的dll文件
2、vb对多线程支持非常不好,且运行库中众多的函数都不能在多线程环境中运行
对于前者,已经有现成的解决方案。只要拦截vb编译过程,然后改一下连接程序的参数就可以攻克这个问题。而后者而言,现在为止没有什么完全的解决方法,唯有针对特定问题的答案。
接下来放送一下我弄这个全局钩子的全过程:
首先解决制作可以导出函数的dll的问题。这个问题我是用我自己以前写的一个叫BuildControl的半拉子插件解决的。之所叫半拉子插件,是因为我只完成了导出所有公共函数的功能而已……不过,对于眼前这个问题是绝对够用了。接着,我就开始写入钩子函数之类(并编译成dll,准备第1次测试。
点击hook后,没有什么反应……光这样是看不出来我们的dll是否已经被进入了目标程序的进程空间di,于是我拿出ollydbg开始调试。按了hook后,每按一次键盘就看到目标程序模块列表中一闪而过我们的dll……看来,dll是已经注入了,而问题可能出在其他地方。我在dll的入口处设置了一个断点,再次按了一下键盘。光标停在了dllmain的入口处,再一步步跟踪后终于发现问题所在:
call    ds:FindWindowW
mov     edi, ds:__vbaSetSystemError
mov     esi, eax
call    edi ; __vbaSetSystemError这个__vbaSetSystemError函数就是症结所在了。__vbaSetSystemError函数的作用是调用api函数GetLastError获得api调用的错误代码,以供Err对象使用。编译器把__vbaSetSystemError插入到了每个api函数的调用后面。所以,虽然我在一开始就使用类型库导入api函数,并在整个dll里都使用Unicode版的api而没有使用vb库中的函数,想避免发生线程相关的问题,但是,没想到还是逃不过这个编译器预设的陷阱。现在,问题变为如何使__vbaSetSystemError函数无效。整个程序里有那么多的api调用,不可能搜索程序替换掉每个__vbaSetSystemError函数,那么只能dll文件的函数输入表(IAT)入手,将其函数地址改为另外一个地方。因为调用一次api就必定会调用一次__vbaSetSystemError,所以我们只有1个api的调用机会。可能这里有些同志会想到,用copymemory或writeprocessmemory,但是我们并不知道__vbaSetSystemError函数在导入函数表里的具体地址,而且,导入函数表所在的内存位置一般都是只读的,需要用VirtualProtect函数来设定其为可写后才能将内容写入。这么多的任务是绝对不可能用一个api就能搞定的。经过一番思索,唯一能胜任的就只有CallWindowProc函数了。用CallWindowProc函数可以调用其lpPrevWndFunc参数所指向地址的代码,用这个函数来执行一段能完成上述任务的汇编代码那就万事ok了。
汇编代码主要做这么几个事情:
1、找到__vbaSetSystemError函数在当前dll中的引入地址位置
2、得到VirtualProtect函数的地址
3、调用VirtualProtect函数设定读写权限
4、改写地址得到函数的引入地址是比较简单的,只要看看pe文件结构相关的材料就可以。但是要得到VirtualProtect函数的地址就麻烦了,因为需要先知道其所在库文件Kernel32.dll的内存首地址。因为不同的系统kernel32的装入位置都不尽相同,而根据所有能找得找的定位kernel32地址的方法都有所缺憾,并不能保证一定正确,所以,只能另想办法……
通过类型库使用的api函数与直接在程序中用delcare语句声明的api函数区别在于,前者在编译时就把这些api函数作为导入函数直接编译进程序,而后者则还要通过vb运行库中的DllFunctionCall函数来调用。既然如此,如果我们在程序中使用了通过类型库引入的VirtualProtect函数,那么运行时在dll的输入函数表里就会有这个函数的正确内存地址了。想到这里我就在程序中添加了一个哑函数:
Private Sub DummyFuncionLib()
    Call VirtualProtect(0, 0, 0, 0)
End Sub
这个函数是永远也不会被调用的,他的存在只是为了让输入函数表里有VirtualProtect。写汇编代码,并将其编译成二进制代码,有了二进制代码,接下去就是把它表示到vb中了。我选择用一个有几十个Currency类型成员的结构来存放这堆数字,而不是用数组,因为天知道用数组又会出现什么问题呢。ok,现在我们有了应该有的所有东西。最后在dll入口点函数dllmain的DLL_PROCESS_ATTACH分支里写上调用补丁的代码,然后整个世界就安静了……~~p.s. 我所用的方法可能不是最好的,可能在实际使用过程中又会有这样那样的奇怪问题,但是,我想,这曾经成功过的经历就足以激励我们继续前进了。VB的魅力从未消去……相关链接:
1、编译控制:BuildControl  http://210.33.90.250/inc/vbsrc/buildcontrol.rar
2、程序源码:KBHook        http://210.33.90.250/inc/vbsrc/kbhook.rar

解决方案 »

  1.   

    偶木给你测试过....感谢下偶..^0^WWD真是好同志啊
      

  2.   

    这是反汇编后的补丁代码,供大家参考:                push    ebp
                    mov     ebp                sub     esp                push    ebx
                    push    esi
                    push    edi
                    lea     edi                mov     ecx                mov     eax                rep stosd
                    mov     dword ptr [ebp-4]                mov     dword ptr [ebp-8]                mov     dword ptr [ebp-0Ch]                push    6233CC3Ch
                    push    1EB3D113h
                    lea     eax                push    eax
                    call    sub_70
                    push    6E2BCA17h
                    push    7946C61Bh
                    lea     eax                push    eax
                    call    sub_70
                    cmp     dword ptr [ebp-8]                jz      loc_10A
                    mov     eax                mov     eax                mov     [ebp-8]                jmp     loc_10A; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹?sub_70          proc near               ; CODE XREF: seg000:0000003Ep
                                            ; seg000:00000054p
                    mov     edi                pusha
                    mov     ebp                mov     eax                xor     ecx                add     eax                add     eax                mov     ebx                test    ebx                jz      short loc_E9
                    mov     ebx
    loc_8A:                                 ; CODE XREF: sub_70+77j
                    mov     esi                test    esi                jz      short loc_E9
                    add     esi                call    sub_ED
                    cmp     edx                jnz     short loc_E4
                    push    ecx
                    push    eax
                    xor     ecx                mov     eax                add     eax                push    ebxloc_A9:                                 ; CODE XREF: sub_70+6Fj
                    mov     esi                test    esi                jz      short loc_E1
                    test    esi                jnz     short loc_DE
                    add     esi                add     esi                call    sub_ED
                    cmp     edx                jnz     short loc_DE
                    add     ebx                add     ebx                mov     ebx                add     ebx                lea     ebx                push    eax
                    lea     eax                mov     eax                mov     [eax]                pop     eax
                    jmp     short loc_E1
    ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?loc_DE:                                 ; CODE XREF: sub_70+46j
                                            ; sub_70+55j
                    inc     ecx
                    jmp     short loc_A9
    ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?loc_E1:                                 ; CODE XREF: sub_70+3Ej
                                            ; sub_70+6Cj
                    pop     ebx
                    pop     eax
                    pop     ecxloc_E4:                                 ; CODE XREF: sub_70+2Cj
                    add     ebx                jmp     short loc_8A
    ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
    loc_E9:                                 ; CODE XREF: sub_70+16j
                                            ; sub_70+20j
                    popa
                    retn    0Ch
    sub_70          endp
    ; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹?sub_ED          proc near               ; CODE XREF: sub_70+24p
                                            ; sub_70+4Dp
                    push    eax
                    xor     eax                cdq
                    cldloc_F2:                                 ; CODE XREF: sub_ED+Fj
                    lodsb
                    test    al                jz      short loc_FE
                    ror     edx                add     edx                jmp     short loc_F2
    ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?loc_FE:                                 ; CODE XREF: sub_ED+8j
                    pop     eax
                    retn
    sub_ED          endp
    ; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹?
    sub_100         proc near               ; CODE XREF: sub_104p
                    mov     ebx                retn
    sub_100         endp
    ; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹?
    sub_104         proc near               ; CODE XREF: seg000:00000133p
                    call    sub_100
                    retn
    sub_104         endp; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
    loc_10A:                                ; CODE XREF: seg000:0000005Dj
                                            ; seg000:0000006Bj
                    mov     eax                test    eax                jz      short loc_142
                    mov     eax                test    eax                jz      short loc_142
                    mov     esi                lea     eax                push    eax
                    push    4
                    push    4
                    mov     ecx                push    ecx
                    call    dword ptr [ebp-8]
                    pusha
                    mov     eax                cmp     dword ptr [ebp+0Ch]                jnz     short loc_13C
                    call    sub_104
                    mov     [eax]                jmp     short loc_141
    ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?loc_13C:                                ; CODE XREF: seg000:00000131j
                    mov     ebx                mov     [eax]
    loc_141:                                ; CODE XREF: seg000:0000013Aj
                    popaloc_142:                                ; CODE XREF: seg000:0000010Fj
                                            ; seg000:00000116j
                    pop     edi
                    pop     esi
                    pop     ebx
                    add     esp                mov     esp                pop     ebp
                    retn    14h
      

  3.   

    根本不用那么麻烦,只要去掉类型库声明中的usesgetlasterror的选项,编译器就不会插入__vbaSetSystemError这个函数
      

  4.   

    狂汗,没有好好学习IDL就是不行……丢脸丢大了……
      

  5.   

    huangguanshu同志的一席话让我觉察到了自己是猪的本质。同志们,不好意思,我把那么简单的事情搞得那么复杂,深深得误导了大家,在这里进行深刻的道歉。所以,我决定将这篇文章的名字改为“脱了裤子放屁……”以上整个程序中唯一还可以学习的就是那段搜索指定DLL中输入函数地址的汇编代码了。谢谢!
      

  6.   

    huangguanshu:  豆子一清早被你一句话郁闷坏了,哇哈哈以下是WWD的若干言论请参考:
    (2005-03-15 08:51:49)   超级绿豆
    我费了那么多事才弄出个钩子,一个兄弟的一句话就解决问题了 
    (2005-03-15 08:52:10)   超级绿豆
    idl没有学好阿…… 
    (2005-03-15 08:54:52)   超级绿豆
    丢人啊~~ 
    (2005-03-15 08:55:26)   超级绿豆
    幸好我脸皮比较厚,哈哈哈 
    (2005-03-15 08:55:56)   超级绿豆
    不过,通过这次弄这个,我学会了用汇编代码搜索IAT 
    (2005-03-15 08:56:02)   超级绿豆
    哈哈哈阿,还是有收获地