【文章标题】: XX找茬的研究(非截屏,非对比像素,源码)
【文章作者】: Sam.com[CCG]
【下载地址】: 自己搜索下载
【使用工具】: Delphi 2007
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
对找茬的辅助工具并不感冒,但也接触过,先说下废话,本文文字比较多,重点在讲思路
由于经常在某论坛混,论坛每天会有找茬活动,就是版主发张像找茬那样的图片,然后圈出来回复图片就可以了,因为图片
是人手处理,有时也有很难找的时候,所以就会用工具把右半的图片复制到左边去,然后把这个图层改变下透明就能看出来不
同的地方,顺便推荐个在线处理图片的网页,感觉比较强大 http://www.pixlr.com/editor/?loc=zh-cn
因为每天要在这花些时间,所以想自己做个程序能帮我把图片不同处自动找出来,因为我接触的辅助工具都是像素比较的
方法,所以开始我也这样做了,原理很简单,因为论坛发布的图片是一张的,包括了左右两部分,所以只要把图片分成两半,然后
逐个像素比较颜色,颜色不同的话就把这个像素点改成红色,这样效果是不错,简单不过有点慢,但经过几次使用问题就来了,
因为原图是人手制作的,所以左右两图有时会坐标有点偏差,或者两图间有空白,导致这样简单的对比法几乎所有像素都对不
上坐标的,结果相当于整张图都标上了红色.
上面的方法行不通,也是找茬辅助的难点所在,找了些资料,好像比较难的都是在确定两图的坐标上,当然应该也有其它
方法或者优秀的算法来解决这个问题,但是我不会,其实两图的不同处都是比较大的一块区域,对比的要求并不用严格到每一
个像素,像用透明的方法,坐标偏差比较大都能用肉眼判断出来,那就换个思路,也是简单的方法,不用把不同处标记出来了,
就直接把右边的图片做为一个图层放在最左边,然后用个定时器把这个图层前置和隐藏,这样一个视觉差就能很明显的看出
不同处.这个源码在下面提供,里面有个测试图片,直接拖图片进程序窗口就可以了,双击图片选择颜色后可以用像素对比的
方法来显示.
然后,回想以前玩XX找茬时经常被人虐,虐人的这个看你点得快有威胁还要把你踢出去,实在是理解不了这种人的心理,
既然我的这种方法简单有效,能不能运用到XX找茬里去呢?于是动手研究XX找茬,思路是这样,找到游戏的图片源,找个地方
Hook把图片取出来,按上面的其中一个方法把不同处显示出来,先不考虑效率,也不考虑截屏,只要达到目的
先找找图片是怎么来的,XX找茬有练习模式,比较方便调试,先拦下封包,看看会不会是每次从服务器传过来,如果是的话
说不定包里还有不同处的坐标之类,但结果很失望,收到的包很少,仔细想想,如果从封包入手也挺麻烦的,还要解密封包什么
的,初步来看应该也不像是从服务器发过来的,最多可能也就只有坐标而已,也或者封包数据是从XXGame.exe这个进程传过来,
反正这个方法要处理起来要花比较多时间去处理,先暂时放弃.
图片是找茬游戏重要的保护对象,无论图片是在本地或者服务器传过来,压缩或者加密是必要的,看看游戏目录,有个
zlib1.dll,这个不用说了,ZIP的模块,有个导出函数uncompress,在这入手看看.启动游戏后附加然后下断,只要一加载图片就
断下来了,来看看uncompress的参数,共4个参数
004604B5 |. 52 push edx
004604B6 |. 8B51 10 mov edx, dword ptr [ecx+10]
004604B9 |. 03C2 add eax, edx
004604BB |. 8B5424 08 mov edx, dword ptr [esp+8]
004604BF |. 8D4C24 10 lea ecx, dword ptr [esp+10]
004604C3 |. 50 push eax
004604C4 |. 51 push ecx
004604C5 |. 52 push edx
004604C6 |. E8 A1730000 call <jmp.&zlib1.uncompress>
004604CB |. 83C4 10 add esp, 10
0012F044 05D8D6F8 <---输出缓冲
0012F048 0012F060 <---返回解压后的实际长度
0012F04C 00F90010 <---输入缓冲
0012F050 0000C904 <---输入长度
函数执行过后,到05D8D6F8看看,一看内容就知道是个JPG,JPG的文件头很容易看出来.堆栈往下拉就能看到文件名如
"res\chaBig\xxx\origin.JPG", xxx是个数字,按实际长度把二进制保存为JPG文件,打开看正好就是找茬的图,那么我的猜
想就没错了,所有图片资源都用ZIP压缩的,那么只要把两个图取出来就大功告成了.
先继续中断看看,当游戏换一次图时,共会中断7,8次,前两次是"res\chaBig\xxx\origin.JPG",后面的都是类似
"res\chaBig\xxx\cha1.JPG"的,把所有图片都保存下来看看,傻眼了,一直认为找茬每次都是两个图片,一个是原图一个是
改过的,但XX找茬不是,他是用两张原图,然后5张小图放在其中一个原图的上面,这5个小图就是找茬的地方,想想这种游戏
要保证最小的传输量又要保证找茬的地方每次不同,这个方法也许是最有效的,那么情况麻烦了,我无法得到修改过的图片,
只有5块碎图,游戏应该是根据内设的坐标把这5个小图重绘在原图上,或者先处理完再显示,那么如果要用之前的方法来处理
的话就必须得找到他处理完的地方才行,但是一想到处理的代码可能非常复杂,又或者根本不是我想像的那样,启不是浪费
我宝贵的时间?
本来想开始这么简单顺利,现在一下就把这个方法推翻了,我可真不想花太多时间在它身上,在放弃前再考虑下有没有
什么简单的方法来达到目的,换个思路,在uncompress这个地方Hook应该是最合适的了,既然解压后JPG就直接在内存中取得
到,那么我把5个小图换成其它的图片行吗?比如一个笑脸,那么显示出来后这5个地方就一目了然了,那光是简单的替换,就
考虑用LPK来做吧,反正我也想试试LPK,代码很简单,只要判断一下文件名,把所有小图都统一换成同一个图,果然有效了.
但是,显示是显示出来了,使用中有问题,最明显的是替换的小图显示有问题,几乎所有小图的右下角都是显示灰色的,
还有就是所有不同处都显示一样大小的图片,那么有效的点击范围就出问题了,只有点击左上角比较小的范围才能100%正确,
也许点击范围也是根据小图的XY来确定的,显示问题虽然不美观,但也不影响使用,这样的话替换的图片太大又会画面乱,太小
又会有时点不正确,真是有点郁闷了.
本人比较追求完美,虽然现在这样能用,但效果实在有些接受不了.再想办法改,首先考虑的是不要改变小图的大小,以
免影响点击的判断区域,比如动态生成一个一样大小的图片来替换,那既然要做到这份上了,不如我就直接修改小图,在小图
上做个标记好了,要做到这个效果光是LPK就不行了,因为要引用jpeg这些单元,所以生成的LPK会出错
具体请参考下面的源码ZhaoCha.rar,LPK借用的是Tee_lpk_Delphi_APIHOOK.rar,谢谢Tee8088分享,不过还是没解决
显示灰色的问题,由于时间关系就不处理了,期待高手指点,对于图像的处理不熟悉,大部分是现找现写的,代码比较粗糙,
好处是不用区分找茬的版本,美女或者卡通都通用(两者只是图片和大小不同而已),2个Dll复制到游戏目录下即可,下面注
释下核心代码.
LPK部分:
procedure uncompressCallback(out OutBuf: Pointer; out OutBytes: Integer; const InBuf: Pointer; InBytes: Integer);cdecl;
var
TmpStr: array [0..1023] of char;
DllHandle: THandle;
begin
//引用zlib1的uncompress直接调用它,先解压再处理
uncompress(OutBuf, OutBytes, InBuf, InBytes);
//取文件名,版本更新后有可能要调整
asm
pushad
push dword ptr [ebp+$48]
lea eax, TmpStr
push eax
call lstrcpy
end;
//由于LPK中不能用VCL,所以要另外写个Dll,在这里才加载
if not assigned(@ChangeJpg) then
begin
DllHandle := LoadLibrary(PChar('CJpg.dll'));
if DllHandle = 0 then
begin
asm
popad
end;
Exit;
end else
@ChangeJpg:= GetProcAddress(DllHandle, 'ChangeJpg');
end;
//判断文件名只处理找茬所需的图片,且只处理小图片
if (Pos('res\cha\', TmpStr) > 0) or ((Pos('res\chaBig\', TmpStr) > 0)) then
begin
if Pos('origin', TmpStr) > 0 then
else
begin
ChangeJpg(OutBuf, OutBytes);
end;
end;
asm
popad
end;
end;
处理图片部分:
procedure ChangeJpg(out OutBuf: Pointer; out OutBytes: Integer);cdecl;
var
Jpg: TJPEGImage;
Bmp: TBitmap;
TmpStream: TMemoryStream;
begin
Jpg:= TJPEGImage.Create;
Bmp := TBitmap.Create;
//为什么要多用个TmpStream?因为Jpg我找不到Size变量,就无法写回文件长度
TmpStream:= TMemoryStream.Create;
//从输出缓冲里读出Jpg
TmpStream.Write(OutBuf, DWORD(OutBytes));
TmpStream.Position:= 0;
Jpg.LoadFromStream(TmpStream);
//因为Jpg的像素是只读的,只能转成Bmp来处理,这是网上找到的方法
Bmp.Assign(jpg);
//在图片四边画上红线和绿线,一目了然
Bmp.Canvas.Pen.Width:= 3;
Bmp.Canvas.Pen.Color:= clRed;
Bmp.Canvas.Polyline([Point(0, 0), Point(Bmp.Width, 0), Point(Bmp.Width, Bmp.Height),
Point(0, Bmp.Height), Point(0, 0)]);
Bmp.Canvas.Pen.Width:= 3;
Bmp.Canvas.Pen.Color:= clLime;
Bmp.Canvas.Polyline([Point(0+3, 0+3), Point(Bmp.Width-3, 0+3), Point(Bmp.Width-3, Bmp.Height-3),
Point(0+3, Bmp.Height-3), Point(0+3, 0+3)]);
//处理后的图片转成Jpg
Jpg.Assign(Bmp);
TmpStream.Position:= 0;
Jpg.SaveToStream(TmpStream);
TmpStream.Position:= 0;
//处理后的图片放回原处
TmpStream.Read(OutBuf, TmpStream.Size);
//改成处理后的长度
DWORD(OutBytes):= TmpStream.Size;
Jpg.Free;
Bmp.Free;
TmpStream.Free;
end;
最后来个未点击的效果图:
源码:
PicCmp.rar
ZhaoCha.rar
--------------------------------------------------------------------------------
【经验总结】
LPK不能使用VCL
换个角度,看似复杂的问题可以简单化
--------------------------------------------------------------------------------
【版权声明】: 此文及源码仅限学习研究之用,请勿用于商业用途!
2010年10月05日 21:17:32
[注意]APP应用上架合规检测服务,协助应用顺利上架!
上传的附件: