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
|
"""
Symtbl data structure
A Symtbl (symbol table) is an associative data container intended to model
arbitrary memory layouts, such as structure definitions or memory-mapped
objects. Elements may be accessed via subscript or attribute notation.
A Symtbl is essentially a dictionary, in which each key (symbol name string)
is associated with an offset value. A special key "base" represents the
base or starting address of the overall table in memory. Whenever offset
values are accessed, they are adjusted relative to the table's base value.
This enables the primary function of Symtbl objects: the ability to resolve
mapped, or absolute, addresses of objects in memory.
Therefore, even though a Symtbl internally tracks symbol offsets, the apparent
value of any symbol will always be its offset plus the table's base address.
The table's base address will also be subtracted from values being stored in
the table, as the provided value is assumed to be mapped in the same manner as
the table itself.
s = Symtbl()
s.a = 10
s.b = 20
print(s.a, s.b) # "10 20"
s.base = 100
print(s.a, s.b) # "110 120"
s.c = 150
s.base = 10
print(s.a, s.b, s.c) # "20 30 60"
A Symtbl's base value may be changed at any time, and this will affect the
interpretation of offsets as described above. However, one may also create a
remapped version of a Symtbl (without modifying the original) using the '@'
operator. This new object will have the base value given on the right hand
side of the '@' and its collection of symbols is referentially linked to the
source object, meaning changes to symbol entries will be visible in both
objects.
s1 = Symtbl()
s1.a = 10
s2 = s1 @ 1000
print(s1.a, s2.a) # "10 1010"
s2.b = 1234
print(s1.b, s2.b) # "234 1234"
Symtbl's are also nestable, to support modeling composite memory layouts. If
a symbol's value is assigned to another Symtbl object, rather than an integer
offset, the child object's base value serves as its offset in the parent
Symtbl. Symbols on the child object may then be accessed recursively from the
parent's scope. If the parent has a non-zero base, it adjusts the offsets
interpreted in the child.
child = Symtbl()
child.a = 1
child.b = 2
parent = Symtbl()
parent.nested = child @ 70
print(parent.nested.a, parent.nested.b) # "71 72"
A Symtbl will allow you to uniformly adjust all offsets contained, while leaving
the base value the same, using the '<<' and '>>' operators. A custom
"rebase" operation is also available via the "%" operator. A rebase applies
a uniform shift, such that the right hand side offset operand ends up coinciding
with the Symtbl base address.
s = Symtbl()
s.a = 1
s.b = 2
s.c = 3
s.d = 4
s.base = 1000
s %= s.c # rebase at symbol 'c'
print(s.a, s.b, s.c, s.d) # "998 999 1000 1001"
"""
from sploit.types.indextbl import IndexTbl
from sploit.types.index_entry import IndexEntry
def Symtbl(*, base=0, **symbols):
"""
Create a new Symtbl object.
Return an empty Symtbl or, optionally, one initialized with the given
symbol values. Arguments must be keyword arguments.
Users should call this function instead of attempting to construct the
Symtbl class. Construction is implemented via a normal function to prevent
any argument name from conflicting with __init__'s bound instance parameter.
"""
self = SymtblImpl(base, 0, dict())
for k, v in symbols.items():
self[k] = v
return self
class SymtblImpl(IndexTbl):
"""Symtbl implementation class"""
def __init__(self, base, adjust, entries):
"""Construct Symtbl from instance data."""
super().__init__(base)
object.__setattr__(self, "__adjust__", adjust)
object.__setattr__(self, "__entries__", entries)
def __repr__(self):
"""Return human-readable Symtbl."""
FMT = "\n{:<20} {:<20}"
s = f"{len(self)} symbols @ {hex(self)}"
if len(self) > 0:
s += FMT.format("ADDRESS", "SYMBOL")
for key, value in self:
key = f"[{key}]" if isinstance(value, IndexEntry) else key
s += FMT.format(hex(value), key)
return s
def __rshift__(self, offset):
"""Return symbol-adjusted version of object."""
adjust = self.__adjust__ + int(offset)
return SymtblImpl(self.base, adjust, self.__entries__)
def __lshift__(self, offset):
"""Return symbol-adjusted version of object."""
return self >> (-offset)
def __mod__(self, offset):
"""Return symbol-rebased version of object."""
return self >> (self.base - offset)
# IndexTbl abstract methods
def __copy__(self):
"""Return copy of object with shared symbol entries."""
return SymtblImpl(self.base, self.__adjust__, self.__entries__)
def __iter__(self):
"""Iterate over table items, sorted by offsets."""
it = { k: self[k] for k in self.__entries__}.items()
return iter(sorted(it, key=lambda x: int(x[1])))
def __len__(self):
"""Return number of defined symbols."""
return len(self.__entries__)
def __getindex__(self, index):
"""Return symbol value or translated offset."""
if isinstance(index, (int, IndexEntry)): offset = index
else: offset = self.__entries__[index]
return offset + (self.base + self.__adjust__)
def __setindex__(self, index, value):
"""Set symbol value."""
if isinstance(index, (int, IndexEntry)):
raise TypeError(f"Symtbl: Unsupported key type: {type(index)}")
self.__entries__[index] = value - (self.base + self.__adjust__)
def __delindex__(self, index):
"""Delete symbol."""
del self.__entries__[index]
|