作为一个J2EE软件攻城师,解决问题解到这个份上,也是被逼的
作为一个J2EE软件攻城师,以下我说的内容和J2EE没有一点关系
作为一个J2EE软件攻城师,以下我提到一些做法、想法,都是来自于直觉,原力于你同在
2016年4月8日,用户又打电话来说OA系统的扫描功能突然又有问题了,扫描不了文件。(其实问题早就有了,只是将就地用着)
在现场折腾了半天,没结果,唯一发现一个异常情况是扫描的过程中和之后一两分钟里,CPU的占用率很高,按我印象当中所知,IE好像是会去终止其内部一些长时间占用CPU的程序的,但是找不到CPU占用率高的原因,最后只能跟用户说:“我回去和公司的同事们研究一下哈~~~”
。。。。。。
一个星期之后才有时间开始研究这个问题,这个OA系统用到的扫描控件是在2006年至2007年间用VB开发的~~~,其扫描功能又是用到了Windows 2000里的那一组柯达扫描控件,一直到现在,这其间我们做的其他客户都是用这个控件的。
一开始,我怀疑是VB控件在64位Windows 7下运行会有问题,为了证实这个想法,我找了一台使用64位Windows 7系统的电脑测试,果然和用户现场的状况一样,CPU占用率很高,于是再在Windows XP下做了一下测试,奇怪了,Windows XP下也是CPU占用率很高,那就是说和操作系统有没关系~~~。(但其实就算是和操作系统有关系那又怎么样能叫用户不要用64位Windows 7的,那就只能是我们想办法让这VB控件在64位Windows 7里运行得良好一些,要么用C++重新开发,那时间就长了,用户等着要解决问题要使用系统,哪里等得了你花几个星期重新开发~~~,几个星期还是最好的打算~~~)
好了,和操作系统没有关系,那我就开始在自己的电脑上反复测试,我发现用其它扫描软件扫描就很正常,例如:清华TH-OCR,这证明的确是扫描控件有问题,继续测试,很快我发现一个问题,就是每次扫描产生的TWAIN.LOG文件都很大,这个TWAIN.LOG文件是在使用Twain接口进行扫描时系统自动产生的日志文件,再进一步观察发现,在CPU占用率很高的时候,这个文件大小也在不断增长,每扫描一页纸就增长700多K,难道是写这个文件会占用CPU这个文件看看,发现有大量的这样的一组记录:
TWAIN_32.DLL – MESSAGE – CTWTRACE– Windows Internet Explorer(423) to TW-Brother DCP-7180DN LAN(419):
TWAIN_32.DLL – MESSAGE – CTWTRACE–IMAGE, IMAGEMEMXFER, GET
TWAIN_32.DLL – MESSAGE – DSM –DsmEntryDiagExit (RC = 0, CC = 0)
从文字上看,估计这组记录表示的就是从扫描仪获取图像数据的一次操作,大概算了一下,每扫描一页纸,就产生3000多组这样的记录。而用清华TH-OCR扫描产生的TWAIN.LOG里只有10多条这组记录。好像找到苗头了,为什么用我们的VB控件扫描就会产生这么大量的获取数据的操作,而人家的就不会呢知道,这种IO操作一般都是要创建一个缓冲区,通过缓冲区分多次把数据取出来,如果缓冲区越小,这IO操作次数就越多,那也就是说,有可能是柯达扫描控件创建的缓冲区太小了,但这柯达控件并没有提供设置缓冲区大小的参数,为了证实缓冲区太小是会导致产生这么多的记录,并且会影响扫描时获取数据的效率,那就只能是我自己写一个测试程序,自己调用Twain接口来扫描,好在几年前我曾初学了一下Twain接口,当时边学边用C++写Demo,但只写了个开头就又要去干别的项目了。于是马上找回那份代码,查阅Twain 规范,继续完成这个Demo,最终证明缓冲区太小的确是会引发这个问题。
但为什么这么多年,这个问题现在才出现呢这个问题一直都存在,只是一直没有遇到激发它的条件。现在终于碰到了。原因可能是这样,这涉及到Twain规范,扫描程序在创建缓冲区时候,缓冲区的大小并不是随意定的,而是由扫描仪或者是扫描驱动来给定的,扫描仪会给出三个值,最小值、最大值、首选值,扫描程序创建的缓冲区大小不能小于最小值或大于最大值,或者要等于首选值,而我测试用的这个扫描仪给出的最小值是104BYTE,最大值是1M,首选值是3M。这样推理,可能柯达控件就是依据最小值来创建缓冲区的。而可能我们以前的用户使用的扫描仪给出的这个最小值都是比较大的,就不会有问题,现在遇到这个兄弟牌打印复印扫描三合一体机居然给了一个这么小的值就出事了。
现在问题来了,柯达扫描控件并没有提供设置缓冲区大小的参数,我没办法指使它使用更大的缓冲区。面对着柯达控件那一堆ocx和dll,那就只能进行逆向分析了。说到逆向,我也就只知道PEiD、OllyICE,汇编语言我也就只知道mov啊、push啊、pop啊什么的,最近才初学了一下保护模式。这个任务对于一个从事逆向工程的人来说真的很容易,但对于我来说真的看不到希望,但用户已经开始主动打电话来问我:“你和同事们研究得怎么样啦strong>”
没办法啊,上马吧。比较幸运的是,柯达控件并没有加壳,不然的话,辞职的心都有了。首先我要找出柯达控件从扫描仪获取缓冲区大小的那一段代码,然后看看这段代码之后它干了些什么再说。Twain接口里经常用的一个函数是DSM_Entry,扫描程序就是调用这个函数,通过不同的参数组合来控制和访问扫描仪的,获取缓冲区大小也是调用这个函数,如下所示:
参数DG_CONTROL,DAT_SETUPMEMXFER,MSG_GET这个组合就是表示要获取缓冲区大小,这三个常量的值分别是1、6、1
参数setupMemXfer用于接收前面提到的最小值、最大值和首选值。
TW_SETUPMEMXFER的定义如下:
typedef struct {
TW_UINT32 MinBufSize;
TW_UINT32 MaxBufSize;
TW_UINT32 Preferred;
} TW_SETUPMEMXFER, FAR * pTW_SETUPMEMXFER;
也就是说,我要找到柯达控件中调用了这个函数,并且第3至5个参数是1、6、1的地方,正常的做法是写一个调用柯达控件的Demo程序,用OllyICE运行这Demo程序,对DSM_Entry函数下断点就行了(后来我才意识其实就是这么简单)。但我却走了个弯路,这是源自我对逆向分析技术的无知吧,我后来也发觉自己那点小聪明其实是多余的。我是这么想的,这个函数传的参数中有1、6、1,那对应的汇编代码应该是:
push 1
push 6
push 1
于是乎,我就用WinHex在ocx里直接找这汇编代码对于的二进制代码6A016A066A01,但没找到,没有的话,那可能扫描功能不是在ocx里实现的,而是在dll里,但dll文件有好几十个,一个开始我还真的逐个逐个打开来找,找了十来个我就没耐性了,于是直接看文件名来猜,看到一个文件名叫oitwa400.dll,twa和Twain很相似,很可疑哦,这时也是鬼使神差的,我换了方法,用OllyICE来打开dll,直接查找push 6这个指令,终于找到一个很可疑的地方,如下图所示:
if (首选值 用首选值
} else if (最小值 用最小值
} else {
用0xFFFC
}
所以,柯达控件最终是使用104BYTE大小的缓冲区。奇怪,为什么不能大于64K呢步就是要篡改这段代码了。最快能看到效果的办法是直接固定用0xFFFC大小,如下图所示,把红框中的代码改成空指令就行了。
至于为什么不能大于64K的问题我没有研究,但我尝试过,如果用大于64K的话是扫不出图片的。但我自己写的C++ Demo用3M都是可以的,这就应该也是柯达控件的问题了。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!