This repository has been archived by the owner on Sep 7, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 17
/
DART.py
198 lines (158 loc) · 7.3 KB
/
DART.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# Legilimency - Memory Analysis Framework for iOS
# --------------------------------------
#
# Written and maintained by Gal Beniamini <[email protected]>
#
# Copyright 2017 Google Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from MemClient import MemClient, qword_at
import struct
from defs import *
from symbols import *
#The base address of the IO-Space mapping governed by DART
DART_IO_SPACE_BASE = 0x80000000
#The memory range translated by a first-level DART entry
DART_FIRST_LEVEL_ENTRY_SIZE= 0x200000
#the size of the mapping governed by a second-level DART entry
DART_SECOND_LEVEL_ENTRY_SIZE = 0x1000
#The size of the first-level DART table
DART_FIRST_LEVEL_TABLE_SIZE = 0x1000
#The size of the second-level DART table
DART_SECOND_LEVEL_TABLE_SIZE = 0x1000
#The mask used to retrieve a second-level entry's address
SECOND_LEVEL_ENTRY_MASK = 0xFFFFFFFFFFFFF000
#The mask used to get a translation's MSBs
TRANSLATION_OFFSET_MASK = 0xFFF
#The offset of the DART table within the DART instance
DART_TABLE_OFFSET = 312
#The offset of the DART registers within the DART instance
DART_REGISTERS_OFFSET = 384
#The offset of the L0 descriptor in the DART registers
L0_DESC_REG_OFFSET = 64
class DART(object):
"""
A class representing the DART IO-Space -> PA resolution table.
"""
def __init__(self, client, sl_dart, verbose=True):
self.sl_dart = sl_dart
self.client = client
self.io_to_pa_map = {}
self.pa_to_io_map = {}
#Reading the DART table and HW-register pointers
self.dart_table = self.client.read64(self.sl_dart + DART_TABLE_OFFSET)
self.dart_registers = self.client.read64(self.sl_dart + DART_REGISTERS_OFFSET)
#Going over each of the table's first-level entries
self.l0_ptr = self.client.read64(self.dart_table)
self.l1_ptr = self.client.read64(self.l0_ptr + QWORD_SIZE)
self.dart_l2_addrs = []
l1_pa_table = self.client.read(self.l1_ptr, DART_FIRST_LEVEL_TABLE_SIZE)
if verbose:
print "l0_ptr: %016X" % self.l0_ptr
print "l1_ptr: %016X" % self.l1_ptr
for l1_idx in range(0, DART_FIRST_LEVEL_TABLE_SIZE, QWORD_SIZE):
#Skipping unpopulated first-level entries
l1_pa = qword_at(l1_pa_table, l1_idx/QWORD_SIZE)
if l1_pa == 0:
continue #Not populated
#Finding the second-level descriptor bytes
l1_va = self.client.read64(self.l0_ptr + 2*QWORD_SIZE + l1_idx)
l2_ptr = self.client.read64(l1_va + QWORD_SIZE)
self.dart_l2_addrs.append(l2_ptr)
l2_pa_table = self.client.read(l2_ptr, DART_SECOND_LEVEL_TABLE_SIZE)
if verbose:
print "L1 VA: %016X" % l1_va
print "l2_ptr: %016X" % l2_ptr
for l2_idx in range(0, DART_SECOND_LEVEL_TABLE_SIZE, QWORD_SIZE):
#Skipping unpopulated second-level entries
l2_entry = qword_at(l2_pa_table, l2_idx/QWORD_SIZE)
if l2_entry == 0:
continue
#Adding the IO-Space -> PA mapping
io_space_addr = DART_IO_SPACE_BASE + \
((l1_idx/QWORD_SIZE) * DART_FIRST_LEVEL_ENTRY_SIZE) + \
((l2_idx/QWORD_SIZE) * DART_SECOND_LEVEL_ENTRY_SIZE)
if verbose:
print "0x%08X -> 0x%016X" % (io_space_addr, l2_entry)
self.io_to_pa_map[io_space_addr] = l2_entry & SECOND_LEVEL_ENTRY_MASK
#Inverting the mapping to keep an IO-Space -> PA mapping
for k, v in self.io_to_pa_map.iteritems():
self.pa_to_io_map[v] = self.io_to_pa_map.get(v, [])
if type(self.pa_to_io_map[v]) != list:
self.pa_to_io_map[v] = [self.pa_to_io_map[v]]
self.pa_to_io_map[v].append(k)
def io_to_pa(self, io_space_addr):
"""
Translates the given IO-Space address to the corresponding physical address
"""
aligned_io_space_addr = io_space_addr & SECOND_LEVEL_ENTRY_MASK
offset = io_space_addr & TRANSLATION_OFFSET_MASK
if aligned_io_space_addr in self.io_to_pa_map:
return self.io_to_pa_map[aligned_io_space_addr] + offset
return None
def pa_to_io(self, pa_addr):
"""
Translates the given IO-Space address to the corresponding physical address
"""
aligned_pa_addr = pa_addr & SECOND_LEVEL_ENTRY_MASK
offset = pa_addr & TRANSLATION_OFFSET_MASK
if aligned_pa_addr in self.pa_to_io_map:
return self.pa_to_io_map[aligned_pa_addr][0] + offset
return None
def map_io_space(self, io_addr, pa_addr):
"""
Maps the given IO-Space address to the given physical address.
"""
#Calculating the first and second level indices
l1_idx = (io_addr - DART_IO_SPACE_BASE) / DART_FIRST_LEVEL_ENTRY_SIZE
l2_idx = ((io_addr - DART_IO_SPACE_BASE) - (l1_idx * DART_FIRST_LEVEL_ENTRY_SIZE)) / DART_SECOND_LEVEL_ENTRY_SIZE
#Is there a second level descriptor at this address?
l1_va = self.client.read64(self.l0_ptr + 2*QWORD_SIZE + l1_idx*QWORD_SIZE)
l2_ptr = self.client.read64(l1_va + QWORD_SIZE)
if l2_ptr == 0:
raise Exception("No second level entry descriptor at index %d" % l1_idx)
#Writing the translation
self.client.write64(l2_ptr + l2_idx*QWORD_SIZE, pa_addr | 0b11)
def get_io_addrs(self):
"""
Returns a list of IO base addresses mapped to IO-Space
"""
return sorted(self.io_to_pa_map.keys())
def read_reg(self, offset):
"""
Reads the hardware register at the given offset
NOTE: The hardware *requires* a strict 32-bit load, anything else returns 0xFF's.
"""
return self.client.read32_strict(self.dart_registers + offset)
def write_reg(self, offset, val):
"""
Writes the hardware register at the given offset.
NOTE: The hardware *requires* a strict 32-bit store.
"""
self.client_write32_strict(self.dart_registers + offset, val)
def get_l0_desc(self):
"""
Returns the L0 descriptor from DART's HW registers.
"""
return self.read_reg(L0_DESC_REG_OFFSET)
def get_l0_pa(self):
"""
Extracts the host physical address of the L0 table from the L0 descriptor in DART's registers.
"""
return (self.get_l0_desc & 0xFFFFFF) << 12
def map_l0_table(self, l0_table_pa):
"""
Maps the given L0 table into DART's L0 descriptor.
NOTE: DART caches *many* of the IO-Space mappings, therefore changes
might take time to become apparent.
"""
self.write_reg(L0_DESC_REG_OFFSET, ((l0_table_pa >> 12) & 0xFFFFFF) | 0x80000000)