59.网游逆向分析与插件开发-游戏增加自动化助手接口-文字资源读取类的C++还原

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

上一个内容:游戏菜单文字资源读取的逆向分析-CSDN博客

之前的内容:接管游戏的自动药水设定功能-CSDN博客

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

码云版本号:55358fb135a0c821d8e80a931f33640494b54b9d

代码下载地址,在 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博客 它的代码为基础进行修改

效果图:

GameEx.cpp文件的修改,修改了 ExitGame函数,上方效果图里的代码只能通过重新登录才能修改好按钮的名字,通过ExitGame函数,可以让游戏在按钮初始化之前把字符串给修改了

cpp 复制代码
#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(线程句柄);
        }
    }
}

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

bool ExitGame(HOOKREFS2) {
    auto read = _pgamebase->SRO_Res->ReadTitle((wchar_t*)0xEBC968);
    CString txt;
    txt.Format(L"------ %s", read->wcstr());
    read->Ptitle = 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()
{
	// 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));
}

新加extern_all.h文件

cpp 复制代码
#pragma once
#include "GameBase.h"

extern GameBase* _pgamebase;

htdMfcDll.h文件的修改,新加 引入GameBase头文件、game_base变量

cpp 复制代码
// htdMfcDll.h: htdMfcDll DLL 的主标头文件
//

#pragma once

#ifndef __AFXWIN_H__
	#error "在包含此文件之前包含 'pch.h' 以生成 PCH"
#endif

#include "resource.h"		// 主符号
#include "CUI.h"
#include "GameProtect.h"
#include "GameEx.h"
#include "GameBase.h"

// ChtdMfcDllApp
// 有关此类实现的信息,请参阅 htdMfcDll.cpp
//

class ChtdMfcDllApp : public CWinApp
{
public:
	ChtdMfcDllApp();

// 重写
public:
	virtual BOOL InitInstance();
	CUI* wndMain{};
	DECLARE_MESSAGE_MAP()
protected:
	GameProtect protect;
	GameEx game_ex;
	GameBase game_base;
};

新加Res.cpp文件

cpp 复制代码
#include "pch.h"
#include "Res.h"
// 这里为了给 _ReadTitle 默认值,防止编译不通过
Res::PROC_WCHAR1 Res::_ReadTitle{};
psro_string 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文件

cpp 复制代码
#pragma once
// sro_string通过逆向分析,文字获取方式得到的一个结构体
typedef class sro_string {
public:
	int un1;
	union {
		wchar_t title[0x4];
		wchar_t* Ptitle;
	};
	int un2[0x2];

	unsigned lenth;
	unsigned size;

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

typedef class Res
{
	// 定义一个函数指针
	typedef psro_string(Res::* PROC_WCHAR1)(wchar_t*);
public:
	// 给函数指针声明一个变量
	static PROC_WCHAR1 _ReadTitle;
	// 封装 _ReadTitle 使用时比较好用
	psro_string ReadTitle(wchar_t* index);

}*PRes;

新加GameBase.h文件

cpp 复制代码
#pragma once
#include "Res.h"
class GameBase
{
	void Init();
	void InitClassProc(LPVOID proc_addr, unsigned value);
public:
	GameBase();
	PRes SRO_Res;
};

新加GameBase.cpp文件

cpp 复制代码
#include "pch.h"
#include "GameBase.h"

GameBase* _pgamebase;

void GameBase::Init()
{
	
	SRO_Res = (PRes)0x1036518;
	InitClassProc(&Res::_ReadTitle, 0x9A46C0);
}

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

GameBase::GameBase()
{
	_pgamebase = this;
	Init();// 初始化机制,完成游戏与我们dll的对接
}
相关推荐
da_vinci_x17 小时前
在Substance Designer里“预演”你的游戏着色器(Shader)
人工智能·游戏·技术美术·着色器·游戏策划·游戏美术·substance designer
reddingtons18 小时前
ZBrush细节烘焙全“糊”了?Painter“平均法线”+“偏斜贴图”的“无笼”烘焙管线
游戏·设计师·贴图·技术美术·substance painter·游戏美术·zbrush
嘀咕博客2 天前
h5游戏免费下载:HTML5拉杆子过关小游戏
前端·游戏·html5
FairGuard手游加固2 天前
Cocos资源加密方案解析
安全·游戏·cocos2d
UWA3 天前
为什么Android游戏画面在30帧运行时有抖动现象
android·游戏
软件开发技术深度爱好者3 天前
python使用Pygame库实现避障小人行走游戏
python·游戏·pygame
星空露珠3 天前
数独生成题目lua脚本
数据结构·数据库·算法·游戏·lua
wanhengidc3 天前
云手机 基于云计算的虚拟手机
运维·服务器·游戏·智能手机·云计算
王火火(DDoS CC防护)4 天前
游戏盾是如何保障游戏安全稳定的?
游戏·网络安全·ddos攻击·sdk游戏盾
上海云盾第一敬业销售4 天前
高防CDN如何确保电商平台在购物节期间运转如常
安全·游戏·ddos