102.网络游戏逆向分析与漏洞攻防-ui界面的设计-反隐身功能的界面设计与实现(有不使用MFC生成,自己手写代码创建复选框与事件的例子)

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!

如果看不懂、不知道现在做的什么,那就跟着做完看效果,代码看不懂是正常的,只要会抄就行,抄着抄着就能懂了

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

上一个内容:101.怪物列表的界面与显示

码云版本号:25d6bc10c10a5df6ad3fe79cb92cd1e7d127ee57

代码下载地址,在 titan 目录下,文件名为:titan-反隐身功能的界面设计与实现.zip

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

提取码:q9n5

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

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

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

提取码:78h8

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

以 101.怪物列表的界面与显示 它的代码为基础进行修改

效果图:只是把界面搞了,对应的功能没有搞

这里先说一件事如果想实现自瞄,可以修改技能数据包中的目标坐标(94.利用数据包实现使用技能),把目标坐标换成对方角色的坐标就可以实现自瞄了,然后反隐身功能,游戏中专职

游戏中仲裁者到达10级后可以专职成执行者

执行者有一个技能可以隐身,反隐身功能就指的是,敌人使用隐身之后的技能我们也可以正常看到它

然后登陆我们可以抓包的客户端(今晚打老虎客户端是可以被抓包的)

然后使用隐身技能,然后查看今晚打老虎客户端抓到的包,下图是使用了隐身技能

然后找属性修改的数据包,然后就看到了下图的东西,Hide是隐藏的意思,Role是角色的意思,连起来就是角色隐身,这就很明显了

然后看一个解除隐身的数据包,解除隐身

解除隐身的方式是鼠标右击下图红框位置

然后看数据包,属性更新的数据包,因为角色是否显示这属于角色结构体里的属性,然后就看到了RoleHide=0,这说明RoleHide=0是显示RoleHide=1是不显示(隐身)

写代码测试

经过测试修改RoleHide属性是可以的

但是现在无法选中对方角色,选中这个操作是在服务端控制的,如果是在客户端控制的,可以通过逆向改(通过观察正常选择的数据包当做逆向的入口点),这种也不能选择,这种是违规操作,正常来说隐身了连看都看不到,更别说选了,如果选中了服务端可以用来认为客户端被搞了,然后进行封号,所以正常来说,如果选中的角色是隐身的,我要把选中隐身角色或违规选中时的发送的数据包给拦截下来这种的不能给服务端发

如果非要选中的话,如下图,可以给客户端伪造一个更新LastObject属性的数据包,LastObject的值就是角色id

然后到这就分析完了反隐身,接下来就给反隐身做一个界面

添加一个Dialog

然后给新加的Dialog改一下id(代码中会用到,最好与图中的名字保持一致)

然后它的属性

然后添加一个类

类名CWndSet

新加CWndSet.cpp文件

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

#include "pch.h"
#include "CWndSet.h"
#include "afxdialogex.h"
#include "resource.h"

// CWndSet 对话框

IMPLEMENT_DYNAMIC(CWndSet, CDialogEx)

CWndSet::CWndSet(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_PAGE_2, pParent)
{
}

CWndSet::~CWndSet()
{
}

void CWndSet::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BOOL CWndSet::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	for (int i = 0; i < (unsigned)SETNAME::MAX; i++) {
		int x = i % 15;
		int y = i / 15;
		// 创建一个按钮,按钮样式是 BS_CHECKBOX,也就是一个复选框
		setButton[i].Create(txtName[i], BS_CHECKBOX | WS_CHILD, CRect(y*110+10, x*30+5, y*110+110, x*30+35), this, IDC_SETBASE + i);
		// 设置字体
		setButton[i].SetFont(GetFont());
		// 显示按钮
		setButton[i].ShowWindow(TRUE);
	}
	return 0;
}

