加载中…
个人资料
g计划
g计划
  • 博客等级:
  • 博客积分:0
  • 博客访问:391
  • 关注人气:1
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
相关博文
推荐博文
谁看过这篇博文
加载中…
正文 字体大小:

易发港式五张看牌器底牌查看器透视易发外挂易发棋牌作

(2009-12-04 15:34:10)
标签:

杂谈

联系QQ675747477

易发港式五张看牌器底牌查看器透视易发外挂易发棋牌作联系QQ675747477
二、数据的协议分析
下面来分析一下联众升级的协议,本来自己接收到数据了,就可以把数据写下来,不过还是用别人的现成工具会更好一点,
在这里我用的是 comview3.3 来接收数据的,这个工具还是很好用的,建议大家可以下载一个去用一下试一试;下面是一些接收到的数据:
下载地址:
0x0000 05 02 00 00 22 00 00 00-00 20 00 00 C 1 00 00 00 ....".... ..?..
0x0010 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0x0020 06 00 00 00 67 67 2E 68-74 6D 05 02 00 00 23 00 ....gg.htm....#.
0x0030 00 00 01 20 00 00 46 00-00 00 00 00 00 00 00 00 ... ..F.........
0x0040 00 00 00 00 00 00 00 00-00 00 07 00 00 00 67 67 ..............gg
0x0050 2E 68 74 6D 6C .html
像这样的数据,相信让谁都会搞不清楚是什么东西,不过不怀疑有一些天才可以异想天开,不过去从原始数据来分析别人的协
议确实要一些灵感的, :) 。好像是在为自己吹嘘,不好意思了 ..... 不过呢,协议的分析要与实际的操作结合起来,如果你不会玩
升级,很难想像你会很容易分析出来别人的协议来。还好我自己在这方面还不算,还是升级中是一个小官员类的分数据了,呵呵。
我们可以想一下游戏一整个流程,登录、进入游戏室、找到玩家、发牌,扣底、出牌和结束。有了这样一个流程在脑子中可以帮
助想像每一个数据的信息。
我用的是很差的方法去让这个过程和接收到的数据合在一起分析的:
1 、启动 comview 或其它的接收数据包的工具,设置好过滤条件,只接收 IP/TCP 包,目标的 IP 是自己的机器,如果不设置这些条件
呵呵,那网络是的大量数据包就全被你吃下来了,到时要从几万个包中找出你所要的数据,太难了吧 ...
2 、接着就启动联众的游戏,进入升级(为了分析这些协议,我总被人家说慢得像乌龟,还失去了很多分 :( ),这时你就可要注
意了收集数据了,记下当前打会,什么时候叫牌,反牌,得分,出的牌,很从数据都要记录下来,同时还要想着如何出牌才能赢,要
不真有些对不起和你一起玩的对家了。
3 、有了数据一实际上过程的记录,分析协议就有了着手的地方了,我们先看下面的一段数据:
0x0000 00 02 00 80 F 0 00 00 00-01 00 00 00 04 00 02 00 ... ..........
0x0010 00 00 00 00 63 61 6F 77-65 69 5F 30 30 31 30 00 ....ddddd_0010.
0x0020 11 20 00 00 18 00 00 00-67 79 75 67 68 00 00 00 . ......dddd...
从这里可以看到, ddddd_0010( 原始数据不是这个,我修改了)与一起的玩的的名称是不是一样呢,如果是一样的就好了,这个可
是服务器返回来的与你一起玩的玩家信息;
如果你做过 SCOKET 方面的编程,你应该可以知道一个包的大体结构,下面我来说一下:
应用包头标志
包序列号
整个包的长度
CRC 校验(或累加和校验)
命令字
数据体长度
数据体
网络上的数据并不是整像我们想像的那样,发一个包出去,就会在对方接下整个包,很多时候会分几次才能接收下来整个完整的包
。我们再看一个包:
0x0000 10 20 00 80 24 00 00 00-D9 54 DF 77 01 00 00 00 . . $... 賂遷 ....
0x0010 01 00 00 00 00 00 00 00-03 00 00 00 00 00 00 00 ................
0x0020 03 00 00 00 01 00 00 00-01 00 00 00 17 71 40 00 .............q@.
这两个包有什么相同之处,就是包头开始的 8 个字节,是不是可以看出来一个包的整体结构了,注意网络数据格式转换为机器数据格
式的话要反转一下数据 MSDN 上可以找到类似这样的函数 ntohl 、 ntohs 、 htonl 和 htons ,所以我们在看数据时也要把字倒转一下 ...
如果你了解的话,和般的 80 是作为一个回应包的标识,这样我们就可以看到了两个命令字了 :
80 00 00 02 十六进制表示: 0x80000002
80 00 20 10 0x80002010
这两个命令都不同,可以看到,协议上并没有统计的应用包头标志,直接从命令字开始了。有了上面的概念,接下来的自然就是
长度过,因为第一个包没有完整,我们看第二个包,一般长度用两个字节就可以了,但会有用四个字节的,也就是一个 int 类型的值,第二
个包的长度不难看到就是 0x24 ,假设长度使用四个字节,那么内容就应该是 D9 54 .... 71 40 00 , 0x24 = 36 ,我们数据一下,刚好就是
这个长度,呵呵,到现在你和我一样,了解一包的结构形式了,我们正在一步往成功的方向去了 ...
到了这一步,大家也差不多可以动手自己去分析一下协议了,因为一些其它的因为,我在这里就不再往下分析各个命令字的具体作用
了,不过可以告诉大家 0x80000002 是用户登录后服务器返回来的所有玩家信息,注意一下,我们在注册联众用户时,最长的登录名称不能
超过 19 个字节,所以在包就应加上一个结束符,就是 20 个字节了。
上面的分析只是一个引子,由本人水平有限,有什么错误的地方请大家指出,共同学习。
另外我做的一个联众升级助理 V1.0 在 [url]www.csdn.net[/url] 中软件(游戏类,其它类型 ) 上发布了,大家可以下载使用,是完全免费的。因为时间有限,没有做完整
的测试,如果发现在什么 bug ,可以用邮件方式通知我,谢谢!
因为上一次正在做一个项目,所以时间不多,并没有很多的时间去写完关于联众升级协议分析的全过程还有所有协议的全部内容就匆匆在 CSDN 上发表了《外挂制用 联众升级协议分析》(也可以在 CSDN 开发高手 9 月份中的《网络游戏的协议分析》找到相关内容),在网上发表后,收到很多读者的来信,希望我把联众协议分析的整个过程详细完成,并把关键的协议分析的说明一下;正好现在手头的工作不多(不要告诉我的老板呵,要不他狠不得又把所有任务都放到我这了 L )可以去完成这协议的分析说明。
上一篇文章已说明了数据是如何接收的,在这里就不再赘述,如果不了解的朋友可以再找一下这篇文章来读一下。现在我就从协议分析的内容,还有如何从程序中得到数据说明每一个命令的意思,希望大家在读这个文章后,也可以自己去做一个外挂了。
在分析命令之前,还是有必要说明一下如何从接收到的网络数据中提取一个我们要的包的说明(下面内容就是从网络数据中找到一个完整的协议包)
int __fastcall TCltSocket::AnalyLinkRecvPack(int nLen, PCHAR pDataBuff, int & nLastLen, int nSrcPort)
{
int nCPosition = -1 ; // 包头的开始位置
int nCmdFlag = -1 ; // 命令字标志
lpPackHead pPackHead ; // 包头结构
int nAppPackHeadSize = sizeof( PackHead ) ; // 包头长度
if( nLastLen nDataLen > ( nLastLen - nAppPackHeadSize )) // 分析是完成包
{
memcpy( pDataBuff, ( pDataBuff + nCPosition ), nLastLen ) ;
return 1 ;
}
else // 这里的 pPackHead->nDataLen 就是上次我说的包长度了
{
if ( nLastLen nDataLen - nAppPackHeadSize ))
{
nLastLen = 0 ;
}
else
{
nLastLen = nLastLen - pPackHead->nDataLen - nAppPackHeadSize ;
}
try
{
DispatchPackets( (PCHAR)pPackHead, nSrcPort ); // 在这里就得到了一个完整的协议包了
}
catch(Exception &e)
{
}
memcpy( pDataBuff, pDataBuff + nCPosition + pPackHead->nDataLen + nAppPackHeadSize , nLastLen ) ; // 当前剩下的网络数据,下一次接收到其它数据后一起分析
return 0 ;
}
}
接收到一个完成的包后,就可以去分析协议的本质内容了(因为联众的协议没有加密,所以在这里不必要再去做解密这一步工作了,呵呵 J 也正好可以放松一下自己的头脑)。大家如果用 Commview 或其它的工具接收一个完成的游戏数据时都会发现这样一个数据过程:
1 、 0x0000 00 02 00 80 C 8 00 00 00-01 00 00 00 04 00 02 00 ... ?..........
2 、 0x0050 17 20 00 00 04 00 00 00-01 00 00 00 16 20 00 00 . ........... ..
3 、 0x0000 11 20 00 00 18 00 00 00-01 00 E0 00 01 00 00 00 . ........?....
4 、 0x0000 03 20 00 00 04 00 00 00-01 00 00 00 . ..........
接下来就是一堆如下面的数据
5 、 0x0000 0D 20 00 00 38 00 00 00-01 00 00 00 01 00 00 00 . ..8...........
最后还有一个这样的命令
6 、 0x0100 15 20 00 00 08 01 00 00-08 00 00 00 02 65 67 65 . ...........ege
如果我们还记得玩升级时的打牌顺序:登录、发牌、叫牌,开始、出牌,结束,再与上面的指令对照一下,呵呵,现在你会明白这些指令的作用了吧,因为这些命令是每一次都会出现的,所以只要的接收几次打牌的全过程的数据就可以分析出来了。(上面标识出来的不同颜色,前一部分是命令字,后一部分是包的长度,这个分法在上一篇文章中已作说明)
下面就去如何去分析每个命令字作说明:
在这之前,我们先定义每一个包的包头结构:
typedef struct _tagPackHead
{
int nCommand ; // 命令字
int nDataLen ; // 包长度
}PackHead, *lpPackHead ;
上面的得到一个完整的协议包时已用到这个结构了。
一、登录用户命令
0x0000 00 02 00 80 C 8 00 00 00-01 00 00 00 04 00 02 00 ... ..........
0x0010 00 00 00 00 79 6F 75 73-68 69 5F 31 39 38 31 00 ....dddddd_1981.
0x0020 00 B3 03 00 00 00 00 00-C5 DC C5 DC C5 DC CC F8 .?..... 是是是是
0x0030 00 C 5 00 00 00 00 00 00-C7 00 FF 00 00 00 00 00 .?.....? ...
0x0040 01 00 00 00 68 6F 6E 64-61 30 34 31 32 00 00 00 ....ffffff0412...
……………… ..
从数据上看,命令字是 0x80000200 我们不必要每一个数据都要知道它是什么意思,只要找到我们的一数据就行了。从上面所标志的颜色来看,前面的是命令字,红色的是什么呢?大家想一下,打升级一共是四个人,呵呵,你大概会明白了,这个是服务器分配给每个用户的编号,这个很重要,因为其它指令中不会再出现用户名称了,只会有一个用户编号。经过多次分析,用户的编号是固定的,我们按座位坐下来时分,如下面所示:(是在大厅中坐下等人时,不是出牌时)
1
0 ------|------ 2
3
包的前面一部分是用户个数据,后面的就是每一个用户的信息,所以我们定义的包结构如下:
typedef struct _tagRespSHJIUserRegister
{
int nFlag0 ;
char cUCount ; // 用户数
char cFlag1 ;
char cDaPai ;
char cFlag4 ;
}RespSHJIUserRegister, *lpRespSHJIUserRegister;
typedef struct _tagGameUserInfo
{
char cUserNum ; // 用户编号
char cFlag1 ;
char cFlag2 ;
char cFlag3 ;
char cUser[ 20 ] ; // 用户登录名称
char cAlia[ 20 ] ; // 用户中文名称
int nFlag4 ;
}GameUserInfo, *lpGameUserInfo;
用户的名称是 20 个字节,上一篇文章已说明;
上面说明 AnalyLinkRecvPack 函数中用到的 DispatchPackets 如下所示:(这个就是分解每一个包了)
void __fastcall TFrmMain::DispatchPackets( const char * pPack )
{
PackHead * pPackHead = ( PackHead * )pPack ; // 包头
if( pPackHead->nDataLen > MAX_COMMAND_SIZE - sizeof( PackHead )) return ;
switch( pPackHead->nCommand )
{
case RESP_SHJI_USER_REGISTER :
if( pPackHead->nDataLen >= sizeof( RespSHJIUserRegister ) + sizeof( GameUserInfo )) // 过滤错误的包
{
RevGameUserInfo(( char * )( pPackHead + 1 )) ; // 包内容
}
break ;
………………………… ..
}
函数 RevGameUserInfo 就是分解用户信息的,上面我们已定义了包的结构,所以取得用户的信息就很容易了:
void __fastcall TFrmMain::RevGameUserInfo( char * pPackContent )
{
RespSHJIUserRegister * pInfo = ( RespSHJIUserRegister * )pPackContent ;
int nUserCount = pInfo->cUCount ;
for( int i = 0 ; i cUserNum )
{
case POS_LEFT : // 编号为 0 的用户
PanelLeft->Caption = ( String )pUser->cAlia ;
break ;
case POS_TOP : // 编号为 1 的用户
PanelTop->Caption = ( String )pUser->cAlia ;
break ;
case POS_RIGHT: // 编号为 2 的用户
PanelRig->Caption = ( String )pUser->cAlia ;
break ;
case POS_BOT : // 编号为 3 的用户
PanelBot->Caption = ( String )pUser->cAlia ;
break ;
}
}
}
二、发牌命令
原来我分析牌时,因为是先从出牌入手的,所以得到的牌的代号只有 13 个,还有牌的颜色,所以在分析这个命令时一度陷入不能得到结果的地步,不过经过一天的苦想,终于明白过来了,呵呵,在这里先为自己能想到这个干杯一下。 J
0x0050 17 20 00 00 04 00 00 00-01 00 00 00 16 20 00 00 . ........... ..
0x0060 29 00 00 00 19 00 00 00-FF FF FF FF F4 01 00 00 )....... ?..
0x0070 00 00 00 00 38 4A 27 29-13 17 3B 5D 44 32 0F 12 ....8J')..;]D2..
0x0080 5A 58 6A 2E 45 0B 64 24-56 16 3A 3E 05 ZXj.E.d$V.:>.
从上面的数据看命令字是 0x00002016 ,再上面一个命令字 0x00002017 是开始一局游戏标志,这个用来标志一些状态用的;再看一下 0x19 (也就是十进制的 25 )呵呵,想一下我们四个人打两付牌时 8 个底牌,总共是( 54*2 – 8 ) / 4 = 25 刚好,这个位置就是发给我们的牌数(因为还有一付牌,三付牌)我们再看一下数据从 38 4A 27 29-13 17 3B 5D 44 32 0F 12 5A 58 6A 2E 45 0B 64 24-56 16 3A 3E 05 是不是刚好也就 25 个,这个就是服务器发给每一个玩家的牌了,在玩这个游戏时,我们在客户端看到的是一个一个牌得到的,但实际上,一开始我们就全部得到了,如果你解开了这个协议,一开始我们就知道了所有自己的牌,不就可以知道了能叫什么颜色,是否能反别人的牌,还有每一种花色的牌的多少,岂不是很好……
我们定义的数据结构如下:
typedef struct _tagSHJIFaDePaiInfo
{
int nCount ; // 牌数
char cUser ;
char cFlag1 ;
char cFlag2 ;
char cFlag3 ;
int nFlag1 ;
int nFlag2 ;
}SHJIFaDePaiInfo, *lpSHJIFaDePaiInfo ;
结构中不包牌的信息,因为移动指针后,就可以读到牌的信息了(每一个位 Byte 表示一个牌),如下所示:
void __fastcall TFrmMain::RevFaDePaiInfo( char * pPackContent )
{
SHJIFaDePaiInfo * pInfo = ( SHJIFaDePaiInfo * )pPackContent ;
for( int i = 0 ; i nCount ; i ++ )
{
byte btCard = * ((byte *)(( byte * )( pInfo + 1 ) + i * sizeof( byte ))) ;
………………
}
}
三、叫牌命令
每一轮出牌前总会有一个人先叫牌的信息,所以在这里这个命令会在出牌前,可能有多个这样的命令,但最后一个命令就是用户最终叫主了。
0x0000 11 20 00 00 18 00 00 00-01 00 E0 00 01 00 00 00 . ........?....
0x0010 01 00 00 00 01 00 00 00-DE 05 00 00 02 00 00 00 ........?......
叫牌的内容应该包括,那一个用户叫的牌,牌的颜色(黑桃,红桃,草花,方块),叫牌的数量(一个或一双),从上面的我添加的颜色的数据看到,正好就是这三个位置的信息了,这个可能你的多接收几次数据,对比后才会得到,我也是接收了几次叫牌的信息后才分析出来的:
所以我们定义的叫牌的数据结构如下所示:
typedef struct _tagSHJIJiaoPaiInfo
{
char cUser ; // 叫牌用户编号
char cFlag0 ;
char cFlag1 ;
char cFlag2 ;
char cColor ; // 颜色 ( 大王 =0x35, 小王 =0x34)
char cFlag3 ;
char cFlag4 ;
char cFlag5 ;
char cCount ; // 叫的张数
char cFlag6 ;
char cFlag7 ;
char cFlag8 ;
char cFlag14 ;
char cFlag9 ;
char cFlag10 ;
char cFlag11 ;
int nFlag12 ;
int nFlag13 ;
}SHJIJiaoPaiInfo, *lpSHJIJiaoPaiInfo ;
数据结构中的其它的数据标志位是暂时我还不清楚有什么用途,不过只要知道我们想要的足够的信息就可以了。
下面是取得叫牌的信息:
void __fastcall TFrmMain::RevJiaoPaiInfo( char * pPackContent )
{
SHJIJiaoPaiInfo * pInfo = ( SHJIJiaoPaiInfo * )pPackContent ;
if( m_eZhuColor != ( GAMECARDCOLOR )pInfo->cColor )
{
m_eZhuColor = ( GAMECARDCOLOR )pInfo->cColor ;
ShowInitImagePic( m_cDaJi, m_eZhuColor ) ; // 显示在界面上
}
SetJiaoPaiPic( pInfo->cUser, m_eZhuColor, pInfo->cCount ) ; // 显示那一个用户叫牌
}
取得叫牌的信息,你就可以显示在自己的外挂界面上,同时也可以区分用户出牌时主还是付牌了 J
四、扣底命令
如果你是庄家,会的扣底牌的信息,否则,服务器发送的这个指令中是不包含这个底牌的信息的。
0x0000 03 20 00 00 E4 00 00 00-03 00 00 00 1E 00 00 00 . ..?..........
0x0010 03 00 00 00 05 00 00 00-00 00 00 00 01 00 00 00 ................
0x0020 00 00 00 00 01 00 00 00-1D 00 00 00 03 00 00 00 ................
在这个指令中,并没有包括底牌的个数据,不知联众在做这个命令字是为会缺了这个?所以你必需自己去计算一下底牌的个数了,不过只有两种,一付牌和三付牌时是 6 个底,二付牌时是 8 个底,这样就可以把底牌的信息取得了,在扣底的信息中还包含了是那一个用户扣底的,也就是第一个出牌的户了,这个信息用来指示那一个用户最先出牌,不过如果知道庄家是谁也同样可以知道第一个出牌人的编号。定义的数据结构如下所示:
typedef struct _tagSHJICaoDiFlag
{
char cUser ; // 第一个出牌用户 ( 庄家 )
char cFlag0 ;
char cFlag1 ;
char cFlag2 ;
}SHJICaoDiFlag, *lpSHJICaoDiFlag ;
typedef struct _tagSHJICaoDiInfo
{
int nPosition ; // 位置信息
char cColor ; // 牌颜色
char cFlag0 ;
char cFlag1 ;
char cFlag2 ;
char cCard ; // 底牌
char cFlag3 ;
char cFlag4 ;
char cFlag5 ;
int nFlag6 ;
int nFlag7 ;
int nFlag8 ;
int nFlag9 ;
}SHJICaoDiInfo, *lpSHJICaoDiInfo ;
因为前段的信息只有一个,后段的每一个牌的信息是循环出现的,所以从扣底的个数就可以得到每一个底牌的信息了。
void __fastcall TFrmMain::RevCaoDiInfo( char * pPackContent, int nLen )
{
SHJICaoDiFlag * pInfo = ( SHJICaoDiFlag * )pPackContent ;
m_bCanCaculate = true ; // 开始计算标志
m_ZhangJia = (( pInfo->cUser cUser > POS_BOT ) ? 0 : pInfo->cUser ) ;
if( nLen == 4 ) return ; // 如果不包括底牌信息返回
// 底牌信息
for( int i = 0 ; i cColor, pCard->cCard ) ;
ShowPicOnImage(( GAMECARDCOLOR )pCard->cColor, pCard->cCard, PanelDIPAI ) ;
}
}
取得和底牌信息可以显示在界面上,不必每次看是要自己去点击客户端的查看底牌信息按键,同时如果你是看别人打牌,他设置了不让旁观者看时,你也可以直接看到他的底牌信息,呵呵 J
五、出牌命令
上面说是这么多,现在终于到了全部协议分析中最重要的部分内容了;记录每一个玩家所出的牌信息,这样就可以分析还剩余什么样的牌了。
0x0000 0D 20 00 00 38 00 00 00-00 00 00 00 01 00 00 00 . ..8...........
0x0010 00 00 00 00 00 00 00 00-01 00 00 00 00 00 00 00 ................
0x0020 3C 00 00 00 2F 00 00 00-01 00 00 00 09 00 00 00 cOutCount ; i ++ )
{
SHJICardInfo * pCard = ( SHJICardInfo * )( ( char * )( pInfo + 1 ) + i * sizeof( SHJICardInfo )) ;
if( i == 0 ) // 对比第一个牌,如果是相同,返回
{
if( m_LastCard.eColor == (GAMECARDCOLOR)pCard->cColor &&
m_LastCard.cCard == pCard->cCard && m_LastCard.nCardPos == pInfo->cCurOut ) return ;
if( m_CurColor.btStatus == 1 ) // 设置可以检没牌状态标志
{
m_CurColor.rCurColor = (GAMECARDCOLOR)pCard->cColor ;
m_CurColor.btStatus = 2 ;
m_CurColor.cCard = pCard->cCard ;
}
}
SetSendCardPic( pInfo->cCurOut, pInfo->cNextOut, pInfo->nScore, (GAMECARDCOLOR)pCard->cColor, pCard->cCard ) ;
if( i == 0 )
{
m_LastCard.eColor = (GAMECARDCOLOR)pCard->cColor ;
m_LastCard.cCard = pCard->cCard ;
m_LastCard.nCardPos = pInfo->cCurOut ;
}
if( m_CurColor.btStatus == 2 ) // 检测当前用户是否没有这种牌
{
// 出的牌的主牌
if( pCard->cCard == m_cDaJi || pCard->cCard == 0x34 || pCard->cCard == 0x35 || (GAMECARDCOLOR)pCard->cColor == m_eZhuColor )
{
if( ( m_CurColor.cCard == m_cDaJi || m_CurColor.cCard == 0x34 || m_CurColor.cCard == 0x35 || m_CurColor.rCurColor == m_eZhuColor ) == false )
{
SetColorHave( pInfo->cCurOut, m_eZhuColor ) ; // 这一种牌没有了
}
}
else if( (GAMECARDCOLOR)pCard->cColor != m_CurColor.rCurColor )
{
SetColorHave( pInfo->cCurOut, m_CurColor.rCurColor ) ; // 这一种牌没有了
}
}
}
每个玩家出牌的信息都收集到之后,就可以根据每一个玩家所现的牌信息来判断一下他有什么样的牌,分析的结果直接显示在外挂界面上。让你的用户一目了然地知道别人有什么牌,知已知彼,百战百胜,呵呵
六、底牌命令
说了这么多,终于一个游戏到了结束部分了,就是是出完牌后,服务器返回来的最后的底牌信息了。这个指令中同时也包含了一些其它的指令,不过就算不知道也无所谓,因为一轮也就结束了,呵呵,如果你是赢,也不要发表太过激烈的话语,要不对家不会和你玩了,呵呵 J
0x0100 15 20 00 00 08 01 00 00-08 00 00 00 02 01 00 00 . ..............
0x0110 00 00 00 00 01 00 00 00-78 00 00 00 00 00 00 00 ........x.......
0x0120 02 00 00 00 FE FF FF FF-02 00 00 00 FE FF FF FF ....? ...?
0x0130 17 00 00 00 03 00 00 00-0B 00 00 00 00 00 00 00 ................
0x0140 00 00 00 00 01 00 00 00-01 00 00 00 14 00 00 00 ................
底牌的信息只要你看一下你的得分,就很容易知道分数的表示在那一个了,在这里,得分是 120 = 0x78 ,是不是很容易就看出来是那一个位置表示的是分数了。还有就是底牌的信息了,这个和出牌时的信息是差不多的,一个是颜色,牌的编号,这样就可以用下面的数据结构来表示:
typedef struct _tagSHJIFinishInfo
{
int nFlag0 ;
int nFlag1 ;
int nFlag2 ;
int nFlag3 ;
int nScore ; // 得分
int nFlag4 ;
int nFlag5 ;
int nFlag6 ;
int nFlag7 ;
int nFlag8 ;
}SHJIFinishInfo, *lpSHJIFinishInfo ;
typedef struct _tagSHJIFinishCard
{
int nFlag0 ;
char cColor ; // 牌的颜色
char cFlag1 ;
char cFlag2 ;
char cFlag3 ;
char cCard ; // 牌编号
char cFlag4 ;
char cFlag5 ;
char cFlag6 ;
int nFlag7 ;
int nFlag8 ;
int nFlag9 ;
int nFlag10 ;
}SHJIFinishCard, *lpSHJIFinishCard ;
在这里边数据结构也是分为两个部分的,如果是两付牌,那么所包含的底牌信息也是 8 个,在这个指令同样也没有包含这个信息,和上面的扣底牌信息一样,只能自己去给出底牌个数。下面就是得到的底牌信息
void __fastcall TFrmMain::RevFinishInfo( char * pPackContent )
{
SHJIFinishInfo * pInfo = ( SHJIFinishInfo * )pPackContent ;
// 底牌信息
for( int i = 0 ; i cColor, pCard->cCard, PanelDIPAI ) ;
}
// 计算下一轮打的级数
if( pInfo->nScore == 0 ) // 得 0 分
{
if( m_ZhangJia == POS_LEFT || m_ZhangJia == POS_RIGHT )
{
m_cDaJi02 = m_cDaJi02 + 3 ;
}
else
{
m_cDaJi13 = m_cDaJi13 + 3 ;
}
}
else if( pInfo->nScore nScore nScore - 40 * m_nPaiNum ) / ( 20 * m_nPaiNum ) ;
nShenji = ( nShenji   POS_BOT ) m_ZhangJia = POS_LEFT ;
}
if( m_ZhangJia == POS_LEFT || m_ZhangJia == POS_RIGHT )
{
m_cDaJi = m_cDaJi02 ;
}
else
{
m_cDaJi = m_cDaJi13 ;
}
}
在最后,得到底牌信息时,也包含了最后的得分,就可以自己去分析下一级别是打什么,还有庄家是那一个用户,所认上面的过程中包含了这个分析过程,有一部分注释,相信大家可以看得明了。
到此,我所要做的联众升级的协议分析就说完了,也给出了我所做的外挂的部分核心代码,大家从这些代码中应可以看到分析与实现的结合,自己去尝试做一个外挂,对于高级的玩家可能对于外挂不捎一顾,不过,在这里我所做的分析也只是与大家一起学习与探讨,当然也可以与我联系 [email]jimzj@21cn.com[/email] 。
在做协议分析时,还想到一个其它的问题,因为联众是的游戏差不多是种格式做出来的,所以相信你有了上面的升级的协议分析的经验,就可以试着去分析其它的如桥牌,斗地主,双拷等棋牌类游戏了,我知道的这些游戏的端口设置如下:
// 游戏的端口
enum OURGAMEPORT
{
PORT_CHAT = 2002, // 联天
PORT_DODZ = 3003, // 斗地主
PORT_SHJI = 3004, // 升级
PORT_SHKO = 3008 // 双拷
} ;
还有几个问题,我没有时间去实现,不过在这里说出来与大家探讨一下:
1 、 在网吧玩时,如果你知道对家的 IP ,你设置外挂捕捉包时设置只捕捉对方的包,这样你就可以得到了对方的全部牌的信息了(因为在局域网中,全部包信息你都可以接收到,呵呵 J );
2 、 联众在打升级时聊天经常整个游戏死了,你可以利用 2002 端口直接发送联天信息到服务器,这样就不必为聊天程序死了担心;
3 、 如果你知道对家的 IP 地直,在网络上可以在外挂中做一个 C/S 的端口,用来相互传递牌的信息,这样也可以达到了解对家的牌信息的目的;
4 、 联众是可以让其它的玩家去旁观的,所以,如果你正在玩时,可以利用外挂去生成 IP 包去向服务器申请旁观另外几个用户的信息,这样就可以得到全部与你一起玩的玩家的牌的信息了,这样想当于看着别人的牌一打了,是不是很卑鄙,呵


0

阅读 收藏 转载 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 电话:4000520066 提示音后按1键(按当地市话标准计费) 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

新浪公司 版权所有