游戲輔助丨手把手簡單實現(xiàn)射擊游戲逆向(2)
作者:問號哥
2.??子彈無后座,子彈連發(fā)實現(xiàn)
同樣的,我們依舊是要找到子彈后座力的地址,子彈后座力有很多的保存形式,比如射擊的次數(shù),比如一個浮點數(shù)的大小--我的子彈擴散的半徑,比如離屏幕準心的2個偏移,既然這么多形式我們該如何下手呢,這時候我們先前找到的子彈地址就排上用場了。不妨假設我們武器相關的數(shù)據(jù)都在一個對象中,那么就有可能他們在內(nèi)存是一段連續(xù)的區(qū)域中。根據(jù)我們的假設我們要查看子彈地址的內(nèi)存區(qū)域。點擊我們的子彈地址,右鍵點擊瀏覽相關內(nèi)存區(qū)域,然后開幾槍試試
不妨將這兩個地址記錄下來,將我們的子彈指針復制幾份,然后改個名字
可疑1和可疑2可以通過子彈指針的帶,只要加上地址的差值即可
一個差值為0x70一個差值為0x98
把鎖住偏移為0x70的,我們進游戲開槍,
鎖前
鎖后
很明顯的可以看到我們的子彈明顯收束了,靠我們的堅持和努力我們的槍法變好了。
那我在把可疑2鎖住,用沖鋒槍效果可能不是很明顯,我們改用狙擊槍,我們發(fā)現(xiàn)狙擊槍可以連發(fā)了,直接變身加特林。
那么我們就知道可疑1是子彈后座,可疑2是子彈的發(fā)射間隔
找到數(shù)據(jù)后就是實現(xiàn)功能的思路
1.通過不斷的寫入值,使得這個值不變,2.改指令,或是跳過后座call,或是nop掉后座call
1如同前面的寫入操作,我們直接看第二個
依舊是什么改寫了這個地址,然后進游戲開槍,看看誰讓槍抖了
我們開了一槍后,出現(xiàn)了三個地方,但是開槍的時候觀察到第一個正好是我們開槍的時候跳出來的,所以我們追第一個,顯示反匯編程序,進去別管啥,直接nop掉,不執(zhí)行這個指令,我們的值就不會改,我們的功能就實現(xiàn)了。
我們進游戲開槍試試,我們發(fā)現(xiàn)槍的子彈是收束的,大功告成,但是我們的視角還是會抖,不夠平滑,怎么辦呢,根據(jù)常識,我們可以假設開槍的時候我們的視角抖了,我們的槍跟著抖,那么有可能這兩個功能的被封裝在一個函數(shù)里,我們只要把這個函數(shù)給nop,我們槍就不抖了,視角也不會抖,豈不是很好,說干就干,那我們就要要追這個函數(shù),點擊我們nop掉的地址,右鍵設置硬件斷點
我們進游戲開槍,我們就暫停在了nop指令上,現(xiàn)在有2思路,一是慢慢往上逆,把跳過這條指令的jcc指令全部改成jmp,跳過所有開槍后嘗試的作痛,第二種思路就是查看上一層的call,把整個視角抖動call nop掉,第1種大家可以去嘗試,這里演示第二種
雙擊堆棧第一條表達式,我們可以追到離我們最近的call,越近越關鍵?。?!
就是這個call里實現(xiàn)了視角抖動,我們將其nop掉
這時,我們發(fā)現(xiàn)我們的槍不抖了,視角也不抖了
接下來追子彈無間隔
同樣的右鍵找出什么改寫了這個地址
我們開了一槍,74的肯定不正常,我們追第一個
還是原來的配方上來直接nop,ok功能實現(xiàn)。
接下來時代碼部分
子彈無后座,我們只要nop第一層的調(diào)用call即可地址是GameAssembly.dll +0x148bbe1
一共填充5個nop
BYTE舊的后坐力硬編碼[5] = {};
void主窗口::OnBnClickedCheck2()
{
????//查詢到改變子彈后座改變的地址
????ULONGLONG模塊地址 = (ULONGLONG)LoadLibraryA("GameAssembly.dll");
????PBYTE后坐力地址 = (PBYTE)(模塊地址 + 0x148bbe1);
?
????//保存5個字節(jié)
?
????if?(子彈無后座.GetCheck()) {
??????? memcpy_s(舊的后坐力硬編碼, 5, 后坐力地址, 5);
????????//改變一下內(nèi)存的讀寫權限
????????//用nop填充
????????DWORD后坐力地址讀寫權限 = 0;
??????? VirtualProtectEx(GetCurrentProcess(),?后坐力地址, 5,?PAGE_EXECUTE_READWRITE, &后坐力地址讀寫權限);
??????? memset(后坐力地址, 0x90, 5);
??????? VirtualProtectEx(GetCurrentProcess(),?后坐力地址, 5, 后坐力地址讀寫權限, 0);
??? }
????else?{
???????
????????DWORD后坐力地址讀寫權限 = 0;
??????? VirtualProtectEx(GetCurrentProcess(),?后坐力地址, 5,?PAGE_EXECUTE_READWRITE, &后坐力地址讀寫權限);
??????? memcpy_s(后坐力地址, 5, 舊的后坐力硬編碼, 5);
??????? VirtualProtectEx(GetCurrentProcess(),?后坐力地址, 5, 后坐力地址讀寫權限, 0);
??? }
?
}
要讓子彈沒有間隔我們只需要改變改子彈間隔的匯編指令即可即可
地址是GameAssenbly.dll+0xe52b20填充7個字節(jié)的0x90即可代碼如同上方
3方框透視
要做到方框透視我們需要目標的坐標,和我們?nèi)宋锏木仃?,這樣即可做到方框透視。那第一步找敵人的坐標。我這里用用另一個賬號和我的賬號聯(lián)機,通過另一個角色的移動,得到敵人移動坐標,坐標一般為浮點數(shù),所以我們進行浮點數(shù)搜索。為什么不搜索血量呢。這里我測試了,敵人血量似乎是再服務器端的,本地似乎找不到。
這里我先教找自身的坐標,敵人的坐標同理.
我們先進游戲,然后浮點搜索未知的初始值,然后動一下搜索,變動的數(shù)值,不動搜未變動的數(shù)值,然后做一些無關的動作,例如甩頭,開槍,然后搜未變動的數(shù)值。然后我們的坐標就剩下幾個了。
不同的地圖可能會有不一樣的坐標,具體以游戲為準。
將這三個坐標添加下來點擊地址欄ctrl+a,然后點右下角的紅箭頭
先鎖住2個移動我們的人物
先鎖住一兩個,然后移動我們的人物,如果我們的人物被吸回來說明這就是我們?nèi)宋镎鎸嵉牡刂贰?/p>
就是它了,鎖住它我們?nèi)宋锞蛣硬涣肆?,ctrl+b瀏覽相關內(nèi)存區(qū)域
將我們的數(shù)據(jù)顯示改為單浮點
這三個地址具有相關性,我們跳一跳發(fā)現(xiàn)第二個地址的值發(fā)生了改變,所以第二個為高度也就是z,我問了一些大佬u3d引擎就是以xzy的順序進行編寫的。
我們跳一跳發(fā)現(xiàn)就第二個動了,說明第二個是z
接下來使找敵人坐標,說實話我沒啥好方法,這游戲血量在服務器上,本地很難入手,通過血量追人物數(shù)組沒法下手。我叫了一個小伙伴進來,讓他動,我進行搜索,跟前面坐標搜索基本類似。這里給出我的一個指針掃描結果。
這是其中一個人物的世界坐標,一般來說人物的坐標要么是以數(shù)組形式或者以鏈表的形式存儲,這個游戲是以數(shù)組的形式存儲的我點擊偏移的左右箭頭,觀察上方的坐標值,從上往下,一個一個試,如果加超過了0x100都沒有觀察到可疑的值,基本就不是了就往下方找。
當我試到第3個的時候就可以發(fā)現(xiàn)每加0x18就會出現(xiàn)一個類似于坐標的值,我們移去上面兩個偏移看30那塊區(qū)域的的內(nèi)存
由于我就兩個人格式不是很明顯,加入多人后就可以很明顯的看到第一排格式的一致性,我們可以初步肯定,這個就是我們所要的人物數(shù)組,可以寫出每個人物坐標的表達式
【【【【【【【【"GameAssembly.dll"+0666C5D8】+0xb8】+0x0】0x18】+0x18】+0x30+0x18*第幾個人物】+40】+0x24
接下來就是找矩陣了
矩陣一般的特點是要么最后一行就很大,要么最后一列會很大
行很大簡稱行大矩陣,本游戲就是行大矩陣
這里xzy的順序和你游戲的排列有關,根據(jù)我們之前找的自身坐標可以知道
這里w計算有關,這里我們先不提
列很大簡稱列大矩陣
那么如何找矩陣呢,我教大家一個技巧:
首先把將游戲中角色的頭抬到最頂上,然后用浮點數(shù)搜索0.8到1.1,然后低頭到最底下,然后搜-1.1到-0.8,間隔幾個選一個然后ctrl+b看他的相關內(nèi)存區(qū)域,
,很快就可以篩選出類似的矩陣,
矩陣是以我們屏幕的中心作為基準,加上對應的算法得到偏移,得到敵人的位置
如圖
那么我們就要先得到中心點的坐標,也就是游戲分辨率的一半,我設置的是1024x768
那么我的中心就是512x384
接下來是算x方向上的偏移,這個偏移是通過屏幕的大小的比例來表示的所以我們得到一個表達式:x方向上的偏移=屏幕大小一半*通過矩陣計算得到的百分比(可正可負)
計算偏移百分比::我們將x一列的數(shù)用x1,x2,x3,x4表示,其他列相同
(這是列大矩陣的算法,給大家看看)
然后根據(jù)如下公式,可以得到4個值,我們只需要相對大小x,y,w
x相對大小=x1*人物坐標X+x2*人物坐標Z+x3*人物坐標Y+x4
z相對大小=z1*人物坐標X+z2*人物坐標Z+z3*人物坐標Y+z4
y相對大小=y1*人物坐標X+y2*人物坐標Z+y3*人物坐標Y+y4
w相對大小=w1*人物坐標X+w2*人物坐標Z+w3*人物坐標Y+w4
其中1/w為距離系數(shù),w越大敵人離我們越遠,當1/w<0.01時敵人離我們已經(jīng)很遠很遠了,即使畫再屏幕上也是很小的。
x的偏移=x的相對大小*1/w*屏幕中心點的x,就是人物在我們屏幕上的x偏移大小
x位置=屏幕的中心+x的偏移,我們就可以得到人物在我們?nèi)宋锏模{色那條我們就知道了
y也是相同的
Y的偏移=Y的相對大小*1/w*屏幕中心點的Y
這樣我們就知道y的偏移(黃色的那條)
接下來繪制方框只要讓我們的敵人的高度高一點,也就是讓其z大一點在計算一次y,就可以的得到y(tǒng)2
那我們的方框的位置和大小就確定了
方框的右上點為屏幕中心位置+Y2
方框的寬度為abs(Y2-Y)*2,方框的長度我們可以大致認為時寬度的1/2,此時我們要畫的矩形就是這樣的:
代碼部分:
1.????我們使用外部繪制,在游戲窗口上創(chuàng)建一個和游戲等大小的窗口,置于桌面最上方,透明化,不接受我們的鼠標點擊。
2.????初始化D3D
3.????讀取敵人的坐標數(shù)據(jù),進行計算出我們矩形的大小和位置,進行繪制
創(chuàng)建一個符合我們要求的窗口:
①????注冊一個窗口類
WNDCLASSEX繪制窗口類;
????繪制窗口類.cbSize =?sizeof(WNDCLASSEX);
????繪制窗口類.lpfnWndProc = 窗口回調(diào)函數(shù);
????繪制窗口類.style =?CS_HREDRAW?|?CS_VREDRAW;//未知
????繪制窗口類.cbClSEXtra = 0;
????繪制窗口類.cbWndExtra = 0;
????繪制窗口類.hInstance = 0;
????繪制窗口類.hIcon = 0;//圖標
????繪制窗口類.hCursor =?LoadCursor(NULL,?IDC_ARROW);//使用系統(tǒng)默認的圖標
????繪制窗口類.hbrBackground = (HBRUSH)RGB(0, 0, 0);//背景顏色
????繪制窗口類.lpszMenuName =?"";
????繪制窗口類.lpszClassName =?"繪制窗口";
????繪制窗口類.hIconSm =?NULL;
????if?(!RegisterClasSEXA(&繪制窗口類)) {
??????? ::MessageBox(0,?"注冊窗口失敗", 0, 0);
????????return?0;
??? }
②????創(chuàng)建窗口(置于最上方,大小等同游戲窗口)
HWND?hWnd = FindWindowA(0,?"PixelStrike3D");
?????????????RECT游戲窗口參數(shù);
??? GetWindowRect(游戲窗口句柄, &游戲窗口參數(shù));
HWND?繪制窗口句柄=CreateWindowExA(WS_EX_TOPMOST|WS_EX_LAYERED|WS_EX_TRANSPARENT,//三個參數(shù):最頂上,點了沒反應,透明度擴展
????????"繪制窗口",
????????"",
??????? WS_VISIBLE | WS_POPUP,
????????游戲窗口參數(shù).left,
????????游戲窗口參數(shù).top,
??????? 1024,
??????? 768,
??????? 0,0,0,0);
??????? MARGINS m = {
????????游戲窗口參數(shù).left,
????????游戲窗口參數(shù).top,
??????? 1024,
??????? 768
??? };
③????使窗口透明化
SetLayeredWindowAttributes(繪制窗口句柄,?RGB(0, 0, 0), 255,LWA_ALPHA);
④????使窗口不接受點擊
DwmExtendFrameIntoClientArea(繪制窗口句柄, &m);//透明
⑤????顯示窗口
ShowWindow(繪制窗口句柄,?SW_SHOW);
初始化D3D
IDirect3D9* m_d3d =?nullptr;
????IDirect3DDevice9* m_device =?nullptr;
m_d3d = Direct3DCreate9(D3D_SDK_VERSION);
????D3DPRESENT_PARAMETERS?pp;
????ZeroMemory(&pp,?sizeof(D3DPRESENT_PARAMETERS));
??? pp.Windowed =?TRUE;
??? pp.SwapEffect =?D3DSWAPEFFECT_DISCARD;
??? pp.BackBufferFormat =?D3DFMT_A8R8G8B8;
??? pp.BackBufferHeight = 768;
??? pp.BackBufferWidth = 1024;
??? pp.hDeviceWindow =?繪制窗口句柄;
??? pp.PresentationInterval =?D3DPRESENT_INTERVAL_IMMEDIATE;
????if?(FAILED(m_d3d->CreateDevice(D3DADAPTER_DEFAULT,?D3DDEVTYPE_HAL,?繪制窗口句柄,
????????D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, &m_device))) {
??????? MessageBoxA(0,?"創(chuàng)建d3d設備失敗", 0, 0);
????????return?0;
??? }
????//構造器
????//繪制線的畫筆初始化
????ID3DXFont* m_font =?nullptr;/
if?(FAILED(D3DXCreateLine(m_device, &m_line))) {
??????? MessageBoxA(0,?"線條初始化失敗", 0, 0);
????????return?0;
??? }
完善一下畫矩形的方法,就是普通的坐標計算,然后調(diào)用接口畫出
void畫線條(intx,?inty,?intx1,?inty1,?ULONGcolor,?ID3DXLine*?m_line) {
????D3DXVECTOR2?line[2] = {?D3DXVECTOR2(x,y),D3DXVECTOR2(x1,y1) };
????m_line->Begin();
????m_line->Draw(line, 2,?color);
????m_line->End();
}
void畫方框(intx,?inty,?intw,?inth,?ULONGcolor,?ID3DXLine*?m_line) {
????畫線條(x,?y,?x?+?w,?y,?color,?m_line);
????畫線條(x,?y,?x,?y?+?h,?color,?m_line);
????畫線條(x?+?w,?y,?x?+?w,?y?+?h,?color,?m_line);
????畫線條(x,?y?+?h,?x?+?w,?y?+?h,?color,?m_line);
}
接下來要獲取人物的坐標地址,然后計算出方框的位置和大小,然后畫方框即可,由于我們要連續(xù)繪制,開始畫之前要清空之前屏幕上的繪制,畫好之后要清空屏幕上的繪制
void開始繪制(IDirect3DDevice9*?m_device) {
????m_device->Clear(0, 0,?D3DCLEAR_TARGET,?D3DCOLOR_RGBA(0, 0, 0, 0), 1.f, 0);
????m_device->BeginScene();
}
void結束繪制(IDirect3DDevice9*?m_device) {
????m_device->EndScene();
????m_device->Present(0, 0, 0, 0);
}
然后我們只需要獲取游戲里人物的位置,和自身的矩陣就可以繪制啦
float屏幕中心x = 1024 * 0.5f;
????float屏幕中心y = 768 * 0.5f;
????float人物矩陣[4][4] = { 0 };
????ULONGLONG人物地址 = 0;
????while(true)
??? {
????????開始繪制(m_device);
????????ULONGLONG人物矩陣地址 = getOtherProcessAddr_byOff(hProcess, 人物矩陣偏移數(shù), 人物矩陣偏移, UnityPlayer);
??????? ReadProcessMemory(hProcess, (LPVOID)人物矩陣地址, 人物矩陣, 64, 0);
????????//獲取人物數(shù)組
????????ULONGLONG人物數(shù)組地址 = getOtherProcessAddr_byOff(hProcess, 人物數(shù)組偏移數(shù), 人物數(shù)組偏移, GameAssembly);
????????for?(size_t?i = 0; i < 10; i++)
??????? {
???????????? ReadProcessMemory(hProcess, (LPVOID)(人物數(shù)組地址 + i * 人物之間的間隔), &(人物地址), 8, 0);
?????????????ULONGLONG人物坐標 = getOtherProcessAddr_byOff(hProcess, 人物坐標偏移數(shù), 人物坐標偏移, 人物地址);
?????????????float人物x = 0;
?????????????float人物y = 0;
?????????????float人物z = 0;
??????? ??? ReadProcessMemory(hProcess, (LPVOID)人物坐標, &人物x, 4, 0);
?????????????if?(人物x == 0) {
?????????????????continue;
???????????? }
???????????? ReadProcessMemory(hProcess, (LPVOID)(人物坐標 + 4), &人物z, 4, 0);
?????????????if?(人物z == -1000) {
?????????????????continue;
???????????? }
???????????? ReadProcessMemory(hProcess, (LPVOID)(人物坐標 + 8), &人物y, 4, 0);
?????????????if?(人物y == 0) {
?????????????????continue;
???????????? }
?????????????//計算
???????????? {
?????????????????float?w =?人物矩陣[0][3] * 人物x + 人物矩陣[1][3] * 人物z + 人物矩陣[2][3] * 人物y + 人物矩陣[3][3];
?????????????????if?(w < 0.01f) {
?????????????????????continue;
???????????????? }
?????????????????float放縮系數(shù) = 1.f / w;
?????????????????float?x =?一般屏幕x + 一般屏幕x * 放縮系數(shù) * (人物矩陣[0][0] * 人物x + 人物矩陣[1][0] * 人物z + 人物矩陣[2][0] * 人物y + 人物矩陣[3][0]);
?????????????????float?y =?一般屏幕y - 一般屏幕y * 放縮系數(shù) * (人物矩陣[0][1] * 人物x + 人物矩陣[1][1] * 人物z + 人物矩陣[2][1] * 人物y + 人物矩陣[3][1]);
?????????????????float?y2 =?一般屏幕y - 一般屏幕y * 放縮系數(shù) * (人物矩陣[0][1] * 人物x + 人物矩陣[1][1] * (人物z+3.f) + 人物矩陣[2][1] * (人物y)+人物矩陣[3][1]);
?????????????????float方框的高度 = y - y2;
?????????????????float方框的寬度 = 方框的高度 / 2;
?????????????????畫方框(x - 方框的寬度 / 2, y2, 方框的寬度, 方框的高度,?D3DCOLOR_RGBA(200, 66, 55, 255), m_line);
?
???????????? }
???????????? Sleep(10);//可以不用,加了防止無響應,程序會崩
??????? }
????????結束繪制(m_device);
??? }
本次教程就到這里,謝謝大家?。。?/p>