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函数,可以让游戏在按钮初始化之前把字符串给修改了

#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 
#include
#include

/**
  声明要拦截的函数地址
*/
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文件

#pragma once
#include "GameBase.h"

extern GameBase* _pgamebase;

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

// 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文件

#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文件

#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文件

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

新加GameBase.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的对接
}

你可能感兴趣的:(游戏,网游逆向)