Find CALL工具功能与Cheat Engine类似,只不过Cheat Engine是找数据,Find CALL工具是找功能调用函数。这两个工具都为外挂制作者和游戏安全人员带来福音
。Cheat Engine和Find CALL工具界面如图1所示:
图1 Cheat Engine和CallTool界面
在分析游戏中,游戏界面中是没有对应功能按钮的,都是绘制的,这样无法下按钮消息断点,定位功能代码不容易,有了CallTool工具就可以快速定位到功能函数。
一、工具使用:
这里使用Find CALL工具快速找记事本的“替换”功能以说明其使用。
1).在PID栏填写记事本进程ID:
图2 在PID栏填写记事本进程ID
2).点击“Call按钮”,弹出成功对话框:
图3 弹出成功对话框
3).点击记事本“编辑”->“替换”,在“查找内容”对话框输入“aaaaaa”后点击“查找下一个”按钮:
图4“查找下一个”按钮
4).现在此功能已经调用了1次,更新调用次数为1次,并点击Updata按钮:
图5 更新调用次数为1次
5).重复步骤4,更新界面选项为2次,点击Updata按钮后仍然提示剩余4个CALL:
图6 更新调用次数为2次
此时说明不能再缩小范围了。
6). 点击Read按钮,右上方的List控件会显示找到的CALL,ID编号、CALL的地址、CALL的目标地址、调用CALL的数量:
图7 点击Read按钮后的显示
7).点击Clear按钮,再点击Order按钮,提示再次执行要定位的功能以便CALL Tool工具能定位:
图8 点击Clear、Order按钮后
8).重复执行步骤3,再点击“Read按钮”,左上方的List控件显示找到Call的调用顺序:
图9 点击Read按钮后界面显示
9). 点击Clear按钮,再点击Stack按钮,提示再次执行要定位的功能以便CALL Tool工具能定位:
图10 点击Clear、Stack按钮后
10). 重复执行步骤3,再点击“Read按钮”,下方的List控件显示找到Call的堆栈调用关系:
图11 点击Read按钮后界面显示
11). 图11有4条堆栈调用,从右往左依次调用,第一条(ID为0)调用路径是0x1007511->0x10029e0->0x100372e->0x1001faf。可以看出第二条路径(ID位0001)的Target为 010029e0在第一条调用路径上; 第三条路径(ID位0002)的Target为 0100372e在第一条调用路径上; 第四条路径(ID位0003)的Target为 0100372e在第一条调用路径上。
所以第一条调用的0x1001faf地址是最底层的调用Call。使用OD附加并 go到对应地址0x1001faf,发现其下有MessageBoxW调用,F9运行,断点触发:
图12 MessageBoxW地址处断下
跟踪到MessageBoxW,可以看到OD堆栈窗口出现找不到“aaaaaa”,正是记事本弹出的提示对话框:
图13 找不到“aaaaaa”弹框提示
因为替换查找功能执行后,找不到,就弹出提示对话框了。这就说明了0x1001faf的地址是最底层的Call,其它3个Call就是真正功能的执行函数了。
上文说明了如何找记事本程序的功能函数,同样可利用CallTool工具在游戏中查找某功能。
二、代码分析:
CALL TOOL工具实现的核心思想是反汇编需分析的程序,寻找Call调用(机器码0xE8、0xFF15),替换成自己的shellcode函数,shellcode函数预先在堆中分配好。shellcode函数中实现记录函数调用次数、调用先后次序、函数之间的堆栈层次关系。以下给出记录函数调用次数的流程如图14:
图14 记录函数调用次数的流程图
以下给出记录函数调用次数的伪代码:
bool CCallToolDlg::GetCallCountHook(PBYTE pModBaseAddr, PCHAR szExePath, HANDLE hProcess)
{
打开需分析的可执行文件…
设置文件读取游标为文件开始处…
读取文件内容到申请的内容…
用反汇编引擎反汇编文件内容,寻找call xxx、call [xxx]调用,并记录下总共call数量;
使用VirtualAllocEx(hProcess… 在进程内存中为每个call申请3个DWORD内存空间;
if (申请成功)
{
…
BYTE shellcode[] =
{
0x60, 0x8D, 0x35, 0x24, 0x09, 0x3E, 0x00, 0x8B, 0x06, 0x40, 0x89, 0x06, 0x61, 0x68, 0x7E, 0x38, 0x00, 0x01, 0xE9, 0x30,
0x14, 0xC2, 0x00
};
while (循环读取代码段)
{
用反汇编引擎反汇编…
寻找0xE8(call xxx)、0xFF 0x15(call [xxx])调用…
获得Call xxxx中xxxx内容…
把Call xxxx中xxxx内容由"相对地址"转换为"绝对地址"(在申请的内存中)…
加判断,防止把数据识别成代码,
RtlCopyMemory(CI.ShellCode, shellcode, 23); //把shellcode copy到结构体
*(PDWORD)(CI.ShellCode + 3) = pRemoteAddr + sizeof(DWORD) * 2; //赋值shellcode中的lea esi,dword ptr ds:[3E0924] 为目标进程dwCount位置
*(PDWORD)(CI.ShellCode + 0x0e) = dwCodeAddr + 5; //赋值shellcode中的push 100387E 为原Call的地址下一条指令[Call(0xE8)指令占字节]
*(PDWORD)(CI.ShellCode + 0x13) = dwTarget - (pRemoteAddr + sizeof(DWORD) * 4 + 0x12) - 5; //赋值shellcode中jmp 01001D73 计算跳转偏移(为原Call到shellcode的偏移)
WriteProcessMemory(hProcess, (LPVOID)pRemoteAddr, &CI, sizeof(CALL_INFO), &NumberOfBytes); //写入shellcode到申请的空间
更改Call Target 为JMP ShellCode…
}
}
关闭打开的文件…
}
该函数打开被分析进程读取其内存,寻址其中的call调用(0xE8 call xxx、0xFF 0x15 call [xxx])替换为自己构造的shellcode地址,shellcode中实现“调用次数”自增操作。Shellcode的内容是60 8D 35 24 09 3E 00 8B 06 40 89 06 61 68 7E 38 00 01 E9 30 14 C2 00,对应汇编代码为:
pushad
lea esi,dword ptr ds:[3E0924]
mov eax,dword ptr ds:[esi]
inc eax
mov dword ptr ds:[esi],eax
popad
push 100387E
jmp 01001D73
以上代码先用pushad保存8个常用寄存器环境(eax、ebx、ecx、edx、ebp、esp、esi、edi)。因为随后的代码将使用这些寄存器。然后用lea esi,dword ptr ds:[3E0924],esi赋值为3E0924,3E0924随后会替换为自申请的空间地址。mov eax,dword ptr ds:[esi]取esi 中的内容也就是取3E0924地址中的内容赋值给eax。inc eax自增1,实现计数器+1。mov dword ptr ds:[esi],eax把eax中的数据保存到esi所指向地址的内存中,也就是计数器保存到3E0924地址中的内存中。Popad还原前面保存的8个寄存器内容,恢复环境。push 100387E把100387E压入栈中,100387E在后续的代码中会替换为原始call地址下面一条指令的地址,跳到shellcode运行完毕后会跳回来继续执行,不影响程序原始逻辑。jmp 01001D73跳到01001D73地址去执行,01001D73后续会替换为call到的地址。
该函数也为每个call调用申请三个DWORD,把函数调用次数记录在此段空间的第三个DOWRD中,其前面两个DWORD分别保存调用顺序编号、栈调用层次。
更详细的请见代码。
注意代码编译前,先安装XTToolkitPro。
exe: CallTool.rar
源代码:
CallTool.part01.rar
CallTool.part02.rar
CallTool.part03.rar
CallTool.part04.rar
CallTool.part05.rar
CallTool.part06.rar
文档: 【原创】Find CALL Tool(带源码).doc
[课程]Linux pwn 探索篇!