// i的值是 OnInitDialog函数里写的 setButton[i].Create 里的 IDC_SETBASE + i
void CWndSet::OnSet(UINT i) {
	i = i - IDC_SETBASE;
	setButton[i].SetCheck(!setButton[i].GetCheck());
}
BEGIN_MESSAGE_MAP(CWndSet, CDialogEx)
	ON_CONTROL_RANGE(BN_CLICKED, IDC_SETBASE, IDC_SETBASE + (unsigned)SETNAME::MAX, OnSet)
END_MESSAGE_MAP()


// CWndSet 消息处理程序

新加 CWndSet.h文件

#pragma once
#include "afxdialogex.h"
#include "CWndCreateRole.h"

#define IDC_SETBASE (WM_USER + 100)// 处理按钮的消息映射
// CWndSet 对话框
enum class SETNAME {
	AntiHide, // 隐身
	AddSpeed, // 加速
	NeverFall, // 摔不死
	MAX
};
class CWndSet : public CDialogEx
{
	DECLARE_DYNAMIC(CWndSet)

public:
	CWndSet(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~CWndSet();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_PAGE_2 };
#endif
	CString txtName[(unsigned)SETNAME::MAX]{
		L"反隐身",
		L"加速",
		L"摔不死",
	};

	CButton setButton[(unsigned)SETNAME::MAX];

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
	virtual BOOL OnInitDialog();
	DECLARE_MESSAGE_MAP()
	afx_msg void OnSet(UINT i);
};

CUI.cpp文件的修改:修改了 OnInitDialog函数

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

#include "pch.h"
#include "htdMfcDll.h"
#include "CUI.h"
#include "afxdialogex.h"
#include "extern_all.h"
// CUI 对话框

IMPLEMENT_DYNAMIC(CUI, CDialogEx)

CUI::CUI(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_MAIN, pParent)
	, txtDetails(_T(""))
	, txtChat(_T(""))
	, txtChatName(_T(""))
{

}

CUI::~CUI()
{
}

void CUI::SetListView(CListCtrl* lst)
{
	auto lStyle = GetWindowLongPtr(lst->m_hWnd, GWL_STYLE); // 获取窗口样式
	lStyle |= LVS_REPORT; // 设置为报表模式
	SetWindowLongPtr(lst->m_hWnd, GWL_STYLE, lStyle);// 给窗口设置样式
	auto dStyle = lst->GetExtendedStyle(); // 获取扩展样式
	dStyle |= LVS_EX_FULLROWSELECT; // 设置选择时选择一行
	dStyle |= LVS_EX_GRIDLINES; // 画网格线
	lst->SetExtendedStyle(dStyle); // 设置扩展样式
}

void CUI::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_TAB1, mTab);
	DDX_Control(pDX, IDC_LIST1, lstlog);
	DDX_Text(pDX, IDC_EDIT1, txtDetails);
	DDX_Text(pDX, IDC_EDIT3, txtChat);
	DDX_Text(pDX, IDC_EDIT2, txtChatName);
	DDX_Control(pDX, IDC_COMBO2, cmbChat);
}

BOOL CUI::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	
	SetListView(&lstlog);

	InstallPage(new CUIWnd_0(this), IDD_PAGE_0, L"角色", TRUE);
	InstallPage(new CUIWnd_1(this), IDD_PAGE_1, L"周围");
	InstallPage(new CWndSet(this), IDD_PAGE_2, L"设置");

	lstlog.InsertColumn(0, L"消息", 0, 70);
	lstlog.InsertColumn(1, L"内容", 0, 700);
	lstlog.InsertColumn(2, L"时间", 0, 200);

	//PageINJ.Init(wAppPath);
	//PageRAN.SetAppPath(wAppPath);

	for (CString & txt : ChatPdName)
	{
		cmbChat.AddString(txt);
	}
	cmbChat.SetCurSel(0);
	return TRUE;
}

bool CUI::InstallPage(CDialogEx* wnd, int IDD_WND, CString&& _Name, BOOL IsShow)
{

	if (CurPage >= (sizeof(WNDS) / sizeof(CDialogEx*))) return false;
	Pages[CurPage] = wnd;
	Pages[CurPage]->Create(IDD_WND, this);
	//Pages[CurPage]->SetParent(this);
	Pages[CurPage]->ShowWindow(IsShow);

	CRect rect;
	mTab.GetClientRect(&rect);
	rect.top += 32;
	rect.left += 5;
	rect.bottom -= 4;
	rect.right -= 5;
	Pages[CurPage]->MoveWindow(&rect);
	mTab.InsertItem(CurPage, _Name);

	CurPage++;
	return true;
}

