一、棋盘结构分析
首先,根据存档文件找到棋子
军旗:0x00000002
地雷:0x00000003
炸弹:0x00000004
司令:0x00000005
军长:0x00000006
师长:0x00000007
旅长:0X00000008
团长:0X00000009
营长:0X0000000A
连长:0X0000000B
排长:0X0000000C
工兵:0X0000000D
而 0表示无,1表示有,但是不知道是什么棋子。
可以用CE找出棋盘结构的内存地址。在上紫色下蓝色的情况下,蓝色正左上角的地址是004991dc(也就是中间九格的左下。)
用OD找到附近地址
004991B4 00 00 00 00 08 00 00 00 02 00 00 00 00 00 00 00 ….……….
004991C4 0A 00 00 00 02 00 00 00 00 00 00 00 07 00 00 00 ….…….…
004991D4 02 00 00 00 00 00 00 00 07 00 00 00 02 00 00 00 …….……
004991E4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
004991F4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
00499204 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
加粗的07就是004991dc,而分析地址的连续性可以看出,这是一个数组,类型大小为12字节。应该是
struct{
int
int
int
} …
这个第一个Int很明显是棋子,而第二个Int根据测试是表示颜色(00橘红色,01蓝色,02绿色,03紫色),第三个不知道是什么。这是在复盘中查看的数据,在实际对战中,除了自己家的棋子,其它的棋子都是1.
二、如何进行记牌
记牌的方式有3种,动态扫描内存、拦截API(替换CALL)和网络协议分析。对于图形识别方式的记牌不考虑,没啥意思。
1、动态扫描内存
这种思路最为简单,在内存中定义一个棋盘结果,单位时间内根据内存变化,来实现记牌。比如,在 某时刻,004992c0(蓝色一行3列)位置,师长叫吃004998df8(紫色一行3列),结果蓝色棋子消失(004992c0值为0),而紫色棋子还在(004998df8值为1),则004998df8处棋子为39+(军长以上,包括军长)。004992c0 +4值表示了颜色,004998df8+4值表示颜色。
这种方式算法上比较复杂,效率也较低,在卡了的情况下要注意处理。这种方式太过简单,效果不太理想。
2、拦截API(替换CALL)
这种方式首先要分析,得出注入点,也就是找CALL。实现方式等同于网游内挂。对数据的识别也是采用内存读取,但是驱动方式改有游戏驱动外挂,而不是外挂去扫描游戏。难点在于找到CALL函数。四国军旗的棋子走动CALL比较难找,今天刚找,还没找到,汗~~~切入点在于数据。用CE监视内存004991dc,右键,查找写入该地址的代码:
在0041e67e处下断点。开始调试调试,上下找一找,找到了播放音乐的call,汗。
004115BD /0F84 CA000000 JE JunQiRpg.0041168D
004115C3 |68 88DE4500 PUSH JunQiRpg.0045DE88 ; res\move.wav
004115C8 |B9 A0CB4900 MOV ECX,JunQiRpg.0049CBA0
004115CD |E8 3E2F0100 CALL JunQiRpg.00424510
004115D2 |8B4424 14 MOV EAX,DWORD PTR SS:[ESP+14]
004115CD 这句,CALL JunQiRpg.00424510 就是播放音乐,换成c++就是
void JunqiRpgCall::MovePlay(void)
{
DWORD address = 0x424510;
__asm{
pushad
PUSH 0x45DE88 //res\move.wav
MOV ECX,0x49CBA0
CALL address
popad
}
}
拦截CALL呢就是要把这段汇编改掉,在汇编中实现跳转到自己的程序(在这里实现记牌),然后再跳回正常流程。(参考API注入)。
3、网络协议分析
这种难度很大,相当于自己实现客户端,原理等同于网游脱机外挂。就不讨论了。