Difference between revisions of "RAM analysis"

From YobiWiki
Jump to navigation Jump to search
Line 94: Line 94:
 
(phoff,) = struct.unpack('<Q', base.read(0x20, 8))
 
(phoff,) = struct.unpack('<Q', base.read(0x20, 8))
 
(phentsize, phnum) = struct.unpack('<HH', base.read(0x36, 4))
 
(phentsize, phnum) = struct.unpack('<HH', base.read(0x36, 4))
found = False
+
foundnotevbcore = False
  +
foundloadfirst = False
 
for phptr in range(phoff, phoff + (phentsize * phnum), phentsize):
 
for phptr in range(phoff, phoff + (phentsize * phnum), phentsize):
 
(stype, flags, offset, vaddr, paddr, filesz, memsz, align) = struct.unpack('<IIQQQQQQ', base.read(phptr, phentsize))
 
(stype, flags, offset, vaddr, paddr, filesz, memsz, align) = struct.unpack('<IIQQQQQQ', base.read(phptr, phentsize))
# Look for first LOAD segment
+
# NOTE segment?
if stype != 1:
+
if ((not foundnotevbcore) and (stype == 4)):
  +
(namesz, descsz, ntype) = struct.unpack('<III', base.read(offset, 12))
  +
if (base.read(offset+12, namesz) == 'VBCORE'):
  +
foundnotevbcore = True
  +
self.as_assert((descsz == 24), 'Abnormal VBCORE size')
  +
# parsing DBGFCOREDESCRIPTOR:
  +
(magic, fmtvers, selfsize, vbvers, vbrev, ncpus) = struct.unpack('<IIIIII', base.read(offset+12+(((namesz>>3)+1)<<3), descsz))
  +
self.as_assert((magic == 0xc01ac0de), 'Could not find VBox core magic signature')
  +
self.as_assert((fmtvers == 0x00010000), 'Unknown VBox core format version')
  +
# For info: VirtualBox version and revision are available in vbvers & vbrev
 
continue
 
continue
found = True
+
# First LOAD segment?
self.moffset = offset
+
if ((not foundloadfirst) and (stype == 1)):
self.msize = filesz
+
foundloadfirst = True
break
+
self.moffset = offset
  +
self.msize = filesz
self.as_assert(found, 'ELF error: no segment LOAD found')
 
  +
self.as_assert(foundnotevbcore, 'ELF error: did not find any segment NOTE VBCORE')
 
self.as_assert(foundloadfirst, 'ELF error: did not find any segment LOAD')
 
standard.FileAddressSpace.__init__(self, base, config, layered = True, **kwargs)
 
standard.FileAddressSpace.__init__(self, base, config, layered = True, **kwargs)
 
self.fsize = min(self.msize, self.fsize - self.moffset)
 
self.fsize = min(self.msize, self.fsize - self.moffset)

Revision as of 16:56, 9 February 2012

Misc notes on physical RAM analysis

Links

  • Volatility, for memory analysis of mainly Windows platforms (Linux support is in beta)
  • Volatilitux, for memory analysis of linux and Android platforms
  • Passware, a commercial tool mostly aiming at recovering passwords, including from memory dumps

RAM dump with VirtualBox

VMWare snapshots contain a .vmem which is basically a RAM dump, but for VirtualBox, it's not that easy...
Snapshot .sav files contain all machine state including memory but in a hardly exploitable format (even if VirtualBox is open-source...) and the few people having looked in that direction didn't succeed.
But there is another way to get a RAM dump with VirtualBox (I'm not talking about tools running in the guest as I don't want to interfere at all with the target):
See Ch8 of the manual about debugvm capabilities:
With dumpguestcore --filename <name>, you can create a system dump of the running VM, which will be written into the given file. This file will have the standard ELF core format (with custom sections);
The dump format itself is described here
So let's try:

  • VM has to run in order to be able to make the RAM dump, then:
$ vboxmanage debugvm "Win7" dumpguestcore --filename test.elf
  • We're interested into the first LOAD section, that's where main memory reference is:
$ readelf --program-headers test.elf|grep -m1 -A1 LOAD
  LOAD           0x0000000000000720 0x0000000000000000 0x0000000000000000
                 0x0000000040000000 0x0000000040000000  R      0

If I unwrap the info and label it, we have:

 Type           Offset             VirtAddr           PhysAddr           FileSiz            MemSiz              Flags  Align
 LOAD           0x0000000000000720 0x0000000000000000 0x0000000000000000 0x0000000040000000 0x0000000040000000  R      0

So memory dump is in test.elf, starting at offset 0x720 and counting 0x40000000 bytes (1024Mb)

Alternatively, using objdump:

$ objdump -h test.elf|egrep -w "(Idx|load1)"
Idx Name          Size      VMA               LMA               File off  Algn
  1 load1         40000000  0000000000000000  0000000000000000  00000720  2**0


