86.网游逆向分析与插件开发-物品使用-物品丢弃的逆向分析与C++代码的封装

news2024/11/16 0:01:20

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

上一个内容:物品使用的逆向分析与C++代码的封装-CSDN博客

 码云地址(ui显示角色数据 分支):https://gitee.com/dye_your_fingers/sro_-ex.git

码云版本号:7563f86877c7b6033de49ed035e095a726f9d8fb

代码下载地址,在 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的分享

以  物品使用的逆向分析与C++代码的封装-CSDN博客 它的代码为基础进行修改

首先还是通过上一个内容中写的,在WSASend函数上下断点,一路ctrl+f9,再按f8,找分界点。

最终可以发现,下图位置是最后的分界点,jmp之后会到,移动的话会进入移动的处理函数,使用物品的话进入使用物品的函数,丢弃物品就会去丢弃物品的函数,这里是一个关键点,0x914E98

然后在往上一层由于看不到入参,就又反了一层,然后就来到下图位置 0x8526A6,这里的入参都是写死的,然后又esi是变化的,通过断点可以看出,它是物品的索引,当前丢弃的是弓箭,工具正好处在数组下标2位置

然后为了找ecx的值从哪来的,一路往上反,然后查看ecx的值第一次出现在那,然后就重复ctrl+f9再按f8然后给上一行下断点测试,最终来到下图位置0x70D8B0

ecx它的值会在esi+7C4位置获取,然后通过esi+7C4的写入再跟

然后设置内存写入断点

然后断点放行会发现esi+7C4位置的值会变,所以必须要追一追了,因为找esi没有用了,因为esi+7C4是用来记录一个临时性的东西

 然后来到下图位置,它肯定还有一个写入的地方

第二个写入位置,它的值来自于eax,eax又来自于参数

然后按ctrl+f9再按f8,来到它的上一层,然后看出它是来自于esp+14所以它一定是来自于上一层了,esp寄存器与栈有关,栈一般用来存放入参,或者局部变量

然后到这游戏掉线了重新登录,内存地址都变了,下方的图不要盯着与上方的内存地址不一样,只需要盯着分析方法。

重新登录之后来到0x852127,然后丢弃物品触发0x852127位置的断点,然后按ctrl+f9再按f8,来当上一层,然后这里它的参数来自于edi

通过x96dbg的高亮查看edi是来自于ecx,所以在此来到上一层

然后就来到了下图红框位置

然后ecx的值来自于esi,esi的值来自于一个函数返回值,下图红框里的函数

然后0xC7E420函数里是一个全局变量,所以找到了基址

由于之前掉线了,导致内存地址不一样,所以这里要测试 0x1256c38 的值对不对,然后在内存里显示它

然后发现 0x1256c38 它的值会根据鼠标移动而变化,应该是跟ui有关,不同的窗口由不同的值,把鼠标移动到丢弃物品窗口上,它俩的值是一样的

所以它不稳定,然后它就并不是我们要找的,然后对 0x1256c38 下写入断点

然后返回游戏就直接卡到下图位置了,然后它的数据又是从上一层得到的,所以还要继续往上

这里的参数是通过函数返回值得到的,然后这个位置它会不停的断下来

它 0xC8DE5E位置调用的函数 的入参好像是鼠标的坐标

它的返回值也是与0x1256C38的值一样

然后把它的入参改成999999

返回值就是0,也就是没找到窗口,它的返回值应该是跟ui绑定的一个东西

它的整个逻辑,首先就是它会弹出一个确认窗口,如下图,它弹这个对话框的时候,为了保证后续的操作,所以必须要,传递一个指针给窗口(ui),传的过程,我们现在所处的位置是弹出窗口不按是它也会触发,所以现在是处于弹出窗口的过程,也就是它把指针给ui的过程,真正给的过程我们是跟踪不到,因为这个东西一定在游戏整个ui初始化的时候来跟踪的,所以现在这个地方它得的时候它也没办法得到,它也没有一个固定的方法得到,它只能通过坐标和ui的一个比对来得到这个东西

然后进入0xC7C220函数分析,第一眼看到下图红框位置,它是mov esi, dword ptr ds:[esi+4] cmpesi,deword ptr ds:[edi+4] jne 0xc7c244,这三行代码的意思是,一个链表,esi是链表首地址然后偏移4位置是下一个链表位置,然后edi+4是最后一个链表的位置,所以如果 cmp不相等jne就会执行,就会往上走,很明显是一个链表操作

