This is the fifteenth part of the Custom memory allocation series. For your convenience you can find other parts in the table of contents in Part 1 — Allocating object on a stack
Last time we saw how to do unsafe operations without the unsafe keyword. This time we’ll allocate some reference type on a stack in a similar manner.
Let’s this code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Makeref_Safe_OnStack { public class Program { public static void Main(string[] args) { Structure structure = new Structure(); structure.syncBlock = 0xBADF00D; structure.methodHandle = 0xBADF00D; structure.field = 0xBADF00D; var method = typeof(Program).GetMethod("GetStackAddress", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); RuntimeHelpers.PrepareMethod(method.MethodHandle); var codeAddress = method.MethodHandle.GetFunctionPointer(); Console.WriteLine(codeAddress.ToString("X")); Marshal.Copy(addressGetter, 0, codeAddress, addressGetter.Length); var structureAddress = GetStackAddress() + 132; Console.WriteLine(structureAddress.ToString("X")); structure.syncBlock = 0; structure.methodHandle = (int)typeof(Klass).TypeHandle.Value; structure.field = 123; Holder<Klass> holder = new Holder<Klass>(); GCHandle holderHandle = GCHandle.Alloc(holder); var holderAddress = Marshal.ReadIntPtr(GCHandle.ToIntPtr(holderHandle)); Marshal.WriteIntPtr(holderAddress, 4, structureAddress + 4); // Skip first integer for sync block, assumes x86 Console.WriteLine(holder.reference.GetType()); Console.WriteLine(holder.reference.Field); structure.field = 456; Console.WriteLine(holder.reference.Field); } static byte[] addressGetter = new byte[] { 0x89, 0xE0, // mov eax, esp 0xC3 // ret }; static IntPtr GetStackAddress() { Console.WriteLine("Some dummy code to be replaced"); return IntPtr.Zero; } } class Holder<T> { public T reference; } class Klass { public int Field; } struct Structure { public int syncBlock; public int methodHandle; public int field; } } |
In line 43 we have a machine code for getting the esp register. In line 22 we modify the GetStackAddress method with our machine code.
We allocate some structure on the stack which we’ll later override with a reference type. We do it in line 34.
Finally, you can see how we take type in line 36 and then modify reference instance field by using structure. This confirms the object is exactly in the stack.
Output:
1 2 3 4 5 |
438F1F0 88BE8E0 Makeref_Safe_OnStack.Klass 123 456 |