【软件逆向】QQ 连连看小游戏去广告与一键消除实现

目录

一、背景介绍

二、去广告实现

2.1 分析广告加载流程

2.2 逆向分析广告加载逻辑

2.3 去广告方案

三、一键消除外挂实现

3.1 分析游戏逻辑

3.2 编写外挂插件

3.3 注入外挂:

四、一键消除效果展示

五、额外扩展


一、背景介绍

        QQ 连连看是一款经典的休闲小游戏,但部分版本中嵌入了广告,影响了用户体验。本文将详细介绍如何通过逆向工程和编程技术,去除游戏中的广告,并实现一键消除功能。

二、去广告实现

2.1 分析广告加载流程

1. 运行程序:
        双击 `qqllk.exe`,弹出第一个广告窗口。
        点击“开始游戏”按钮,弹出第二个广告窗口(百度广告)。
        点击“继续”按钮后,真正的游戏窗口 `kyodai.exe` 启动。


        通过 PChunter32 查看可知 qqllk.ocx 是 qqllk.exe 的子进程广告程序,qqllk.exe 又是 explorer.exe 的子进程。在百度广告窗口点击继续按钮就打开了真正的游戏窗口 kyodai.exe。

2.程序结构:
使用 `PEid` 扫描程序文件夹,发现以下文件:
        `qqllk.exe`:Delphi 编写的程序。
        `qqllk.ocx`:是一个加 aps 壳可执行文件,负责广告加载。
        `kyodai.exe`:真正的游戏程序。

火绒剑检测 Qqllk.ocx 行为,发现修改了 Keyodai.exe
直接运行 `kyodai.exe` 无法启动游戏,因为 `qqllk.ocx` 修改了 `kyodai.exe` 的行为。

2.2 逆向分析广告加载逻辑

1. 使用 OD(OllyDbg)调试:
        将 `qqllk.exe` 拖入 OD,在 `CreateWindowExW` 和 `CreateProcessW` API 下断点。
        分析广告窗口的创建和游戏进程的启动过程。

2. 广告窗口分析:
        第一个广告窗口通过 `CreateWindowExW` 创建。


        第二个广告窗口通过 `CreateProcessW` 启动 `qqllk.ocx`。

        下图是第二个程序进程

        第二个广告窗口

        打开第二个 OD,附加或者直接打开第二个广告程序,但是要先设置 strongOD 才能调试多个

然后重启 OD。CreateprocessA 或 W 下断点

查看到 kyodai.exe 被创建即被挂起

直接用另一个 OD 打开游戏程序找到 winmain 函数

跟进去看看,一看貌似没有恢复

当执行完第二个广告后 Winmain 代码被改变,游戏程序便能正常执行

猜测是第二个广告程序在创建游戏进程挂起的时候修改了 winmian 的地方。
那么我们可以在 Qqllk.ocx 程序中的 resumethread 和 writeprocessmemory 下断点

然后执行唤起操作

总结游戏进程修改:
        `qqllk.ocx` 在启动 `kyodai.exe` 时,将其挂起并修改其内存(如 `WinMain` 函数)。
        使用 OD 附加 `kyodai.exe`,发现 `WinMain` 函数在广告加载后被修改。

2.3 去广告方案

由此去广告暂时有了两个方案:
1、只要启动 keyoai.exe 到指定的 0x43817A 地址上修改,使用 loadPE 计算出这个地址在文件
中的位置,然后将其值修改成 0 就可以了。
2、通过运行第二个广告程序后,运行到游戏程序的 OEPC 处直接 dump。

直接用第二种方式:
现在第一个 OD 运行 Qqllk.ocx,执行到唤起处,
另外一个 OD 附加 keyoai.exe,然后查看内存,查看 PE 头,找到 OEP 下断,
第一个 OD 继续执行,另外一个 OD 继续执行到 OEP 处直接 dump 出来即可。

到此处 dump

然后可以直接双击打开运行 dump 出来的程序,即可正常运行。

三、一键消除外挂实现

3.1 分析游戏逻辑

思路一 : 指南针 配合清除函数
1.根据 ce 搜索到减少道具数值的地方
2.跟出减少道具数值函数
3.来到 调用减少道具数值函数的地方

4.查找是谁调用的减少道具数值函数
5.即可找到关键点 道具分发函数 f0-fb 间
6.可以通过该函数调用多个道具

思路二 : 查找炸弹函数

可以先从简单的思路二入手,尝试查找炸弹函数,内联汇编调用
经过观察,可以通过炸弹的声效文件 flystar.wav 查找所有参考文本字串

查找所有命令 push 00459250

全部命令下断点,再次运行游戏,玩到点击炸弹的时候,断点在下图

然后查看堆栈调用

回车,在此下断点,重新运行游戏(注意每次运行游戏可以选 A 级别然后点击练习可以简
单点),玩到获得炸弹道具后,点击炸弹道具,运行到此后单步步过执行

执行到此发现 edx 发生变化,多次执行其他道具可以发现

指南针对应 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的值。

但是由于栈保存的值不是固定的,所以要找到固定的。

可以打开CE软件,打开调试进程查找12A1F4,找到几个静态地址

经过测试上面三个绿色地址都可以赋值给esi。

3.2 编写外挂插件

1. 创建 MFC DLL 工程:
        使用 Visual Studio 创建 MFC DLL 项目,命名为 `QQPlugin`。

2. 关键代码实现:
        在 `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);

}

}

3.3 注入外挂:

        使用注入工具将生成的 DLL 文件注入到游戏进程中。
        运行游戏后,点击外挂窗口的“一键消除”按钮,即可实现一键消除功能。

四、一键消除效果展示

        一键消除效果:点击外挂窗口的按钮,快速消除所有卡牌。

五、额外扩展

一键消除解决完后,可以再说说思路一的做法:

每次按练习后都会随机生成地图,由此可以给关键api函数下断,bp rand

找到地图数据

483AC8 地图数据地址

483AC8内存拷贝给12BB50

12BB50=[Esi+0x195C] 地图地址

地图数组首地址是12BB50,但是数据开始的地方是12BB58,前面八个字节没用。

在点击游戏中两张相同卡牌或点击指南针之前,找两个相同数字靠在一起的下硬件访问断点

尝试断下来通过栈回溯找指南针函数和消除函数的地方

总结

        通过逆向分析和编程技术,我们成功去除了 QQ 连连看中的广告,并实现了一键消除功能。本文详细介绍了去广告和外挂实现的思路与步骤,希望对读者有所帮助。需要注意的是,此类技术仅用于学习和研究,请勿用于非法用途。

评论 37
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

攻城狮7号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值