#!/usr/bin/env python
from struct import unpack
# Offsets
# Add other Windows versions here as time permits
# Vista values taken from Andreas Schuster's post:
# http://computer.forensikblog.de/en/2007/01/eprocess_6_0_6000_16386.html
offsets = {
"XPSP2": {
"EPROC_SIZE": 0x260, # Size of EPROCESS structure
"PDBA_OFFSET": 0x18, # Page Directory Base Address
"PEB_OFFSET": 0x1B0, # Offset to Process Environment Block
"VAD_ROOT_OFFSET": 0x11c, # Offset to VadRoot
"HANDLE_TABLE_OFFSET": 0x0c4, # Offset to Handle Table
"PEB_IMG_BASE": 0x08, # Offset within PEB of Image Base Address
"PEB_LDR_DATA": 0x0c # Offset within PEB of _PEB_LDR_DATA
},
"2KSP4": {
"EPROC_SIZE": 0x290,
"PDBA_OFFSET": 0x18,
"PEB_OFFSET": 0x1B0,
"VAD_ROOT_OFFSET": 0x194,
"HANDLE_TABLE_OFFSET": 0x128,
"PEB_IMG_BASE": 0x08
},
"XP": {
"EPROC_SIZE": 0x258,
"PDBA_OFFSET": 0x18,
"PEB_OFFSET": 0x1B0,
"VAD_ROOT_OFFSET": 0x11c,
"HANDLE_TABLE_OFFSET": 0x0c4,
"PEB_IMG_BASE": 0x08
},
"Vista": {
"EPROC_SIZE": 0x26C,
"PDBA_OFFSET": 0x18,
"PEB_OFFSET": 0x188,
"VAD_ROOT_OFFSET": 0x238,
"PEB_IMG_BASE": 0x08 # FIXME: UNVERIFIED (but probably correct)
}
}
# Mask for base address in Page Directory Entries as well as Page
# Table Entries
PT_MASK = 0xFFFFF000
PD_MASK_4M = 0xFFC00000
# Page directory flags
PAGE_PRESENT = 0x00000001
PAGE_4M = 0x00000080
# Virtual address masks
PD_ENTRY_MASK = 0xFFC00000
PT_ENTRY_MASK = 0x003FF000
PAGE_OFFSET_MASK_4K = 0x00000FFF
PAGE_OFFSET_MASK_4M = 0x003FFFFF
class Unsupported(Exception):
"""Exception thrown when requested operation is not suppored yet."""
class PagedOutException(Exception):
"""Empty exception class to indicate that requested page is swapped out."""
def read_range(memdump,pdba,start,end,step=4096,zero_invalid_pages=True):
"""Read the contents of a memory range. This is done a page at a time,
as adjacent virtual memory addresses may reference completely different
areas of physical memory.
Arguments:
memdump - file pointer to the memory dump image
pdba - address of the page directory
start - virtual address of the start of the region
end - virtual address of the end of the region
step - how many bytes to read at a time [default: a 4k page, or 4096 bytes]
zero_invalid_pages - fill any invalid (paged out) areas with zeroes
Returns: a string containing the contents of the memory range
"""
accum = ''
for addr in range(start, end, step):
if end - addr < step:
bytes_to_read = end - addr
else:
bytes_to_read = step
try:
real_addr = virt2phys(memdump,pdba,addr)
#print "DEBUG: reading memory at %08x virt, %08x phys" % (addr,real_addr)
memdump.seek(real_addr)
accum += memdump.read(bytes_to_read)
except PagedOutException:
if zero_invalid_pages:
#print "WARN: skipping memory page at 0x%08x as it is paged out." % addr
accum += ('\0' * bytes_to_read)
else:
raise PagedOutException
return accum
def virt2phys(memdump, pdba, addr):
"""Convert a virtual address to a physical one.
A file pointer to a memory dump, the offset to a page directory,
and a virtual address are required. The return value is a 32-bit
unsigned integer giving the physical address of the page."""
orig_pos = memdump.tell()
pd_num = (addr & PD_ENTRY_MASK) >> 22
pt_num = (addr & PT_ENTRY_MASK) >> 12
memdump.seek(pdba + (pd_num*4))
page_directory_entry = unpack("<L", memdump.read(4))[0]
# Check if page table is present
if (page_directory_entry & PAGE_PRESENT) == 0:
raise PagedOutException("Page table is paged out")
# Check if it is a 4M page
if (page_directory_entry & PAGE_4M):
page_offset = (addr & PAGE_OFFSET_MASK_4M)
page_base = page_directory_entry & PD_MASK_4M
return page_base + page_offset
#raise Unsupported("4M Pages are not yet supported")
else:
page_offset = (addr & PAGE_OFFSET_MASK_4K)
ptba = page_directory_entry & PT_MASK
memdump.seek(ptba + (pt_num*4))
page_table_entry = unpack("<L", memdump.read(4))[0]
if (page_table_entry & PAGE_PRESENT) == 0:
raise PagedOutException("Memory page is paged out")
page_base = page_table_entry & PT_MASK
memdump.seek(orig_pos)
return page_base + page_offset