找回密码
 点一下
查看: 10979|回复: 13

(5.1日更新)检测其它玩家使用Maphack捡取不可见物品/神符的工具

[复制链接]
发表于 2008-4-27 21:20:39 | 显示全部楼层 |阅读模式
感谢给我非常大帮助的Kook、猪头和rahxephon三位前辈!!!以及许多热心的朋友!
感谢GA研究院。

    当另一方玩家使用Maphack,并且捡取本来不可见(如在黑雾中)的物品/神符,或用瓶子装取不可见的神符时会用盟友模式报告。
所有记录在同目录的""Save.txt""文件中,如:Player(2) Use MapHack TO Pick Up Invisible Item


可能的问题:
两人游戏中对战没问题,RPG若人不满且位置不连续时会错报;
少数时候会漏报;
多人游戏且人满时Player的序号可能是错误的(未严格测试);





*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

下面是思路,魔兽版本1.20E,针对2人游戏分析,附件是代码、双开工具,主要用到Olldbg、编辑工具C32Asm、Japi参考手册JassCraft,希望文章对热爱War3反MH的朋友有一些帮助。

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
魔兽版本为1.20E
'一、下面全是针对1v1二人游戏的调试:

如何自己调试war3协议?
'War3没有加密协议,内容只有一些编号如指令/行为编号,单位编号或目标坐标或玩家编号,稍微进行一些转化后再进行具体使用,
'首先使用双开在本地机器窗口模式打两个魔兽,分别用OLLY-DBG加载后,建立游戏加入并开始;
'在我的调试中发现魔兽使用Ws2_32.dll的WSARecv()接受数据,使用Ws2_32.dll的Send()发送数据,他们俩不是对应的网络函数,下断时候不能弄错

'*************
主机转化了客户端发来的内容并重新发送给所有客户端;而最终程序是要运行在客户端的,所以我们可以在主机方下Send断或在客户端下WSARecv断:

'以下都针对主机方:
'在主机方的Send下断,会不停的被断,复制数次到记事本并比较后,我们发现接到数据大都为这样:{F7 XX 0C 00 XXXXXXX}和{F7 XX 09 00 XXXXXXX}
'这应该是用来效验数据和表示时间流逝的数据;

'因为不停的被断,我们补丁来解决,补丁内容是当byte[DataAddr+2]<>&h0c或&h09时断下:
'用一个JMP从Send的开头跳到我们的补丁0X6f704c00:
'71A2428A >  - E9 7109CEFD   jmp     Game.6F704C00,这里为Send的开头

'6F704C00      60            pushad
'6F704C01      8B5C24 08     mov     ebx, dword ptr [esp+8]           ;  [esp+8]=发送的DATA
'6F704C05      807B 02 0C    cmp     byte ptr [ebx+2], 0C             ;  [ebx+2]=0C或09的位置
'6F704C09      74 08         je      short 6F704C13
'6F704C0B      807B 02 09    cmp     byte ptr [ebx+2], 9
'6F704C0F      74 02         je      short 6F704C13
'6F704C11      CD 03         int     3                                ;  here INT3=数据内容不为0C,09
'6F704C13      90            nop
'6F704C14      61            popad
'6F704C15      8BFF          mov     edi, edi                         ;  send被修改为{JMP Pacth}的内容
'6F704C17      55            push    ebp
'6F704C18      8BEC          mov     ebp, esp
'6F704C1A    - E9 70F63102   jmp     WS2_32.71A2428F                  ;  跳回Send

'回到游戏,当进行一个指令比如选择单位时会被断下,数据是这样的开头{F7 0C 31 00 XXXXXX},而移动是{F7 0C 2A 00 XXXXXX]开头
'记录很多次数据内容到记事本并比较,就可以慢慢分析了。