BEGIN_MESSAGE_MAP(CUI, CDialogEx)
	ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, &CUI::OnTcnSelchangeTab1)
	ON_WM_TIMER()
	ON_BN_CLICKED(IDC_BUTTON1, &CUI::OnBnClickedButton1)
END_MESSAGE_MAP()


// CUI 消息处理程序


void CUI::OnTcnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult)
{
	// TODO: 在此添加控件通知处理程序代码
	*pResult = 0;
	int n = mTab.GetCurSel();
	for (int i = 0; i < CurPage; i++)
	{
		Pages[i]->ShowWindow(i == n);
	}
}

PTextManger CUI::getTxtManger()
{
	if (!txtManger)txtManger = new TextManger("F:\\语言包.txt");
	return txtManger;
}

void CUI::loginok(ROLE_DATA* _roles, int count)
{
	PushLog(LOGTYPE::SYS, L"账号登录成功");
	NetClient::loginok(_roles, count);
	wnds.wndRole->lstRole.DeleteAllItems();
	CString txtInfos;
	for (int i = 0; i < count; i++)
	{
		txtInfos = _roles[i].infos.value();
		txtInfos = txtInfos + L";";
		CString txtIndex, txtSex, txtMap, txtJob, txtCamp, txtRace, txtLv;
		CStringA stxtMap, stxtJob, stxtCamp, stxtRace;
		txtSex = ReadValue(txtInfos, L"1,", L";");
		txtMap = ReadValue(txtInfos, L"56,", L";");
		txtJob = L"job_" + ReadValue(txtInfos, L"37,", L";");
		txtCamp = L"camp_" + ReadValue(txtInfos, L"35,", L";");
		txtRace = L"race_" + ReadValue(txtInfos, L"36,", L";");
		txtLv = ReadValue(txtInfos, L"38,", L";");
		txtIndex.Format(L"%d", _roles[i].index.value());

		stxtMap = txtMap;
		stxtJob = txtJob;
		stxtCamp = txtCamp;
		stxtRace = txtRace;
		txtSex = SexName[txtSex == L"1"];
		wnds.wndRole->lstRole.InsertItem(0, txtIndex);
		wnds.wndRole->lstRole.SetItemText(0, 1, _roles[i].name);
		wnds.wndRole->lstRole.SetItemText(0, 2, txtLv);
		wnds.wndRole->lstRole.SetItemText(0, 3, txtSex);
		wnds.wndRole->lstRole.SetItemText(0, 4, getTxtManger()->ReadTextById(stxtCamp.GetBuffer()));
		wnds.wndRole->lstRole.SetItemText(0, 5, getTxtManger()->ReadTextById(stxtMap.GetBuffer()));
		wnds.wndRole->lstRole.SetItemText(0, 6, getTxtManger()->ReadTextById(stxtRace.GetBuffer()));
		wnds.wndRole->lstRole.SetItemText(0, 7, getTxtManger()->ReadTextById(stxtJob.GetBuffer()));
	}
}

bool CUI::Tips(int code)
{
	CString logName;
	logName.Format(L"服务器提示:%d", code);
	PushLog(LOGTYPE::TIPS, logName.GetBuffer());
	// NetClient::Tips(code);
	return true;
}

bool CUI::OnInited()
{
	SetTimer(0x10001, 50, NULL);
	return true;
}