上图是没找到的跳出方式,下图是找到了跳出的方式,它是通过调用一个函数,通过判断这个函数的返回值是不是0来决定的,如果不是0就跳转,是0就进入下一轮循环

然后下图红框的三行代码,它循环的过程会把esi+4位置拿出来,通过eax重新赋值,赋予的值是从edi里得到的,所以edi是下一个链表位置,这里三行代码就更加确定了这里是链表操作了,到这也就没法追了,因为到这就是遍历ui获取绑定的指针了,绑定过程要去ui初始化里找,但是现在我们要找物品的丢弃,ui初始化是另外一个东西,然后我们的丢弃功能怎么办?

现在要重新思考一下这个问题,目前想用下图 0x8526A6 位置调用的函数,找了一圈分析下来以后,这个函数是使用起来最方便的函数,但是很遗憾,这个函数我们没办法提供它的 ecx,这个使用有个解决方案,就是通过Cheat Engine搜索这个数据,看看能否通过Cheat Engine来把这个问题解决了,这个就有点没有技术含量了,附有技术含量的,数据它是存在关联的,然后不只数据存在关联,函数它也是存在关联的,假设如果这个函数我们来做的话,这个函数会属于谁?要么是物品要么是物品管理的一个结构,所以可以测试它是不是有关,之前找到的物品管理的几个类里的,上一个内容里的ecx是来自于 0x1256E3C,它俩本质上是属于同一种操作,所以可以看一下它

然后就发现 0x1256E3C 的位置的值就是我们要找的ecx,这个 0x1256E3C 是一个很稳定的值,这是第一个解决办法,这个办法需要分清楚这俩函数的作用体系,如果不是0x1256E3C,那也一定是0x1036518或0x1037D3C里的(0x1037D3C是SRO_Player我们的结构,0x1036518是我们的SRO_Res结构),如果都不属于,可以使用 Cheat Engine 搜索去解决这个问题,如果都不行,那就要用封包了(封包后面写),封包解决就非常简单了,如果就是非要跟它干那就只能去ui初始化的时候,因为前面非常清晰的看到它整个逻辑,当丢一个物品的时候会把它写入到一个结构体(应该是某个ui操作的结构体里),这个数据得到的时候是依据坐标来得到的,最后很明显是遍历一遍一遍的找,链表只能去生成链表的地方去找,不管它是不是ui,这个链表的生成一定不是丢物品的过程,所以只能想办法去找到它整个链表生成或者初始化的过程,这里是通过一个比较简单方法,通过数据关联性来解决的,可以发现逆向依赖于关联的能力,不依赖于硬干的能力

效果图:通过自动化助手可以正常丢弃物品

CUIWnd_1.cpp文件的修改:修改了 OnBnClickedButton3函数(Button3按钮的点击事件处理函数)

// CUIWnd_1.cpp: 实现文件
//

#include "pch.h"
#include "htdMfcDll.h"
#include "CUIWnd_1.h"
#include "afxdialogex.h"
#include "extern_all.h"


// CUIWnd_1 对话框

IMPLEMENT_DYNAMIC(CUIWnd_1, CDialogEx)

CUIWnd_1::CUIWnd_1(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_PAGE_1, pParent)
{

}

CUIWnd_1::~CUIWnd_1()
{
}

void CUIWnd_1::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST1, lstPack);
}


BEGIN_MESSAGE_MAP(CUIWnd_1, CDialogEx)
	ON_BN_CLICKED(IDC_BUTTON1, &CUIWnd_1::OnBnClickedButton1)
	ON_BN_CLICKED(IDC_BUTTON2, &CUIWnd_1::OnBnClickedButton2)
	ON_BN_CLICKED(IDC_BUTTON3, &CUIWnd_1::OnBnClickedButton3)
END_MESSAGE_MAP()


// CUIWnd_1 消息处理程序