'####################################
'2人游戏部分截取内容:
'send1$$$$$$$$$$(跑像泉水装瓶子)
'F7 0C 2A 00 6E 00 A1 2B 01 1F 00 12 40 00 28 00 0D 00 2D B8 00 00 17 3F 04 00 AD 35 D1 C5 0D 11
'BB C5 F9 55 00 00 F9 55 00 00 7C 00
'send2$$$$$$$$$$
'F7 0C 2A 00 6E 00 49 0C 01 1F 00 12 40 00 28 00 0D 00 74 9D 00 00 75 D5 04 00 3A DB D0 C5 03 C0
'B7 C5 F9 55 00 00 F9 55 00 00 00 01 0E
'send3$$$$$$$$$&
'F7 0C 2A 00 6E 00 73 29 01 1F 00 12 40 00 28 00 0D 00 86 EE 00 00 64 4B 06 00 BC 15 D2 C5 3A 00
'B9 C5 F9 55 00 00 F9 55 00 00 28 00

'****************************错误start
(注意以下分析后来发现是错误的,在二人游戏不会有问题,但多人游戏就完全不适用,留着它是为了作个对比希望大家不要像我一样犯错,正确的在下面的多人游戏分析部分)

二人游戏部分分析:
'以SEND1对于DOTA分析:
'指向性指令:
地址 大小          内容
'(1  ) [DWORD] {F7 0C 2A 00}表示此消息为指向性命令(但不包括全部)
'(9  ) [ BYTE ]     {01-0C}玩家编号
'(12) [Dword]     表示命令具体代号,已知指向性物品栏N代号为28004012-3C004012;移动=03000012(捡物品/神符都包括在此内)
'           Attack=0F000012,
'(27)[DWORD]从此处开始的两个连续DWORD表示坐标Y,X
'(35)[WORd ]若目的时候单位/物品,此处为单位编号,否则为FFFF(这条不太肯定)

'其他:
'(1 )[dword]{F7 0C 31 00}:此消息为非指向性指令,如HOLD,STOP,选择单位,购买物品
'(9 )[byte ]玩家编号
'(12)[dword]当选择单位时为目标单位编号(这条不太肯定)

***********************************错误分析END



***********************************************
***********************************************start
这部分是对War3协议正确的分析,适用所有人数的游戏:
===部分分析:
{F7 0C XX 00}:
(3 )[Byte ]XX表示此条消息数据总长度,可能包括任何”单位能发出“的指令;每条消息由数个玩家的编号及一个对应指令组成,格式为:【玩家,1F,XXXX,指令;.....;玩家,1F,XXXX,指令;.....】


{01 1F}表示此条消息的玩家编号为01,
(1F+2)[Dword]为指令编号,后面跟一些此指令的附加数据
指令的具体代号已知使用指向性物品栏N=12400028-1240003C,移动=12000003,Attack=1200000F,巡逻=12000016

为什么要把很多人的指令聚集到一块发送?可能是为了节省数据量和加快执行速度(减少Send的执行次数)吧。

*******************************************************end
*******************************************************


'$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
'检测原理:
'检测必须用接受数据的函数(这里是WSARecv)进行,因为Send只发送自己的数据,而WSARecv接受主机发来的所有其他人的数据
'在数据中我们可以获得玩家编号,指令编号,单位编号(若有的话),坐标(若有的话)等,
'若想检测其他玩家捡取不可见物品,大概思路是这样【获得数据=》判断时候是捡取=》
'=》是则判断目标物品(或目标物品的坐标)是否对此玩家可见/不是则直接跳回原函数=》跳回原函数】
'前面2步都没问题,第三步如何判断目标物品或目标物品坐标对玩家是否可见呢?使用Japi。


