相信很多人都知道用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、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
解决方案 »
- 不能加载控件mschart1 许可证未找到
- asp如何中如何设定超时就跳出循环?
- 鼠标划过文本框1里的某些文字时选中文字,自动复制到文本框2,可能应该算划词吧,如何实现??
- vb取最大号码并发的问题
- 为什么不能改工程文件夹的名字?(我没有分了)
- 请问怎样在VB中做成带icon的button呀?
- 请问各位:如果我在一个Activex中声明了一个dll中的api,浏览器下载时怎么说找不到这个dll啊!
- 如何用ado操作excel表格(不用vba)
- 您觉得抽烟对程序设计有好处吗?抽烟的程序员请进.
- 用adoConnection.Open打开EXCEL文件,为何出现运行错误^-2147418113^
- 在webbrowser中如何直拉进行GET/post方法调用?
- 在VB程序中如何检查IE的版本号
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
(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) 超级绿豆
哈哈哈阿,还是有收获地