实用代码:【源码下载】全局键盘钩子的VB实现

supergreenbean 2005-03-14 04:23:21
相信很多人都知道用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
...全文
767 33 打赏 收藏 转发到动态 举报
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhangqiushui 2005-04-05
  • 打赏
  • 举报
回复
不错
学习了
huangjianyou 2005-04-05
  • 打赏
  • 举报
回复
^_^
jlum99 2005-04-05
  • 打赏
  • 举报
回复
MARK
boywhp 2005-04-05
  • 打赏
  • 举报
回复
我要怎么样才能这样NB啊?有没有学习资料啊?
ningkang 2005-04-05
  • 打赏
  • 举报
回复
up
qyii 2005-04-04
  • 打赏
  • 举报
回复
完全都不明白~~不知道楼上讨论的是什么东西~!
不明不白的up了
bacaihong 2005-04-03
  • 打赏
  • 举报
回复
感谢楼主
dongge2000 2005-04-03
  • 打赏
  • 举报
回复
呵呵,这个是要顶地……
MysticBoys 2005-04-03
  • 打赏
  • 举报
回复
豆子大哥啊!谢谢你让我彻底学会了插入机器码!以前看了个例子,半懂不懂,这会明白了!
vbnewer 2005-04-03
  • 打赏
  • 举报
回复
支持
laviewpbt 2005-04-02
  • 打赏
  • 举报
回复
在支持一下
wzzwwz 2005-04-02
  • 打赏
  • 举报
回复
顶,好东西
boyzhang 2005-04-02
  • 打赏
  • 举报
回复
shiyunlong 2005-03-15
  • 打赏
  • 举报
回复
mark
daisy8675 2005-03-15
  • 打赏
  • 举报
回复
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) 超级绿豆
哈哈哈阿,还是有收获地
supergreenbean 2005-03-15
  • 打赏
  • 举报
回复
huangguanshu同志的一席话让我觉察到了自己是猪的本质。同志们,不好意思,我把那么简单的事情搞得那么复杂,深深得误导了大家,在这里进行深刻的道歉。所以,我决定将这篇文章的名字改为“脱了裤子放屁……”

以上整个程序中唯一还可以学习的就是那段搜索指定DLL中输入函数地址的汇编代码了。谢谢!

zyg0 2005-03-15
  • 打赏
  • 举报
回复
支持,学习
supergreenbean 2005-03-15
  • 打赏
  • 举报
回复
狂汗,没有好好学习IDL就是不行……丢脸丢大了……
rainstormmaster 2005-03-15
  • 打赏
  • 举报
回复
踩一脚:)
laviewpbt 2005-03-14
  • 打赏
  • 举报
回复
女子女子烟酒烟酒去!
加载更多回复(13)

1,451

社区成员

发帖
与我相关
我的任务
社区描述
VB 控件
社区管理员
  • 控件
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