This is the ninth 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
We already hijacked new in C#. Last time we did this for 32-bit applications, today we will modify the code to support x64. Let’s go.
Object allocation
Let’s start with object allocation. We had the helper method (which was not used anyway) for getting pointer to the object. Just for keeping everything clean, we can fix it:
1 2 3 4 5 6 7 8 9 10 11 |
private unsafe IntPtr* GetReferenceAsPointer(object obj) { // The order of memory is: // [raw bytes] <- reference <- __makeref(reference) <- &__makeref(reference) var managedPointerToRawMemory = obj; // This is reference TypedReference managedPointerToPointerToRawMemory = __makeref(managedPointerToRawMemory); // This is pointer to reference, we need to cast it to correct type, but we cannot do it directly and we need to get address of it var pointerToPointerToPointerToRawMemory = (IntPtr*)&managedPointerToPointerToRawMemory; // We get an address var pointerToPointerToRawMemory = (IntPtr*)*pointerToPointerToPointerToRawMemory; // And we dereference it, this is __makeref(reference) var pointerToRawMemory = (IntPtr*)*pointerToPointerToRawMemory; //This is reference return pointerToRawMemory; } |
No big changes, just replaced int
with IntPtr
.
Now, let’s modify RawAllocate
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
private static bool Is64 = IntPtr.Size == sizeof(long); private static IntPtr[] Memory = new IntPtr[102400]; // Array big enough to be stored in Generation 2 // Method needs to be static in order to maintain calling convention public static unsafe IntPtr RawAllocate(IntPtr methodTable) { int objectSize = Marshal.ReadInt32(methodTable, 4) / sizeof(int); // Calculate the object size by extracting it from method table and dividing by int size. We assume that the size starts 4 bytes after the beginning of method table (works on .NET 4 and .NET 3.5) _currentOffset++; // Skip sizeof(int) bytes for syncblock Memory[_currentOffset] = methodTable; // Write address to method table TypedReference newObjectReference = __makeref(Dummy); // Get handle for newly created object TypedReference memoryReference = __makeref(Memory); // Get handle for memory var spawnedObjectAddress = *(IntPtr*)*(IntPtr*)&memoryReference + (_currentOffset + 2) * sizeof(IntPtr); // Calculate the address of spawned object. We need to add 2 since we need to skip methodtable of array and methodtable of elements type *(IntPtr*)*(IntPtr*)&newObjectReference = spawnedObjectAddress; // Modify handle for new object using address of existing memory _currentOffset += objectSize; // Move within the memory return *(IntPtr*)*(IntPtr*)&newObjectReference; } |
Once again, we remove unnecessary casting to int
and use IntPtr
all the way.
Hijacking new
Now we need to modify method getting address of .NET allocation method:
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 |
private static long GetAllocMethodAddress() { // Get handle to method creating object var methodHandle = typeof(GenericMemoryAllocator).GetMethod(nameof(CreateObject), BindingFlags.NonPublic | BindingFlags.Instance).MethodHandle; // JIT methods RuntimeHelpers.PrepareMethod(methodHandle); // Get address of jitted method IntPtr methodAddress = Marshal.ReadIntPtr(methodHandle.Value, 8); // Call to internal function differs between builds #if DEBUG int offset = Is64 ? 0x2D : 0x22; #else int offset = Is64 ? 0x1A : 0xE; #endif // Read jump offset int jumpOffset = 0; for(int i = 1; i < 5; ++i) { jumpOffset = jumpOffset + (Marshal.ReadByte(methodAddress, offset + i) << (i-1)*8); } // Calculate absolute address long absoluteAddress = (long)methodAddress + offset + jumpOffset + 1 + 4; // 1 byte for jmp instruction, 4 bytes for relative address return absoluteAddress; } |
We replace integers with long integers, add two more offsets (for x64), and change casting to long
instead of int
. We can use the same code for x86 now as we can always cast to the wider types.
Last part, hijacking the operator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public static void HijackNew() { var methodHandle = typeof(GenericMemoryAllocator).GetMethod(nameof(RawAllocate)).MethodHandle; RuntimeHelpers.PrepareMethod(methodHandle); var myAllocAddress = Marshal.ReadIntPtr(methodHandle.Value, 8); var defaultAllocAddress = GetAllocMethodAddress(); int offset = (int)((long)myAllocAddress - defaultAllocAddress - 4 - 1); // 4 bytes for relative address and one byte for opcode byte[] instruction = { 0xE9, // Long jump instruction (byte)(offset & 0xFF), (byte)((offset >> 8) & 0xFF), (byte)((offset >> 16) & 0xFF), (byte)((offset >> 24) & 0xFF) }; UnlockPage((IntPtr)defaultAllocAddress); Marshal.Copy(instruction, 0, (IntPtr)defaultAllocAddress, instruction.Length); } |
Different cast for offset calculation, nothing else. We rely on the method placement when subtracting addresses, if the methods would be in different order, we would need to do the absolute jump (e.g., via push
+ ret
trick).
You should also notice call to UnlockPage
which we do in the following way:
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 |
public enum Protection { PAGE_NOACCESS = 0x01, PAGE_READONLY = 0x02, PAGE_READWRITE = 0x04, PAGE_WRITECOPY = 0x08, PAGE_EXECUTE = 0x10, PAGE_EXECUTE_READ = 0x20, PAGE_EXECUTE_READWRITE = 0x40, PAGE_EXECUTE_WRITECOPY = 0x80, PAGE_GUARD = 0x100, PAGE_NOCACHE = 0x200, PAGE_WRITECOMBINE = 0x400 } [DllImport("kernel32.dll", SetLastError = true)] static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect); // Unlocks page for executing private static void UnlockPage(IntPtr address) { uint old; VirtualProtect(address, 6, (uint)Protection.PAGE_EXECUTE_READWRITE, out old); } |
Testing
Just for the sake of completeness, here is the debugging session for doing the same manually:
Getting .NET allocation method:
1 2 3 4 5 6 |
0:003> !name2ee * Program Module: 00007ffbaf491000 Assembly: mscorlib.dll -------------------------------------- Module: 00007ffb51944110 Assembly: GenericUnsafeAlloc.exe |
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 |
0:003> !dumpmodule -mt 00007ffb51944110 Name: C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\bin\x64\Debug\GenericUnsafeAlloc.exe Attributes: PEFile SupportsUpdateableMethods Assembly: 000001fbfa6f1a60 LoaderHeap: 0000000000000000 TypeDefToMethodTableMap: 00007ffb51940070 TypeRefToMethodTableMap: 00007ffb519400a0 MethodDefToDescMap: 00007ffb519401b8 FieldDefToDescMap: 00007ffb51940228 MemberRefToDescMap: 0000000000000000 FileReferencesMap: 00007ffb519402e8 AssemblyReferencesMap: 00007ffb519402f0 MetaData start address: 000001fbfa562384 (3872 bytes) Types defined in this module MT TypeDef Name ------------------------------------------------------------------------------ 00007ffb51945bf0 0x02000003 GenericUnsafeAlloc.GenericMemoryAllocator 00007ffb51945a20 0x02000004 GenericUnsafeAlloc.Program 00007ffb51945ce8 0x02000005 GenericUnsafeAlloc.TestClass Types referenced in this module MT TypeRef Name ------------------------------------------------------------------------------ 00007ffbafb270c0 0x02000014 System.Object 00007ffbafb28d30 0x02000016 System.RuntimeTypeHandle 00007ffbafb46980 0x02000018 System.IntPtr 00007ffbafb28778 0x02000019 System.Type 00007ffbafb464b0 0x0200001a System.Runtime.InteropServices.Marshal 00007ffbafb610a8 0x02000020 System.Console 00007ffbafb2a490 0x02000021 System.GC 00007ffbafb29510 0x02000022 System.Int32 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
0:003> !dumpmt -md 00007ffb51945a20 EEClass: 00007ffb51942748 Module: 00007ffb51944110 Name: GenericUnsafeAlloc.Program mdToken: 0000000002000004 File: C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\bin\x64\Debug\GenericUnsafeAlloc.exe BaseSize: 0x18 ComponentSize: 0x0 Slots in VTable: 6 Number of IFaces in IFaceMap: 0 -------------------------------------- MethodDesc Table Entry MethodDesc JIT Name 00007ffbaf93b1f0 00007ffbaf497538 PreJIT System.Object.ToString() 00007ffbaf93fd90 00007ffbaf497540 PreJIT System.Object.Equals(System.Object) 00007ffbaf961dc0 00007ffbaf497568 PreJIT System.Object.GetHashCode() 00007ffbaf93ce50 00007ffbaf497580 PreJIT System.Object.Finalize() 00007ffb51a50080 00007ffb51945a18 NONE GenericUnsafeAlloc.Program..ctor() 00007ffb51a50480 00007ffb51945a08 JIT GenericUnsafeAlloc.Program.Main() |
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
0:003> !U 00007ffb51a50480 Normal JIT generated code GenericUnsafeAlloc.Program.Main() Begin 00007ffb51a50480, size 265 *** WARNING: Unable to verify checksum for C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\bin\x64\Debug\GenericUnsafeAlloc.exe C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\Program.cs @ 162: >>> 00007ffb`51a50480 55 push rbp 00007ffb`51a50481 57 push rdi 00007ffb`51a50482 4881ece8000000 sub rsp,0E8h 00007ffb`51a50489 488dac24f0000000 lea rbp,[rsp+0F0h] 00007ffb`51a50491 488dbd38ffffff lea rdi,[rbp-0C8h] 00007ffb`51a50498 b930000000 mov ecx,30h 00007ffb`51a5049d 33c0 xor eax,eax 00007ffb`51a5049f f3ab rep stos dword ptr [rdi] 00007ffb`51a504a1 833de840efff00 cmp dword ptr [00007ffb`51944590],0 00007ffb`51a504a8 7405 je 00007ffb`51a504af 00007ffb`51a504aa e8e1e7a95f call clr!JIT_DbgIsJustMyCode (00007ffb`b14eec90) 00007ffb`51a504af 90 nop C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\Program.cs @ 164: 00007ffb`51a504b0 48b9f05b9451fb7f0000 mov rcx,7FFB51945BF0h (MT: GenericUnsafeAlloc.GenericMemoryAllocator) 00007ffb`51a504ba e85120605f call clr!JIT_TrialAllocSFastMP_InlineGetThread (00007ffb`b1052510) 00007ffb`51a504bf 488945c0 mov qword ptr [rbp-40h],rax 00007ffb`51a504c3 488b4dc0 mov rcx,qword ptr [rbp-40h] 00007ffb`51a504c7 e854fcffff call 00007ffb`51a50120 (GenericUnsafeAlloc.GenericMemoryAllocator..ctor(), mdToken: 0000000006000009) 00007ffb`51a504cc 488b4dc0 mov rcx,qword ptr [rbp-40h] 00007ffb`51a504d0 48894de0 mov qword ptr [rbp-20h],rcx C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\Program.cs @ 165: 00007ffb`51a504d4 488b4de0 mov rcx,qword ptr [rbp-20h] 00007ffb`51a504d8 48ba205e9451fb7f0000 mov rdx,7FFB51945E20h (MD: GenericUnsafeAlloc.GenericMemoryAllocator.Allocate[[GenericUnsafeAlloc.TestClass, GenericUnsafeAlloc]]()) 00007ffb`51a504e2 3909 cmp dword ptr [rcx],ecx 00007ffb`51a504e4 e867fcffff call 00007ffb`51a50150 (GenericUnsafeAlloc.GenericMemoryAllocator.Allocate[[System.__Canon, mscorlib]](), mdToken: 0000000006000004) 00007ffb`51a504e9 488945b8 mov qword ptr [rbp-48h],rax 00007ffb`51a504ed 488b4db8 mov rcx,qword ptr [rbp-48h] 00007ffb`51a504f1 48894dd8 mov qword ptr [rbp-28h],rcx C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\Program.cs @ 168: 00007ffb`51a504f5 48b9c070b2affb7f0000 mov rcx,offset mscorlib_ni+0x6970c0 (00007ffb`afb270c0) (MT: System.Object) 00007ffb`51a504ff e80c20605f call clr!JIT_TrialAllocSFastMP_InlineGetThread (00007ffb`b1052510) 00007ffb`51a50504 488945b0 mov qword ptr [rbp-50h],rax 00007ffb`51a50508 488b4db0 mov rcx,qword ptr [rbp-50h] 00007ffb`51a5050c e80fd0ee5d call mscorlib_ni+0x4ad520 (00007ffb`af93d520) (System.Object..ctor(), mdToken: 0000000006000221) 00007ffb`51a50511 488b45b0 mov rax,qword ptr [rbp-50h] 00007ffb`51a50515 488945d0 mov qword ptr [rbp-30h],rax C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\Program.cs @ 172: 00007ffb`51a50519 e8326d795e call mscorlib_ni+0xd57250 (00007ffb`b01e7250) (System.Console.ReadLine(), mdToken: 0000000006000b56) 00007ffb`51a5051e 488945a8 mov qword ptr [rbp-58h],rax 00007ffb`51a50522 90 nop C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\Program.cs @ 173: 00007ffb`51a50523 48b9c070b2affb7f0000 mov rcx,offset mscorlib_ni+0x6970c0 (00007ffb`afb270c0) (MT: System.Object) 00007ffb`51a5052d e8de1f605f call clr!JIT_TrialAllocSFastMP_InlineGetThread (00007ffb`b1052510) 00007ffb`51a50532 488945a0 mov qword ptr [rbp-60h],rax 00007ffb`51a50536 488b4da0 mov rcx,qword ptr [rbp-60h] 00007ffb`51a5053a e8e1cfee5d call mscorlib_ni+0x4ad520 (00007ffb`af93d520) (System.Object..ctor(), mdToken: 0000000006000221) 00007ffb`51a5053f 488b4da0 mov rcx,qword ptr [rbp-60h] 00007ffb`51a50543 48894dc8 mov qword ptr [rbp-38h],rcx C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\Program.cs @ 176: 00007ffb`51a50547 48b9d8350090fb010000 mov rcx,1FB900035D8h 00007ffb`51a50551 488b09 mov rcx,qword ptr [rcx] 00007ffb`51a50554 48894d98 mov qword ptr [rbp-68h],rcx 00007ffb`51a50558 488b4de0 mov rcx,qword ptr [rbp-20h] 00007ffb`51a5055c e8afd9a65f call clr!GCInterface::GetGeneration (00007ffb`b14bdf10) 00007ffb`51a50561 8945f4 mov dword ptr [rbp-0Ch],eax 00007ffb`51a50564 48b91095b2affb7f0000 mov rcx,offset mscorlib_ni+0x699510 (00007ffb`afb29510) (MT: System.Int32) 00007ffb`51a5056e e89d1f605f call clr!JIT_TrialAllocSFastMP_InlineGetThread (00007ffb`b1052510) 00007ffb`51a50573 48894590 mov qword ptr [rbp-70h],rax 00007ffb`51a50577 488b4d98 mov rcx,qword ptr [rbp-68h] 00007ffb`51a5057b 48898d70ffffff mov qword ptr [rbp-90h],rcx 00007ffb`51a50582 488b4d90 mov rcx,qword ptr [rbp-70h] 00007ffb`51a50586 8b55f4 mov edx,dword ptr [rbp-0Ch] 00007ffb`51a50589 895108 mov dword ptr [rcx+8],edx 00007ffb`51a5058c 488b4d90 mov rcx,qword ptr [rbp-70h] 00007ffb`51a50590 48898d68ffffff mov qword ptr [rbp-98h],rcx 00007ffb`51a50597 488b8d70ffffff mov rcx,qword ptr [rbp-90h] 00007ffb`51a5059e 488b9568ffffff mov rdx,qword ptr [rbp-98h] 00007ffb`51a505a5 e8268ffa5d call mscorlib_ni+0x5694d0 (00007ffb`af9f94d0) (System.Console.WriteLine(System.String, System.Object), mdToken: 0000000006000b65) 00007ffb`51a505aa 90 nop C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\Program.cs @ 177: 00007ffb`51a505ab 48b9e0350090fb010000 mov rcx,1FB900035E0h 00007ffb`51a505b5 488b09 mov rcx,qword ptr [rcx] 00007ffb`51a505b8 48894d88 mov qword ptr [rbp-78h],rcx 00007ffb`51a505bc 488b4dd8 mov rcx,qword ptr [rbp-28h] 00007ffb`51a505c0 e84bd9a65f call clr!GCInterface::GetGeneration (00007ffb`b14bdf10) 00007ffb`51a505c5 8945f0 mov dword ptr [rbp-10h],eax 00007ffb`51a505c8 48b91095b2affb7f0000 mov rcx,offset mscorlib_ni+0x699510 (00007ffb`afb29510) (MT: System.Int32) 00007ffb`51a505d2 e8391f605f call clr!JIT_TrialAllocSFastMP_InlineGetThread (00007ffb`b1052510) 00007ffb`51a505d7 48894590 mov qword ptr [rbp-70h],rax 00007ffb`51a505db 488b4d88 mov rcx,qword ptr [rbp-78h] 00007ffb`51a505df 48898d60ffffff mov qword ptr [rbp-0A0h],rcx 00007ffb`51a505e6 488b4d90 mov rcx,qword ptr [rbp-70h] 00007ffb`51a505ea 8b55f0 mov edx,dword ptr [rbp-10h] 00007ffb`51a505ed 895108 mov dword ptr [rcx+8],edx 00007ffb`51a505f0 488b4d90 mov rcx,qword ptr [rbp-70h] 00007ffb`51a505f4 48898d58ffffff mov qword ptr [rbp-0A8h],rcx 00007ffb`51a505fb 488b8d60ffffff mov rcx,qword ptr [rbp-0A0h] 00007ffb`51a50602 488b9558ffffff mov rdx,qword ptr [rbp-0A8h] 00007ffb`51a50609 e8c28efa5d call mscorlib_ni+0x5694d0 (00007ffb`af9f94d0) (System.Console.WriteLine(System.String, System.Object), mdToken: 0000000006000b65) 00007ffb`51a5060e 90 nop C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\Program.cs @ 178: 00007ffb`51a5060f 48b9e8350090fb010000 mov rcx,1FB900035E8h 00007ffb`51a50619 488b09 mov rcx,qword ptr [rcx] 00007ffb`51a5061c 48894d80 mov qword ptr [rbp-80h],rcx 00007ffb`51a50620 488b4dd0 mov rcx,qword ptr [rbp-30h] 00007ffb`51a50624 e8e7d8a65f call clr!GCInterface::GetGeneration (00007ffb`b14bdf10) 00007ffb`51a50629 8945ec mov dword ptr [rbp-14h],eax 00007ffb`51a5062c 48b91095b2affb7f0000 mov rcx,offset mscorlib_ni+0x699510 (00007ffb`afb29510) (MT: System.Int32) 00007ffb`51a50636 e8d51e605f call clr!JIT_TrialAllocSFastMP_InlineGetThread (00007ffb`b1052510) 00007ffb`51a5063b 48894590 mov qword ptr [rbp-70h],rax 00007ffb`51a5063f 488b4d80 mov rcx,qword ptr [rbp-80h] 00007ffb`51a50643 48898d50ffffff mov qword ptr [rbp-0B0h],rcx 00007ffb`51a5064a 488b4d90 mov rcx,qword ptr [rbp-70h] 00007ffb`51a5064e 8b55ec mov edx,dword ptr [rbp-14h] 00007ffb`51a50651 895108 mov dword ptr [rcx+8],edx 00007ffb`51a50654 488b4d90 mov rcx,qword ptr [rbp-70h] 00007ffb`51a50658 48898d48ffffff mov qword ptr [rbp-0B8h],rcx 00007ffb`51a5065f 488b8d50ffffff mov rcx,qword ptr [rbp-0B0h] 00007ffb`51a50666 488b9548ffffff mov rdx,qword ptr [rbp-0B8h] 00007ffb`51a5066d e85e8efa5d call mscorlib_ni+0x5694d0 (00007ffb`af9f94d0) (System.Console.WriteLine(System.String, System.Object), mdToken: 0000000006000b65) 00007ffb`51a50672 90 nop C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\Program.cs @ 179: 00007ffb`51a50673 48b9f0350090fb010000 mov rcx,1FB900035F0h 00007ffb`51a5067d 488b09 mov rcx,qword ptr [rcx] 00007ffb`51a50680 48898d78ffffff mov qword ptr [rbp-88h],rcx 00007ffb`51a50687 488b4dc8 mov rcx,qword ptr [rbp-38h] 00007ffb`51a5068b e880d8a65f call clr!GCInterface::GetGeneration (00007ffb`b14bdf10) 00007ffb`51a50690 8945e8 mov dword ptr [rbp-18h],eax 00007ffb`51a50693 48b91095b2affb7f0000 mov rcx,offset mscorlib_ni+0x699510 (00007ffb`afb29510) (MT: System.Int32) 00007ffb`51a5069d e86e1e605f call clr!JIT_TrialAllocSFastMP_InlineGetThread (00007ffb`b1052510) 00007ffb`51a506a2 48894590 mov qword ptr [rbp-70h],rax 00007ffb`51a506a6 488b8d78ffffff mov rcx,qword ptr [rbp-88h] 00007ffb`51a506ad 48898d40ffffff mov qword ptr [rbp-0C0h],rcx 00007ffb`51a506b4 488b4d90 mov rcx,qword ptr [rbp-70h] 00007ffb`51a506b8 8b55e8 mov edx,dword ptr [rbp-18h] 00007ffb`51a506bb 895108 mov dword ptr [rcx+8],edx 00007ffb`51a506be 488b4d90 mov rcx,qword ptr [rbp-70h] 00007ffb`51a506c2 48898d38ffffff mov qword ptr [rbp-0C8h],rcx 00007ffb`51a506c9 488b8d40ffffff mov rcx,qword ptr [rbp-0C0h] 00007ffb`51a506d0 488b9538ffffff mov rdx,qword ptr [rbp-0C8h] 00007ffb`51a506d7 e8f48dfa5d call mscorlib_ni+0x5694d0 (00007ffb`af9f94d0) (System.Console.WriteLine(System.String, System.Object), mdToken: 0000000006000b65) 00007ffb`51a506dc 90 nop C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\Program.cs @ 180: 00007ffb`51a506dd 90 nop 00007ffb`51a506de 488d65f8 lea rsp,[rbp-8] 00007ffb`51a506e2 5f pop rdi 00007ffb`51a506e3 5d pop rbp 00007ffb`51a506e4 c3 ret |
1 2 3 4 5 6 7 8 9 10 11 12 |
0:003> !U 00007ffb`b1052510 Unmanaged code 00007ffb`b1052510 8b5104 mov edx,dword ptr [rcx+4] 00007ffb`b1052513 654c8b1c2598140000 mov r11,qword ptr gs:[1498h] 00007ffb`b105251c 4d8b5370 mov r10,qword ptr [r11+70h] 00007ffb`b1052520 498b4368 mov rax,qword ptr [r11+68h] 00007ffb`b1052524 4803d0 add rdx,rax 00007ffb`b1052527 493bd2 cmp rdx,r10 00007ffb`b105252a 7708 ja clr!JIT_TrialAllocSFastMP_InlineGetThread__PatchTLSOffset+0x21 (00007ffb`b1052534) 00007ffb`b105252c 49895368 mov qword ptr [r11+68h],rdx 00007ffb`b1052530 488908 mov qword ptr [rax],rcx 00007ffb`b1052533 c3 ret |
As we can see, now we are calling different method clr!JIT_TrialAllocSFastMP_InlineGetThread (00007ffb`b1052510)
Let’s grab our allocator:
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 |
0:003> !dumpmt -md 00007ffb51945bf0 EEClass: 00007ffb51a91020 Module: 00007ffb51944110 Name: GenericUnsafeAlloc.GenericMemoryAllocator mdToken: 0000000002000003 File: C:\Users\adafurma\Desktop\msp_windowsinternals\GenericUnsafeAlloc\bin\x64\Debug\GenericUnsafeAlloc.exe BaseSize: 0x18 ComponentSize: 0x0 Slots in VTable: 14 Number of IFaces in IFaceMap: 0 -------------------------------------- MethodDesc Table Entry MethodDesc JIT Name 00007ffbaf93b1f0 00007ffbaf497538 PreJIT System.Object.ToString() 00007ffbaf93fd90 00007ffbaf497540 PreJIT System.Object.Equals(System.Object) 00007ffbaf961dc0 00007ffbaf497568 PreJIT System.Object.GetHashCode() 00007ffbaf93ce50 00007ffbaf497580 PreJIT System.Object.Finalize() 00007ffb51a50700 00007ffb51945bd8 JIT GenericUnsafeAlloc.GenericMemoryAllocator..cctor() 00007ffb51a507d0 00007ffb51945bd0 JIT GenericUnsafeAlloc.GenericMemoryAllocator..ctor() 00007ffb51a500d0 00007ffb51945b70 NONE GenericUnsafeAlloc.GenericMemoryAllocator.Allocate() 00007ffb51a500a0 00007ffb51945b08 NONE GenericUnsafeAlloc.GenericMemoryAllocator.VirtualProtect(IntPtr, UInt32, UInt32, UInt32 ByRef) 00007ffb51a500b0 00007ffb51945b50 NONE GenericUnsafeAlloc.GenericMemoryAllocator.UnlockPage(IntPtr) 00007ffb51a500c0 00007ffb51945b60 NONE GenericUnsafeAlloc.GenericMemoryAllocator.GetReferenceAsPointer(System.Object) 00007ffb51a50970 00007ffb51945b90 JIT GenericUnsafeAlloc.GenericMemoryAllocator.RawAllocate(IntPtr) 00007ffb51a500f0 00007ffb51945ba0 NONE GenericUnsafeAlloc.GenericMemoryAllocator.CreateObject() 00007ffb51a50100 00007ffb51945bb0 NONE GenericUnsafeAlloc.GenericMemoryAllocator.GetAllocMethodAddress() 00007ffb51a50110 00007ffb51945bc0 NONE GenericUnsafeAlloc.GenericMemoryAllocator.HijackNew() |
And now modify the allocation:
1 2 3 4 |
0:003> a 0x00007ffb`b1052510 00007ffb`b1052510 jmp 0x51a50970 jmp 0x51a50970 00007ffb`b1052515 |
Let’s verify:
1 2 3 4 5 6 7 8 9 10 11 12 |
0:003> !U 00007ffb`b1052510 Unmanaged code 00007ffb`b1052510 e95be49fa0 jmp 00007ffb`51a50970 00007ffb`b1052515 8b1c2598140000 mov ebx,dword ptr [1498h] 00007ffb`b105251c 4d8b5370 mov r10,qword ptr [r11+70h] 00007ffb`b1052520 498b4368 mov rax,qword ptr [r11+68h] 00007ffb`b1052524 4803d0 add rdx,rax 00007ffb`b1052527 493bd2 cmp rdx,r10 00007ffb`b105252a 7708 ja clr!JIT_TrialAllocSFastMP_InlineGetThread__PatchTLSOffset+0x21 (00007ffb`b1052534) 00007ffb`b105252c 49895368 mov qword ptr [r11+68h],rdx 00007ffb`b1052530 488908 mov qword ptr [rax],rcx 00007ffb`b1052533 c3 ret |
Jump is in place, we are good.
This code works for x86 and x64, both Debug and Release modes. Tested with Windows 10 x64 1703 and .NET Framework 4.5.2.