一、需实现的功能
- 找到源程序exe
- 去掉程序中的广告
- 连连看的辅助工具
二、分析思路
此处我们已知改程序为VC6.0程序(MFC程序),故不再赘述辨识方法。
程序特点:
①多进程
②有动态修改代码
③MFC界面库程序
1.找到源程序EXE
①源程序有可能加壳
- 所以我们可能需要进行脱壳的操作
②多进程
- 使用进程遍历工具分析
- 调试,CreateProcessA/W下断点分析
2.去广告思路
①广告
- 对话框
- 网页
②去广告(API)
- DialogBoxA/W
- CreateWindowExA/W
- WinExec
- CreateProcessA/W
- ShellExecuteA/W
在这些API处下断点,进行栈回溯分析,找到对应的代码
3.连连看的辅助工具
功能:一键秒杀
①分析+算法+模拟点击
- 找到连连看数组,使用算法完成自动连接
- 模拟点击对应位置,实现一键秒杀
②利用游戏本身功能函数完成一键秒杀
- 分析道具,使用指南针完成自动连接
- 找到消除CALL,实现一键秒杀
4.要想实现功能需要分析以下数据以及代码
-
实现功能需要的数据及代码
1.连连看数组
2.指南针CALL
3.消除CALL -
分析工具
1.Ollydbg——动态调试代码
2.Cheat Engine——搜索数据
3.Spy++——寻找窗口回调函数
4.PEID/exeinfo——查壳,查编译环境
5.Vistual Studio——开发程序 -
需要使用的技术
1.MFC DLL
使用MFC DLL,方便之处在于不需要自己写DLLMain的case了,直接写在InitInstance函数中即可,且调试时使用Cstring比较方便。
2.SetWindowLong
修改窗口回调函数,在自己的窗口回调函数中处理快捷键响应。
3.CallWindowProc,调用指定窗口回调函数
4.多线程
5.分析测试
在搭建完框架之后,就是边分析边测试,流程如下:
1.先去广告
2.分析数据
3.搭建框架、测试
4.分析道具CALL、测试
5.分析消除CALL、测试
6.CE操作
- CE搜索步骤记录
①游戏时间
1)使用CE工具NOP掉DEC DWORD PTR DS:[ESI+0xC0]
2)根据CE原理编写NOP代码
//获取连连看进程ID
GetWindowThreadProcessId(hWnd, &Pid);
//获取连连看进程句柄
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
//将时间自增的语句使用NOP填充
result = WriteProcessMemory(hProcess, (LPVOID)g_pTime, &szInc, 6, 0);
3)注入前代码
4)注入后代码
5)游戏效果图
②方块数量
三、去广告分析过程
1.我们通过测试可以发现,登陆器的进程名为qqllk.exe,弹出来的广告程序为qqllk.ocx,但是他在我们的进程中出现,其实本质来说还是一个exe
2.点击qqllk.ocx程序的继续后会弹出我们真正的游戏程序kyodai.exe
3.使用OllyDbg附加qqllk.ocx程序,并在CreateProcessA和CreateProcessW处下断点
4.点击继续后我们发现程序在CreateProcessA处断下来了,所以我们需要在这个位置观察一下接下来的代码
5.qqllk.ocx程序在创建后挂起,可以发现kyodai.exe是无法直接启动的,而是需要先启动qqllk.ocx后才能够去启动,故推测创建qqllk.ocx进程后挂起,挂起后将数据远程访问kyodai.exe,进行写入操作将其数据进行修改操作,之后再唤醒线程
6.故此处我们在WriteProcessMemory处下断点
7.写入操作后还需要唤醒线程,故我们继续在ResumeThread处下断点
8.我们可以看到继续运行程序断在了WriteProcessMemory,观察一下程序的变化
9.由于写入地址不在该程序中,所以我们再使用OD附加一下kyodai.exe程序,观察写入情况
10.找到kyodai.exe程序的入口点,并下断点
11.我们唤醒线程后,kyodai.exe程序在入口点处断下,观察0043817A处数据
12.故我们可以使用010Edit,找到0043817A的地址,修改其数据完成单独启动kyodai.exe程序的操作
四、游戏辅助工具分析过程
1.在rand函数处下断点
2.重新排列游戏时触法断点,通过栈回溯寻找数组基地址
3.在初始化数组的下方找到了疑似初始化操作的地址,循环结束后对比内存中的数据与游戏数据
循环结束后观察内存数据与游戏界面,可以发现相同值对应的图案也相同
4.接下来分析指南针道具,设置内存访问断点
5.,程序断下来都进行栈回溯,因为不能明确具体是哪一个CALL,故将每个CALL都设置上断点,逐一尝试
6.找到指南针连接的两个地址
7.寻找ECX赋值的位置,此处复制ESI的值去CE中搜索
8.确定45DEBC为我们寻找的地址
五、DLL注入
1.使用PCHunter查看连连看程序的消息钩子
2.使用Spy++获取窗口名
3.新建一个MFC DLL工程,添加Dialog资源
4.编写对应功能代码
①指南针
1)指南针CALL调用顺序
- 调用道具CALL
0041DE5C |. FF50 28 CALL DWORD PTR DS:[EAX+0x28] ; 使用道具 41E691 - 调用指南针CALL
0041E696 . E8 C5960100 CALL kyodai1.00437D60 ; 指南针 41E76C - 获取连接点
0041E76C . E8 CEAA0000 CALL kyodai1.0042923F ; 获取两个可以连接的点
2)代码部分
if (Msg == WM_DATA1)
{
//指南针
OutputDebugString(L"指南针");
//0x45DEBC
//0041DE4D | . 8B86 94040000 MOV EAX, DWORD PTR DS : [ESI + 0x494]
//0041DE53 | . 8D8E 94040000 LEA ECX, DWORD PTR DS : [ESI + 0x494] ; ECX赋值
//0041DE59 | . 52 PUSH EDX
//0041DE5A | . 53 PUSH EBX
//0041DE5B | . 53 PUSH EBX
//0041DE5C | .FF50 28 CALL DWORD PTR DS : [EAX + 0x28] ; 使用道具 41E691
_asm {
mov ecx,0x45DEBC
mov ecx, [ecx]
lea ecx,DWORD PTR ds:[ecx + 0x494]
push 0xF0
push 0
push 0
mov eax,0x0041E691
call eax
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
②单消
1)在连连看的数据窗口下一个内存写入断点
2)通过栈回溯找到调用的CALL
3)在反汇编代码中找到消除CALL
4)代码部分
if (Msg == WM_DATA2)
{
//0041E75E > \8B8E F0190000 MOV ECX, DWORD PTR DS : [ESI + 0x19F0] ; Case F0(BM_GETCHECK) of switch 0041E749
//0041E764 . 8D45 D8 LEA EAX, DWORD PTR SS : [EBP - 0x28]
//0041E767 . 50 PUSH EAX
//0041E768 . 8D45 E0 LEA EAX, DWORD PTR SS : [EBP - 0x20]
//0041E76B . 50 PUSH EAX
//0041E76C.E8 CEAA0000 CALL kyodai1.0042923F; 获取两个可以连接的点
//1.获取可以连接的两个点
POINT pt1 = { 0 };
POINT pt2 = { 0 };
_asm {
mov ecx, 0x45DEBC
mov ecx, [ecx]
lea ecx, DWORD PTR ds : [ecx + 0x494]
mov ecx, DWORD PTR ds : [ecx + 0x19F0]
lea eax, pt1.x
push eax
lea eax, pt2.x
push eax
mov eax, 0x0042923F
call eax //获取可以消除的图案坐标
}
CString strCode;
strCode.Format(L"pt1:%d,%d pt2:%d,%d", pt1.x, pt1.y, pt2.x, pt2.y);
OutputDebugString(strCode.GetBuffer());
//2.调用消除CALL
//00129BB8 00000000 0
//00129BBC 0012BB50 数组地址
//00129BC0 00129BEC 坐标点1
//00129BC4 00129BF4 坐标点2
//00129BC8 014AB930 坐标点数组
//00129BCC 00000002 数值
_asm {
mov ecx, 0x45DEBC
mov ecx, [ecx] //传递ecx的值
push 0x4 //参数1 固定值
lea eax, DWORD PTR ds : [ecx + 0x494] //0012BB50
mov eax, DWORD PTR ds : [eax + 0x19F0]
add eax, 0x40
push eax //参数2 变量地址 坐标数组地址
lea eax, pt1.x //参数3 点1地址
push eax
lea eax, pt2.x //参数4 点2地址
push eax
lea eax, DWORD PTR ds : [ecx + 0x494] //014AB930
mov eax, DWORD PTR ds : [eax + 0x19F0]
mov eax, DWORD PTR ds : [eax + 0x4]
push eax //参数5 数组地址
push 0 //参数6 0
mov eax, 0x0041C68E //CALL kyodai1.0041C68E
call eax //调用消除
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
③全屏消除
1)全屏消除原理是在单消的基础上增加一个循环,直至消除完成,则退出循环
2)代码部分
//全屏消除
CMFCLLKApp* pApp = (CMFCLLKApp*)AfxGetApp();
//循环消除
for (size_t i = 0; i < 100; i++)
{
int nRet = ::SendMessage(pApp->m_hWnd, WM_DATA2, 0, 0);
if (nRet == -1)
{
break;
}
}
//判断循环结束,即坐标点为0
if (pt1.x == 0 && pt1.x == pt1.y)
{
return -1;
}
④炸弹
1)寻找道具炸弹的参数
2)炸弹使用效果
3)代码部分
if (Msg == WM_DATA3)
{
//炸弹
OutputDebugString(L"炸弹");
_asm {
mov ecx, 0x45DEBC
mov ecx, [ecx]
lea ecx, DWORD PTR ds : [ecx + 0x494]
push 0xF4 //与指南针道理大致相同,仅道具的参数不同(指南针此处为0xF0)
push 0
push 0
mov eax, 0x0041E691
call eax
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
⑤道具拓展
代码部分均参考道具炸弹的修改部分,即可实现其余拓展道具功能
1)闹钟道具
2)镜子道具
3)禁手道具
4)蒙眼道具
5)障碍道具
6)重列道具