<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
非常有用的部分:如何获得和使用JAPI?
'在编写地图中,作者们使用JAPI,JAPI中的函数IsVisibleToPlayer(坐标X,坐标Y,玩家),或IsUnitVisible(单位编号,玩家)
'而JAPI是一个又一个类似输出函数的CALL,例如查找JAPI IsVisibleToPlayer:我们用C32ASM打开魔兽进程=》
'=》搜索ASC字符串IsVisibleToPlayer直到最后一个(必须是在0X6F000000以后的)=》在OLLY-DBG中的数据窗口跳到这个地址,在地址上右键并
'查找参考(isvisibletoPlayer是在6F817760),会发现一共有两个调用此字符串的地方,在汇编代码中取到这个位置,发现是这样的形式:
'{{
'6F2AAC61  |.  68 7477816F   push    6F817774                         ; /Arg1 = 6F817774 ASCII "(RRHplayer;)B"
'6F2AAC66  |.  BA 6077816F   mov     edx, 6F817760                    ; |ASCII "IsVisibleToPlayer"
'6F2AAC6B  |.  B9 00DC2C6F   mov     ecx, 6F2CDC00                    ; |入口地址
'6F2AAC70  |.  E8 FB383F00   call    6F69E570                         ; \Game.6F69E570
'}}
'第三行的Mov ecx,6f2cdc00中的6f2cdc00就是此JAPI的入口地址;
'只要参数正确,我们就可以直接调用它了。

'那么如何判断他的参数呢?
'它不是个JAPI吗?我们用地图编辑器做个地图用触发器来调用此函数,如
'【
'Call IsVisibleToPlayer(1.1, 2.2, Player(0))
'Call IsVisibleToPlayer(1.1, 2.2, Player(1))
'Call IsVisibleToPlayer(1.1, 2.2, Player(2))
'】
'然后在此Japi入口处下断慢慢看到底是个什么样,
'发现有一些函数的参数非常简单,但是有一些函数的参数相当麻烦,
'幸运的这个IsVisibleToPlayer很简单,X和Y在截取到的数据中都有了,剩下的参数只有一个,但比较奇怪的是参数Player(0)在函数被调用时被转化成了100008,100009的形式而不是
'我们截取WsaRECV到的数据中的玩家编号01-0C,如何转化?
'再多截取几次,就发现这些函数的参数编号100008、100009等都是固定的对应Player(0),Player(1)等
'这样一来,钩住WsaRecv的补丁中就能对应的将数据中的玩家编号01-0C正确转化成IsVisibleToPlayer中的参数了。
'(要注意的是在DOTA中IsVisibleToPlayer的参数是10000C-1000015与玩家编号01-0C对应,对战是100008-100014与01-0C对应)

'(转化参数与玩家编号的另一个方法是用Japi GetPlayerID,,在游戏开始时枚举出所有参数与玩家对应的编号
',保存起来,然后在补丁中使用,程序用的是这种方法)


'最后还有一个问题,玩家编号01-0C和函数参数Player(x)的10000C-100015对应在RPG两人连续位置或对战中没问题,但在超过俩人
'时可能出现问题对不上号:当作为参数时100008永远对应位置1,100009永远对应位置2,而玩家编号01、02是按"存在的玩家1"、"存在的玩家2"计算的而不是按照位置计算,少一个玩家而空出的位置会打乱这个对应。


^^^^^^^^^^^^^^^^^^^^^^^
检测"点击不可见单位"(Select Invisible Unit):
一种思路是:
1、当接受的数据是选择单位消息时=>从接受的数据中取出unit编号和玩家编号=》调用IsUnitVisible(参数单位编号,Player编号)判断是否可见,不可见就是使用MAPHACK了。
实际操作时发现取出的这个参数"单位编号"调用IsUnitVisible无效。
先来验证数据的单位编号和参数的单位编号是否一样:
用地图编辑器建立地图,调用

call IsUnitVisible(unit_001,Player(0))  \\unit_001是编辑时地图上的一个单位的编号
call IsUnitVisible(unit_002,Player(0))

然后用老方法获得JAPI并下断,发现unit_001被转化成了10000E,10000F
原来数据单位编号和参数单位编号不一样,如何转化?本来卡在这儿了,感谢rahxephon前辈的文章 http://www.ourga.com/bbs/read.php?tid=10851&keyword=
给我很大启发得以继续下去。


