Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arm64 #726

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open

Arm64 #726

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions volatility/dwarf.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class DWARFParser(object):
'unsigned int': 'unsigned int',
'sizetype' : 'unsigned long',
'ssizetype' : 'long',
'__int128 unsigned': 'unsigned long long',
}


Expand Down
143 changes: 143 additions & 0 deletions volatility/plugins/addrspaces/arm64.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Volatility
#
# 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 struct
import volatility.obj as obj
import volatility.debug as debug #pylint: disable-msg=W0611
import volatility.plugins.addrspaces.paged as paged


class AbstractArm64AddressSpace(paged.AbstractWritablePagedMemory):
"""Address space for ARM64 processors"""

order = 800
pae = False
paging_address_space = True
checkname = 'Arm64ValidAS'
minimum_size = 0x1000
alignment_gcd = 0x1000
_longlong_struct = struct.Struct('<Q')
_valid_dtb_base = None

def read_longlong_phys(self, addr):
'''
Returns an unsigned 64-bit integer from the address addr in
physical memory. If unable to read from that location, returns None.
'''
try:
string = self.base.read(addr, 8)
except IOError:
string = None
if not string:
return obj.NoneObject("Could not read_longlong_phys at offset " + hex(addr))
longlongval, = self._longlong_struct.unpack(string)
return longlongval

def ptbl_lowbits(self, level):
return 3 + (self.pgtable_level_bits) * (4 - level)

def ptbl_index(self, vaddr, level):
return (vaddr >> self.ptbl_lowbits(level)) & ((1 << self.pgtable_level_bits) - 1)

def ptbl_walk(self, vaddr, base):
if (vaddr == 0):
return None

# 4KB pages will further mask only 48 bits using ptbl_index
vaddr = vaddr & ((1 << 52) - 1)

for level in xrange(self.pgtable_level_start, 4):
index = self.ptbl_index(vaddr, level)
entry = self.read_longlong_phys(base + (index << 3))
debug.debug("level: {0:d} index: {1:3x} base: {2:16x} entry {3:16x}".format(level, index, base, entry), 4)
if not entry:
return None
entry_addressbits = entry & self._entry_addressmask
# entry not valid
if not (entry & 0x1):
return None
if (level == 3 and (entry & 0x3 != 0x3)):
return None
# entry points to final address
if (level == 3 or (entry & 0x3 == 0x1)):
lowbitmask = (1 << self.ptbl_lowbits(level)) - 1
result = entry_addressbits & (~lowbitmask) | (vaddr & lowbitmask)
debug.debug("-result {0:16x}".format(result), 4)
return result
# entry points to next table
base = entry_addressbits
return None

def vtop(self, vaddr):
debug.debug("\n--vtop start: {0:x}".format(vaddr), 4)

# if we already had a valid dtb - it was the single TTBR1, and should
# be used to translate all kernel-space
if type(self)._valid_dtb_base and (vaddr >> 63):
return self.ptbl_walk(vaddr, type(self)._valid_dtb_base)
else:
return self.ptbl_walk(vaddr, self.dtb)

def set_curr_base_valid(self):
cls = type(self)
if not cls._valid_dtb_base:
cls._valid_dtb_base = self.dtb

def get_available_pages(self):
level = self.pgtable_level_start
ptbl_descs = [self.dtb, 0, 0, 0]
ptbl_indexes = [0, 0, 0, 0]
while True:
if ptbl_indexes[level] == 1 << self.pgtable_level_bits:
if level == self.pgtable_level_start:
break
level -= 1
ptbl_indexes[level] += 1
continue

entry = self.read_longlong_phys(ptbl_descs[level] + (ptbl_indexes[level] << 3))

# entry points to next table
if ((level < 3) and (entry & 0x3 == 0x3)):
level += 1
entry_addressbits = entry & self._entry_addressmask
ptbl_descs[level] = entry_addressbits
ptbl_indexes[level] = 0
continue

# entry points to physical address
if (level == 3 and (entry & 0x3 == 0x3)) or (entry & 0x3 == 0x1):
vaddr = 0
for ilvl in xrange(level+1):
vaddr += ptbl_indexes[ilvl] << self.ptbl_lowbits(ilvl)
yield vaddr, (1 << self.ptbl_lowbits(level))

ptbl_indexes[level] += 1


# 4-level , 4KB pagetable
class Arm64AddressSpace_4KB(AbstractArm64AddressSpace):
pgtable_level_bits = 9
_entry_addressmask = ((1 << 48) - 1) & (~((1 << 12) - 1))
pgtable_level_start = 0