void CUIWnd_1::OnBnClickedButton1()
{
	// int count = _pgamebase->SRO_Control->GetPPack()->GetPackBack()->PackCount();
	CString tmp;
	// tmp.Format(L"%d", count);
	// AfxMessageBox(tmp);
	PBackPack _PackBack = _pgamebase->SRO_Control->GetPPack()->GetPackBack();

	lstPack.ResetContent();
	for (int i = 0; i < _PackBack->PackCount(); i++)
	{

		PITEM item = _PackBack->GetItem(i);
		if ((item != NULL) && (item->Type)) {
			tmp.Format(L"[%s][数量:%d][耐久:%d/%d]\n", item->GetNameByWide(), item->Count, item->Durabillty, item->MaxDurabillty);
			lstPack.AddString(tmp);
		}
	}

}


void CUIWnd_1::OnBnClickedButton2()
{
	// int count = _pgamebase->SRO_Control->GetPPack()->GetPackBack()->PackCount();
	CString tmp;
	// tmp.Format(L"%d", count);
	// AfxMessageBox(tmp);
	PEquipPack _PackBack = _pgamebase->SRO_Control->GetPPack()->GetEquipBack();

	lstPack.ResetContent();
	for (int i = 0; i < 13; i++)
	{

		PITEM item = _PackBack->GetItem((EquipType)i);
		if ((item != NULL) && (item->Type > 0)) {
			tmp.Format(L"[%s][数量:%d][耐久:%d/%d]\n", item->GetNameByWide(), item->Count, item->Durabillty, item->MaxDurabillty);
			lstPack.AddString(tmp);
		}
	}
}


void CUIWnd_1::OnBnClickedButton3()
{
	_pgamebase->SRO_Control->DropItem(3);
}

GameBase.cpp文件的修改:修改了Init函数

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

GameBase* _pgamebase;

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

	SRO_Res = (PRes)0x1036518;
	SRO_Control = (PControl)addrRead[0];
	addrRead = (unsigned*)0x1037D3C;
	SRO_Player = (PAIM)addrRead[0];
	
	InitClassProc(&Res::_ReadTitle, 0x9A46C0);
	InitClassProc(&Res::_ReadItemTitle, 0x9A4640);
	InitClassProc(&Control::_NormalNotice, 0x848580);
	InitClassProc(&Control::_NetNotice, 0x844E40);
	InitClassProc(&Control::_ChatNotice, 0x844E80);
	InitClassProc(&Control::_GetPPack, 0x866140);
	InitClassProc(&Control::_UseItem, 0x85F640);
	InitClassProc(&Control::_DropItem, 0x864220);
	InitClassProc(&ITEM::_GetItemRes, 0x995800);
	InitClassProc(&Pack::_GetPackPack, 0x7722C0);
	InitClassProc(&Pack::_GetEquipPack, 0x772300);

}

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

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

Control.cpp文件的修改:新加 DropItem函数、_DropItem初始化

#include "pch.h"
#include "Control.h"

Control::PROC Control::_GetPPack{};
Control::PROC_PSROSTR Control::_NormalNotice{};
Control::PROC_PSROSTR Control::_NetNotice{};
Control::PROC_D_PWSTR_D_D Control::_ChatNotice{};
Control::PROC_D_D_D Control::_UseItem{};
Control::PROC_D_D_D_D_D_D Control::_DropItem{};

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

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

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

void Control::UseItem(int index, int p1, int p2)
{
	(this->*_UseItem)(index, p1, p2);
}

void Control::DropItem(int index, int p1, int p2, int p3, int p4, int p5)
{
	(this->*_DropItem)(p1, index, p2, p3, p4, p5);
}

PPack Control::GetPPack()
{
	return (this->*_GetPPack)();
}

Control.h文件的修改:新加 PROC_D_D_D_D_D_D类型的函数指针、_DropItem函数指针变量、DropItem函数声明

#pragma once
#include "SRO_String.h"
#include "Pack.h"
typedef class Control
{
	typedef PPack (Control::* PROC)();
	typedef void (Control::* PROC_PSROSTR)(PSROSTRING);
	typedef void (Control::* PROC_D_PWSTR_D_D)(int, wchar_t*, int, int);
	typedef void (Control::* PROC_D_D_D)(int, int, int);
	typedef void (Control::* PROC_D_D_D_D_D_D)(int, int, int, int, int, int);
public:
	static PROC _GetPPack;
	static PROC_PSROSTR _NormalNotice;
	static PROC_PSROSTR _NetNotice;
	static PROC_D_PWSTR_D_D _ChatNotice;
	static PROC_D_D_D _UseItem;
	static PROC_D_D_D_D_D_D _DropItem;
public:
	void NormalNotice(PSROSTRING _txt);
	void NetNotice(PSROSTRING _txt);
	void ChatNotice(wchar_t* _txt, int color=0xFFFFAEC3, int type1=0x3, int type2=0x1);
	void UseItem(int index, int p1 = -1, int p2 = -1);
	void DropItem(int index, int p1 = 0x46, int p2 = 0, int p3 = 0, int p4 = 1, int p5 = 0);
	PPack GetPPack();
}*PControl;

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

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

