
* Framework 4.8 Upgrade and small memory access change * update tests to 4.8 * update mod_id for torch mod
116 lines
4.0 KiB
C#
116 lines
4.0 KiB
C#
using System;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
using Torch.Utils;
|
|
|
|
namespace Torch.Managers.PatchManager
|
|
{
|
|
internal class AssemblyMemory
|
|
{
|
|
#pragma warning disable 649
|
|
[ReflectedMethod(Name = "GetMethodDescriptor")]
|
|
private static Func<DynamicMethod, RuntimeMethodHandle> _getMethodHandle;
|
|
#pragma warning restore 649
|
|
|
|
/// <summary>
|
|
/// Gets the address, in RAM, where the body of a method starts.
|
|
/// </summary>
|
|
/// <param name="method">Method to find the start of</param>
|
|
/// <returns>Address of the method's start</returns>
|
|
public static long GetMethodBodyStart(MethodBase method)
|
|
{
|
|
RuntimeMethodHandle handle;
|
|
if (method is DynamicMethod dyn)
|
|
handle = _getMethodHandle.Invoke(dyn);
|
|
else
|
|
handle = method.MethodHandle;
|
|
RuntimeHelpers.PrepareMethod(handle);
|
|
return handle.GetFunctionPointer().ToInt64();
|
|
}
|
|
|
|
internal static void UnprotectMemoryPage(long memory) {
|
|
if (NativeLibrary.IsWindows) {
|
|
var succ = NativeLibrary.VirtualProtect(
|
|
new IntPtr(memory), new UIntPtr(1),
|
|
NativeLibrary.Protection.PAGE_EXECUTE_READWRITE, out var _ignored);
|
|
|
|
if (!succ) {
|
|
throw new System.ComponentModel.Win32Exception();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// x64 ISA format:
|
|
// [prefixes] [opcode] [mod-r/m]
|
|
// [mod-r/m] is bitfield:
|
|
// [7-6] = "mod" adressing mode
|
|
// [5-3] = register or opcode extension
|
|
// [2-0] = "r/m" extra addressing mode
|
|
|
|
|
|
// http://ref.x86asm.net/coder64.html
|
|
/// Direct register addressing mode. (Jump directly to register)
|
|
private const byte MODRM_MOD_DIRECT = 0b11;
|
|
|
|
/// Long-mode prefix (64-bit operand)
|
|
private const byte REX_W = 0x48;
|
|
|
|
/// Moves a 16/32/64 operand into register i when opcode is (MOV_R0+i)
|
|
private const byte MOV_R0 = 0xB8;
|
|
|
|
// Extra opcodes. Used with opcode extension.
|
|
private const byte EXT = 0xFF;
|
|
|
|
/// Opcode extension used with <see cref="EXT"/> for the JMP opcode.
|
|
private const byte OPCODE_EXTENSION_JMP = 4;
|
|
|
|
|
|
/// <summary>
|
|
/// Reads a byte array from a memory location
|
|
/// </summary>
|
|
/// <param name="memory">Address to read from</param>
|
|
/// <param name="bytes">Number of bytes to read</param>
|
|
/// <returns>The bytes that were read</returns>
|
|
public static byte[] ReadMemory(long memory, int bytes)
|
|
{
|
|
var data = new byte[bytes];
|
|
Marshal.Copy(new IntPtr(memory), data,0, bytes);
|
|
return data;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a byte array to a memory location.
|
|
/// </summary>
|
|
/// <param name="memory">Address to write to</param>
|
|
/// <param name="bytes">Data to write</param>
|
|
public static void WriteMemory(long memory, byte[] bytes)
|
|
{
|
|
Marshal.Copy(bytes,0, new IntPtr(memory), bytes.Length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes an x64 assembly jump instruction at the given address.
|
|
/// </summary>
|
|
/// <param name="memory">Address to write the instruction at</param>
|
|
/// <param name="jumpTarget">Target address of the jump</param>
|
|
/// <returns>The bytes that were overwritten</returns>
|
|
public static byte[] WriteJump(long memory, long jumpTarget)
|
|
{
|
|
byte[] result = ReadMemory(memory, 12);
|
|
unsafe
|
|
{
|
|
var ptr = (byte*)memory;
|
|
*ptr = REX_W;
|
|
*(ptr + 1) = MOV_R0;
|
|
*((long*)(ptr + 2)) = jumpTarget;
|
|
*(ptr + 10) = EXT;
|
|
*(ptr + 11) = (MODRM_MOD_DIRECT << 6) | (OPCODE_EXTENSION_JMP << 3) | 0;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
}
|