免责声明:内容仅供学习参考,请合法使用知识,克制进行违法犯罪活动!
如果看不懂、不知道现在做的什么,那就跟着做完看效果,代码看不懂是正常的,只要会抄就行,抄着抄着就能懂了
内容参考于:易道云信息技术研究院
上一个内容:94.使用数据包实现使用技能
码云版本号:2827cd4dfd810afb514b838b6a7c1ee0a11e639e
代码下载地点,在 titan 目次下,文件名为:titan-ui的设计与架构.zip
链接:https://pan.baidu.com/s/1W-JpUcGOWbSJmMdmtMzYZg
提取码:q9n5
--来自百度网盘超级会员V4的分享
HOOK引擎,文件名为:黑兔sdk升级版.zip
链接:https://pan.baidu.com/s/1IB-Zs6hi3yU8LC2f-8hIEw
提取码:78h8
--来自百度网盘超级会员V4的分享
以 94.使用数据包实现使用技能 它的代码为基础进行修改
现在实现了好多功能,但是没有一个界面显示都有什么功能,没有界面感觉有一点黑灯瞎火的,所以接下来先把ui给实现了,再搞其它的
下方的ui界面是黑兔sdk模板里的(去百度网盘下载)
tab控件的属性,属性可不改,改它只是为了悦目点的
 添加一个list控件,属性可不改,改它只是为了悦目点的
 给控件添加一个变量
 输入个变量名,直接点完成,推荐变量名与下图中划一(lstlog)方便以后跟节奏
 效果图:下图的窗口在游戏中按键盘上的Home键就会弹出,实现弹出的代码在htdMfcDll.cpp里的KeyCallBack函数中实现
 CUIWnd_0.cpp文件也修改了,它修改的内容不紧张,所以不在这把代码贴出来了
