mirror of
https://github.com/volatilityfoundation/volatility
synced 2026-06-08 18:04:46 +00:00
258 lines
9.4 KiB
Python
258 lines
9.4 KiB
Python
# Volatility
|
|
# Copyright (C) 2013 Volatility Foundation
|
|
#
|
|
# Authors:
|
|
# Mike Auty
|
|
#
|
|
# This file is part of Volatility.
|
|
#
|
|
# Volatility 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.
|
|
#
|
|
# Volatility 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 Volatility. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
import volatility.plugins.addrspaces.paged as paged
|
|
import volatility.obj as obj
|
|
import struct
|
|
|
|
|
|
ptrs_page = 2048
|
|
entry_size = 8
|
|
pde_shift = 21
|
|
ptrs_per_pde = 512
|
|
page_shift = 12
|
|
ptrs_per_pae_pgd = 512
|
|
ptrs_per_pae_pte = 512
|
|
|
|
class AMD64PagedMemory(paged.AbstractWritablePagedMemory):
|
|
""" Standard AMD 64-bit address space.
|
|
|
|
This class implements the AMD64/IA-32E paging address space. It is responsible
|
|
for translating each virtual (linear) address to a physical address.
|
|
This is accomplished using hierachical paging structures.
|
|
Every paging structure is 4096 bytes and is composed of entries.
|
|
Each entry is 64 bits. The first paging structure is located at the
|
|
physical address found in CR3 (dtb).
|
|
|
|
Additional Resources:
|
|
- Intel(R) 64 and IA-32 Architectures Software Developer's Manual
|
|
Volume 3A: System Programming Guide. Section 4.3
|
|
http://www.intel.com/products/processor/manuals/index.htm
|
|
- AMD64 Architecture Programmer's Manual Volume 2: System Programming
|
|
http://support.amd.com/us/Processor_TechDocs/24593_APM_v2.pdf
|
|
- N. Petroni, A. Walters, T. Fraser, and W. Arbaugh, "FATKit: A Framework
|
|
for the Extraction and Analysis of Digital Forensic Data from Volatile
|
|
System Memory" ,Digital Investigation Journal 3(4):197-210, December 2006.
|
|
(submitted February 2006)
|
|
- N. P. Maclean, "Acquisition and Analysis of Windows Memory,"
|
|
University of Strathclyde, Glasgow, April 2006.
|
|
- Russinovich, M., & Solomon, D., & Ionescu, A.
|
|
"Windows Internals, 5th Edition", Microsoft Press, 2009.
|
|
"""
|
|
order = 60
|
|
pae = False
|
|
checkname = 'AMD64ValidAS'
|
|
paging_address_space = True
|
|
minimum_size = 0x1000
|
|
alignment_gcd = 0x1000
|
|
_longlong_struct = struct.Struct("<Q")
|
|
|
|
def entry_present(self, entry):
|
|
if entry:
|
|
if (entry & 1):
|
|
return True
|
|
|
|
# The page is in transition and not a prototype.
|
|
# Thus, we will treat it as present.
|
|
if (entry & (1 << 11)) and not (entry & (1 << 10)):
|
|
return True
|
|
|
|
return False
|
|
|
|
def page_size_flag(self, entry):
|
|
if (entry & (1 << 7)) == (1 << 7):
|
|
return True
|
|
return False
|
|
|
|
def get_2MB_paddr(self, vaddr, pgd_entry):
|
|
paddr = (pgd_entry & 0xFFFFFFFE00000) | (vaddr & 0x00000001fffff)
|
|
return paddr
|
|
|
|
def is_valid_profile(self, profile):
|
|
'''
|
|
This method checks to make sure the address space is being
|
|
used with a supported profile.
|
|
'''
|
|
return profile.metadata.get('memory_model', '32bit') == '64bit' or profile.metadata.get('os', 'Unknown').lower() == 'mac'
|
|
|
|
def pml4e_index(self, vaddr):
|
|
'''
|
|
This method returns the Page Map Level 4 Entry Index
|
|
number from the given virtual address. The index number is
|
|
in bits 47:39.
|
|
'''
|
|
return (vaddr & 0xff8000000000) >> 39
|
|
|
|
def get_pml4e(self, vaddr):
|
|
'''
|
|
This method returns the Page Map Level 4 (PML4) entry for the
|
|
virtual address. Bits 47:39 are used to the select the
|
|
appropriate 8 byte entry in the Page Map Level 4 Table.
|
|
|
|
"Bits 51:12 are from CR3" [Intel]
|
|
"Bits 11:3 are bits 47:39 of the linear address" [Intel]
|
|
"Bits 2:0 are 0" [Intel]
|
|
'''
|
|
pml4e_paddr = (self.dtb & 0xffffffffff000) | ((vaddr & 0xff8000000000) >> 36)
|
|
return self.read_long_long_phys(pml4e_paddr)
|
|
|
|
def get_pdpi(self, vaddr, pml4e):
|
|
'''
|
|
This method returns the Page Directory Pointer entry for the
|
|
virtual address. Bits 32:30 are used to select the appropriate
|
|
8 byte entry in the Page Directory Pointer table.
|
|
|
|
"Bits 51:12 are from the PML4E" [Intel]
|
|
"Bits 11:3 are bits 38:30 of the linear address" [Intel]
|
|
"Bits 2:0 are all 0" [Intel]
|
|
'''
|
|
pdpte_paddr = (pml4e & 0xffffffffff000) | ((vaddr & 0x7FC0000000) >> 27)
|
|
return self.read_long_long_phys(pdpte_paddr)
|
|
|
|
def get_1GB_paddr(self, vaddr, pdpte):
|
|
'''
|
|
If the Page Directory Pointer Table entry represents a 1-GByte
|
|
page, this method extracts the physical address of the page.
|
|
|
|
"Bits 51:30 are from the PDPTE" [Intel]
|
|
"Bits 29:0 are from the original linear address" [Intel]
|
|
'''
|
|
return (pdpte & 0xfffffc0000000) | (vaddr & 0x3fffffff)
|
|
|
|
def pde_index(self, vaddr):
|
|
return (vaddr >> pde_shift) & (ptrs_per_pde - 1)
|
|
|
|
def pdba_base(self, pdpe):
|
|
return pdpe & 0xFFFFFFFFFF000
|
|
|
|
def get_pgd(self, vaddr, pdpe):
|
|
pgd_entry = self.pdba_base(pdpe) + self.pde_index(vaddr) * entry_size
|
|
return self.read_long_long_phys(pgd_entry)
|
|
|
|
def pte_index(self, vaddr):
|
|
return (vaddr >> page_shift) & (ptrs_per_pde - 1)
|
|
|
|
def ptba_base(self, pde):
|
|
return pde & 0xFFFFFFFFFF000
|
|
|
|
def get_pte(self, vaddr, pgd):
|
|
pgd_val = self.ptba_base(pgd) + self.pte_index(vaddr) * entry_size
|
|
return self.read_long_long_phys(pgd_val)
|
|
|
|
def pte_pfn(self, pte):
|
|
return pte & 0xFFFFFFFFFF000
|
|
|
|
def get_paddr(self, vaddr, pte):
|
|
return self.pte_pfn(pte) | (vaddr & ((1 << page_shift) - 1))
|
|
|
|
def vtop(self, vaddr):
|
|
'''
|
|
This method translates an address in the virtual
|
|
address space to its associated physical address.
|
|
Invalid entries should be handled with operating
|
|
system abstractions.
|
|
'''
|
|
vaddr = long(vaddr)
|
|
retVal = None
|
|
pml4e = self.get_pml4e(vaddr)
|
|
if not self.entry_present(pml4e):
|
|
return None
|
|
|
|
pdpe = self.get_pdpi(vaddr, pml4e)
|
|
if not self.entry_present(pdpe):
|
|
return retVal
|
|
|
|
if self.page_size_flag(pdpe):
|
|
return self.get_1GB_paddr(vaddr, pdpe)
|
|
|
|
pgd = self.get_pgd(vaddr, pdpe)
|
|
if self.entry_present(pgd):
|
|
if self.page_size_flag(pgd):
|
|
retVal = self.get_2MB_paddr(vaddr, pgd)
|
|
else:
|
|
pte = self.get_pte(vaddr, pgd)
|
|
if self.entry_present(pte):
|
|
retVal = self.get_paddr(vaddr, pte)
|
|
return retVal
|
|
|
|
def read_long_long_phys(self, addr):
|
|
'''
|
|
This method returns a 64-bit little endian
|
|
unsigned integer from the specified address in the
|
|
physical address space. If the address cannot be accessed,
|
|
then the method returns None.
|
|
|
|
This code was derived directly from legacyintel.py
|
|
'''
|
|
try:
|
|
string = self.base.read(addr, 8)
|
|
except IOError:
|
|
string = None
|
|
if not string:
|
|
return obj.NoneObject("Unable to read_long_long_phys at " + hex(addr))
|
|
longlongval, = self._longlong_struct.unpack(string)
|
|
return longlongval
|
|
|
|
def get_available_pages(self):
|
|
'''
|
|
This method generates a list of pages that are
|
|
available within the address space. The entries in
|
|
are composed of the virtual address of the page
|
|
and the size of the particular page (address, size).
|
|
It walks the 0x1000/0x8 (0x200) entries in each Page Map,
|
|
Page Directory, and Page Table to determine which pages
|
|
are accessible.
|
|
'''
|
|
|
|
for pml4e in range(0, 0x200):
|
|
vaddr = pml4e << 39
|
|
pml4e_value = self.get_pml4e(vaddr)
|
|
if not self.entry_present(pml4e_value):
|
|
continue
|
|
for pdpte in range(0, 0x200):
|
|
vaddr = (pml4e << 39) | (pdpte << 30)
|
|
pdpte_value = self.get_pdpi(vaddr, pml4e_value)
|
|
if not self.entry_present(pdpte_value):
|
|
continue
|
|
if self.page_size_flag(pdpte_value):
|
|
yield (vaddr, 0x40000000)
|
|
continue
|
|
|
|
pgd_curr = self.pdba_base(pdpte_value)
|
|
for j in range(0, ptrs_per_pae_pgd):
|
|
soffset = vaddr + (j * ptrs_per_pae_pgd * ptrs_per_pae_pte * 8)
|
|
entry = self.read_long_long_phys(pgd_curr)
|
|
pgd_curr = pgd_curr + 8
|
|
if self.entry_present(entry) and self.page_size_flag(entry):
|
|
yield (soffset, 0x200000)
|
|
elif self.entry_present(entry):
|
|
pte_curr = entry & 0xFFFFFFFFFF000
|
|
for k in range(0, ptrs_per_pae_pte):
|
|
pte_entry = self.read_long_long_phys(pte_curr)
|
|
pte_curr = pte_curr + 8
|
|
if self.entry_present(pte_entry):
|
|
yield (soffset + k * 0x1000, 0x1000)
|
|
|
|
@classmethod
|
|
def address_mask(cls, addr):
|
|
return addr & 0xffffffffffff
|