bool CUI::OnSvrChat(PCHAT_PRV _coder)
{
	
	CString txt;
	CString txtPd;
	CString txtName = _coder->name;
	CString txtInfo = _coder->txt;
	txt.Format(L"%d", _coder->ChartId.value());
	AfxMessageBox(txt);

	switch (_coder->ChartId)
	{
	case 1:// 附近频道
		txtPd = L"附近";
		break;
	case 2:// 区域频道
		txtPd = L"区域";
		break;
	case 3:// 私聊
		txtPd = L"接收的私聊";
		// return OnChatPrivate((PCHAT_PRV)_coder);
		break;
	case 6:// 公会频道
		txtPd = L"公会";
		break;
	case 9:// 阵营频道
		txtPd = L"阵营";
		break;
	case 21:// 喊话频道
		txtPd = L"喊话";
	    // return OnChatPublic((PCHAT_PUB)_coder);
	    break;
	case 103:// 喊话频道
		txtPd = L"发送的私信";
	    // return OnChatPublic((PCHAT_PUB)_coder);
	    break;
	}

	txt.Format(L"[%s][%s][%s]", txtPd, txtInfo, txtName);
	PushLog(LOGTYPE::CHAT, txt.GetBuffer());
	return true;
}

CString CUI::ReadValue(CString& txt, wchar_t* key, wchar_t* endStr)
{
	CString result = L"";
	int iStart = txt.Find(key);
	if (iStart > -1) {
		iStart = iStart + wcslen(key);
		int iend = txt.Find(endStr, iStart);
		if (iend > -1)result = txt.Mid(iStart, iend - iStart);
	}
	return result;
}

void CUI::PushLog(LOGTYPE type, wchar_t* txt)
{
	struct tm newtiem {};
	time_t t;
	time(&t);
	localtime_s(&newtiem, &t); // 获取时间
	CString logName;
	logName.Format(L"%.4d-%.2d-%.2d %.2d:%.2d:%.2d", newtiem.tm_year + 1900, newtiem.tm_mon + 1, newtiem.tm_mday, newtiem.tm_hour, newtiem.tm_min, newtiem.tm_sec);

	lstlog.InsertItem(0, MsgName[(int)type]);
	lstlog.SetItemText(0, 1, txt);
	lstlog.SetItemText(0, 2, logName);
}


void CUI::OnTimer(UINT_PTR nIDEvent)
{
	wnds.wndAIM->UI();
	CStringA tmp;
	tmp.Format("%d", Player.CurArea);
	txtDetails.Format(L"lv.%d.%s 生命值[%d/%d] 经验值[%d/%d] 当前所在场景[%s](%.2f|%.2f|%.2f)", Player.Level, Player.Name,  Player.HP, Player.MaxHP + Player.MaxHPAdd, Player.PlayerExp, Player.PlayerUpgradeExp, getTxtManger()->ReadTextById(tmp.GetBuffer()), Player.x, Player.h, Player.y);
	SetDlgItemText(IDC_EDIT1, txtDetails.GetBuffer());
	// UpdateData(FALSE);
	__super::OnTimer(nIDEvent);
}


void CUI::OnBnClickedButton1()
{
	int nsel = cmbChat.GetCurSel();
		UpdateData(TRUE);
	if (nsel > -1) {
		if (ChatPdId[nsel] != -1) {
			Talk(txtChat.GetBuffer(), ChatPdId[nsel]);
		}
		else {
			TalkTo(txtChatName.GetBuffer(), txtChat.GetBuffer());
		}
		txtChat = L"";
	}
		SetDlgItemText(IDC_EDIT3, txtChat.GetBuffer());

}

CUI.h文件的修改:引入 CWndSet.h文件,修改了 WNDS结构体

#pragma once
#include "afxdialogex.h"
#include "NetClient.h"
#include "TextManger.h"
//增加页面头文件
#include "CUIWnd_0.h"
#include "CUIWnd_1.h"
#include "CWndSet.h"
//游戏辅助UI类
// CUI 对话框

enum class LOGTYPE {
	TIPS = 0,
	SYS = 1,
	CHAT = 2,
	MAX
};

typedef struct WNDS {
	CUIWnd_0* wndRole;
	CUIWnd_1* wndAIM;
	CWndSet* wndSet;
};

// #define MAX_PAGE_MAIN 3

