Last active
March 13, 2020 09:32
-
-
Save MoePus/daac23dbe292589f1d81d44bcb0c2ede to your computer and use it in GitHub Desktop.
Get.NetAssemblyOffset
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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