免責聲明:內容僅供學習參考,請合法利用知識,禁止進行違法犯罪活動!
如果看不懂、不知道現在做的什么,那就跟著做完看效果,代碼看不懂是正常的,只要會抄就行,抄著抄著就能懂了
內容參考于:易道云信息技術研究院
上一個內容: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)方便以后跟節奏
![]()
效果圖:
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 = 0x80int 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結構,用與存放從文件中讀取的中文與對應idPTextTable 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 ebxmov ebx, thispush _idcall ReadTextProcmov index, eaxpop 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;
#endifBEGIN_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 WNDHOOKmDll.KbdProc = (DWORD)(KeyCallBack);mDll.SetDll = 0;
#elsekeyHook = SetWindowsHook(WH_KEYBOARD, KeyCallBack);// 代碼一開始,在這注冊一個鍵盤事件的鉤子
#endifPGameProc = 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 Anlyanly = 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一次,一次性的HOOKhooker->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_TIMEenum { IDD = IDD_MAIN };
#endifprotected: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;
};