Thursday, July 15, 2010

GDI Utilities: Taking Screenshots of Memory Dumps

I've posted about this before (twice!), but somehow never gotten around to releasing functioning code. Here (click), for your downloading pleasure, is a set of plugins designed to extract information about on-screen (graphical) windows from Windows XP SP2/3 memory images. This includes:
  • window_list - give a text listing of the window hierarchy, with each window's on-screen coordinates, current style, and its class (Button, Window, etc.). Here's some example output to whet your appetite.
  • screenshot - save a wireframe "screenshot" of the on-screen windows in a memory image. See later in this post for some examples. Requires PIL.
  • wndmon - continuously monitor a memory image and provide an updating view of the on-screen windows. Works best in a live environment, e.g. with XenAccess and PyXa. Requires PyGame. (This is what I used for the video demo).
All three plugins require the distorm disassembly library to work. I had a bit of trouble getting it to work under Linux, so here's the steps required so you don't have to go through the pain:
  1. Get distorm3 from its Google Code site.
  2. Go into build/linux and type "make".
  3. Copy the resulting libdistorm3.so into the Python directory.
  4. Rename the Python directory to "distorm" and move it somewhere in your Python path (I use Debian, and found that /usr/local/lib/python2.6/dist-packages/ worked well).
Hopefully this is a bit simpler under Windows, but I don't have a Windows box handy so I can't test that at the moment.

Have fun with the code. If you go exploring in the source, you may find some interesting things -- there's more functionality there than is exposed through the plugins, including some functions and data structures that can extract HTML content from IE in memory... ;)

Anyway, to wrap things up, here's an example of the output from the screenshot plugin, running on the two NIST memory images:

From the 6/25 image:



From the 7/4 image:



And that, my friends, is the power of memory analysis.

9 comments:

Paul Bobby said...

This is the error I get when running

$ python volatility window_list -f ram.dd

Traceback (most recent call last):
File "volatility", line 219, in
main()
File "volatility", line 215, in main
command.execute()
File "memory_plugins/windowlist.py", line 142, in execute
UserAtomTableHandle = read_value(addr_space, 'pointer', atom_table_off)
File "/cygdrive/d/Downloads/Volatility-1.3_Beta/forensics/object.py", line 71, in read_value
AttributeError: 'NoneType' object has no attribute 'read'

Paul Bobby said...
This comment has been removed by the author.
Paul Bobby said...

I get the error because I was trying this against an SP3 memory image and not SP2

hinoue said...

I'm trying this against SP3 as well. Does anyone know the offsets?

Sherif Eldeeb said...

root@bt:/pentest/forensics/volatility# ./volatility screenshot -f ~/CH3/Windows\ XP\ Professional.vmem
Traceback (most recent call last):
File "./volatility", line 219, in
main()
File "./volatility", line 215, in main
command.execute()
File "memory_plugins/screenshot.py", line 113, in execute
atom_table_off = find_atomtable(addr_space, types, symtab, theProfile)
File "/pentest/forensics/volatility/forensics/win32/findatomtable.py", line 94, in find_atomtable
uaa_addr = find_UserAddAtom(thrd.vm, reg_addr)
File "/pentest/forensics/volatility/forensics/win32/findatomtable.py", line 55, in find_UserAddAtom
asm = Decode(reg_addr, fn_data, Decode32Bits)
File "/usr/lib/python2.5/distorm/__init__.py", line 440, in Decode
return list( DecodeGenerator(offset, code, type) )
File "/usr/lib/python2.5/distorm/__init__.py", line 408, in DecodeGenerator
p_code = byref(code_buf, instruction_off)
TypeError: byref() takes exactly one argument (2 given)

=======================
Steps taken - linux "BackTrack4R2"
1- downloaded http://distorm.googlecode.com/files/distorm3.zip
2- unzip && cd include && cp * ../
I did that because of error complaining about missing files
3- cd ../make/linux && make
the result was libdistorm3.so
4- put libdistorm3.so & __init__.py in one directory called "distorm" && cp /usr/lib/python2.5/

thanks in advance for the help, and thanks for your excellent tools that have proven to be life-savers...

Sherif Eldeeb said...

Sorry, was using python 2.5, and the tool asks for 2.6 minimum.

It works! :)

thanks again, and again, your tool made me look good...

Mic said...

Is there a new version for Volatility 2.0 available?

Mic said...

Using Volatility 1.3 I got window_list to work: Great Stuff!
I use a Win7 system and the solution for getting distorm imported was copying

c:\Python27\Lib\site-packages\distorm3\ to

c:\Python27\Lib\site-packages\distorm\

Sadly screenshot as the most impressive feature caused an error:

c:\Micha\Forensics\Volatility-1.4>python volatility screenshot -f D:\X-Ways-Images\RAMHans_RipperFRES.001
Saving screenshot to 624.81d64980.png
Traceback (most recent call last):
File "volatility", line 219, in
main()
File "volatility", line 215, in main
command.execute()
File "c:\Micha\Forensics\Volatility-1.4\memory_plugins/screenshot.py", line 152, in execute
gdi.traverse_windows(win, fun=draw_win, do_node=gdi.is_visible)
File "c:\Micha\Forensics\Volatility-1.4\forensics\win32\gdi.py", line 306, in traverse_windows
traverse_windows(cur.pChildWindow,fun=fun,do_node=do_node,level=level+1)
File "c:\Micha\Forensics\Volatility-1.4\forensics\win32\gdi.py", line 306, in traverse_windows
traverse_windows(cur.pChildWindow,fun=fun,do_node=do_node,level=level+1)
File "c:\Micha\Forensics\Volatility-1.4\forensics\win32\gdi.py", line 303, in traverse_windows
fun(cur, level)
File "c:\Micha\Forensics\Volatility-1.4\memory_plugins/screenshot.py", line 138, in draw_win
text = gdi.get_editbox_text(win)
File "c:\Micha\Forensics\Volatility-1.4\forensics\win32\gdi.py", line 427, in get_editbox_text
return ed.hBuf
File "c:\Micha\Forensics\Volatility-1.4\forensics\object2.py", line 96, in __getattribute__
return object.__getattribute__(self, attr)
File "c:\Micha\Forensics\Volatility-1.4\memory_objects\Windows/gdi.py", line 163, in getBuf
s = RtlRunDecodeUnicodeString(self.encKey, s)
File "c:\Micha\Forensics\Volatility-1.4\forensics\object2.py", line 109, in __getattribute__
off = self.get_member_offset(attr, relative=True)
File "c:\Micha\Forensics\Volatility-1.4\forensics\object2.py", line 200, in get_member_offset
thisMemberType = self.type.get_member(memname)
File "c:\Micha\Forensics\Volatility-1.4\forensics\object2.py", line 413, in get_member
return self.members[memname]
KeyError: 'encKey'

Any help appreciated...

Mic said...

With me as his guinea pig Brendan he has checked out a typo (BEnckey instead of encKey) and I have maneged it to port screenshot to windows: Cool stuff. THX a lot or the support.
@Brendan: The Vol2.0-version will be launched this week, right? ;-)