Всем привет!
Не давно тут решил поиграться с такой замечательной вещью как видовая матрица. В качестве подопытной игры выбрал Call of Duty: Modern Warfare 2. В качестве инструмента выбрал язык C# и сканер памяти Cheat Engine.
Соответственно нашёл адрес игрового объекта к примеру вертолёт, хранящий ось X и далее по смещениям в 4 байта нашёл Y и Z. Использовал Win API функцию ReadProccessMemory, предварительно описав её:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(int processAccess, bool bInheritHandle, int processId);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadProcessMemory(IntPtr pHandle, IntPtr Address, ref float Buffer, int Size, IntPtr NumberofBytesRead);
Читаю байты из памяти:
/// <summary>
/// Reads bytes from the specified address in the proccess memory, saves bytes to the buffer
/// </summary>
private void ReadBytes()
{
ReadProcessMemory(handle, (IntPtr)BaseAddress, ref tempCoord, size, (IntPtr)bytesRead);
objCoord[0] = tempCoord; //X coordinate
ReadProcessMemory(handle, (IntPtr)(BaseAddress + 4), ref tempCoord, size, (IntPtr)bytesRead);
objCoord[1] = tempCoord; //Y coordinate
ReadProcessMemory(handle, (IntPtr)(BaseAddress + 4 + 4), ref tempCoord, size, (IntPtr)bytesRead);
objCoord[2] = tempCoord; //Z coordinate
}
В общем по игрался с координатами всё замечательно работает, потом решил поискать видовую матрицу. Само собой искал неизвестное значение в диапазоне от -1.0 до 1.0
Нашёл, это матрица 4 на 4 = всего 16 байт для чтения, записал в массив:
/// <summary>
/// Reads bytes for the view matrix, saves it to the buffer
/// </summary>
private void GetViewMatrix()
{
for (int i = 0; i < 16; i++)
{
ReadProcessMemory(handle, (IntPtr)(VMBaseAddress + (i * 4)), ref tempCoord, size, (IntPtr)bytesRead);
view_matrix[i] = tempCoord;
}
}
А затем преобразовывал их в координаты на экране и писал результат (x, y координаты) в поля моей структуры, чтобы потом использовать их для рисования объекта, который как раз таки будет отображать игровой объект подобно ESP хакам:
/// <summary>
/// Converts coordinates from 3D world of the game to 2D coordinates for the Form, sets Indicator position using converted 2D coordinates, returns 1 - object is visible on form; 0 - otherwise
/// </summary>
/// <returns>integer value 1 - object is visible on form; 0 - otherwise</returns>
private int WorldToScreen()
{
screenCoords[0] = objCoord[0] * view_matrix[0] + objCoord[1] * view_matrix[1] + objCoord[2] * view_matrix[2] + view_matrix[3];
screenCoords[1] = objCoord[0] * view_matrix[4] + objCoord[1] * view_matrix[5] + objCoord[2] * view_matrix[6] + view_matrix[7];
screenCoords[2] = objCoord[0] * view_matrix[8] + objCoord[1] * view_matrix[9] + objCoord[2] * view_matrix[10] + view_matrix[11];
screenCoords[3] = objCoord[0] * view_matrix[12] + objCoord[1] * view_matrix[13] + objCoord[2] * view_matrix[14] + view_matrix[15];
if (screenCoords[3] < 0.1f)
{
return 0;
}
ndc[0] = screenCoords[0] / screenCoords[3];
ndc[1] = screenCoords[1] / screenCoords[3];
ndc[2] = screenCoords[2] / screenCoords[3];
strucPoint.X = Convert.ToInt32((this.Width / 2 * ndc[0]) + (ndc[0] + this.Width / 2));
strucPoint.Y = Convert.ToInt32(-(this.Height / 2 * ndc[1]) + (ndc[1] + this.Height / 2));
return 1;
}
Всё прекрасно работало, но потом решил сделать тоже самое для игры Phasmophobia, и так, сперва нашёл адрес объекта, в данном случае баскетбольный мяч (его можно найти на карте главного меню), читаю значения из адреса с координатами. Координаты там представлены довольно "маленькими значениями" типа float (для сравнения: 324.5643 - Cod:MW2; 1.0344543533 - Phasmophobia).
К тому же адреса довольно длинные (больше 8 символов в HEX формате) для этого мне пришлось использовать тип данных структуры System.Int64 (тип long) для хранения адресов, и запускать проект в x64 сборке чтобы преобразование (IntPtr)(VMBaseAddress) было рабочим при передаче аргумента в функцию ReadProccessMemory.
Учитывая то что игра написана на движке Unity, координата по оси Z находится второй по счёту (после первого смещения в 4 байта), а не третьей (после второго смещения в 4 байта) как в остальных играх, ведь в Unity немного по другому расположены координатные прямые.
И так нашёл адреса и значения всё также по смещениям в 4 байта, пришло время искать видовую матрицу. И вот тут собственно начались проблемы, я нашёл очень похожый массив байт в 16 элементов как в предыдущей игре и попытался преобразовать и нарисовать объект на экране, но годного ESP не получилось, вместо этого объект рисовался вокруг да около нужного места.
Вот по этому прошу помощи в поиске и разъяснение касательно именно для Unity:
Может ли быть такое, что подобное преобразование (описано в методе WorldToScreen() см. код выше) может являться некорректным, учитывая особенность координат в Unity, ведь я записывал координаты в том порядке в котором они представлены в игре, то есть Z во вторую ячейку, а Y в третью??