在前面的FPS游戲實現GDI透視中,我們通過三角函數,並配合坐標計算出了敵人的位置,該方法時比較笨的一種方式,但卻很通用,基本上,只要是FPS類游戲,稍微修改一下代碼中的基地址,就可以通用,本次我們將研究通過查找相機矩陣獲得自身位置,上一篇文章中我已經講解了關於各種坐標的找法。
上篇文章(找基址):https://www.cnblogs.com/LyShark/p/11620244.html
快速找基地址:https://www.cnblogs.com/LyShark/p/13519135.html
> 本人坐標x = server.dll+4F2FEC + 288 - 8
> 本人坐標y = server.dll+4F2FEC + 288 - 4
> 本人坐標z = server.dll+4F2FEC + 288
> 敵人x = server.dll+4F2FFC + 288 - 8
> 敵人y = server.dll+4F2FFC + 288 - 4
> 敵人z = server.dll+4F2FFC + 288
> 鼠標 x = engine.dll+61D254 + 4
> 鼠標 y = engine.dll+61D254
> 鼠標 x = client.dll+4C0300 + 4
> 鼠標 y = client.dll+4C0300
> FOV = client.dll+5046F0
> FOV = client.dll+504628
> FOV = client.dll+5047B8
> FOV = client.dll+50489C
> FOV = engine.dll+3C1720
> 算上我自己的人機數量: server.dll+4EEFE8
> 算上我自己的人機數量: engine.dll+5D29BC
> 不算我自己的人機數量: server.dll+4EEFE0
> 不算我自己的人機數量: server.dll+588878
自己血量偏移 = e4
> 自己血量: server.dll+54B6C8
> 自己血量: server.dll+54A82C
> 自己血量: server.dll+4F2FEC
> 敵人血量: server.dll + 4F2FFC + e4
> 本人陣營: server.dll+4F2FEC + 1F4
> 敵人陣營: server.dll+4F2FFC + 1F4
> 本人陣營: server.dll+54A82C + 1F4
> 本人陣營: server.dll+54B6C8 + 1F4
熟悉矩陣特點
通常情況下Dx9中會采用4*4的矩陣,這里我分別找了三款CS系列游戲的矩陣,並來分析一下他們的異同點。
1.找矩陣的方法就是不斷移動自己相機位置,最好拿把狙擊槍,然后開鏡搜索變動的數值,移動身體搜索變動數值,或者是開鏡移動身體搜索變動的數值,
這樣配合來找,最終可以鎖定在2000個數值左右,然后就可以開找,通常矩陣頭晃動鼠標不會出現大於3的值,一般會在-0.x - 1.x 之間徘徊。
豎矩陣的第三個值,通常為0,就算亂晃,也會保持0的位置。
找到矩陣地址,當我們讓人物只跳動時,矩陣呈現出來的效果如下。

上下晃動鼠標,矩陣呈現出來的效果如下。

左右晃動鼠標,矩陣呈現出來的效果如下。

開啟關閉狙擊鏡,矩陣變化。

完全亂晃狀態如下,4*4

矩陣,不只一個,如下是另一處,矩陣的數據。

另一處矩陣位置。

人物死后,矩陣被初始化。

不是矩陣的矩陣
第一種不是矩陣的情況,數據跳動幅度較亂,並且不是4*4在跳動,而是很多行。

第二種,混亂無序,不連貫,不是一個結構體,不是矩陣。

第三種,最常見的亂序,不是矩陣。


第四種,加密后的地址,晃動鼠標,不會發生任何變化。

代碼中相機對應關系
相機X對應關系

相機Y對應關系

相機Z對應關系,我們只用到了前三行,后面的不用了。

4x4豎矩陣,是這樣,只是前三行,最后跑到了下面。

如果是橫矩陣會發生變化,如下。


