Analyzing Heap Objects
Analyzing Heap Objects
Hi all,
While preparing for my Advanced exploit dev course at Derbycon, I’ve been playing
with heap allocation primitives in IE. One of the things that causes some frustration
(or, at least, tends to slow me down during the research) is the ability to quickly
identify objects that may be useful. After all, I’m trying to find objects that contain
arbitrary data, or pointers to arbitrary data, and it’s not always easy to do so because of
the noise.
I decided to add a few new features to mona.py, that should allow you to find
interesting objects in a faster way. The new features are only available under WinDBG.
To get the latest version of mona, simply run !py mona up. Since I also upgraded to
the latest version of pykd, you may have to update pykd.pyd as well before you can run
the latest version of mona. (You should see update instructions in the WinDBG log
window when you try to run mona with an outdated version of pykd)
dumpobj (do)
The first new feature is "dumpobj". This mona.py command will dump the contents of
an object and provide (hopefully) useful information about the contents. The
command takes the following arguments:
Arguments:
-a
: Address of object
-s : Size of object (default value: 0x28 or size of
chunk)
Optional arguments:
-l : Recursively dump objects
-m : Size for recursive objects (default value: 0x28)
As you can see in the output of !py mona help dumpobj above, we need to provide at
least 2 arguments:
-a
: the start location (address of the object, but you can specifiy any location you want)
-s : the size of the object. If you don’t specify the -s argument, mona will attempt to
determine the size of the object. If that is not possible, mona will dump 0x28 bytes of
the object.
Additionally, you can tell mona to dump linked objects as well. Argument -l takes a
number, which refers to the number of levels for the recursive dump. In order to
somewhat limit the size of the output (and for performance reasons), only the first 0x28
bytes of the linked objects will be printed (unless you use argument -m to overrule this
behavior).
Of course, it is quite trivial to dump the contents of an object in WinDBG. The dds or
dc commands will print out objects and show some information about its contents. In
some cases, the output of dds/dc is not sufficient and it would require some additional
work to further analyze the object and optional objects that are linked inside this object.
Let’s look at an example. Let’s say we have a 0x78 byte object at 0x023a1bc0. Of
course, we can dump the contents of the object using native WinDBG commands:
Nice. We can see all kinds of things – values that appear to be pointers, nulls, and
some other "garbage". Hard to tell what it is without looking at each value individually.
With mona, we can dump the same object, and mona will attempt to gather more
information about each dword in the object:
----------------------------------------------------
[+] Dumping object at 0x023a1bc0, 0x78 bytes
Apparently some of the values in the object point at strings (ASCII and Unicode),
another point appears to link to another object
(ADVAPI32!g_CodeLevelObjTable+0x4). This is a lot more useful than dds or dc. But
we can make it even better. We can tell mona to automatically print out linked objects
as well, up to any level deep. Let’s repeat the mona command, this time asking for
linked objects up to one level deep:
----------------------------------------------------
[+] Dumping object at 0x023a1bc0, 0x78 bytes
[+] Also dumping up to 1 levels deep, max size of nested
objects: 0x28 bytes
As you can see in the output above, mona determined that the source object contained
references to 2 linked objects, and decided to dump those linked object as well. It’s
important to know that mona won’t consider strings (ASCII or Unicode) as objects,
because mona already shows the strings, even if they are referenced inside the
object. The output of the dumpobj command is written to a text file called
"dumpobj.txt".
includes the output of mona dumpobj (without printing recursive objects). If you want
to understand what exactly a given address is, you’ll get something like this:
Data : 30 1d 3a 02 18 18 3a 02 00 00 00 00 3c 1d 3a 02 24
18 3a 02 0d f0 ad ba 00 00 02 00 01 00 00 00 ...
----------------------------------------------------
[+] Dumping object at 0x023a1bc0, 0x90 bytes
[+] Disassembly:
Instruction at 023a1bc0 : XOR BYTE PTR
dumplog (dl)
It is clear that the dumpobj command will make it easier to visualize important
information inside an object. This is certainly helpful if you already know the starting
object. What if you have been logging all Heap allocations and free operations in the
application and storing the output in a log file? Even a few lines of javascript code can
be quite noisy from a Heap perspective, making it less trivial to identify interesting
objects.
To make our lives easier, I decided to implement "dumplog", which will parse a log file
(based on a certain syntax) and perform a "dumpobj" on each object that has been
allocated, but not freed. In the current version, dumplog will not dump linked objects,
but I plan on adding this feature soon. (probably tomorrow)
Dumplog requires a proper setup. We need to tell WinDBG to create a log file that
follows a specific convention, and we obviously must run mona dumplog in the same
debug session (to make sure the logged allocations and free operations are still
relevant).
The idea is to log all Heap allocations and free operations to a log file. In WinDBG this
can be achieved using the following steps:
Before running the process and triggering the alloc/free operations that you want to
capture and analyze, tell WinDBG to write the output of the log window to a text file:
.logclose
.logopen c:\\allocs.txt
These 2 breakpoints will print a message to the WinDBG log window each time
RtlAllocateHeap and RtlFreeHeap are called, printing out valuable information about the
API call. It’s important to stick to this format, but you are free to add more text to the
end of the message string. With these 2 breakpoints active, and WinDBG configured
to start writing the output of the log window to a text file, we can run the application.
When you’re ready to do analysis, break WinDBG. Don’t close it at this point, but close
the log file using the .logclose command.
We can now use mona to parse the log file, find the objects that have been allocated
and not freed, and perform a mona dumpobj on each of those objects.
cheers
-corelanc0d3r
Update (Aug 17) – the "dumplog" feature now supports dumping linked objects
(option -l). Linked objects will have a reference to the parent object. (This applies to
dumpobj as well).