// 这里用了多重继承,这回有一个问题,函数名一样的会发生冲突
// 所以在继承的时候要注意函数名
class CUI : public CDialogEx,public NetClient
{
	DECLARE_DYNAMIC(CUI)

public:
	CUI(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~CUI();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_MAIN };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
	union {
		CDialogEx* Pages[sizeof(WNDS)/sizeof(CDialogEx*)];
		WNDS wnds;
	};
	
	short      CurPage = 0;
public:
	CTabCtrl mTab;
	virtual BOOL OnInitDialog();
	bool    InstallPage(CDialogEx* wnd, int IDD_WND, CString&& _Name, BOOL IsShow=FALSE);
	afx_msg void OnTcnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult);
public:
	void SetListView(CListCtrl* lst);
	PTextManger getTxtManger();
	CListCtrl lstlog;
protected:
	CString MsgName[(unsigned int)LOGTYPE::MAX]{
		L"错误",
		L"系统",
		L"聊天"
	};

	CString SexName[2]{
		L"男",
		L"女"
	};

	CString ChatPdName[6]{
		L"附近",
		L"区域",
		L"公会",
		L"阵营",
		L"喊话",
		L"私信"
	};

	int ChatPdId[6]{
		1, 2, 6, 9, 21, -1
	};

	void PushLog(LOGTYPE type, wchar_t* txt);
protected:
	void virtual loginok(ROLE_DATA* _roles, int count);
	bool virtual Tips(int code);
	bool virtual OnInited();
	bool virtual OnSvrChat(PCHAT_PRV _coder);

	// 解析角色的信息,性别、种族、阵营等
	CString ReadValue(CString&txt, wchar_t* key, wchar_t* endStr);
public:
	CString txtDetails;
	afx_msg void OnTimer(UINT_PTR nIDEvent);
	CString txtChat;
	CString txtChatName;
	afx_msg void OnBnClickedButton1();
	CComboBox cmbChat;
};

AIM.cpp文件的修改:新加 SetRoleHide函数

#include "pch.h"
#include "AIM.h"
#include "extern_all.h"

// 复制游戏数据包到自己的内存中
// Create是预留的一个接口暂时没用到
void AIM::SetHeadDatas(void* buffStart, bool Create)
{
	Isfree = false;
	unsigned lStart = (unsigned)&lId;
	unsigned lEnd = (unsigned)&endclass;
	memcpy(&lId, buffStart, lEnd - lStart);
}

bool AIM::SetHeadCord(void* buffStart)
{
	Isfree = false;
	unsigned lStart = (unsigned)&lId;
	unsigned lEnd = (unsigned)&tx;
	memcpy(&lId, buffStart, lEnd - lStart);
	return true;
}

// 怪物只有ConfigId没有名字,所以重写SetConfigID让它根据ConfigId获取中文名
void AIM::SetConfigID(char*& buffStart)
{
	GAMEOBJECT::SetConfigID(buffStart);
	Name = txtManger->ReadTextById(ConfigID);
}

void AIM::SetRoleHide(char*& buffStart)
{
	// 下方的判断是用来判断当前属性的更新是否是我们玩家自己
	// this->lId == Client->Player.lId;
	int* ntRead = (int*)buffStart;
	int value = ntRead[0];
	ntRead[0] = 0;
	GAMEOBJECT::SetRoleHide(buffStart);
	RoleHide = value;
}

AIM.h文件的修改:新加 SetRoleHide函数

#pragma once
#include "GameOBJECT.h"
typedef class AIM:public GAMEOBJECT {
public:
	long long lId;
	float x;
	float h;
	float y;
	float face;

	float tx;
	float th;
	float ty;
	float tface;

	int IState;
	int endclass;

	// Create是预留的一个接口暂时没用到
	void virtual SetHeadDatas(void* buffStart, bool Create = true);
	bool virtual SetHeadCord(void* buffStart);
	// 怪物只有ConfigId没有名字,所以重写SetConfigID让它根据ConfigId获取中文名
	void virtual SetConfigID(char*& buffStart);
	void virtual SetRoleHide(char*& buffStart);
}*PAIM;


  • 34
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值