-
Notifications
You must be signed in to change notification settings - Fork 117
/
dyld_info.py
244 lines (207 loc) · 7.05 KB
/
dyld_info.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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# dyld.py
#
# Adapted from EcaFretni
#
# Copyright 2010, KennyTM~ <[email protected]>
# Copyright 2014, espes
#
# Licensed under GPL Version 3 or later
#
BIND_TYPE_POINTER = 1
BIND_TYPE_TEXT_ABSOLUTE32 = 2
BIND_TYPE_TEXT_PCREL32 = 3
BIND_SPECIAL_DYLIB_SELF = 0
BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1
BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2
BIND_SYMBOL_FLAGS_WEAK_IMPORT = 0x1
BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION = 0x8
BIND_OPCODE_MASK = 0xF0
BIND_IMMEDIATE_MASK = 0x0F
BIND_OPCODE_DONE = 0x00
BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10
BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20
BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30
BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40
BIND_OPCODE_SET_TYPE_IMM = 0x50
BIND_OPCODE_SET_ADDEND_SLEB = 0x60
BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70
BIND_OPCODE_ADD_ADDR_ULEB = 0x80
BIND_OPCODE_DO_BIND = 0x90
BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0
BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0
BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0
REBASE_TYPE_POINTER = 1
REBASE_TYPE_TEXT_ABSOLUTE32 = 2
REBASE_TYPE_TEXT_PCREL32 = 3
REBASE_OPCODE_MASK = 0xF0
REBASE_IMMEDIATE_MASK = 0x0F
REBASE_OPCODE_DONE = 0x00
REBASE_OPCODE_SET_TYPE_IMM = 0x10
REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x20
REBASE_OPCODE_ADD_ADDR_ULEB = 0x30
REBASE_OPCODE_ADD_ADDR_IMM_SCALED = 0x40
REBASE_OPCODE_DO_REBASE_IMM_TIMES = 0x50
REBASE_OPCODE_DO_REBASE_ULEB_TIMES = 0x60
REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB = 0x70
REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB = 0x80
def readString(f):
r = ""
while True:
c = f.read(1)
if c == "\x00":
break
r += c
return r
def readULeb128(f):
"""Read an unsigned little-endian base-128 integer"""
res = 0
bit = 0
while True:
c = ord(f.read(1))
s = c & 0x7f
res |= s << bit
bit += 7
if not (c & 0x80):
break
return res
def readSLeb128(f):
"""Read a signed little-endian base-128 integer """
res = 0
bit = 0
while True:
c = ord(f.read(1))
s = c & 0x7f
res |= s << bit
bit += 7
if not (c & 0x80):
break
if c & 0x40:
res |= (-1) << bit
return res
def read_rebases(f, size, segs, ptrwidth=4):
addr = 0
rebases = []
end = f.tell() + size
while f.tell() < end:
c = ord(f.read(1))
opcode = c & REBASE_OPCODE_MASK
imm = c & REBASE_IMMEDIATE_MASK
if opcode == REBASE_OPCODE_DONE:
pass
elif opcode == REBASE_OPCODE_SET_TYPE_IMM:
assert imm == REBASE_TYPE_POINTER
elif opcode == REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
addr = segs[imm].vmaddr + readULeb128(f)
elif opcode == REBASE_OPCODE_ADD_ADDR_ULEB:
addr = (addr + readULeb128(f)) % (2 ** 64)
elif opcode == REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
addr += imm * ptrwidth
elif opcode == REBASE_OPCODE_DO_REBASE_IMM_TIMES:
for i in xrange(imm):
rebases.append(addr)
addr += ptrwidth
elif opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
count = readULeb128(f)
for i in xrange(count):
rebases.append(addr)
addr += ptrwidth
elif opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
rebases.append(addr)
addr += ptrwidth + readULeb128(f)
elif opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
count = readULeb128(f)
skip = readULeb128(f)
for i in xrange(count):
rebases.append(addr)
addr += skip + ptrwidth
else:
raise NotImplementedError
return rebases
def read_binds(f, size, segs, ptrwidth=4):
libord = 0
sym = None
addr = 0
symbols = []
end = f.tell() + size
while f.tell() < end:
c = ord(f.read(1))
opcode = c & BIND_OPCODE_MASK
imm = c & BIND_IMMEDIATE_MASK
if opcode == BIND_OPCODE_DONE:
pass
elif opcode == BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
libord = imm
elif opcode == BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
libord = readULeb128(f)
elif opcode == BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
libord = (imm | 0xf0) if imm else 0
elif opcode == BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
sym = readString(f)
elif opcode == BIND_OPCODE_SET_TYPE_IMM:
assert imm == BIND_TYPE_POINTER
elif opcode == BIND_OPCODE_SET_ADDEND_SLEB:
readSLeb128(f)
elif opcode == BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
addr = segs[imm].vmaddr + readULeb128(f)
elif opcode == BIND_OPCODE_ADD_ADDR_ULEB:
addr = (addr + readULeb128(f)) % (2 ** 64)
elif opcode == BIND_OPCODE_DO_BIND:
symbols.append((sym, addr, libord))
addr += ptrwidth
elif opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
symbols.append((sym, addr, libord))
addr += ptrwidth + readULeb128(f)
elif opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
symbols.append((sym, addr, libord))
addr += (imm+1) * ptrwidth
elif opcode == BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
count = readULeb128(f)
skip = readULeb128(f)
for i in xrange(count):
symbols.append((sym, addr, libord))
addr += skip + ptrwidth
else:
raise NotImplementedError
return symbols
def walk_trie(f, start, cur, end, prefix, symbols):
if cur >= end:
return
f.seek(cur)
termSize = ord(f.read(1))
if termSize:
sym = prefix
readULeb128(f)
addr = readULeb128(f)
symbols.append((sym, addr))
f.seek(cur + termSize + 1)
childCount = ord(f.read(1))
for i in range(childCount):
suffix = readString(f)
offset = readULeb128(f)
lastPos = f.tell()
walk_trie(f, start, start + offset, end, prefix + suffix, symbols)
f.seek(lastPos)
class DyldInfo(object):
def __init__(self, filename, cmd, segs):
with open(filename, "rb") as f:
self.rebases = []
self.binds = []
self.week_binds = []
self.lazy_binds = []
self.exports = []
if cmd.rebase_size:
f.seek(cmd.rebase_off)
self.rebases = read_rebases(f, cmd.rebase_size, segs)
if cmd.bind_size:
f.seek(cmd.bind_off)
self.binds = read_binds(f, cmd.bind_size, segs)
if cmd.weak_bind_size:
f.seek(cmd.weak_bind_off)
self.week_binds = read_binds(f, cmd.weak_bind_size, segs)
if cmd.lazy_bind_size:
f.seek(cmd.lazy_bind_off)
self.lazy_binds = read_binds(f, cmd.lazy_bind_size, segs)
if cmd.export_size:
walk_trie(f, cmd.export_off, cmd.export_off,
cmd.export_off + cmd.export_size,
"", self.exports)