TextManger.h文件的修改:新加 TextManger构造函数
- #pragma once
- #include <iostream>
- #include <fstream>
- /*
- 03E1B73B | 68 58120304 | push fxgamelogic.4031258 | 4031258:"gui"
- 03E1B740 | E8 7BEF0F00 | call fxgamelogic.3F1A6C0 |
- 03E1B745 | 83C4 04 | add esp,4 |
- 03E1B748 | 85C0 | test eax,eax |
- 03E1B74A | 0F84 10090000 | je fxgamelogic.3E1C060 |
- 03E1B750 | 68 4C120304 | push fxgamelogic.403124C | 403124C:"TextManager"
- 03E1B755 | 50 | push eax |
- 03E1B756 | 8D4C24 64 | lea ecx,dword ptr ss:[esp+64] |
- 03E1B75A | 51 | push ecx |
- 03E1B75B | E8 00091000 | call fxgamelogic.3F1C060 |
- 03E1B760 | 8D5424 68 | lea edx,dword ptr ss:[esp+68] |
- 03E1B764 | 52 | push edx |
- 03E1B765 | E8 16EF0F00 | call fxgamelogic.3F1A680 | 111111111111
- 03E1B76A | 83C4 10 | add esp,10 |
- 03E1B76D | 85C0 | test eax,eax |
- 03E1B76F | 894424 14 | mov dword ptr ss:[esp+14],eax | [esp+14]:CD3DDDIDX10_DrawIndexedPrimitive+113
- */
- // 游戏中的中文表结构是 中文名英文id 这样的
- typedef class TextTable {
- public:
- int next = ((int)0xFFFFFFFF);
- int un = sizeof(TextTable);
- wchar_t* Txt;// 中文
- char TxtId[0x24];// 中文对应的英文id
- }*PTextTable;
- typedef class TextManger
- {
- public:
- static unsigned ReadTextProc;
- static const wchar_t NullText[2];
- TextManger(const char* file);
- private:
- //int un[0x20];// 0x20 * 0x4 = 0x80
- int un[0x1E];
- union {
- char* TextBuff;
- unsigned* Count;
- };
- PTextTable textDatas = nullptr; // 中文结构
- public:
- PTextTable* textTable = nullptr; // 暂时无用
- private:
- int un1;
- public:
- unsigned hashCount;
- unsigned* HashIndexTable = nullptr; // 哈希表
- public:
- bool CreateTextDat(const char* filename); // 导出语言包
- bool LoadTextData(const char* _filename); // 加载 CreateTextDat函数写出的文件
- const wchar_t* ReadTextById(const char* _id); // 根据id获取中文名
- unsigned Hashcode(const char* id);// 哈希函数
- // 获取一个未使用的中文与对应id结构,用与存放从文件中读取的中文与对应id
- PTextTable GetNullText(PTextTable _table);
- }*PTextManger;
复制代码 TextManger.cpp文件的修改:新加 TextManger构造函数
- #include "pch.h"
- #include "TextManger.h"
- unsigned TextManger::ReadTextProc = 0x10295FB0;
- const wchar_t TextManger::NullText[2]{};
- TextManger::TextManger(const char* file)
- {
- LoadTextData(file);
- }
- bool TextManger::CreateTextDat(const char* _filename)
- {
- if (!textTable) {
- return false;
- }
- std::ofstream out(_filename, std::ios::out|std::ios::binary);
- if (out.bad())return false;
- out.write((char*)&hashCount, sizeof(hashCount));
- for (int i = 0; i < hashCount; i++) {
- // 游戏中的中文表结构是 中文名英文id 这样的
- int lenId = strlen(textTable[i]->TxtId) + 1;
- // 宽字节一个字是2字节,wcslen函数返回值是字的个数
- int lenTxt = wcslen(textTable[i]->Txt) + 1;
- lenTxt = lenTxt * 2;
- out.write(textTable[i]->TxtId, lenId);
- out.write((char*)textTable[i]->Txt, lenTxt);
- }
- out.close();
- return true;
- }
- bool TextManger::LoadTextData(const char* _filename)
- {
- CString txt;
- if (textDatas)return false;
- std::ifstream file(_filename, std::ios::in | std::ios::binary|std::ios::ate);
- if(file.bad()) {
- return false;
- }
- unsigned buffSize = file.tellg();
- TextBuff = new char[buffSize];
- file.seekg(std::ios::beg);
- file.read(TextBuff, buffSize);
- // textTable = new PTextTable[Count[0]];
- textDatas = new TextTable[Count[0]];
- txt.Format(L"%X", &textDatas);
- AfxMessageBox(txt);
- char* buffStart = TextBuff + 4;
- char* buffEnd = buffStart + buffSize;
- int icount = 0;
- while(buffStart < buffEnd){// 读取语言文件的数据到 textDatas 变量里
- int lenId = strlen(buffStart) + 1;
- memcpy(textDatas[icount].TxtId, buffStart, 0x24);
- buffStart = buffStart + lenId;
- int lenTxt = wcslen((wchar_t*)buffStart) + 1;
- lenTxt = lenTxt * 2;
- textDatas[icount].Txt = (wchar_t*)buffStart;
- buffStart = buffStart + lenTxt;
- icount++;
- }
- hashCount = Count[0];
- HashIndexTable = new unsigned[hashCount];
- memset(HashIndexTable, 0xFF, hashCount * sizeof(unsigned));
- for (int i = 0; i < Count[0]; i++) {// 给 textDatas 变量添加哈希算法索引(哈希表存放 textDatas的下标)
- unsigned hash = Hashcode(textDatas[i].TxtId);
- hash = hash % (hashCount + 1);
- if (HashIndexTable[hash] == -1) {
- HashIndexTable[hash] = i;
- }
- else {
- PTextTable nullTable = GetNullText(&textDatas[HashIndexTable[hash]]);
- nullTable->next = i;
- }
- }
- return false;
- }
- const wchar_t* TextManger::ReadTextById(const char* _id)
- {
- // unsigned callProc = 0x10295FB0;
- if (textTable) {
- int index;
- _asm {
- push ebx
- mov ebx, this
- push _id
- call ReadTextProc
- mov index, eax
- pop ebx
- }
- if (index < 0)return NullText;
- PTextTable* _table = textTable;
- return (wchar_t*)_table[index]->Txt;
- }
- else {
- unsigned hash = Hashcode(_id);
- hash = hash % (hashCount + 1);
- unsigned index = HashIndexTable[hash];
- if (index > hashCount) {// index的值如果是-1那么它肯定比hashCount大
- return NullText;
- }
- while (strcmp(textDatas[index].TxtId, _id)) {
- index = textDatas[index].next;
- if (index > hashCount)return NullText;
- }
- return textDatas[index].Txt;
- }
- }
- unsigned TextManger::Hashcode(const char* id)
- {
- unsigned hash = 0;
- for (int i = 0; id[i]; i++)
- {
- hash = hash * 131 + id[i];
- }
- return hash;
- }
- PTextTable TextManger::GetNullText(PTextTable _table)
- {
- while (_table->next != -1) {// 从链表中找出最后一个(next的值是-1的)
- _table = &textDatas[_table->next];
- }
- return _table;
- }
复制代码 htdMfcDll.cpp文件的修改:修改了 InitInstance函数、KeyCallBack函数
- // htdMfcDll.cpp: 定义 DLL 的初始化例程。这是一个MFC的dll
- //
- #include "pch.h"
- #include "framework.h"
- #include "htdMfcDll.h"
- #include "extern_all.h"
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #endif
- // 注释掉下方宏,以入口点注入的方式注入
- // #define WNDHOOK
- #ifdef WNDHOOK
- typedef struct htdDll
- {
- HHOOK keyHook;
- unsigned KbdProc;
- unsigned SetDll;
- }*PHtdDll;
- void htdSetDll(htdDll hDll);
- htdDll mDll;
- #endif
- BEGIN_MESSAGE_MAP(ChtdMfcDllApp, CWinApp)
- END_MESSAGE_MAP()
- // ChtdMfcDllApp 构造
- ChtdMfcDllApp::ChtdMfcDllApp()
- {
-
- }
- ChtdMfcDllApp theApp;
- ChtdMfcDllApp* PtheApp;
- HHOOK keyHook;
- LRESULT CALLBACK KeyCallBack(int nCode, WPARAM w, LPARAM l);
- BOOL ChtdMfcDllApp::InitInstance()
- {
- CWinApp::InitInstance();
- PtheApp = this;
- #ifdef WNDHOOK
- mDll.KbdProc = (DWORD)(KeyCallBack);
- mDll.SetDll = 0;
- #else
- keyHook = SetWindowsHook(WH_KEYBOARD, KeyCallBack);// 代码一开始,在这注册一个键盘事件的钩子
- #endif
- PGameProc = new GameProc();
- wndMain = new CUI();
- wndMain->Create(IDD_MAIN);
- Client = wndMain;
- return TRUE;
- }
- // 钩子处理事件
- LRESULT CALLBACK KeyCallBack(int nCode, WPARAM w, LPARAM l)
- {
- if (nCode == 0)
- {
- if ((l & (1 << 31)) == 0)
- {
- switch (w)
- {
- case VK_HOME:
- PtheApp->wndMain->ShowWindow(TRUE);
- break;
- }
- }
- }
- return CallNextHookEx(keyHook, nCode, w, l);
- }
- #ifdef WNDHOOK
- void htdInit(htdDll* hDll)
- {
- hDll->KbdProc = mDll.KbdProc;
- hDll->keyHook = mDll.keyHook;
- hDll->SetDll = mDll.SetDll;
- }
- void htdSetDll(htdDll hDll)
- {
- mDll = hDll;
- }
- #else
- void htdInit()
- {
- }
- #endif
复制代码 GameProc.cpp文件的修改:修改了 Init函数
- #include "pch.h"
- #include "GameProc.h"
- #include "extern_all.h"
- // typedef bool(GameWinSock::* U)(char*, unsigned);
- bool _OnComprase(HOOKREFS2) {
- return WinSock->OnRecv((char*)_EDI, _EBP);
- }
- bool _OnRecv(HOOKREFS2) {
- _EAX = WinSock->RecvPoint;
- return true;
- }
- bool _OnConnect(HOOKREFS2) {
- /*
- 根据虚函数表做HOOK的操作
- 截取 ecx 获取 winsock 的值(指针)
- */
- unsigned* vtable = (unsigned*)_EDX;
- //WinSock = (GameWinSock *)_ECX;
- /*
- 联合体的特点是共用一个内存
- 由于 GameWinSock::OnConnect 的 OnConnect函数是 GameWinSock类的成员函数
- 直接 vtable[0x34 / 4] = (unsigned)&GameWinSock::OnConnect; 这样写语法不通过
- 所以使用联合体,让语法通过
- */
- union {
- unsigned value;
- bool(GameWinSock::* _proc)(char*, unsigned);
- } vproc;
- DWORD oldPro, backProc;
- VirtualProtect(vtable, 0x100, PAGE_EXECUTE_READWRITE, &oldPro);
- /*
- vproc._proc = &GameWinSock::OnConnect; 这一句是把我们自己写的调用connect函数的地址的出来
- */
- vproc._proc = &GameWinSock::OnConnect;
- /*
- InitClassProc函数里做的是给指针赋值的操作
- InitClassProc(&GameWinSock::_OnConnect, vtable[0x34/4]);这一句的意思是把
- GameWinSock类里的_OnConnect变量的值赋值成vtable[0x34/4],这个 vtable[0x34/4] 是虚表里的函数
- vtable[0x34/4]是游戏中调用connect函数的函数地址,经过之前的分析调用connect是先调用了虚表中的
- 一个函数,然后从这个函数中调用了connect函数
- */
- InitClassProc(&GameWinSock::_OnConnect, vtable[0x34/4]);
- vtable[0x34 / 4] = vproc.value;
- vproc._proc = &GameWinSock::OnSend;
- InitClassProc(&GameWinSock::_OnSend, vtable[0x3C / 4]);
- vtable[0x3C / 4] = vproc.value;
- VirtualProtect(vtable, 0x100, oldPro, &backProc);
- PGameProc->InitTextManger();
- return true;
- }
- GameProc::GameProc()
- {
- hooker = new htd::hook::htdHook2();
- Init();
- InitInterface();
- }
- void GameProc::LoadBase()
- {
- LoadLibraryA("fxnet2.dll");
- hlogic = LoadLibraryA("fxgamelogic.dll");
- // 如果fxgamelogic.dll里用到了其它dll里面的东西,在这赋值可能会有问题
- // 最好在游戏网络连接成功哪里赋值,或者进入到游戏中之后再搞
- GetClassByName = (PGetClassByName)((unsigned)hlogic + 0x60A6C0);
- GetClassModByName = (PGetClassModByName)((unsigned)hlogic + 0x60C060);
- GetClassObject = (PGetClassObject)((unsigned)hlogic + 0x60A680);
- }
- void GameProc::InitTextManger()
- {
- CLASS_OFFSET offSet;
- void* classPointer = GetClassByName("gui");
- GetClassModByName(&offSet, classPointer, "TextManager");
- txtManger = GetClassObject(&offSet);
- }
- void GameProc::Init()
- {
- #ifdef Anly
- anly = new CAnly();
- GameAnlyer = new GameAnly();
- #endif
- /*
- 这里的 WinSock 是0没有创建对象,但是还是能调用Init函数
- 这是因为Init函数里面没有用到this,没用到就不会报错
- */
- // Client = new NetClient();
- init_datadesc();
- }
- void GameProc::InitInterface()
- {
- LoadBase();
- // MessageBoxA(0, "1", "1", MB_OK);
- // 只会HOOK一次,一次性的HOOK
- hooker->SetHook((LPVOID)0x10617046, 0x1, _OnConnect, 0, true);
- /*
- 第一个参数是HOOK的位置
- 第二个参数是HOOK的位置的汇编代码的长度(用于保证执行的汇编代码完整)
- 第三个参数是HOOK之后当游戏执行到第一个参数的位置的时候跳转的位置
- 第四个参数是 _OnRecv 函数返回 false 之后跳转的位置
- */
- hooker->SetHook((LPVOID)0x10618480, 0x1, _OnRecv, 0);
- hooker->SetHook((LPVOID)0x1061161D, 0x3, _OnComprase, (LPVOID)0x10611602);
- /*
- 在这里绑定游戏处理数据包函数(0x10618480函数)
- 然后0x10618480函数在上面一行代码已经进行了HOOK
- 所以在调用_OnRecv函数指针时,它就会进入我们HOOK
- */
- InitClassProc(&GameWinSock::_OnRecv, 0x10618480);
- }
复制代码 CUI.cpp文件的修改:新加 SetListView函数、getTxtManger函数,修改了 DoDataExchange函数、OnInitDialog函数、InstallPage函数
- // 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)
- {
- }
- 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);
- }
- BOOL CUI::OnInitDialog()
- {
- CDialogEx::OnInitDialog();
-
- SetListView(&lstlog);
- InstallPage(new CUIWnd_0(), IDD_PAGE_0, L"角色", TRUE);
- InstallPage(new CUIWnd_1(), IDD_PAGE_1, L"信息");
- lstlog.InsertColumn(0, L"消息", 0, 70);
- lstlog.InsertColumn(1, L"内容", 0, 700);
- lstlog.InsertColumn(2, L"时间", 0, 130);
- //PageINJ.Init(wAppPath);
- //PageRAN.SetAppPath(wAppPath);
- return TRUE;
- }
- bool CUI::InstallPage(CDialogEx* wnd, int IDD_WND, CString&& _Name, BOOL IsShow)
- {
- if (CurPage >= MAX_PAGE_MAIN) 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 += 28;
- 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)
- 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;
- }
复制代码 CUI.h文件的修改:修改了 CUI类,引入 NetClient头文件、TextManger头文件,新加 SetListView函数、getTxtManger函数、lstlog变量
- #pragma once
- #include "afxdialogex.h"
- #include "NetClient.h"
- #include "TextManger.h"
- //增加页面头文件
- #include "CUIWnd_0.h"
- #include "CUIWnd_1.h"
- //游戏辅助UI类
- // CUI 对话框
- #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()
- CDialogEx* Pages[MAX_PAGE_MAIN];
- 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;
- };
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |