62.网游逆向分析与插件开发-游戏增加自动化助手接口-游戏公告类的C++还原

news2024/12/23 6:05:35

内容来源于:易道云信息技术研究院VIP课

上一个内容:游戏红字公告功能的逆向分析-CSDN博客

码云地址(master分支):https://gitee.com/dye_your_fingers/sro_-ex.git

码云版本号:0888e34878d9e7dd0acd08ef045184a44c9ef567

代码下载地址,在 SRO_EX 目录下,文件名为:SRO_Ex-游戏公告类的C++还原.zip

链接:https://pan.baidu.com/s/1W-JpUcGOWbSJmMdmtMzYZg

提取码:q9n5

--来自百度网盘超级会员V4的分享

HOOK引擎,文件名为:黑兔sdk.zip

链接:https://pan.baidu.com/s/1IB-Zs6hi3yU8LC2f-8hIEw

提取码:78h8

--来自百度网盘超级会员V4的分享

以 游戏红字公告功能的逆向分析-CSDN博客它的代码为基础进行修改

根据之前的分析,发现一个新的类,现在有三个函数,848580黑色文字公告函数,844E40红色文字公告函数,844E80游戏聊天框打印文字(公告打印到聊天框)函数,详情看项目里的分析记录.txt文件

效果图:三个公告全部正常弹出

注意的点0x1256E3C这个地址是在游戏全部初始化完才有值的,所以根据现在的hook位置只有自动药水设定按钮才满足条件,之后再找更合适的初始化位置。

GameEx.cpp文件的修改,修改了 GameEx构造函数,ExitGame函数,AutoHelper函数

#include "pch.h"
#include "GameEx.h"
#include "htdHook2.h"
#include "GameProtect.h"
#include "extern_all.h"

extern int client;
extern GameProtect* _protect;
extern unsigned _stdcall GetFunctionAddress(int index);
htd::hook::htdHook2 hooker;

#include <windows.h>
#include<stdio.h>
#include<TlHelp32.h>

/**
  声明要拦截的函数地址
*/
auto h = GetModuleHandle(NULL);
DWORD address = (DWORD)h;
DWORD addRExit = address + 0x88C77E;

size_t 被拦截修改的函数的地址 = (size_t)addRExit;

LONG NTAPI 异常回调(struct _EXCEPTION_POINTERS* Excep)
{
    printf("异常回调1\n");

    /**
      判断出异常的地方是否为 我们修改的地方
    */
    if ((size_t)Excep->ExceptionRecord->ExceptionAddress == 被拦截修改的函数的地址) {

        //const char* szStr = "nei Rong Bei Xiu Gai";
        //*(DWORD*)(Excep->ContextRecord->Esp + 0x8) = (DWORD)szStr;
        //szStr = "biao Ti Bei Xiu Gai";
        //*(DWORD*)(Excep->ContextRecord->Esp + 0xC) = (DWORD)szStr;

        AfxMessageBox(L"游戏退出!");
        DWORD* _esp = (DWORD*)Excep->ContextRecord->Esp;
        DWORD _val = _esp[1];

        if (_val == 0x1035D0C) {
            AfxMessageBox(L"游戏退出2!");
            auto hMuls = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, L"system_seamp");
            if (hMuls) ReleaseSemaphore(hMuls, 1, 0);
            ExitProcess(0);
        }

        Excep->ContextRecord->Eip = *(DWORD *) Excep->ContextRecord->Esp;
        Excep->ContextRecord->Esp += 8;
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    else {
        /**
          防止被其它地方修改了函数地址
        */
        Excep->ContextRecord->Dr0 = 被拦截修改的函数的地址;
        Excep->ContextRecord->Dr7 = 0x405;
        return EXCEPTION_CONTINUE_SEARCH;
    }

}

VOID 设置线程的dr寄存器(HANDLE 线程句柄) {

    printf("设置线程的dr寄存器1\n");
    CONTEXT ctx;
    ctx.ContextFlags = CONTEXT_ALL;
    GetThreadContext(线程句柄, &ctx);
    ctx.Dr0 = 被拦截修改的函数的地址;
    ctx.Dr7 = 0x1;
    SetThreadContext(线程句柄, &ctx);
    printf("设置线程的dr寄存器2\n");
}

VOID 使用dr寄存器拦截修改函数() {
    printf("使用dr寄存器拦截修改函数1\n");
    HANDLE 线程快照句柄 = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId());
    if (线程快照句柄 == INVALID_HANDLE_VALUE) {
        printf("线程快照创建失败");
        return;
    }

    THREADENTRY32* 线程结构体 = new THREADENTRY32;
    线程结构体->dwSize = sizeof(THREADENTRY32);

    /**
      Thread32First获取快照中第一个线程
       返回值bool类型
    */
    // Thread32First(线程快照句柄, &线程结构体);

    HANDLE 线程句柄 = NULL;

    printf("使用dr寄存器拦截修改函数2\n");

    /**
      Thread32Next获取线程快照中下一个线程
    */
    while (Thread32Next(线程快照句柄, 线程结构体))
    {
        if (线程结构体->th32OwnerProcessID == GetCurrentProcessId()) {
            printf("使用dr寄存器拦截修改函数3\n");
            线程句柄 = OpenThread(THREAD_ALL_ACCESS, FALSE, 线程结构体->th32ThreadID);
            printf("使用dr寄存器拦截修改函数4\n");
            设置线程的dr寄存器(线程句柄);
            printf("使用dr寄存器拦截修改函数5\n");
            CloseHandle(线程句柄);
        }
    }
}

SRO_String vip_notice;

bool AutoHelper(HOOKREFS2) {
    // 使用通过获取游戏中的sro_string结构
    //auto read = _pgamebase->SRO_Res->ReadTitle((wchar_t*)0xEBC968);
    // 使用自己创建的sro_string结构
    /**
        sro_string str;
        str.Ptitle = L"您还没有开通VIP服务,只能使用普通药水辅助功能,开通VIP可以使用更高级的自动化助手功能!";
        str.lenth = 47;
        str.size = 48 * 2 + 1;
        auto read = &str;
        unsigned* _ecx = (unsigned*)0x1256E3C;
        unsigned readEcx = _ecx[0];
        unsigned _call = 0x848580;
        _asm {
            mov ecx, readEcx
            push read
            call _call
        }
    */
    _pgamebase->Init();
    _pgamebase->SRO_Notice->NetNotice(&vip_notice);
    _pgamebase->SRO_Notice->ChatNotice(vip_notice.wcstr(), 0xFFFF0000);
    _pgamebase->SRO_Notice->NormalNotice(&vip_notice);

    bool vip = false;
    if (vip)return false;
    else return true;
}

bool ExitGame(HOOKREFS2) {
    //AfxMessageBox(L"ExitGame");
    //auto read = _pgamebase->SRO_Res->ReadTitle((wchar_t*)0xEBC968);
    //CString txt;
    //txt.Format(L"------ %s", read->wcstr());
     read->Ptitle = L"自动助手 [VIP] (%s)";
    //*read = L"自动助手 [VIP] (%s)";
    //AfxMessageBox(txt);

    // if (_protect->CheckDebugByNT())AfxMessageBox(L"检测到了DEBUG程序的存在");
	// AfxMessageBox(L"游戏退出2222!");
	DWORD* _esp = (DWORD*)_ESP;
	DWORD _val = _esp[1];

	if (_val == 0x1035D0C) {
		// AfxMessageBox(L"游戏退出!");
		auto hMuls = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, L"system_seamp");
		if (hMuls) ReleaseSemaphore(hMuls, 1, 0);
        client--;
		ExitProcess(0);
	}
	return true;
}

GameEx::GameEx()
{
    vip_notice = L"(自动化助手公告)您还没有海通VIP服务,只能使用普通的药水设定功能,开通VIP后可以享受全自动辅助功能!";
	// AfxMessageBox(L"注册hook!");
	// auto h = GetModuleHandle(NULL);
	// DWORD address = (DWORD)h;
    // DWORD* addRExit = (DWORD*)(address + 0x88C77E);
    /**addRExit = 0;*/
	// CString txt;
    // txt.Format(L"addRExit[0]D:%d,addRExit[0]X:%X,addRExit:%X", addRExit[0], addRExit[0], addRExit);
    // AfxMessageBox(txt);

    // hooker.SetHook((LPVOID)addRExit, 3, ExitGame);
    //AddVectoredExceptionHandler(1, 异常回调);
    //设置线程的dr寄存器(GetCurrentThread());
}

void GameEx::InitInterface()
{
    unsigned addr_cps =  GetFunctionAddress(0);
    hooker.SetHook((LPVOID)(addr_cps + 0x30 - 2), 0x3, ExitGame);
    hooker.SetHook((LPVOID)(addr_cps + 0x51 - 2), 0x3, ExitGame);

    unsigned addr_autohelper = GetFunctionAddress(1);
    hooker.SetHook((LPVOID)(addr_autohelper), 0x03, AutoHelper, (LPVOID)(addr_autohelper + 0x90));
}

