如何挂接API

   在调用Java的打印时,发现调用Java提供的纸张调整功能有问题(需要实现连续走纸的动态微调,在打印过程中,动态改变纸张高度,来消除走纸误差)。对于该应用,我是在Windows上用MFC做过相应程序的,没有任何问题。网上也有很多介绍,应该说Windows的打印是进行了很好支持的。但为啥Java的实现会有问题呢?   

   看过Java的相关代码后找到原因:发现它在纸张调整时,做了额外的根据服务端纸张匹配工作,导致动态调整纸张高度没有达到预期的效果,也就是说纸张高度被Java的JNI给改掉了。OH,MY GOD,真不知道为啥会这样?      

   不管它了,先头痛医头吧,要解决这个问题,按我的经验,只能是将Java的JNI Dll的中的相应API换掉,去掉相应的匹配逻辑。于是,Long long ago的 API挂接方法这次全面用到了。在使用过程中,有很多选择,也有参考资烊,但还是遇到些问题,我把它整理了一下。

   挂接API实际上有多种方式:        

    方式一:采用木马方式(也有叫特洛依木马)     将原有DLL如aaa.dll更名,如更名为aaa_real.dll。创建自已的aaa.dll,在aaa.dll中,输出所有原始同名符号,不需要替换的API,可以使用函数转发器来实现,        如:在VC中,可以使用#pragma comment(linker,"/export:api_func=aaa_real.api_func")        最后,编写你需要替换的API的实现。            总结:这种方式其实有点不象API挂接,倒更象是插入了新DLL,但确实能达到想要的效果。可是,缺点很明显,假设aaa.dll升级了,添加了新API,那么,我们用来替换的aaa.dll也得重新编写,添加新的API符号,以保证程序能正常运行。    感觉这有很大风险,谁知道用户用啥版本的JDK,我不能所有版本的JDK的JNI都换一个,即使换了,万一以后JDK出新版本呢,那可麻烦大了。没有采用。

   方式二:改写调用者的执行代码。            首先,保证确定DLL已经装载,然后找到需挂接函数的内存地址。           第2步:从内存地址,取出必需的几个字节,保存起来。然后用一个JUMP指令去替换这几个字节,JUMP的目的地是你新定义的函数,注意:该函数应该与原函数的调用规则(签名)完全相同。这样,对于当前进程中,所有对该DLL指定API的调用,都调用了我们替换的API。           第3步:如果要在挂接前先调用原有函数,可以取出保存的字节,放回挂接函数的开头,这样,原有函数得到执行。            总结:这种方式看起来好象非常OK,也比较简单。但实际上存在很大的问题:             问题一:不同CPU的JUMP方式是不同的。所以,不能跨CPU。                 问题二;这是比较要命的,同步问题,当我们在改DLL函数地址时,改写过程中,可能有其它线程要来调用该函数,这下真是完蛋了。                    所以,这种方案我也是不能采用的。

   方式三:改写调用模块的输入节来挂接API。           这种方式相对来说比较复杂,有几个要点:        1:输入节的概念。任何执行模块如果调用了其它DLL,都会有输入节,并且会指明调用函数的地址。这是在模块装载时写入的。            2:嗯,看了1,你可能会想到,事情简单了,无非就是要动态改掉调用模块的输入节,那么,问题又来了,如何才能进入调用者的进程中去改呢?    可以采用Windows挂钩,将我们的修改输入节的DLL插入到调用者的进程。嘿嘿,好象很简单。               3:慢着,还有问题,假如调用是通过LoadLibray和GetProcAddress来调用API的呢?那可是不会在输入节中出现的噢。    那也好办,我们把这两个函数(其实不止两个,分unicode版,还有扩展版),用我们的挂接方式给挂接掉,然后在挂接的函数中,将我们已经要决定的应用挂接API,通通再替换一次。哈哈哈,搞定了。不知看官有没有明白,反正我写完这段,算是完全明白了。             4:最后,还有一个疑问,就是挂钩的问题,使用SetWindowsHookEx时,采用哪种挂法,如果使用WH_GETMESSAGE参数,能够保证所有调用该API的进程都是GUI吗?其实,想想应该没问题,既然是要打印,那自然是会有Message发送的吧。           另外,小小的注意一下,这里我们并不是想更改消息的发送和接收行为,我们只是借用这个挂钩,把我们的DLL插入到调用者的进程(因为,Windows的挂钩并不仅仅是挂某个函数,而是把整个DLL给挂进去了),然后就可以执行我们DLL的代码,来更改调用者的输入节了。嘿嘿嘿,这点一定要搞清楚,要不就会头大了。             5:对了,挂钩不是真的挂了,挂了当然就拉倒了,我们还得解挂,easy,使用UnhookWindowsHookEx。             总结:这种方式比较OK,只要保证多测试一下,代码不要有问题,就不存在CPU啊,DLL版本啊,同步之内的问题。非常OK。              至于实现,代码实在太多,不再列举,可以参见Windows核心编程的第22章,清单22-4例程。我的代码也是以示例代码为主,在VS2005上调试使用通过。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值