# 3-level 64KB pagetable, for kernel compiled with CONFIG_ARM64_PA_BITS_52
class Arm64AddressSpace_64KB(AbstractArm64AddressSpace):
pgtable_level_bits = 13
_entry_addressmask = ((1 << 48) - 1) & (~((1 << 16) - 1))
pgtable_level_start = 1
43 changes: 37 additions & 6 deletions volatility/plugins/overlays/linux/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ def is_writable(self):
'ArmValidAS' : [ 0x0, ['VolatilityLinuxARMValidAS']],
'IA32ValidAS' : [ 0x0, ['VolatilityLinuxIntelValidAS']],
'AMD64ValidAS' : [ 0x0, ['VolatilityLinuxIntelValidAS']],
'Arm64ValidAS' : [ 0x0, ['VolatilityLinuxARM64ValidAS']],
}],
'vm_area_struct' : [ None, {
'vm_flags' : [ None, ['LinuxPermissionFlags', {'bitmap': {'r': 0, 'w': 1, 'x': 2}}]],
Expand Down Expand Up @@ -156,6 +157,9 @@ def parse_system_map(data, module):
if symbol == "arm_syscall":
arch = "ARM"

if symbol == "arm64_dma_phys_limit":
arch = "arm64"

if not symbol in sys_map[module]:
sys_map[module][symbol] = []

Expand Down Expand Up @@ -192,9 +196,6 @@ def LinuxProfileFactory(profpkg):
sysmapdata = profpkg.read(f.filename)
arch, memmodel, sysmap = parse_system_map(profpkg.read(f.filename), "kernel")

if memmodel == "64bit":
arch = "x64"

if not sysmapdata or not dwarfdata:
# Might be worth throwing an exception here?
return None
Expand Down Expand Up @@ -2330,7 +2331,12 @@ def generate_suggestions(self):
config = self.obj_vm.get_config()
tbl = self.obj_vm.profile.sys_map["kernel"]

if profile.metadata.get('memory_model', '32bit') == "32bit":
if profile.metadata.get('arch') == 'arm64':
sym = "swapper_pg_dir"
shifts = [0xffff000000000000, 0xffff800000000000]
read_sz = 8
fmt = "<Q"
elif profile.metadata.get('memory_model', '32bit') == "32bit":
sym = "swapper_pg_dir"
shifts = [0xc0000000]
read_sz = 4
Expand Down Expand Up @@ -2415,8 +2421,9 @@ def generate_suggestions(self):

good_dtb = (dtb_sym_addr - shifts[0] + 0) + tmp_physical_shift

if pas.zread(good_dtb, 8) != "\x00\x00\x00\x00\x00\x00\x00\x00":
continue
if profile.metadata.get('arch') != 'arm64':
if pas.zread(good_dtb, 8) != "\x00\x00\x00\x00\x00\x00\x00\x00":
continue

if sched_class_offset != -1:
sched_class_val = pas.read(swapper_address + sched_class_offset, read_sz)
Expand Down Expand Up @@ -2493,6 +2500,29 @@ def generate_suggestions(self):

yield fork_off - task_off == sym_addr_diff

class VolatilityLinuxARM64ValidAS(obj.VolatilityMagic):
"""An object to check that an address space is a valid Arm64 Paged space"""
def generate_suggestions(self):

init_task_addr = self.obj_vm.profile.get_symbol("init_task")
do_fork_addr = self.obj_vm.profile.get_symbol("_do_fork")

if not do_fork_addr or not init_task_addr:
return

task_paddr = self.obj_vm.vtop(init_task_addr)
do_fork_paddr = self.obj_vm.vtop(do_fork_addr)

if not do_fork_paddr or not task_paddr:
return

res = (do_fork_paddr - task_paddr == do_fork_addr - init_task_addr)

if res:
self.obj_vm.set_curr_base_valid()

yield res

class LinuxObjectClasses(obj.ProfileModification):
conditions = {'os': lambda x: x == 'linux'}
before = ['BasicObjectClasses']
Expand All @@ -2518,6 +2548,7 @@ def modification(self, profile):
'Ipv6Address': basic.Ipv6Address,
'VolatilityLinuxIntelValidAS' : VolatilityLinuxIntelValidAS,
'VolatilityLinuxARMValidAS' : VolatilityLinuxARMValidAS,
'VolatilityLinuxARM64ValidAS' : VolatilityLinuxARM64ValidAS,
'kernel_param' : kernel_param,
'kparam_array' : kparam_array,
'desc_struct' : desc_struct,
Expand Down