新加SRO_String.cpp文件

#include "pch.h"
#include "SRO_String.h"

int SRO_String::stringlength(const wchar_t* str)
{
	for (lenth = 0; str[lenth]; lenth++);
	size = lenth * 2 + 1;
	return size;
}

wchar_t* SRO_String::wcstr()
{
	if (size < 0x8) {
		return title;
	}
	else {
		return Ptitle;
	}
}

SRO_String& SRO_String::operator=(const wchar_t* _txt)
{
	int _size = stringlength(_txt);

	if (_size > 8) {
		Ptitle = (wchar_t*)_txt;
	}
	else {
		memcpy(title, _txt, _size);
	}

	return *this;
}

新加SRO_String.h文件

#pragma once

typedef class SRO_String
{
private:
	int un1;
	union {
		wchar_t title[0x4];
		wchar_t* Ptitle;
	};
	int un2[0x2];

	unsigned lenth;
	unsigned size;
	int stringlength(const wchar_t* str);
public:
	wchar_t* wcstr();
	SRO_String& operator=(const wchar_t* _txt);
}*PSROSTRING;

Res.cpp文件的修改,修改函数的返回值为PSROSTRING类型

#include "pch.h"
#include "Res.h"
// 这里为了给 _ReadTitle 默认值,防止编译不通过
Res::PROC_WCHAR1 Res::_ReadTitle{};
PSROSTRING Res::ReadTitle(wchar_t* index)
{
	/**
		获取按钮文字通过逆向分析,看出是一个thiscall
		在调用获取文字函数时给ecx赋值了,这是典型的thiscall方式
		下方(this->*_ReadTitle)(index)写法翻译成汇编就会是
		mov ecx,this
		mov eax, [ecx+xxx];
		push index
		call eax
		大概就会变成上方四行代码,也就是一个thiscall
		如果穿插汇编代码,会很lou,这样用c++写就很优雅
	*/
	return (this->*_ReadTitle)(index);
}

Res.h文件的修改,新加SRO_String.h文件并使用

#pragma once
#include "SRO_String.h"
// sro_string通过逆向分析,文字获取方式得到的一个结构体
typedef class Res
{
	// 定义一个函数指针
	typedef PSROSTRING(Res::* PROC_WCHAR1)(wchar_t*);
public:
	// 给函数指针声明一个变量
	static PROC_WCHAR1 _ReadTitle;
	// 封装 _ReadTitle 使用时比较好用
	PSROSTRING ReadTitle(wchar_t* index);

}*PRes;

新加Notice类

Notice.h文件

#pragma once
#include "SRO_String.h"
typedef class Notice
{
	typedef void (Notice::* PROC_PSROSTR)(PSROSTRING);
	typedef void (Notice::* PROC_D_PWSTR_D_D)(int, wchar_t*, int, int);
public:
	static PROC_PSROSTR _NormalNotice;
	static PROC_PSROSTR _NetNotice;
	static PROC_D_PWSTR_D_D _ChatNotice;
public:
	void NormalNotice(PSROSTRING _txt);
	void NetNotice(PSROSTRING _txt);
	void ChatNotice(wchar_t* _txt, int color=0xFFFFAEC3, int type1=0x3, int type2=0x1);
}*PNotice;

Notice.cpp文件

#include "pch.h"
#include "Notice.h"

Notice::PROC_PSROSTR Notice::_NormalNotice{};
Notice::PROC_PSROSTR Notice::_NetNotice{};
Notice::PROC_D_PWSTR_D_D Notice::_ChatNotice{};

void Notice::NormalNotice(PSROSTRING _txt)
{
	(this->*_NormalNotice)(_txt);
}

void Notice::NetNotice(PSROSTRING _txt)
{
	(this->*_NetNotice)(_txt);
}

/**
	type1 默认0x3
	type2 默认0x1
*/
void Notice::ChatNotice(wchar_t* _txt, int color, int type1, int type2)
{
	(this->*_ChatNotice)(type1, _txt, color, type2);
}

GameBase.cpp文件的修改,修改Init函数

#include "pch.h"
#include "GameBase.h"

GameBase* _pgamebase;