Let's extract the RAM. Computations deserve some automation but doing it by hand (32*57 = 0x720, 32*33554432 = 0x40000000):

$ dd if=test.elf of=test.raw bs=32 skip=57 count=33554432


Now using volatility on the obtained file:

$ ./vol.py -f test.raw --profile=Win7SP1x86 pslist
Volatile Systems Volatility Framework 2.0
 Offset(V)  Name                 PID    PPID   Thds   Hnds   Time 
---------- -------------------- ------ ------ ------ ------ ------------------- 
0x83d33d40 System                    4      0     69    506 2012-02-07 16:06:19       
0x84c5c758 smss.exe                228      4      2     29 2012-02-07 16:06:19       
0x85475030 csrss.exe               304    296      9    398 2012-02-07 16:06:30       
0x83d9b108 wininit.exe             340    296      3     76 2012-02-07 16:06:41       
0x83d70528 csrss.exe               352    332      7    103 2012-02-07 16:06:41       
0x84d9c568 winlogon.exe            392    332      5    111 2012-02-07 16:06:42       

etc

Volatility with VirtualBox

We just saw how to extract a RAM image but it would be nice if Volatility was able to read directly the VirtualBox core dump.
Thanks to the support of Mike Auty I managed to write a plugin for Volatility.
You can download it [{{#file: vboxelf.py}} as vboxelf.py]

# Volatility
# Copyright (C) 2007,2008 Volatile Systems
# Copyright (C) 2005,2006,2007 4tphi Research
#
# Authors: 
# {npetroni,awalters}@4tphi.net (Nick Petroni and AAron Walters)
# phil@teuwen.org (Philippe Teuwen)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details. 
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
#

""" An AS for processing VirtualBox ELF64 coredumps """
# References:
# VirtualBox core format: http://www.virtualbox.org/manual/ch12.html#guestcoreformat
# ELF64 format: http://downloads.openwatcom.org/ftp/devel/docs/elf-64-gen.pdf

import struct
import volatility.plugins.addrspaces.standard as standard

#pylint: disable-msg=C0111

class VirtualBoxCoreDumpElf64(standard.FileAddressSpace):
    """ This AS supports VirtualBox ELF64 coredump format """
    order = 30
    def __init__(self, base, config, **kwargs):
        ## We must have an AS below us
        self.as_assert(base, "No base Address Space")
        # Testing for ELF64, little-endian:
        self.as_assert((base.read(0, 6) == '\x7fELF\x02\x01'), "ELF64 Header signature invalid")
        self.as_assert((base.read(0x10, 2) == '\x04\x00'), "ELF64 type is not a Core file")
        (phoff,) = struct.unpack('<Q', base.read(0x20, 8))
        (phentsize, phnum) = struct.unpack('<HH', base.read(0x36, 4))
        foundnotevbcore = False
        foundloadfirst = False
        for phptr in range(phoff, phoff + (phentsize * phnum), phentsize):
            (stype, flags, offset, vaddr, paddr, filesz, memsz, align) = struct.unpack('<IIQQQQQQ', base.read(phptr, phentsize))
            # NOTE segment?
            if ((not foundnotevbcore) and (stype == 4)):
                (namesz, descsz, ntype) = struct.unpack('<III', base.read(offset, 12))
                if (base.read(offset+12, namesz) == 'VBCORE'):
                    foundnotevbcore = True
                    self.as_assert((descsz == 24), 'Abnormal VBCORE size')
                    # parsing DBGFCOREDESCRIPTOR:
                    (magic, fmtvers, selfsize, vbvers, vbrev, ncpus) = struct.unpack('<IIIIII', base.read(offset+12+(((namesz>>3)+1)<<3), descsz))
                    self.as_assert((magic == 0xc01ac0de), 'Could not find VBox core magic signature')
                    self.as_assert((fmtvers == 0x00010000), 'Unknown VBox core format version')
                    # For info: VirtualBox version and revision are available in vbvers & vbrev
                continue
            # First LOAD segment?
            if ((not foundloadfirst) and (stype == 1)):
                foundloadfirst = True
                self.moffset = offset
                self.msize = filesz
        self.as_assert(foundnotevbcore, 'ELF error: did not find any segment NOTE VBCORE')
        self.as_assert(foundloadfirst, 'ELF error: did not find any segment LOAD')
        standard.FileAddressSpace.__init__(self, base, config, layered = True, **kwargs)
        self.fsize = min(self.msize, self.fsize - self.moffset)

    def read(self, addr, length):
        return self.base.read(addr + self.moffset, length)

    def zread(self, addr, length):
        return self.base.zread(addr + self.moffset, length)

    def read_long(self, addr):
        return self.base.read_long(addr + self.moffset)

    def write(self, addr, data):
        return self.base.write(addr + self.moffset, data)

    def is_valid_address(self, addr):
        return self.base.is_valid_address(addr + self.moffset)

To be placed in your Volatility installation under plugins/addrspaces