Skip to content

Instantly share code, notes, and snippets.

@MoePus
Last active March 13, 2020 09:32
Show Gist options
  • Save MoePus/daac23dbe292589f1d81d44bcb0c2ede to your computer and use it in GitHub Desktop.
Save MoePus/daac23dbe292589f1d81d44bcb0c2ede to your computer and use it in GitHub Desktop.
Get.NetAssemblyOffset
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Linq;
using System.Runtime.Serialization;
using System.IO;
//partially from https://devblogs.microsoft.com/premier-developer/managed-object-internals-part-4-fields-layout/
namespace Main
{
class InspectorHelper
{
private static Func<object, long[]> GenerateFieldOffsetInspectionFunction(FieldInfo[] fields)
{
var method = new DynamicMethod(
name: "GetFieldOffsets",
returnType: typeof(long[]),
parameterTypes: new[] { typeof(object) },
m: typeof(InspectorHelper).Module,
skipVisibility: true);
ILGenerator ilGen = method.GetILGenerator();
// Declaring local variable of type long[]
ilGen.DeclareLocal(typeof(long[]));
// Loading array size onto evaluation stack
ilGen.Emit(OpCodes.Ldc_I4, fields.Length);
// Creating an array and storing it into the local
ilGen.Emit(OpCodes.Newarr, typeof(long));
ilGen.Emit(OpCodes.Stloc_0);
for (int i = 0; i < fields.Length; i++)
{
// Loading the local with an array
ilGen.Emit(OpCodes.Ldloc_0);
// Loading an index of the array where we're going to store the element
ilGen.Emit(OpCodes.Ldc_I4, i);
// Loading object instance onto evaluation stack
ilGen.Emit(OpCodes.Ldarg_0);
// Getting the address for a given field
ilGen.Emit(OpCodes.Ldflda, fields[i]);
// Converting field offset to long
ilGen.Emit(OpCodes.Conv_I8);
// Storing the offset in the array
ilGen.Emit(OpCodes.Stelem_I8);
}
ilGen.Emit(OpCodes.Ldloc_0);
ilGen.Emit(OpCodes.Ret);
return (Func<object, long[]>)method.CreateDelegate(typeof(Func<object, long[]>));
}
public static (FieldInfo fieldInfo, int offset)[] GetFieldOffsets(Type t)
{
var fields = t.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
Func<object, long[]> fieldOffsetInspector = GenerateFieldOffsetInspectionFunction(fields);
var instance = FormatterServices.GetUninitializedObject(t);
var addresses = fieldOffsetInspector(instance);
if (addresses.Length == 0)
{
return Array.Empty<(FieldInfo, int)>();
}
var baseLine = addresses.Min();
// Converting field addresses to offsets using the first field as a baseline
return fields
.Select((field, index) => (field: field, offset: (int)(addresses[index] - baseLine)))
.OrderBy(tuple => tuple.offset)
.ToArray();
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Input the Assembly file path:");
var asmFile = Console.ReadLine();
asmFile = asmFile.Trim('\"');
Console.WriteLine($"Input the type name:");
var typeName = Console.ReadLine();
Assembly ere;
try {
ere = Assembly.LoadFrom(asmFile);
} catch (FileNotFoundException e)
{
Console.WriteLine($"Could not find {asmFile}.");
return;
}
var ty = ere.GetType(typeName);
if (ty == null)
{
Console.WriteLine($"Could not find {typeName}.");
return;
}
Console.WriteLine(
string.Join("\r\n",
InspectorHelper.GetFieldOffsets(ty)
.Select(tpl => $"Field {tpl.fieldInfo.Name}: starts at offset 0x{tpl.offset + 0x18:X3}"))
);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment