0% found this document useful (0 votes)
80 views

Analyzing Heap Objects

The document introduces new features in the mona.py tool to help identify interesting objects in heap memory dumps. The dumpobj command dumps the contents of an object and provides useful information about values like pointers to strings. Specifying the -l argument directs mona.py to recursively dump linked objects up to a specified level of depth, helping to analyze relationships between objects in memory. In an example, dumpobj reveals that values in an object point to strings and another object, providing more useful information than typical WinDBG commands.

Uploaded by

ooo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
80 views

Analyzing Heap Objects

The document introduces new features in the mona.py tool to help identify interesting objects in heap memory dumps. The dumpobj command dumps the contents of an object and provides useful information about values like pointers to strings. Specifying the -l argument directs mona.py to recursively dump linked objects up to a specified level of depth, helping to analyze relationships between objects in memory. In an example, dumpobj reveals that values in an object point to strings and another object, providing more useful information than typical WinDBG commands.

Uploaded by

ooo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 14

Introduction

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:

Usage of command 'dumpobj' :


-----------------------------
Dump the contents of an object.

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:

0:001> dds 0x023a1bc0 L 0x78/4


023a1bc0 023a1d30
023a1bc4 023a1818
023a1bc8 00000000
023a1bcc 023a1d3c
023a1bd0 023a1824
023a1bd4 baadf00d
023a1bd8 00020000
023a1bdc 00000001
023a1be0 00160014
023a1be4 023a1a38
023a1be8 013a0138
023a1bec 023a1a68
023a1bf0 00000000
023a1bf4 00000001
023a1bf8 023a18a8
023a1bfc 00000000
023a1c00 00000000
023a1c04 00000007
023a1c08 00000007
023a1c0c 023a18d0
023a1c10 00000000
023a1c14 00000000
023a1c18 00000000
023a1c1c 00000000
023a1c20 00000000
023a1c24 00000000
023a1c28 00000000
023a1c2c 00000000
023a1c30 00000000
023a1c34 00000000

0:001> dc 0x023a1bc0 L 0x78/4


023a1bc0 023a1d30 023a1818 00000000 023a1d3c 0.:...:.....<.:.
023a1bd0 023a1824 baadf00d 00020000 00000001 $.:.............
023a1be0 00160014 023a1a38 013a0138 023a1a68 ....8.:.8.:.h.:.
023a1bf0 00000000 00000001 023a18a8 00000000 ..........:.....
023a1c00 00000000 00000007 00000007 023a18d0 ..............:.
023a1c10 00000000 00000000 00000000 00000000 ................
023a1c20 00000000 00000000 00000000 00000000 ................
023a1c30 00000000 00000000 ........

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:

0:001> !py mona do -a 0x023a1bc0


Hold on...
[+] No size specified, checking if address is part of known heap
chunk
Address found in chunk 0x023a1bb8, heap 0x00240000,
(user)size 0x78

----------------------------------------------------
[+] Dumping object at 0x023a1bc0, 0x78 bytes

[+] Preparing output file 'dumpobj.txt'


- (Re)setting logfile c:\logs\HeapAlloc2\dumpobj.txt
[+] Generating module info table, hang on...
- Processing modules
- Done. Let's rock 'n roll.

>> Object at 0x023a1bc0 (0x78 bytes):


Offset Address Contents Info
------ ------- -------- -----
+00 0x023a1bc0 | 0x023a1d30 (Heap) ptr to ASCII '0::'
+04 0x023a1bc4 | 0x023a1818 (Heap) ptr to ASCII ':'
+08 0x023a1bc8 | 0x00000000
+0c 0x023a1bcc | 0x023a1d3c (Heap) ptr to 0x77e46464 :
ADVAPI32!g_CodeLevelObjTable+0x4
+10 0x023a1bd0 | 0x023a1824 (Heap) ptr to ASCII ':'
+14 0x023a1bd4 | 0xbaadf00d
+18 0x023a1bd8 | 0x00020000 = UNICODE ' '
+1c 0x023a1bdc | 0x00000001
+20 0x023a1be0 | 0x00160014 = UNICODE ''
+24 0x023a1be4 | 0x023a1a38 (Heap) ptr to UNICODE 'Basic
User'
+28 0x023a1be8 | 0x013a0138 (Heap) ptr to ASCII
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...'
+2c 0x023a1bec | 0x023a1a68 (Heap) ptr to UNICODE 'Allows
programs to execute as a user that does not have Administrator
or Power User access rights, but can still access resouces
accessible by normal users.'
+30 0x023a1bf0 | 0x00000000
+34 0x023a1bf4 | 0x00000001
+38 0x023a1bf8 | 0x023a18a8
+3c 0x023a1bfc | 0x00000000
+40 0x023a1c00 | 0x00000000
+44 0x023a1c04 | 0x00000007
+48 0x023a1c08 | 0x00000007
+4c 0x023a1c0c | 0x023a18d0 (Heap) ptr to ASCII ' :H:p:'
+50 0x023a1c10 | 0x00000000
+54 0x023a1c14 | 0x00000000
+58 0x023a1c18 | 0x00000000
+5c 0x023a1c1c | 0x00000000
+60 0x023a1c20 | 0x00000000
+64 0x023a1c24 | 0x00000000
+68 0x023a1c28 | 0x00000000
+6c 0x023a1c2c | 0x00000000
+70 0x023a1c30 | 0x00000000
+74 0x023a1c34 | 0x00000000

[+] This mona.py action took 0:00:00.579000

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:

0:001> !py mona do -a 0x023a1bc0 -l 1


Hold on...
[+] No size specified, checking if address is part of known heap
chunk
Address found in chunk 0x023a1bb8, heap 0x00240000,
(user)size 0x78

----------------------------------------------------
[+] Dumping object at 0x023a1bc0, 0x78 bytes
[+] Also dumping up to 1 levels deep, max size of nested
objects: 0x28 bytes

[+] Preparing output file 'dumpobj.txt'


- (Re)setting logfile c:\logs\HeapAlloc2\dumpobj.txt
[+] Generating module info table, hang on...
- Processing modules
- Done. Let's rock 'n roll.

>> Object at 0x023a1bc0 (0x78 bytes):


Offset Address Contents Info
------ ------- -------- -----
+00 0x023a1bc0 | 0x023a1d30 (Heap) ptr to ASCII '0::'
+04 0x023a1bc4 | 0x023a1818 (Heap) ptr to ASCII ':'
+08 0x023a1bc8 | 0x00000000
+0c 0x023a1bcc | 0x023a1d3c (Heap) ptr to 0x77e46464 :
ADVAPI32!g_CodeLevelObjTable+0x4
+10 0x023a1bd0 | 0x023a1824 (Heap) ptr to ASCII ':'
+14 0x023a1bd4 | 0xbaadf00d
+18 0x023a1bd8 | 0x00020000 = UNICODE ' '
+1c 0x023a1bdc | 0x00000001
+20 0x023a1be0 | 0x00160014 = UNICODE ''
+24 0x023a1be4 | 0x023a1a38 (Heap) ptr to UNICODE 'Basic
User'
+28 0x023a1be8 | 0x013a0138 (Heap) ptr to ASCII
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAA...'
+2c 0x023a1bec | 0x023a1a68 (Heap) ptr to UNICODE 'Allows
programs to execute as a user that does not have Administrator
or Power User access rights, but can still access resouces
accessible by normal users.'
+30 0x023a1bf0 | 0x00000000
+34 0x023a1bf4 | 0x00000001
+38 0x023a1bf8 | 0x023a18a8 (Heap) ptr to 0x00000101 :
+3c 0x023a1bfc | 0x00000000
+40 0x023a1c00 | 0x00000000
+44 0x023a1c04 | 0x00000007
+48 0x023a1c08 | 0x00000007
+4c 0x023a1c0c | 0x023a18d0 (Heap) ptr to ASCII ' :H:p:'
+50 0x023a1c10 | 0x00000000
+54 0x023a1c14 | 0x00000000
+58 0x023a1c18 | 0x00000000
+5c 0x023a1c1c | 0x00000000
+60 0x023a1c20 | 0x00000000
+64 0x023a1c24 | 0x00000000
+68 0x023a1c28 | 0x00000000
+6c 0x023a1c2c | 0x00000000
+70 0x023a1c30 | 0x00000000
+74 0x023a1c34 | 0x00000000

>> Object at 0x023a1d3c (0x28 bytes):


Offset Address Contents Info
------ ------- -------- -----
+00 0x023a1d3c | 0x77e46464
ADVAPI32!g_CodeLevelObjTable+0x4
+04 0x023a1d40 | 0x023a1bcc (Heap) ptr to ASCII '<:$:'
+08 0x023a1d44 | 0xbaadf00d
+0c 0x023a1d48 | 0x00040000 = UNICODE ' '
+10 0x023a1d4c | 0x00000101
+14 0x023a1d50 | 0x001a0018 = UNICODE ''
+18 0x023a1d54 | 0x023a1c50 (Heap) ptr to UNICODE
'Unrestricted'
+1c 0x023a1d58 | 0x0090008e (Heap) ptr to ASCII
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAA...'
+20 0x023a1d5c | 0x023a1c88 (Heap) ptr to UNICODE 'Software
access rights are determined by the access rights of the user.'
+24 0x023a1d60 | 0x00000000

>> Object at 0x023a18a8 (0x28 bytes):


Offset Address Contents Info
------ ------- -------- -----
+00 0x023a18a8 | 0x00000101
+04 0x023a18ac | 0x05000000
+08 0x023a18b0 | 0x0000000a
+0c 0x023a18b4 | 0xabababab
+10 0x023a18b8 | 0xabababab
+14 0x023a18bc | 0xfeeefeee
+18 0x023a18c0 | 0x00000000
+1c 0x023a18c4 | 0x00000000
+20 0x023a18c8 | 0x0005000a = UNICODE ''
+24 0x023a18cc | 0x051807c2

[+] This mona.py action took 0:00:00.640000

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".

For your info, the output of !mona info -a

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:

0:001> !py mona info -a 0x023a1bc0


Hold on...
[+] Generating module info table, hang on...
- Processing modules
- Done. Let's rock 'n roll.
[+] NtGlobalFlag: 0x00000070
0x00000040 : +hpc - Enable Heap Parameter Checking
0x00000020 : +hfc - Enable Heap Free Checking
0x00000010 : +htc - Enable Heap Tail Checking
[+] Information about address 0x023a1bc0
{PAGE_READWRITE}
Address is part of page 0x013c0000 - 0x023a2000
This address resides in the heap

Address 0x023a1bc0 found in


_HEAP @ 00240000, Segment @ 013c0040
( bytes )
(bytes)
HEAP_ENTRY Size PrevSize Unused Flags UserPtr
UserSize Remaining - state
023a1bb8 00000090 00000158 00000018 [07] 023a1bc0
00000078 00000010 Fill pattern,Extra present,Busy (hex)
00000144 00000344 00000024
00000120 00000016 Fill pattern,Extra present,Busy (dec)

Chunk header size: 0x8 (8)


Size initial allocation request: 0x78 (120)
Total space for data: 0x88 (136)
Delta between initial size and total space for data: 0x10
(16)

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

[+] Preparing output file 'dumpobj.txt'


- (Re)setting logfile c:\logs\HeapAlloc2\dumpobj.txt
>> Object at 0x023a1bc0 (0x90 bytes):
Offset Address Contents Info
------ ------- -------- -----
+00 0x023a1bc0 | 0x023a1d30 (Heap) ptr to ASCII '0::'
+04 0x023a1bc4 | 0x023a1818 (Heap) ptr to ASCII ':'
+08 0x023a1bc8 | 0x00000000
+0c 0x023a1bcc | 0x023a1d3c (Heap) ptr to 0x77e46464 :
ADVAPI32!g_CodeLevelObjTable+0x4
+10 0x023a1bd0 | 0x023a1824 (Heap) ptr to ASCII ':'
+14 0x023a1bd4 | 0xbaadf00d
+18 0x023a1bd8 | 0x00020000 = UNICODE ' '
+1c 0x023a1bdc | 0x00000001
+20 0x023a1be0 | 0x00160014 = UNICODE ''
+24 0x023a1be4 | 0x023a1a38 (Heap) ptr to UNICODE 'Basic
User'
+28 0x023a1be8 | 0x013a0138 (Heap) ptr to ASCII
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAA...'
+2c 0x023a1bec | 0x023a1a68 (Heap) ptr to UNICODE 'Allows
programs to execute as a user that does not have Administrator
or Power User access rights, but can still access resouces
accessible by normal users.'
+30 0x023a1bf0 | 0x00000000
+34 0x023a1bf4 | 0x00000001
+38 0x023a1bf8 | 0x023a18a8 (Heap) ptr to 0x00000101 :
+3c 0x023a1bfc | 0x00000000
+40 0x023a1c00 | 0x00000000
+44 0x023a1c04 | 0x00000007
+48 0x023a1c08 | 0x00000007
+4c 0x023a1c0c | 0x023a18d0 (Heap) ptr to ASCII ' :H:p:'
+50 0x023a1c10 | 0x00000000
+54 0x023a1c14 | 0x00000000
+58 0x023a1c18 | 0x00000000
+5c 0x023a1c1c | 0x00000000
+60 0x023a1c20 | 0x00000000
+64 0x023a1c24 | 0x00000000
+68 0x023a1c28 | 0x00000000
+6c 0x023a1c2c | 0x00000000
+70 0x023a1c30 | 0x00000000
+74 0x023a1c34 | 0x00000000
+78 0x023a1c38 | 0xabababab
+7c 0x023a1c3c | 0xabababab
+80 0x023a1c40 | 0x00000000
+84 0x023a1c44 | 0x00000000
+88 0x023a1c48 | 0x00120007 = UNICODE ''
+8c 0x023a1c4c | 0x051e0752

[+] 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 output of "!py mona help dumplog" shows this:

Usage of command 'dl' :


------------------------
Dump all objects recorded in an alloc/free log
Note: dumplog will only dump objects that have not been freed in
the same logfile.
Expected syntax for log entries:
Alloc : 'alloc(size in hex) = address'
Free : 'free(address)'
Additional text after the alloc & free info is fine.
Just make sure the syntax matches exactly with the examples
above.
Arguments:
-f <path/to/logfile> : Full path to the logfile

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

Next, set up 2 logging breakpoints:

bp !ntdll + 0002e12c ".printf \"alloc(0x%x) = 0x%p\",


poi(esp+c), eax; .echo; g"
bp ntdll!RtlFreeHeap "j (poi(esp+c)!=0) '.printf \"free(0x%p)\",
poi(esp+c); .echo; g'; 'g';"

(based on a recent version of kernel32.dll, Windows 7 SP1).

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.

!py mona dl -f c:\allocs.txt

The output will be written to dump_alloc_free.txt

I hope you’ll enjoy these 2 new features.

Stay safe & take care

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).

You might also like