%%%%%%%
录像Replay与协议数据:
录像Replay是用zlib.dll压缩过的,解压以后:对一个指令的记录它俩非常相似,除了头有些不一样以外。因为它俩是对应的,所以其实我们也可以从文档中分析出协议数据的大致内容,如指令标志、坐标等
Replay文档可直接点击下载:
http://b2n4jg.tuk.livefilestore. ... Replay.rar?download


%%%%%%%%%%%%%%%%%%%%%%
二、如何调试多人游戏?
没有条件找那么多朋友的话,我们可以上HF或VS,和大家开始游戏后,
用SockMon2005开始截取,发现一直获得数据,关闭截取后看这些数据大都为{F7 27 09 00 XXXXXXXX},猜测应该是用来效验或表示时间流逝的。

为了尽量过滤掉这些,我们修改SockMon的过滤条件,我的主要过滤条件是【API=>只Recv;TYPE=》仅TCP;DATA=》过滤{F7 27 09 00};Other=>不记录错误的调用】,
然后点SockMon的"开始监视",尽快回到游戏,连续狂使用那个要截取的指令,如Mov、Hold、Attack、Stop等的其中一个,使用了差不多十几个后尽快回到SockMon并点"停止监视",接着慢慢看截下的数据,主要是看开头2字节是否为{F7 0C},然后就可以慢慢分析了,也可以参照上面的分析来。




******************************************
三、不涉及协议检测Maphack Select Unit :
这就几乎全使用Japi 了,思路是【循环:{获得每个玩家当前选中的单位=》若被选中单位数量>1则不是作弊;=1则=》判断此单位对此玩家是否可见=》不可见就是此玩家使用Maphck}】

可以使用“GroupEnumUnitsSelected”这些Japi编写,把这些代码写入War3,最后可以给这些代码远程创建一个线程,也可以钩到War3中常被执行的代码中去。

但麻烦的是难以找出那些Unit、Group之类的数据到底是什么格式,rahxephon前辈的文章对分析数据格式非常有启发性:
http://www.ourga.com/bbs/read.php?tid=10851&keyword=

这种方法可能会错过一些非常快速的Maphack点击。





*************************************
程序大致过程(代码只适用二人游戏):
钩住游戏开始:在游戏开始时调用Japi GetPlayerID获得俩个玩家的编号ID,并保存到0X6F87EFF0;
钩住Ws2_32的WSARecv:获得数据后判断是否是玩家右键点击宝物或用瓶子装神符=》是则用Japi IsVisibleToPlayer()判断宝物或神符对此玩家是否可见,参数X,Y坐标从数据内容获得,玩家ID从刚保存的0X6F87EFF0处获得=》不可见则是使用了Maphack,将0X6F87EFFC置为对方玩家ID/可见为正常=》跳回原函数
<3>在程序中用Timer检测0X6F87EFFC,若不为0则表示检测Maphack,
记录并提醒玩家。







1

WarTools.rar

76 KB, 下载次数: 2053

Maphack PickUpDetect1.1.rar

26 KB, 下载次数: 428

评分

参与人数 1威望 +10 收起 理由
kook + 10 原创内容

查看全部评分

发表于 2008-4-27 21:48:54 | 显示全部楼层
Wooh,这看来得慢慢看老
回复

使用道具 举报

发表于 2008-4-28 08:49:08 | 显示全部楼层
分析过魔兽内存的人就知道楼主的工程是多么的艰巨~
回复

使用道具 举报

发表于 2008-4-28 08:52:13 | 显示全部楼层
测试了一下,双开工具没成功~我试了蛮多次的
          那个检测MH只能用于DOTA55?
回复

使用道具 举报

发表于 2008-4-28 11:58:31 | 显示全部楼层
完全没看懂~~~
回复

使用道具 举报

发表于 2008-4-28 12:21:34 | 显示全部楼层
没法看懂楼主那深奥的文章。。。
回复

使用道具 举报

发表于 2008-4-28 12:58:53 | 显示全部楼层
支持。
回复

使用道具 举报

发表于 2008-4-28 15:30:30 | 显示全部楼层
'最后还有一个问题,玩家编号01-0C和函数参数Player(x)的10000C-100015对应在RPG两人连续位置或对战中没问题,但在超过俩人
'时可能出现问题对不上号,暂时没什么好办法改进。