void GameBase::Init()
{
	unsigned* addrRead = (unsigned*)0x1256E3C;

	SRO_Res = (PRes)0x1036518;
	SRO_Notice = (PNotice)addrRead[0];
	
	InitClassProc(&Res::_ReadTitle, 0x9A46C0);
	InitClassProc(&Notice::_NormalNotice, 0x848580);
	InitClassProc(&Notice::_NetNotice, 0x844E40);
	InitClassProc(&Notice::_ChatNotice, 0x844E80);

}

void GameBase::InitClassProc(LPVOID proc_addr, unsigned value)
{
	unsigned* uWrite = (unsigned*)proc_addr;
	uWrite[0] = value;
}

GameBase::GameBase()
{
	_pgamebase = this;
	// Init();// 初始化机制,完成游戏与我们dll的对接
}

GameBase.h文件的修改,修改了Init函数

#pragma once
#include "Res.h"
#include "Notice.h"

class GameBase
{
	void InitClassProc(LPVOID proc_addr, unsigned value);
public:
	void Init();
	GameBase();
	PRes SRO_Res;
	PNotice SRO_Notice;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1368944.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HarmonOS 日期选择组件(DatePicker)

本文 我们一起来看基础组件中的 DatePicker 这就是 日程开发中的日期组件 他可以创建一个日期的范围 并创建一个日期的滑动选择器 这里 我们先写一个组件的骨架 Entry Component struct Index {build() {Row() {Column() {}.width(100%)}.height(100%)} }然后 我们先在Column组…

聚道云软件连接器助力某贸易公司实现付款流程自动化

客户介绍&#xff1a; 某贸易公司是一家集进出口贸易、国内贸易、电子商务等业务于一体的综合性贸易企业。公司业务遍及全球多个国家和地区&#xff0c;拥有庞大的供应商网络和采购需求。 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 客户痛点&#…

OpenVoice实时语音克隆功能实现

前言 在【OpenVoice本地部署教程与踩坑记录】一文中介绍了OpenVoice的基本概念与&#xff0c;并且完成了项目的安装与运行。官方给的示例和用法中仅包含了文本转TTS再克隆音色的功能&#xff0c;仅能用于TTS场景下的文字朗读。 本文基于官方示例改造&#xff0c;实现了实时采集…

公司新买的BI,和金蝶系统配合太默契了

公司一直都用金蝶系统来实现包括财务管理、供应链管理、人力资源管理等多个方面的资源的合理配置和业务流程的自动化。但到了数据分析这块&#xff0c;金蝶系统就明显力不从心&#xff0c;需要一个专业的数据分析工具来接手。财务经理推荐用奥威BI&#xff0c;说这款BI的一大特…

Windows内存管理(一):Windows性能监视器(PerfMon)

一、什么是性能监视器 什么是性能监视器&#xff1f; (What is Performance Monitor? )很多时候&#xff0c;我们的计算机只是停止响应、意外关闭或行为异常。这种行为可能有多种原因&#xff0c;指出确切原因可能会有很大帮助。Windows有一个名为Performance Monitor的工具&…

Python——数据类型转换

# 将数字类型转换成字符串 num_str str(111) print(type(num_str), num_str) \# 将浮点类型转换成字符串 float_str str(12.34) print(type(float_str), float_str) # 将字符串转变成数字 num int("234") print(type(num)) # 将字符串转变成浮点型 num2 float(&q…

(已解决)踩坑spring-session-data-redis包出现sessionId不一致问题

问题&#xff1a;今天在使用spring-session-data-redis的jar包时&#xff0c;出现了本地使用时sessionId是一致的&#xff0c;线上使用的时候sessionid是不一致的。 在网上查了半天资料&#xff0c;知道是其中这个包 DefaultCookieSerializer 出现了问题&#xff0c;但是里面的…

基于Listener实现在线人数监测的简单案例

一、需求 只要有用户登录到服务器&#xff0c;就记录在线用户1。 二、使用到的Listner介绍 1、HttpSessionListener 监听器 当一个HttpSession刚被创建或者失效&#xff08;invalidate&#xff09;的时候&#xff0c;将会通知HttpSessionListener监听器。 方法声明功能介绍v…

Vscode设置git账户密码(不需要每次都输入)

在Vscode提交项目代码或者拉取代码的时候&#xff0c;如果每次都需要输入git的账户密码&#xff0c;那么就在终端输入&#xff1a; git config --global credential.helper store 命令 然后执行git pull 提示输入用户密码后&#xff0c;就会缓存&#xff1b; ※注&#xff1a;如…

没有货源是不是就没办法在家做抖店?打包发货怎么完成?解答如下

我是王路飞。 有人问了我一个问题&#xff1a;无货源模式的抖店&#xff0c;自己一个人在家里做不了是吧&#xff1f;毕竟打包发货这些问题怎么解决呢&#xff1f; 店铺要是发货不及时被平台罚款怎么办&#xff1f;产品有质量问题怎么解决呢&#xff1f;店铺一直不出单怎么办…

Unity中URP下深度图的线性转化

文章目录 前言一、_ZBufferParams参数有两组值二、LinearEyeDepth1、使用2、Unity源码推导&#xff1a;3、使用矩阵推导&#xff1a; 三、Linear01Depth1、使用2、Unity源码推导3、数学推导&#xff1a; 前言 在之前的文章中&#xff0c;我们实现了对深度图的使用。因为&#…

阿里云弹性网络接口技术的容器网络基础教程

基于容器的虚拟化是一种虚拟化技术。与虚拟机 &#xff08;VM&#xff09; 相比&#xff0c;容器更轻量级&#xff0c;部署更方便。Docker是目前主流的容器引擎&#xff0c;支持Linux、Windows等平台&#xff0c;以及Kubernetes&#xff08;K8S&#xff09;、Swarm、Rocket&…

奇偶链表00

题目链接 奇偶链表 题目描述 注意点 在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题偶数组和奇数组内部的相对顺序应该与输入时保持一致 解答思路 奇数组的头节点是head&#xff0c;偶数组的头节点是head.next&#xff0c;关键是要改变每个节点的next指针及…

[ArkUI开发技巧] 应用的全屏式沉浸适配

引言 在开发应用的过程中&#xff0c;为了使用户聚焦在应用本身&#xff0c;最好对应用进行沉浸适配。先前有一种适配方法&#xff0c;将SystemBarProperties设置成应用页面顶部和底部的颜色&#xff0c;但是这种方法在切换页面的过程中过渡十分僵硬&#xff0c;且应用在小窗模…

数据结构实验3:顺序表的基本操作

目录 一、实验目的 二、实验原理 1. 连续存储空间 2. 元素访问 3. 固定大小 4. 容量管理 5. 动态顺序表 6. 顺序表的插入 7. 顺序表的删除 8. 顺序表的应用 三、实验内容 问题描述 代码 截图 分析 一、实验目的 1、 熟练掌握顺序表结构体的实现。 2、 熟练掌握…

记录汇川:ITP与Autoshop进行仿真连接

1、定义如下程序&#xff1a; 2、ITP新建工程&#xff1a; 3、依次选择&#xff0c;最后修改IP 4、定义两个变量 5、拖一个按钮和一个圈出来&#xff0c;地址绑定&#xff1a;M1 6、地址绑定&#xff1a;Y1 7、PLC启动仿真 8、ITP启动在线模拟器 9、即可实现模拟仿真

Redis 配置(二)

目录 redis 配置 Redis 主从复制 主从复制的作用 主从复制流程 搭建Redis 主从复制 Redis 哨兵模式 哨兵模式的作用 哨兵结构 故障转移机制 主节点的选举 搭建Redis 哨兵模式 Redis 群集模式 集群的作用 Redis集群的数据分片 Redis集群的主从复制模型 搭建R…

拓数派加入 OpenCloudOS 操作系统开源社区,作为成员单位参与社区共建

近日&#xff0c;拓数派签署 CLA(Contributor License Agreement 贡献者许可协议)&#xff0c;正式加入 OpenCloudOS 操作系统开源社区。 拓数派&#xff08;英文名称“OpenPie”&#xff09;是国内基础数据计算领域的高科技创新企业。作为国内云上数据库和数据计算领域的引领者…

CentOS7部署GitLab-ce-16.7.0-ce.0.el7

文章目录 下载地址上传服务器安装访问配置external_url修改防火墙端口开放 重新加载配置访问GitLab出现502访问错误继续访问gitlab账户和密码修改GitLab常用命令 下载地址 gitlab 下载地址 上传服务器 scp -r C:\Users\xxx.xxxx\Downloads\gitlab-ce-16.7.0-ce.0.el7.x86_64…

黑马苍穹外卖学习Day3

目录 公共字段自动填充问题分析实现思路代码实现 新增菜品需求分析和设计接口设计代码开发开发文件上传接口功能开发 菜品分页查询需求分析和设计代码开发 菜品删除功能需求分析与设计代码实现代码优化 修改菜品需求分析和设计代码实现 公共字段自动填充 问题分析 员工表和分…