想实现自动喊话怎么办?可以通过键鼠模拟,也可以找到喊话call,或者也可以找到控件输入call,在调用发送喊话的call,我们这里要找的是控件输入call
我们输入字符的时候肯定是要访问当前控件对象的,那么我可以从这个编辑框内容的长度来当做一个突破口, 当前有多少字符长读,我们就搜多少
最终有7个结果
我们可以移动编辑里的光标,发现有个地址的内容变了,很显然这个地址是存放是光标的位置
剩下的3个都是存储字符串长度的,我们随便找个然后下内存写入断点
输入字符被断下
然后我们Ctrl+F9不断的返回,沿途记录call
那么先从第一个call 分析
分析第一个参数,发现很像是一个结构体
具体分析如下:
0019FA64 0000000B //字符的数量 0019FA68 0000000C //字符的数量+1 0019FA6C 14F6F360 ASCII "ddad4631111" //ascll的指针 0019FA70 0000000C //unicode的长度 0019FA74 0000000C //unicode的长度 0019FA78 12BD3E00 UNICODE "ddad4631111" //unicode的指针
在来看看ecx是什么
从这些名字不难看出来应该是控件相关的
并且在+11c的位置可以到是当前编辑框里的内容,那么这个应该就是控件对象了
先构造这么一个结构体
调用一下,发现可以成功输入
他要发送内容,肯定是要发包的,我们可以在Send函数上下个断点
喊话之后被断下
Ctrl+F9返回,沿途记录call
直接从后面的call开始测试,这里有个简易的方法就是把call Nop掉,然后看看效果还在不在,但是Nop的时候注意堆栈平衡
然后把这个call Nop掉之后发现不会喊话了
那么我们直接调用这个call做个测试,发现可以调用成功
来到刚才的控件输入call,我们追ecx的来源
eax
eax又是这个call的返回值
然后重点来分析一下这个call
006E1A20 8BD1 mov edx,ecx ; 跟节点 006E1A22 8B8A 9C000000 mov ecx,dword ptr ds:[edx+0x9C] ; 大循环 006E1A28 85C9 test ecx,ecx 006E1A2A 75 04 jnz short 画江山.006E1A30 ; 0 006E1A2C B0 01 mov al,0x1 006E1A2E EB 05 jmp short 画江山.006E1A35 ; 1 006E1A30 E8 FBF5FFFF call 画江山.006E1030 006E1A35 807A 79 00 cmp byte ptr ds:[edx+0x79],0x0 ; 大循环结束条件 006E1A39 74 36 je short 画江山.006E1A71 ; 大循环非正常跳出 006E1A3B 84C0 test al,al 006E1A3D 74 32 je short 画江山.006E1A71 ; 大循环非正常跳出 006E1A3F 8B82 80000000 mov eax,dword ptr ds:[edx+0x80] ; 数量=([edx+0x80]-[edx+0x7C])/4 006E1A45 2B42 7C sub eax,dword ptr ds:[edx+0x7C] 006E1A48 C1F8 02 sar eax,0x2 ; eax/4 006E1A4B EB 03 jmp short 画江山.006E1A50 ; 1 006E1A4D 8D49 00 lea ecx,dword ptr ds:[ecx] 006E1A50 8BC8 mov ecx,eax ; 小循环 006E1A52 48 dec eax ; eax-- 006E1A53 85C9 test ecx,ecx 006E1A55 74 1D je short 画江山.006E1A74 ; 正常跳出循环,返回要寻找的控件对象 006E1A57 8B8A 8C000000 mov ecx,dword ptr ds:[edx+0x8C] 006E1A5D 8B0C81 mov ecx,dword ptr ds:[ecx+eax*4] ; 画江山.01010101 006E1A60 8079 79 00 cmp byte ptr ds:[ecx+0x79],0x0 ; 当[ecx+0x79]不等于0的时候循环结束 006E1A64 ^ 74 EA je short 画江山.006E1A50 006E1A66 8B92 8C000000 mov edx,dword ptr ds:[edx+0x8C] ; edx=[edx+0x8C] 006E1A6C 8B1482 mov edx,dword ptr ds:[edx+eax*4] ; edx=[edx+eax*4], 控件对象 006E1A6F ^ EB B1 jmp short 画江山.006E1A22 ; 循环尾 006E1A71 33C0 xor eax,eax 006E1A73 C3 retn 006E1A74 8BC2 mov eax,edx ; edx 006E1A76 C3 retn
这是一个链表套数组的结构
1、一开始会有一个根节点,记做Root
1、有两个循环,一个大循环,一个小循环
2、大循环我们可以看做是一个大控件,小循环是大控件里的那些小控件
3、大循环的终止条件是:[控件对象+0x79]==0
小循环的终止条件是:[控件对象+0x79]!=0
4、在开始遍历小循环开始,会先计算出当前这个大控件里有多少小控件,怎么计算呢?
小控件数量=([对象+0x80]-[对象+0x7C])/4
5、然后小循环开始遍历,用这个数量当做下标i来遍历,但是这并不是终止条件,循环完一次下标-1
6、小循环结束之后,下一个大控件也是用这个下标继续遍历,然后又取出数量
7、遍历方式大循环和小循环都一样,下一个控件对象=[[当前对象+0x8C]+i*4]
以上的这种遍历方式是按照这个call的遍历方式来写的,,但是现在可以推测出这个call应该是游戏用来寻找编辑框对象的,所以会有一些多余的条件,但是我们的目的是要把所有的控件全部遍历出来
那么正确的流程应该是这样:从根节点开始遍历,判断一下这个节点是否存在小控件,如果有小控件的话就继续进去,然后继续判断是否有小控件这种,直到全部结束,那么最好的解决办法就是递归
现在我们知道是怎么遍历的了,就去找根节点的来源即可
来源于ecx
edi
[eax+0x2C]
[esi+0x2c]
[ecx+0x2c]
根节点: [[0x29D40AC]+0x2c]
char* content = (char*)malloc(256); char* str1 = NULL; char* str2 = NULL; //递归遍历 参数:节点:层级 VOID Recursive(DWORD node, DWORD i) { USES_CONVERSION; memset(content, 0, 256); DWORD Size= (*(int*)(node + 0x80) - *(int*)(node + 0x7C)) / 4; //计算控件的数量 DWORD node_temp = 0; for (int i = 0; i < Size; i++) { node_temp= *(int*)((*(int*)(node + 0x8C)) + i * 4); if (node_temp == 0) //这里需要判断一下节点是否存在 { continue; } str1 = W2A((WCHAR*)(*(int*)(node_temp + 0x40))); //控件名 unicode转ascll //判断一下内容是否是空的 if ((*(int*)(node_temp + 0x11C)) != 0) { str2 = W2A((WCHAR*)(*(int*)(node_temp + 0x11C))); //控件内容 memcpy(content, str2, strlen(str2)); memset(content + 32, 0, 100); //这个做了一个长度限制,因为超过这个长度输出的话就会崩溃 MyOutputDebugStringA("层级:%d 控件对象:%X 控件名:%s 控件内容:%s", i, node_temp, str1, content); //输出大控件信息 memset(content, 0, 256); } else { MyOutputDebugStringA("层级:%d 控件对象:%X 控件名:%s ", i, node_temp, str1); } //判断这个对象里还有没有小控件,如果有的话继续递归 if (((*(int*)(node + 0x80) - *(int*)(node + 0x7C)) / 4) > 0) { Recursive(node_temp, i + 1); //递归 } } } //遍历控件按钮 void HookSend::OnBnClickedButton12() { // 根节点=[[0x29D40AC]+0x2c] //数量=([edx+0x80]-[edx+0x7C])/4 DWORD Root = *(int*)(*(int*)(0x29D40AC) + 0x2C); //根节点 MyOutputDebugStringA("控件遍历开始"); Recursive(Root, 0); MyOutputDebugStringA("控件遍历结束"); free(content); }
遍历也花了一点时间,将近6万多个控件,把游戏中的所有控件遍历出来了