什么是对不上号?10000C之类的数可以直接通过调用Player(X)获得,01-0C应该是固定对应X参数的吧?
回复

使用道具 举报

 楼主| 发表于 2008-4-28 18:08:33 | 显示全部楼层
引用第7楼rahxephon于2008-04-28 15:30发表的  :
'最后还有一个问题,玩家编号01-0C和函数参数Player(x)的10000C-100015对应在RPG两人连续位置或对战中没问题,但在超过俩人
'时可能出现问题对不上号,暂时没什么好办法改进。

什么是对不上号?10000C之类的数可以直接通过调用Player(X)获得,01-0C应该是固定对应X参数的吧?

.......
谢谢大家关注^^
在超过俩人
'时可能出现问题对不上号是因为:当作为参数时100008永远对应位置1,100009永远对应位置2,而玩家编号01、02是按"存在的玩家1"、"存在的玩家2"计算的而不是按照位置计算,少一个玩家而空出的位置会打乱这个对应。
还有双开工具不能用的话,对War31.20e试试:)
回复

使用道具 举报

发表于 2008-4-28 20:38:35 | 显示全部楼层
引用第8楼bitblt_1于2008-04-28 18:08发表的  :

谢谢大家关注^^
在超过俩人
'时可能出现问题对不上号是因为:当作为参数时100008永远对应位置1,100009永远对应位置2,而玩家编号01、02是按"存在的玩家1"、"存在的玩家2"计算的而不是按照位置计算,少一个玩家而空出的位置会打乱这个对应。
还有双开工具不能用的话,对War31.20e试试:)

你的意思是说GetPlayerId返回的编号对不上了? 那么截获的消息中的01到0C也是这个编号而不是Player(X)中的那个X了是吧?

另外JAPI的单位编号是存放单位数据地址的HASH表项编号(不是直接查表,需要转换下,我发过如何根据编号查找单位数据地址的函数也是使用了GAME.DLL的部分内部函数),不过不知道你截获的消息中单位编号是什么内容,应该也有个转换关系的
回复

使用道具 举报

发表于 2008-4-28 20:51:45 | 显示全部楼层
反MH又开始了,支持一下.
回复

使用道具 举报

 楼主| 发表于 2008-4-28 21:29:50 | 显示全部楼层
引用第9楼rahxephon于2008-04-28 20:38发表的  :


你的意思是说GetPlayerId返回的编号对不上了? 那么截获的消息中的01到0C也是这个编号而不是Player(X)中的那个X了是吧?

另外JAPI的单位编号是存放单位数据地址的HASH表项编号(不是直接查表,需要转换下,我发过如何根据编号查找单位数据地址的函数也是使用了GAME.DLL的部分内部函数),不过不知道你截获的消息中单位编号是什么内容,应该也有个转换关系的

GetPlayerId返回的编号对战中一直是100008,DOTA中一直是10000C起头,间隔是1;
截获的消息的玩家编号一直是01起头,间隔为1;
现在发现似乎多人游戏中协议与俩人游戏有很大差异,多人游戏用不了好像主要问题在这,呵呵。
如果单位编号能转化参数编号成实在太好了,我找找以前您发的帖子去,估计截到的就是实际单位编号,不太确定,因为特别乱,有些分析不来。
回复

使用道具 举报

发表于 2008-6-23 16:49:23 | 显示全部楼层
非常精彩的文章。
本来想注册个ID不发帖的 看到这篇忍不住了  谢谢楼主。

另有一点要指出。  OD可以很方便的设置条件断点。
楼主SMC程序而做条件下段有点画蛇添足了。
回复

使用道具 举报

发表于 2008-8-28 14:14:42 | 显示全部楼层
下了源码。。。报告
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 点一下

本版积分规则

Archiver|移动端|小黑屋|地精研究院

GMT+8, 2024-5-5 11:53 , Processed in 0.332973 second(s), 23 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表