- 找到程序本身的 exe
- 去掉程序中的广告
- 写一个 QQ 连连看的外挂
先双击运行 qqllk.exe,出现一广告窗口,后点击开始游戏按钮,出现另外一个百度广告窗口。
通过 PChunter32 查看可知 qqllk.ocx 是 qqllk.exe 的子进程广告程序,qqllk.exe 又是 explorer.exe
的子进程。在百度广告窗口点击继续按钮就打开了真正的游戏窗口 kyodai.exe。
然后使用 PEid 扫描程序所在文件夹就知道存在这三个程序执行文件。
image.pngQqllk.exe delphi 程序
Qqllk.ocx 是一个加 aps 壳可执行文件
Keyodai.exe 可执行文件
发现直接打开 keydai.exe 运行不了游戏
火绒剑检测 Qqllk.ocx 行为,发现修改了 Keyodai.exe
想要了解程序的执行流程,看看程序之间到底干了什么,可以直接把 qqllk.exe 拖到 OD
CreateWindowExW CreateProcessW api 下断点
系统 api 代码
image.png image.png第一个广告窗口
image.png
第二个程序进程
image.png
第二个广告窗口
image.png
打开第二个 OD,附加或者直接打开第二个广告程序,但是要先设置 strongOD 才能调试多个
image.png
然后重启 OD。
CreateprocessA 或 W 下断点
查看到 kyodai.exe 被创建即被挂起
image.png直接用另一个 OD 打开游戏程序找到 winmain 函数
image.png
跟进去看看
一看貌似没有恢复
当执行完第二个广告后 Winmain 代码被改变,游戏程序便能正常执行
image.png
猜测是第二个广告程序在创建游戏进程挂起的时候修改了 winmian 的地方。
那么我们可以在 Qqllk.ocx 程序中的 resumethread 和 writeprocessmemory 下断点
然后执行唤起操作
image.png由此去广告暂时有了两个方案:
1、只要启动 keyoai.exe 到指定的 0x43817A 地址上修改,使用 loadPE 计算出这个地址在文件
中的位置,然后将其值修改成 0 就可以了。
2、通过运行第二个广告程序后,运行到游戏程序的 OEPC 处直接 dump。
直接用第二种方式:
现在第一个 OD 运行 Qqllk.ocx,执行到唤起处,
另外一个 OD 附加 keyoai.exe,然后查看内存,查看 PE 头,找到 OEP 下断,
第一个 OD 继续执行,另外一个 OD 继续执行到 OEP 处直接 dump 出来即可。
到此处 dump
image.png然后可以直接双击打开运行 dump 出来的程序,即可正常运行。
/*思路一 : 指南针 配合清除函数
1.根据 ce 搜索到减少道具数值的地方
2.跟出减少道具数值函数
3.来到 调用减少道具数值函数的地方
- 查找是谁调用的减少道具数值函数
5.即可找到关键点 道具分发函数 f0-fb 间
6.可以通过该函数调用多个道具
*/
思路二 : 查找炸弹函数
可以先从简单的思路二入手,尝试查找炸弹函数,内联汇编调用
经过观察,可以通过炸弹的声效文件 flystar.wav 查找所有参考文本字串
查找所有命令 push 00459250
image.png全部命令下断点
再次运行游戏,玩到点击炸弹的时候,断点在下图
然后查看堆栈调用
image.png回车,在此下断点,重新运行游戏(注意每次运行游戏可以选 A 级别然后点击练习可以简
单点),玩到获得炸弹道具后,点击炸弹道具,运行到此后单步步过执行
执行到此发现 edx 发生变化,多次执行其他道具可以发现
image.png指南针对应 edx = F0
重列对应 edx = F1
炸弹对应 edx = F4
执行完 0041de5c 处后断到 0041ec07,执行完后炸弹减少,两个卡牌消失
在此往上找到 case F4,可知是道具分支调用
因此 0041de5c 处 call 的是道具分发函数
由此内嵌调用以下汇编代码即可调用炸弹做出一个小外挂
0041DE4D |. 8B86 94040000 MOV EAX,DWORD PTR DS:[ESI+0x494] ; dump.0044CE70
0041DE53 |. 8D8E 94040000 LEA ECX,DWORD PTR DS:[ESI+0x494]
0041DE59 |. 52 PUSH EDX
0041DE5A |. 53 PUSH EBX
0041DE5B |. 53 PUSH EBX
0041DE5C |. FF50 28 CALL DWORD PTR DS:[EAX+0x28]
据观察,edx,ebx的赋值容易,但是要找esi的值。
image.png但是由于栈保存的值不是固定的,所以要找到固定的。
可以打开CE软件,打开调试进程查找12A1F4,找到几个静态地址
image.png经过测试上面三个绿色地址都可以赋值给esi。
下面就可以编写外挂插件
新建MFC DLL 工程
image.png关键文件代码如下:
QQPlugin.cpp
#include "stdafx.h"
#include "QQPlugin.h"
#include "QQPlugindll.h"
#ifdef _DEBUG
#define new *DEBUG_NEW*
#endif
// CQQPluginApp
*BEGIN_MESSAGE_MAP*(CQQPluginApp, *CWinApp*)
*END_MESSAGE_MAP*()
// CQQPluginApp 构造
CQQPluginApp::CQQPluginApp()
{
// TODO: 在此处添加构造代码,
// 将所有重要的初始化放置在 InitInstance 中
}
// 唯一的一个 CQQPluginApp 对象
CQQPluginApp theApp;
// CQQPluginApp 初始化
#define WM_MOD_WINNAME *WM_USER*+110
*LRESULT* *CALLBACK* WindowProc(*HWND* hWnd, *UINT* uMsg,*WPARAM* wParam, *LPARAM* lParam)
{
if (uMsg== WM_MOD_WINNAME)
{
*OutputDebugString*(L"ok");
}
else if (uMsg== *WM_KEYDOWN*)
{
if (wParam== *VK_F**1*)
{
int BaseAddr = 0x45DEBC;
int m_Esi = *(int*)BaseAddr;
_asm
{
mov esi, [m_Esi];
mov eax, dword *ptr* ds : [esi + 0x494];
lea ecx, dword *ptr* ds : [esi + 0x494];
*push* 0xf4;
*push* ebx;
*push* ebx;
*call*dword *ptr* ds : [eax + 0x28];
}
}
return *DefWindowProc*(hWnd, uMsg, wParam, lParam);
}
return *CallWindowProc*(theApp.m_pOldProc, hWnd, uMsg, wParam, lParam);
}
*UINT* __stdcall ThreadProc(*PVOID* pVar)
{
// 初始化,防止崩溃
*AFX_MANAGE_STATE*(*AfxGetStaticModuleState*());
QQPlugindll* pDlg= new QQPlugindll;
pDlg->*Create*(IDD_DIALOG1);
pDlg->*ShowWindow*(*SW_SHOW*);
pDlg->*RunModalLoop*();
return 0;
}
*BOOL* CQQPluginApp::InitInstance()
{
//CWinApp::InitInstance();
*OutputDebugString*(L"InitInstance");
m_hMainWnd= *FindWindow*(*NULL*, L"QQ连连看");
m_pOldProc = (*WNDPROC*)*SetWindowLongPtr*(m_hMainWnd, *GWLP_WNDPROC*,
(*LONG_PTR*)WindowProc);
::*SendMessage*(m_hMainWnd, WM_MOD_WINNAME, 0, 0);
// 创建线程,在线程中启动一个对话框
*_beginthreadex*(0, 0, ThreadProc, this, 0, 0);
return *TRUE*;
}
QQPlugindll.cpp
#include "stdafx.h"
#include "QQPlugin.h"
#include "QQPlugindll.h"
#include "afxdialogex.h"
// QQPlugindll 对话框
*IMPLEMENT_DYNAMIC*(QQPlugindll, *CDialogEx*)
QQPlugindll::QQPlugindll(*CWnd** pParent /*=NULL*/)
: *CDialogEx*(IDD_DIALOG1, pParent)
{
}
QQPlugindll::~QQPlugindll()
{
}
void QQPlugindll::DoDataExchange(*CDataExchange** pDX)
{
*CDialogEx*::*DoDataExchange*(pDX);
}
*BEGIN_MESSAGE_MAP*(QQPlugindll, *CDialogEx*)
*ON_BN_CLICKED*(IDC_BUTTON1, &QQPlugindll::OnBnClickedButton1)
*END_MESSAGE_MAP*()
// QQPlugindll 消息处理程序
//循环炸弹
void QQPlugindll::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
for (int i= 0; i< 100; i++)
{
::*SendMessage*(theApp.m_hMainWnd, *WM_KEYDOWN*, *VK_F**1*, 0);
}
}
最后使用注入工具往游戏程序注入生成的dll文件,出现一个窗口,点击一键消消乐按钮后实现一键消除。
效果图如下:
image.png解决完后可以再说说思路一的做法:
每次按练习后都会随机生成地图
由此可以给关键api函数下断,bp rand
image.png找到地图数据
483AC8 地图数据地址
483AC8内存拷贝给12BB50
12BB50=[Esi+0x195C] 地图地址
地图数组首地址是12BB50,但是数据开始的地方是12BB58,前面八个字节没用。
在点击游戏中两张相同卡牌或点击指南针之前,找两个相同数字靠在一起的下硬件访问断点
尝试断下来通过栈回溯找指南针函数和消除函数的地方
image.png
网友评论