Исходник Helldivers 2 patcher

Начинающий
Статус
Оффлайн
Регистрация
19 Апр 2024
Сообщения
9
Реакции[?]
1
Поинты[?]
1K
Сделал патчер на основе таблиц CE, вдруг кому-то пригодится

Unlimited Stamina
No recoil
Infinite Strategems
Open all Strategems
Open all Armor
Open all Items

так-же мой первый опыт с аллокейтом памяти и лонг джампом, для god mode



C#:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Net;

internal class Program
{
    [DllImport("kernel32.dll")]
    public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out int lpNumberOfBytesWritten);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint dwFreeType);


    const int PROCESS_WM_READ = 0x0010;
    const int PROCESS_VM_WRITE = 0x0020;
    const int PROCESS_VM_OPERATION = 0x0008;
    const int PROCESS_VM_READ = 0x0010;
    const int PROCESS_QUERY_INFORMATION = 0x0400;
    const uint MEM_COMMIT = 0x1000;
    const uint MEM_RESERVE = 0x2000;
    const uint PAGE_EXECUTE_READWRITE = 0x40;
    const uint MEM_RELEASE = 0x8000;


    static void Main(string[] args)
    {
        var patterns = new Dictionary<string, int>
        {
            //{ "41 89 28 49 8B 84 CA 28 20 00 00 8B 48 10", 3 },         // No reload
            //{ "FF 4C 87 04 83 FE FF 74 52 49 8B 00 F6 40 14 01", 4 },
            //{ "41 FF 08 4A 8B 84 ED", 3},                               // Infinite grenades
            //{ "41 FF CF 3B C2 74 61", 3}                                // Infinite strygle
        };

        var processId = GetProcessIdByName("helldivers2");
        if (processId == -1)
        {
            Console.WriteLine("Process not found.");
            return;
        }

        IntPtr processHandle = OpenProcess(PROCESS_WM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, false, processId);
        if (processHandle == IntPtr.Zero)
        {
            Console.WriteLine("Problem with opening the process.");
            return;
        }

        var (baseAddress, moduleSize) = GetModuleBaseAndSize(processId, "game.dll");
        if (baseAddress == IntPtr.Zero)
        {
            Console.WriteLine("Module not found.");
            return;
        }

        byte[] moduleMemory = new byte[moduleSize];
        if (ReadProcessMemory(processHandle, baseAddress, moduleMemory, moduleSize, out int bytesRead))
        {
            List<byte?> bytePattern;
            List<int> skipIndexes;
            List <nint> addresses;

            foreach (var pattern in patterns)
            {
                (bytePattern, skipIndexes) = ParsePatternString(pattern.Key);
                addresses = FindPatternAddresses(baseAddress, moduleMemory, bytesRead, bytePattern, skipIndexes, IntPtr.Zero);
                foreach (var address in addresses)
                {
                    ApplyNopPatch(processHandle, address, pattern.Value);
                }
            }

            // Unlimited Stamina
            ApplyPatternAndPatch(processHandle, baseAddress, moduleMemory, bytesRead, "F3 41 0F 11 08 8B 48 10 E8 ?? ?? ?? ?? 41 8B 47 48", [0xF3, 0x41, 0x0F, 0x11, 0x30]);

            // No recoil
            ApplyPatternAndPatch(processHandle, baseAddress, moduleMemory, bytesRead, "44 8B 7C 24 ?? 41 3B 46 08", [0xEB], 9);

            // Infinite Strategems
            ApplyPatternAndPatch(processHandle, baseAddress, moduleMemory, bytesRead, "0F 86 BF 01 00 00 0F", [0x90, 0xE9]);

            // Open all Strategems
            ApplyPatternAndPatch(processHandle, baseAddress, moduleMemory, bytesRead, "48 89 5C 24 ?? 48 8B D9 85 D2 75 09", [0xB0, 0x01, 0xC3]);

            // Open all Armor
            ApplyPatternAndPatch(processHandle, baseAddress, moduleMemory, bytesRead, "48 83 EC ?? 44 8B 49 ?? 45 33 C0", [0xB0, 0x01, 0xC3]);

            // Open all Armor
            ApplyPatternAndPatch(processHandle, baseAddress, moduleMemory, bytesRead, "0x83 0xB9 ?? ?? ?? ?? ?? 0x75 ?? 0x85 0xD2 0x74 ?? 0x44 0x8B 0x89 ?? ?? ?? ?? 0x45 0x33 0xC0 0x45 0x85 0xC9 0x74 ?? 0x48 0x8D 0x81 ?? ?? ?? ?? 0x39 0x50 ?? 0x74 ?? 0x41 0xFF 0xC0 0x48 0x83 0xC0 ?? 0x45 0x3B 0xC1 0x72 ?? 0x32 0xC0 0xC3 0x8B 0x00 0x48 0x69 0xC8", [0xB0, 0x01, 0xC3]);


            // God Mode
            //IntPtr a = new IntPtr(0x0000000180745230);
            //int length = 18;
            //byte[] originalBytes = ReadOriginalBytes(processHandle, a, length);


            (bytePattern, skipIndexes) = ParsePatternString("41 8B 84 8B 28 4C 00 00 48 8B 5C 24 20 48 8B 74 24 28");
            addresses = FindPatternAddresses(baseAddress, moduleMemory, bytesRead, bytePattern, skipIndexes, IntPtr.Zero);

            foreach (var address in addresses)
            {
                IntPtr newMem = VirtualAllocEx(processHandle, IntPtr.Zero, 1024, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
                if (newMem == IntPtr.Zero)
                {
                    Console.WriteLine("Failed to allocate memory in the target process.");
                    continue;
                }

                Console.WriteLine($"NewMem addr {newMem.ToString("X16")}");

                byte[] customCode = { 0x48, 0x85, 0xD2, 0x75, 0x0e, 0x41, 0xc7, 0x84, 0x8b,
                                      0x28, 0x4c, 0x00, 0x00, 0x0f, 0x27, 0x00, 0x00, 0xEB,
                                      0x00, 0x41, 0x8b, 0x84, 0x8b, 0x28, 0x4c, 0x00, 0x00,
                                      0x48, 0x8B, 0x5C, 0x24, 0x20, 0x48, 0x8B, 0x74, 0x24,
                    0x28, 0x5F, 0xC3 };

                WriteProcessMemory(processHandle, newMem, customCode, customCode.Length, out _);

                byte[] jmpInstruction = new byte[] { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 }; // JMP [RIP+0]
                byte[] addressToJump = BitConverter.GetBytes(newMem.ToInt64());
                byte[] specialNops = { 0x0F, 0x1F, 0x40, 0x00 };
                int nopsCount = 18 - jmpInstruction.Length - sizeof(long); // Calculate remaining space for NOPs
                byte[] nops = Enumerable.Repeat((byte)0x90, Math.Max(0, nopsCount - specialNops.Length)).ToArray();
                byte[] patchBytes = new byte[jmpInstruction.Length + sizeof(long) + specialNops.Length + nops.Length];

                Buffer.BlockCopy(jmpInstruction, 0, patchBytes, 0, jmpInstruction.Length);
                Buffer.BlockCopy(addressToJump, 0, patchBytes, jmpInstruction.Length, sizeof(long));
                Buffer.BlockCopy(specialNops, 0, patchBytes, jmpInstruction.Length + sizeof(long), specialNops.Length);
                Buffer.BlockCopy(nops, 0, patchBytes, jmpInstruction.Length + sizeof(long) + specialNops.Length, nops.Length);

                WriteProcessMemory(processHandle, address, patchBytes, patchBytes.Length, out _);

                Console.WriteLine($"Patched {patchBytes.Length} bytes at {address.ToString("X16")} with absolute JMP to {newMem.ToString("X16")} and special NOPs count: {nops.Length + specialNops.Length}");
            }
        }
        else
        {
            Console.WriteLine("Failed to read process memory.");
        }

        Console.ReadLine();
    }

    public static byte[] ReadOriginalBytes(IntPtr processHandle, IntPtr address, int length)
    {
        Console.WriteLine($"Attempting to read {length} bytes from address: {address.ToString("X16")}");

        byte[] buffer = new byte[length];
        if (ReadProcessMemory(processHandle, address, buffer, length, out int bytesRead))
        {
            if (bytesRead == length)
            {
                Console.WriteLine($"Original bytes at 0x{address.ToString("X16")}: {BitConverter.ToString(buffer)}");
                return buffer;
            }
            else
            {
                Console.WriteLine("Warning: Expected to read {0} bytes but only read {1} bytes.", length, bytesRead);
                return null;
            }
        }
        else
        {
            Console.WriteLine("Error: Failed to read memory from process. Last Error: {0}", Marshal.GetLastWin32Error());
            return null;
        }
    }

    static void ApplyPatternAndPatch(IntPtr processHandle, IntPtr baseAddress, byte[] moduleMemory, int bytesRead, string pattern, byte[] patchBytes, int offset = 0)
    {
        var (bytePattern, skipIndexes) = ParsePatternString(pattern);
        var addresses = FindPatternAddresses(baseAddress, moduleMemory, bytesRead, bytePattern, skipIndexes, IntPtr.Zero);
        foreach (var address in addresses)
        {
            PatchBytes(processHandle, address + offset, patchBytes);
        }
    }
    static int GetProcessIdByName(string processName)
    {
        var process = Process.GetProcessesByName(processName).FirstOrDefault();
        return process?.Id ?? -1;
    }

    static (IntPtr baseAddress, int moduleSize) GetModuleBaseAndSize(int processId, string moduleName)
    {
        using var process = Process.GetProcessById(processId);
        var module = process.Modules.Cast<ProcessModule>().FirstOrDefault(m => string.Equals(m.ModuleName, moduleName, StringComparison.OrdinalIgnoreCase));
        return module != null ? (module.BaseAddress, module.ModuleMemorySize) : (IntPtr.Zero, 0);
    }

    static List<IntPtr> FindPatternAddresses(IntPtr baseAddress, byte[] buffer, int bytesRead, List<byte?> pattern, List<int> skipIndexes, IntPtr startAddress)
    {
        List<IntPtr> addresses = new List<IntPtr>();
        long startOffset = Math.Max(startAddress.ToInt64() - baseAddress.ToInt64(), 0);
        int end = bytesRead - pattern.Count;

        Console.WriteLine($"Starting pattern search from offset: {startOffset:X}");
        Console.WriteLine($"Total bytes read: {bytesRead}");
        Console.WriteLine($"Pattern length: {pattern.Count}");

        for (int i = (int)startOffset; i <= end; i++)
        {
            bool match = true;
            for (int j = 0; j < pattern.Count; j++)
            {
                if (skipIndexes.Contains(j)) continue;

                if (pattern[j].HasValue && buffer[i + j] != pattern[j])
                {
                    match = false;
                    break;
                }
            }

            if (match)
            {
                addresses.Add(new IntPtr(baseAddress.ToInt64() + i));
            }
        }

        return addresses;
    }

    static (List<byte?> pattern, List<int> skipIndexes) ParsePatternString(string patternStr)
    {
        var bytes = new List<byte?>();
        var skipIndexes = new List<int>();
        var parts = patternStr.Split(' ');

        for (int i = 0; i < parts.Length; i++)
        {
            bytes.Add(parts == "??" ? (byte?)null : Convert.ToByte(parts, 16));
            if (parts == "??")
            {
                skipIndexes.Add(i);
            }
        }

        return (bytes, skipIndexes);
    }

    static void ApplyNopPatch(IntPtr processHandle, IntPtr address, int length)
    {
        var nopArray = Enumerable.Repeat((byte)0x90, length).ToArray();
        if (WriteProcessMemory(processHandle, address, nopArray, nopArray.Length, out int bytesWritten))
        {
            Console.WriteLine($"NOP patch applied successfully at 0x{address.ToString("X")}, {bytesWritten} bytes written.");
        }
        else
        {
            Console.WriteLine($"Failed to apply NOP patch at 0x{address.ToString("X")}. Check your permissions and whether the address is correct.");
        }
    }

    static void PatchBytes(IntPtr processHandle, IntPtr address, byte[] bytesToWrite)
    {
        if (WriteProcessMemory(processHandle, address, bytesToWrite, bytesToWrite.Length, out int bytesWritten))
        {
            Console.WriteLine($"Patch applied successfully at 0x{address.ToString("X")}, {bytesWritten} bytes written.");
        }
        else
        {
            Console.WriteLine($"Failed to apply patch at 0x{address.ToString("X")}. Check your permissions and whether the address is correct.");
        }
    }
}
 
Сверху Снизу