所以上面的算法部分,需要稍微小改一下,即可完成橫矩陣的透視效果。
番外篇(轉載一段代碼)
在前面的GID透視教程中我使用的是易語言封裝的DC函數實現的外部透視,今天在網上找到一個別人用C#實現的透視代碼,我把它轉過來備份一下哈,其實原理是差不多的,基本上就是寫法會有些變化漢語變英文。
轉載了別人的東西,多少還是要留下來源,以表示對作者成果的尊重:https://www.cnblogs.com/xuexidememeda/
c# 調用dll封裝類
class DllHelper
{
public const int COLORBLACK = 0;//黑色
public const int COLORWHITE = 16777215;//#白色
public const int COLORPINKISHRED = 16711935;//#品紅
public const int COLORCYAN = 8388608;//#藏青
public const int COLORGREEN = 32768;//#墨綠
public const int COLORDEEPCYAN = 8421376;//#深青
public const int COLORYELLOW = 65535;//#黃色
public const int COLORBLUE = 16711680;//#藍色
public const int COLORRED = 255;//#紅色
public const int COLOGRAY = 8421504;//#灰色
public const int COLOORANGE = 33023;//#橙黃
[DllImport("SoftwareHelper.dll", EntryPoint = "InitDX11")]
public static extern void InitDX11(int wordsize);//初始化一個dx11透明窗口參數為字體大小建議15
[DllImport("SoftwareHelper.dll", EntryPoint = "SafetyQuit")]
public static extern void SafetyQuit();//安全退出放在窗口是否可以被關閉事件卸載繪圖
[DllImport("SoftwareHelper.dll", EntryPoint = "SetWindowPos_Top")]
public static extern bool SetWindowPos_Top(int handle);//設置窗口置頂參數為窗口句柄
[DllImport("SoftwareHelper.dll", EntryPoint = "MoveWindows")]
public static extern void MoveWindows(int handle,int newleft,int newtop,int newwidth,int newheight);//移動窗口 窗口句柄 窗口左邊 窗口頂部 窗口高度寬度
[DllImport("SoftwareHelper.dll", EntryPoint = "DrawText")]//寫字
public static extern void DrawText(int X, int Y, string text, int color); //屏幕xy 和文本 顏色用上面定義的常量
[DllImport("SoftwareHelper.dll", EntryPoint = "DrawLine")]//畫線
public static extern void DrawLine(int X, int Y, int Xend, int Yend, int linePx, int color);//x為開始x坐標 x1為結束x坐標 linepx為線寬度 顏色用上面定義的常量
[DllImport("SoftwareHelper.dll", EntryPoint = "ProcessIsExist")]//判斷指定進程是否存在(此判斷與進程其它信息無關聯)(存在返回真,不存在或失敗返回假)
public static extern bool ProcessIsExist(string processname);//進程名稱帶后zhui
[DllImport("SoftwareHelper.dll", EntryPoint = "DrawSquare")]//畫出描邊方框
public static extern void DrawSquare(int X, int Y, int W, int H ,int colorout, int colorin,int linepx);//colorout外部方框顏色 in為內部 linepx線寬
[DllImport("SoftwareHelper.dll", EntryPoint = "GetDX11CreateWindowHandle")]//得到對象dx11創建的透明窗口句柄 為了防止重復和游戲 枚舉窗口名稱為 DX11t?h?i?s? ?i?s? ?y?o?u? ?w?i?n?d?o?w?
public static extern int GetDX11CreateWindowHandle();
[DllImport("SoftwareHelper.dll", EntryPoint = "OptimizeCUP")]//優化cup防止繪制卡
public static extern void OptimizeCUP(int time);//毫秒
[DllImport("SoftwareHelper.dll", EntryPoint = "OptimizeDelayed")]//優化延時
public static extern void OptimizeDelayed(int time);//毫秒
[DllImport("SoftwareHelper.dll", EntryPoint = "OptimizeSystem")]//系統處理事件同樣是為了優化cup防止卡
public static extern void OptimizeSystem();
[DllImport("SoftwareHelper.dll", EntryPoint = "StartDrawing")]//開始繪圖 繪圖前調用
public static extern void StartDrawing();
[DllImport("SoftwareHelper.dll", EntryPoint = "EndDrawing")]//結束繪圖 繪圖結束調用
public static extern void EndDrawing();
[DllImport("SoftwareHelper.dll", EntryPoint = "MouseMoveInGame")]//鼠標可以在游戲內移動游戲外部自瞄調用
public static extern void MouseMoveInGame(int handle,int x,int y);//游戲窗口句柄 xy坐標
}
c#內存輔助類
class GameUtil
{
public struct RECT
{
public int Left; //最左坐標
public int Top; //最上坐標
public int Right; //最右坐標
public int Bottom; //最下坐標
}
public struct RECTClient
{
public uint Left;
public uint Top;
public uint Right;
public uint Bottom;
}
public struct LPPOINT
{
public int x;
public int y;
}
[DllImport("kernel32.dll", EntryPoint = "ReadProcessMemory")]
public static extern int _MemoryReadByteSet(int hProcess, int lpBaseAddress, byte[] lpBuffer, int nSize, int lpNumberOfBytesRead);
[DllImport("kernel32.dll", EntryPoint = "ReadProcessMemory")]
public static extern int _MemoryReadInt32(int hProcess, int lpBaseAddress, ref int lpBuffer, int nSize, int lpNumberOfBytesRead);
[DllImport("kernel32.dll", EntryPoint = "WriteProcessMemory")]
public static extern int _MemoryWriteByteSet(int hProcess, int lpBaseAddress, byte[] lpBuffer, int nSize, int lpNumberOfBytesWritten);
[DllImport("kernel32.dll", EntryPoint = "WriteProcessMemory")]
public static extern int _MemoryWriteInt32(int hProcess, int lpBaseAddress, ref int lpBuffer, int nSize, int lpNumberOfBytesWritten);
[DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess")]
public static extern int GetCurrentProcess();
[DllImport("kernel32.dll", EntryPoint = "OpenProcess")]
public static extern int OpenProcess(int dwDesiredAccess, int bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", EntryPoint = "CloseHandle")]
public static extern int CloseHandle(int hObject);
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
public static extern int _CopyMemory_ByteSet_Float(ref float item, ref byte source, int length);
const int PROCESS_POWER_MAX = 2035711;
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(IntPtr hProcess, int lpBaseAddress, byte[] lpBuffer, uint nSize, out int lpNumberOfBytesRead);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[DllImport("user32")]
public static extern bool GetClientRect(IntPtr hwnd,out RECTClient lpRect );
[DllImport("user32")]
public static extern bool ClientToScreen(IntPtr hwnd, out LPPOINT point);
//打開進程
//kernel32.dll系統動態鏈接庫
[DllImportAttribute("kernel32.dll", EntryPoint = "OpenProcess")]
public static extern IntPtr OpenProcess
(
int iAccess,
bool Handle,
int ProcessID
);
//關閉句柄
[DllImport("kernel32.dll", EntryPoint = "CloseHandle")]
private static extern void CloseHandle
(
IntPtr hObject
);
//取窗口句柄 FindWindow
[DllImport("User32.dll", EntryPoint = "FindWindow")]
public extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName);
//移動窗口
/// <summary>
/// 設置目標窗體大小,位置
/// </summary>
/// <param name="hWnd">目標句柄</param>
/// <param name="x">目標窗體新位置X軸坐標</param>
/// <param name="y">目標窗體新位置Y軸坐標</param>
/// <param name="nWidth">目標窗體新寬度</param>
/// <param name="nHeight">目標窗體新高度</param>
/// <param name="BRePaint">是否刷新窗體</param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool BRePaint);
//---------------------------------------------------------------------------------------------------------------
/// <summary>
/// 讀內存整數型
/// </summary>
/// <param name="pID">進程ID</param>
/// <param name="bAddress">0x地址</param>
/// <returns>0失敗</returns>
public static int ReadMemoryInt32(int pID, int bAddress)
{
int num = 0;
int handle = getProcessHandle(pID);
int num3 = GameUtil._MemoryReadInt32(handle, bAddress, ref num, 4, 0);
GameUtil.CloseHandle(handle);
if (num3 == 0)
{
return 0;
}
else
{
return num;
}
}
/// <summary>
/// 寫內存整數型
/// </summary>
/// <param name="pID">進程ID</param>
/// <param name="bAddress">0x地址</param>
/// <param name="value">寫入值</param>
/// <returns>false失敗 true成功</returns>
public static bool WriteMemoryInt32(int pID, int bAddress, int value)
{
int handle = getProcessHandle(pID);
int num2 = GameUtil._MemoryWriteInt32(handle, bAddress, ref value, 4, 0);
GameUtil.CloseHandle(handle);
return num2 != 0;
}
/// <summary>
/// 讀內存小數型
/// </summary>
/// <param name="pID">進程ID</param>
/// <param name="bAddress">0x地址</param>
/// <returns>0失敗</returns>
public static float ReadMemoryFloat(int pID, int bAddress)
{
//byte[] array = test.GetVoidByteSet(4);
byte[] array = new byte[4];//不取空字節集也可以正確轉換成單精度小數型
int handle = getProcessHandle(pID);
int temp = GameUtil._MemoryReadByteSet(handle, bAddress, array, 4, 0);
if (temp == 0)
{
return 0f;
}
else
{
return GameUtil.getFloatFromByteSet(array, 0);
}
}
/// <summary>
/// 寫內存小數型
/// </summary>
/// <param name="pID">進程ID</param>
/// <param name="bAddress">0x地址</param>
/// <param name="value">寫入數據</param>
/// <returns>false失敗</returns>
public static bool WriteMemoryFloat(int pID, int bAddress, float value)
{
//byte[] byteSet = test.GetByteSet(value);
byte[] byteSet = BitConverter.GetBytes(value);//https://msdn.microsoft.com/en-us/library/yhwsaf3w
//byte[] byteSet = Encoding.GetEncoding("gb2312").GetBytes(value.ToString());
return GameUtil.WriteMemoryByteSet(pID, bAddress, byteSet, 0);
}
/// <summary>
/// 寫內存字節集
/// </summary>
/// <param name="pID">進程ID</param>
/// <param name="bAddress">0x地址</param>
/// <param name="value">字節數據</param>
/// <param name="length">寫入長度 0代表字節數據的長度</param>
/// <returns>false失敗</returns>
private static bool WriteMemoryByteSet(int pID, int bAddress, byte[] value, int length = 0)
{
int handle = GameUtil.getProcessHandle(pID);
int nSize = (length == 0) ? value.Length : length;
int tmp = GameUtil._MemoryWriteByteSet(handle, bAddress, value, nSize, 0);//byte[]屬於引用類型 引用類型不用ref也是以傳址方式進行運算
//test.CloseHandle(pID);
return tmp != 0;
}
/// <summary>
/// 取空白字節集
/// </summary>
/// <param name="num"></param>
/// <returns></returns>
public static byte[] getVoidByteSet(int num)
{
if (num <= 0)
{
num = 1;
}
string text = "";
for (int i = 0; i < num; i++)
{
text += "0";
}
return Encoding.UTF8.GetBytes(text);
}
/// <summary>
/// 取進程句柄
/// </summary>
/// <param name="pID">進程ID</param>
/// <returns>進程句柄</returns>
public static int getProcessHandle(int pID)
{
if (pID == -1)
{
return GameUtil.GetCurrentProcess();
}
else
{
return GameUtil.OpenProcess(PROCESS_POWER_MAX, 0, pID);
}
}
/// <summary>
/// 字節集轉小數型
/// </summary>
/// <param name="sourceValue">字節集</param>
/// <param name="index">索引</param>
/// <returns></returns>
public static float getFloatFromByteSet(byte[] sourceValue, int index)
{
float result = 0f;
GameUtil._CopyMemory_ByteSet_Float(ref result, ref sourceValue[index], 4);
return result;
}
/// <summary>
/// 獲取字節集
/// </summary>
/// <param name="data">需要轉換到字節集的數據</param>
/// <returns></returns>
public static byte[] getByteSet(float data)
{
return Encoding.UTF8.GetBytes(data.ToString());
}
//根據名稱取進程PID 不得有后綴.exe
//根據進程名獲取PID 不得有后綴.exe
public static int getPidByProcessName(string processName)
{
Process[] ArrayProcess = Process.GetProcessesByName(processName);
foreach (Process pro in ArrayProcess)
{
return pro.Id;
}
return 0;
}
//獲取模塊地址
public static int getMoudleAddress(string processName ,string dllname )
{
foreach (Process p in Process.GetProcessesByName(processName))
{
foreach (ProcessModule m in p.Modules)
{
if (m.ModuleName.Equals(dllname))
return (int)m.BaseAddress;
}
}
return 0;
}
//取窗口句柄 這里傳入的值是窗口標題
public static IntPtr getWindowsHandler_findwindow(String classname,String windowtitle)
{
IntPtr maindHwnd = FindWindow(classname, windowtitle); //獲得QQ登陸框的句柄
return maindHwnd;
}
public static RECT getWindowsRect(IntPtr windowsHandler)
{
RECT rec = new RECT();
GetWindowRect(windowsHandler, ref rec);//h為窗口句柄
return rec;
}
}
窗口類c#
public partial class Form1 : Form
{
int PID;
int matrix;//矩陣基質
float formwidth;//窗口寬度
float formheight;//窗口高度
int processmoudle;//進程模塊;
IntPtr windowshandle;//窗口句柄
int dx11hwnd;//dx11創建的透明窗口句柄
private void loop()//循環
{
while (true)
{
Util.DllHelper.OptimizeCUP(2);//防止占用cpu
reflushWindows();
startDrawit();
Util.DllHelper.OptimizeDelayed(1);//防止占用cpu
}
}
private void intData()
{
PID= Util.GameUtil.getPidByProcessName("cstrike");
windowshandle=Util.GameUtil.getWindowsHandler_findwindow("Valve001", "Counter-Strike");
processmoudle=Util.GameUtil.getMoudleAddress("cstrike", "cstrike.exe");
matrix = processmoudle + 25297152;
formwidth= Util.GameUtil.getWindowsRect(windowshandle).Right - Util.GameUtil.getWindowsRect(windowshandle).Left;
formheight= Util.GameUtil.getWindowsRect(windowshandle).Bottom - Util.GameUtil.getWindowsRect(windowshandle).Top;
//this.ShowInTaskbar = false;//任務欄顯示為假
Debug.Print(PID + "PID" + "windowshandle" + windowshandle + "matrix" + matrix + "formwidth" + formwidth + "formheight" + formheight);
}
private void startIt()
{
Util.DllHelper.InitDX11(14);//初始化dx11
dx11hwnd = Util.DllHelper.GetDX11CreateWindowHandle();//取得dx11創建的透明窗口的句柄
if (dx11hwnd == 0) MessageBox.Show("DX11加載失敗"); else MessageBox.Show("DX11加載成功");
reflushWindows();
loop();
}
private void startDrawit()
{
Util.DllHelper.StartDrawing();//開始繪圖
Util.DllHelper.DrawText(0, 0, "C#dx11", Util.DllHelper.COLORYELLOW);
worldtoScreen();
Util.DllHelper.EndDrawing();//結束繪圖
}
private void worldtoScreen()//游戲世界轉屏幕
{
float arr00 = Util.GameUtil.ReadMemoryFloat(PID, matrix);
float arr01 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 4);
float arr02 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 8);
float arr03 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 12);
float arr10 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 16);
float arr11 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 20);
float arr12 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 24);
float arr13 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 28);
float arr20 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 32);
float arr21 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 36);
float arr22 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 40);
float arr23 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 44);
float arr30 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 48);
float arr31 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 52);
float arr32 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 56);
float arr33 = Util.GameUtil.ReadMemoryFloat(PID, matrix + 60);
//得到矩陣所有值
float sightwidth = formwidth / 2;//視角寬
float sightheight = formheight / 2;//視角高
float enemy_x = Util.GameUtil.ReadMemoryFloat(PID, processmoudle + 17369300 - 8);
float enemy_y = Util.GameUtil.ReadMemoryFloat(PID, processmoudle + 17369300 - 4);
float enemy_z = Util.GameUtil.ReadMemoryFloat(PID, processmoudle + 17369300);
float camera_Z = arr02 * enemy_x + arr12 * enemy_y + arr22 * enemy_z + arr32;
if (camera_Z <= 0.01) return;
float zoom = 1 / camera_Z;//縮放比例
float camera_x = sightwidth + (arr00 * enemy_x + arr10 * enemy_y + arr20 * enemy_z + arr30) * zoom * sightwidth;
float camera_y = sightheight - (arr01 * enemy_x + arr11 * enemy_y + arr21 * (enemy_z + 30) + arr31) * zoom * sightheight * 1;
float camera_y2 = sightheight - (arr01 * enemy_x + arr11 * enemy_y + arr21 * (enemy_z - 50) + arr31) * zoom * sightheight * 1 - 20;
float squareheight = camera_y2 - camera_y;//方框高度
Util.DllHelper.DrawSquare((int)(camera_x - squareheight / 4), (int)camera_y,(int) (squareheight / 2), (int)squareheight, Util.DllHelper.COLORYELLOW, Util.DllHelper.COLORRED,1);//繪制方框
Util.DllHelper.OptimizeSystem();//系統處理事件防止cup過多
Util.DllHelper.DrawLine((int)sightwidth,0, (int)camera_x, (int)camera_y,1,Util.DllHelper.COLORBLUE);//畫直線
}
private void reflushWindows()//刷新窗口
{
Util.GameUtil.RECTClient rec = new Util.GameUtil.RECTClient() ;
Util.GameUtil.LPPOINT point = new Util.GameUtil.LPPOINT();
Util.GameUtil.GetClientRect(windowshandle,out rec);
Util.GameUtil.ClientToScreen(windowshandle, out point);
formwidth = rec.Right-rec.Left;
formheight = rec.Bottom-rec.Top ;
Util.DllHelper.MoveWindows(dx11hwnd, point.x, point.y, (int)formwidth, (int)formheight);
Util.DllHelper.SetWindowPos_Top(dx11hwnd);
}
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
startIt();
}
private void Form1_Load(object sender, EventArgs e)
{
intData();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// Util.DllHelper.SafetyQuit();
Environment.Exit(0);
}
}