相关文章

关于bypassuac的探究——思考

我们所使用的几个api&#xff0c;如RegCreateKeyExA、RegSetKeyExA都是直接修改注册表的操作&#xff0c;这种操作应该被归类为敏感操作&#xff0c;那么这里会不会被杀软拦截呢&#xff0c;去测试一下 windows defender正常上线 获取到的权限也是bypassuac后的权限 再看一下…

备战蓝桥杯---搜索(BFS基础1)

如果DFS是时光回溯&#xff0c;那么BFS则是影子分身。 下面是它的定义&#xff1a; 下面直接看题&#xff1a; 十分经典&#xff0c;在这注意存的时候可以用i*mj的形式&#xff0c;可以当作模板&#xff0c;下面是AC代码&#xff1a; #include<bits/stdc.h> using name…

阿里云AI通义千问出bug,今天修复了,一切都是莫名其妙,国产AI又可以了?

怎么隔一天就好了&#xff1f; 引言我的处理感想再次提问AI代码结尾 引言 前天我的阿里云AI 通义千问 不是抽风了嘛 详情见 阿里云AI通义千问出bug,解决不了直接弃,开始对国产AI由支持变失望 就是我的一些对话莫名消失了 我的处理 我在这里进行了反馈 但是没有回应 我以为…

NUXTJS安装始终报错无法正常运行问题解决

近日在了解NuxtJS&#xff0c;按照官方给出方法进行安装后&#xff0c;不是报错&#xff0c;就是安装成功后运行不了。执行npm run dev后始终运行出错&#xff0c;判断肯定是对应版本问题&#xff0c;沿着这方向研究&#xff0c;最终运行成功了。 文档地址&#xff1a;安装 - …

后端——go系统学习笔记(不断更新中......)

数组 固定大小 初始化 arr1 : [3]int{1, 2, 3} arr2 : [...]int{1, 2, 3} var arr3 []int var arr4 [4]int切片 长度是动态的 初始化 arr[0:3] slice : []int{1,2,3} slice : make([]int, 10)len和cap len是获取切片、数组、字符串的长度——元素的个数cap是获取切片的容量—…

telnet笔记

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、场景二、介绍1.测试端口2.访问百度3. 简单的爬虫 前言 最近telnet命令用的比较多&#xff0c;所以记录一下。 一、场景 ping应该是大家最常用的命令&…

算法学习——华为机考题库6(HJ36 - HJ40)

算法学习——华为机考题库6&#xff08;HJ36 - HJ40&#xff09; HJ36 字符串加密 描述 有一种技巧可以对数据进行加密&#xff0c;它使用一个单词作为它的密匙。下面是它的工作原理&#xff1a;首先&#xff0c;选择一个单词作为密匙&#xff0c;如TRAILBLAZERS。如果单词中…

51单片机学习笔记 --步进电机驱动说明

文章目录 工作原理代码编写驱动方式全步进驱动半步进驱动微步进驱动 工作原理 工作原理简要说明&#xff0c;和单片机一起配合使用的步进电机多为28BYJ28 五线四相步进电机&#xff0c;配合ULN2003驱动板进行控制&#xff0c;如图所示&#xff0c;对于扭矩、精度要求较高的还有…

HiSilicon352 android9.0 开机视频调试分析

一&#xff0c;开机视频概念 开机广告是在系统开机后实现播放视频功能。 海思Android解决方案在原生Android基础上&#xff0c;增加了开机视频模块&#xff0c;可在开机过程中播放视频文件&#xff0c;使用户更好的体验系统开机过程。 二&#xff0c;模块结构 1. 海思自研开机…

舔狗送女生生日祝福在线源码,可设置查看密码,附带搭建教程

