这篇文章介绍喊话功能的实现, 游戏还是热血江湖。
1. 原理:
游戏中, 喊话的函数应该是这样的:
void speak(string contex)
{
doSomthing(contex);
}
我们要实现喊话功能, 只需要找到speak函数的地址, 压入参数然后调用之即可。
2. speak函数分析
逆向工程都是从数据开始的。 所以我们首先用CE来分析数据。
打开CE, 进行如下搜索:
然后,用22222222222过滤
这里我们发现0xD0A784和0x0470766c都可能为喊话内容的地址。修改者两个地址的内容如下:
然后在游戏中按下回车, 发现输出的是222222, 所以我们喊话内容的地址就为0x0470766C。
接下来我们就可以分析是谁调用了这个地址的内容了。用CE查看是谁访问了这个地址:
Ok 下,CE的使命完成, 下面轮到OD上场。
打开od,附加进程, 首先分析0058735f附近的代码:
这里分析得出,这里是字符串的预处理过程, 所以可以往上查看是谁调用了他。
发现是这里, 所以我们可以这样测试一下代码:
1 mov esi, 05FE6758
2 mov edx, [esi]
3 push 0d
4 push 0d
5 push 3ed
6 mov ecx, esi
7 call [edx+4]
验证一下是OK的, 所以这个位置就是喊话的call了。然后我们再来分析各个参数代表的意义,其中0x0D表示的是回车键的键值。
然后esi代表的意义呢, 我们用ce 搜索esi的值,发现如下:
所以ESI = [0X03D4A270]
一切OK了, 所以这里只剩下最后一步了, 往保存喊话内容的地址写入我们需要的字符串。首先我们往字符串的地址下一个内存断点,看什么代码往这一块地址写了值
其中
所以在这里我们只需继续查找esi是如何被赋值的。
分析(CTRL+A)看是谁执行到了0043CA07,
我们验证一下:
发现是OK的。
所以喊话的内容就非常简单了, 执行往[[d0a988]+13c]这个地址写入所需要的字符串,然后调用喊话call就好了。
3. 代码实现
有了前面的分析,代码的实现就非常简单了:
1 void CGameOperate::speak(char* contex)
2 {
3 int*tmp = (int*)(0x0d0a988);
4 char* s = (char*)(*tmp+0x13c);
5 memcpy(s, contex, strlen(contex));
6 _asm
7 {
8 mov eax, 0X03D4A270
9 mov esi, [eax]
10 mov edx, [esi]
11 push 0x0d
12 push 0x0d
13 push 0x3ed
14 mov ecx, esi
15 call [edx+4]
16 }
17 }
4. 总结
这个功能代码实现是非常简单, 主要的工作量就体现在分析上。