&#x1f389; 女神专属生日祝福源码 &#x1f389; ✨ 在这个特别的日子里&#xff0c;想要为心中的女神送上一份独一无二的生日祝福吗&#xff1f;快来试试这款专为舔狗设计的生日祝福源码吧&#xff01; &#x1f48c; 这款源码不仅可以展示你满满的诚意和祝福&#xff0c…

C++ 日期类的实现

目录 前言 日期类中的成员函数和成员变量 日期类中成员函数的详解和实现 1.天数前后的判断 2.天数加减的实现 3.前置 && 后置 4.计算天数差值 前言 日期类的实现将综合前面所学的&#xff08;类的6个默认成员函数&#xff09;&#xff0c;进一步理解和掌握类的…

面试八股文(4)

文章目录 1.sleep和wait区别2.为什么调用start()方法会执行run()方法&#xff0c;为什么不能直接调用run()方法3.synchronized关键字4.并发编程的三个重要特性5.synchronized和volatile关键字区别6.ThreadLocal7.为什么要用线程池&#xff1f;8.实现Runnable接口和Callable接口…

数据图表方案,企业视频生产数据可视化

在信息爆炸的时代&#xff0c;如何将复杂的数据转化为直观、生动的视觉信息&#xff0c;是企业在数字化转型中面临的挑战。美摄科技凭借其独特的数据图表方案&#xff0c;为企业在数据可视化领域打开了一扇全新的大门。 一、数据图表方案的优势 1、高效便捷&#xff1a;利用数…

模拟钉钉官网动画

实现思路&#xff1a;利用粘性定位sticky&#xff0c;以及滚动事件实现。首先我们应该设置滚动动画开始位置和结束位置 &#xff0c;然后根据位置计算透明度或者transform&#xff0c;scale的值。 首先根据上述图线计算属性值&#xff0c;代码如下&#xff1a; function creat…

技术分享 | app测试中常用的Android模拟器

Emulator Emualor 是 Android Studio 自带的模拟器&#xff0c;是官方提供的工具&#xff0c;Android 开发最常使用的就是这一款。 它功能非常齐全&#xff0c;电话本、通话等功能都可正常使用。用户可以使用键盘输入&#xff0c;鼠标点击模拟器按键输入&#xff0c;甚至还可…

DockerCompose+SpringBoot+Nginx+Mysql实践

DockerComposeSpringBootNginxMysql实践 1、Spring Boot案例 首先我们先准备一个 Spring Boot 使用 Mysql 的小场景&#xff0c;我们做这样一个示例&#xff0c;使用 Spring Boot 做一个 Web 应 用&#xff0c;提供一个按照 IP 地址统计访问次数的方法&#xff0c;每次请求时…

Docker部署xxl-job调度器并结合SpringBoot测试

文章目录 一、Docker部署1. 创建数据库2. 启动容器3. 访问4. 新建执行器 二、SpringBoot整合1. 模块注册到执行器2. 创建配置类3. 启动测试 三、任务发布-普通任务1. 编写任务代码2. 创建任务3. 启动任务 四、任务发布-分片任务1. 编写任务代码2. 启动多个实例3. 创建任务4. 启…

机器学习复习(2)——线性回归SGD优化算法

目录 线性回归代码 线性回归理论 SGD算法 手撕线性回归算法 模型初始化 定义模型主体部分 定义线性回归模型训练过程 数据demo准备 模型训练与权重参数 定义线性回归预测函数 定义R2系数计算 可视化展示 预测结果 训练过程 sklearn进行机器学习 线性回归代码…

浙政钉(专有钉钉)

专有钉钉是浙政钉的测试版本&#xff0c;可在正式发布之前进行业务开发。 专有钉钉 原名政务钉钉 是高安全、强管控、灵活开放的面向大型组织专有独享的协同办公平台。支持专有云、混合云等多种方式灵活部署&#xff0c;以满足客户特定场景所需为目标&#xff0c;最大化以“平…

elk之简介

写在前面 本文看下es的简介。 1&#xff1a;简介 背后公司&#xff0c;elastic&#xff0c;08年纽交所上市&#xff0c;与腾讯&#xff0c;阿里等云厂商有合作&#xff0c;推出云产品&#xff0c;类似功能的产品由solr&#xff0c;splunk&#xff0c;但使用量es当前遥遥领先…