Skip to content

Commit aca1685

Browse files
committed
feat: Update reverse operations to Python3
Up until now all reverse operations needed `Python2` to run. This lead to a user needing to have 2 different versions of Python installed on their system. Now, only `Python3` is required to run all `Sandblaster` operations. Signed-off-by: David Bors <daviddvd267@gmail.com>
1 parent 6a5cffd commit aca1685

8 files changed

Lines changed: 56 additions & 43 deletions

File tree

reverse-sandbox/operation_node.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ def __eq__(self, other):
426426
return self.raw == other.raw
427427

428428
def __hash__(self):
429-
return struct.unpack('<I', ''.join([chr(v) for v in self.raw[:4]]))[0]
429+
return struct.unpack('<I', b''.join([bytes([v]) for v in self.raw[:4]]))[0]
430430

431431

432432
# Operation nodes processed so far.

reverse-sandbox/regex_parser_v1.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,11 @@ def parse_parantheses_close(node_type, node_arg, node_transition, node_idx):
111111

112112
def node_parse(re, i, regex_list, node_idx):
113113
node_type = struct.unpack('>I',
114-
''.join([chr(x) for x in re[i:i+4]]))[0]
114+
b''.join([bytes([x]) for x in re[i:i+4]]))[0]
115115
node_transition = struct.unpack('>I',
116-
''.join([chr(x) for x in re[i+4:i+8]]))[0]
116+
b''.join([bytes([x]) for x in re[i+4:i+8]]))[0]
117117
node_arg = struct.unpack('>I',
118-
''.join([chr(x) for x in re[i+8:i+12]]))[0]
118+
b''.join([bytes([x]) for x in re[i+8:i+12]]))[0]
119119
i += 12
120120

121121
logger.debug('node idx:{:#010x} type: {:#02x} arg: {:#010x}' \
@@ -136,10 +136,10 @@ def transform(x):
136136
return c
137137

138138
class_size = struct.unpack('>I',
139-
''.join([chr(x) for x in re[i:i+4]]))[0]
139+
b''.join([bytes([x]) for x in re[i:i+4]]))[0]
140140
i += 0x4
141141
content = struct.unpack('>{}I'.format(class_size),
142-
''.join([chr(x) for x in re[i:i+4*class_size]]))
142+
b''.join([bytes([x]) for x in re[i:i+4*class_size]]))
143143
i += 0x4 * class_size
144144
assert(class_size % 2 == 0)
145145

@@ -162,23 +162,23 @@ class RegexParser(object):
162162
@staticmethod
163163
def parse(re, i, regex_list):
164164
node_count = struct.unpack('>I',
165-
''.join([chr(x) for x in re[i:i+0x4]]))[0]
165+
b''.join([bytes([x]) for x in re[i:i+0x4]]))[0]
166166
logger.debug('node count = {:#x}'.format(node_count))
167167

168168
start_node = struct.unpack('>I',
169-
''.join([chr(x) for x in re[i+0x4:i+0x8]]))[0]
169+
b''.join([bytes([x]) for x in re[i+0x4:i+0x8]]))[0]
170170
logger.debug('start node = {:#x}'.format(start_node))
171171

172172
end_node = struct.unpack('>I',
173-
''.join([chr(x) for x in re[i+0x8:i+0xC]]))[0]
173+
b''.join([bytes([x]) for x in re[i+0x8:i+0xC]]))[0]
174174
logger.debug('end node = {:#x}'.format(end_node))
175175

176176
cclass_count = struct.unpack('>I',
177-
''.join([chr(x) for x in re[i+0xC:i+0x10]]))[0]
177+
b''.join([bytes([x]) for x in re[i+0xC:i+0x10]]))[0]
178178
logger.debug('character class count = {:#x}'.format(cclass_count))
179179

180180
submatch_count = struct.unpack('>I',
181-
''.join([chr(x) for x in re[i+0x10:i+0x14]]))[0]
181+
b''.join([bytes([x]) for x in re[i+0x10:i+0x14]]))[0]
182182
i += 0x14
183183
logger.debug('submatch count = {:#x}'.format(submatch_count))
184184

reverse-sandbox/regex_parser_v2.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,13 @@ def parse_parantheses_close(node_type, node_arg, node_transition, node_idx):
112112

113113
def node_parse(re, i, regex_list, node_idx):
114114
node_type = struct.unpack('<B',
115-
''.join([chr(x) for x in re[i:i+1]]))[0]
115+
b''.join([bytes([x]) for x in re[i:i+1]]))[0]
116116
node_transition = struct.unpack('<H',
117-
''.join([chr(x) for x in re[i+1:i+3]]))[0]
117+
b''.join([bytes([x]) for x in re[i+1:i+3]]))[0]
118118
pad = struct.unpack('<B',
119-
''.join([chr(x) for x in re[i+3:i+4]]))[0]
119+
b''.join([bytes([x]) for x in re[i+3:i+4]]))[0]
120120
node_arg = struct.unpack('<I',
121-
''.join([chr(x) for x in re[i+4:i+8]]))[0]
121+
b''.join([bytes([x]) for x in re[i+4:i+8]]))[0]
122122
i += 8
123123

124124
logger.debug('node idx:{:#06x} type: {:#02x} arg: {:#010x}' \
@@ -156,17 +156,17 @@ def transform_content(content):
156156
return
157157

158158
classes_magic, classes_size = struct.unpack('<II',
159-
''.join([chr(x) for x in re[i:i+8]]))
159+
b''.join([bytes([x]) for x in re[i:i+8]]))
160160
i += 0x8
161161
logger.debug('classes magic = {:#x} size = {:#x}'.format(
162162
classes_magic, classes_size))
163163
assert(len(re) - i == classes_size)
164164
starts = struct.unpack('<{}I'.format(cclass_count),
165-
''.join([chr(x) for x in re[i:i+4*cclass_count]]))
165+
b''.join([bytes([x]) for x in re[i:i+4*cclass_count]]))
166166
i += 0x4 * cclass_count
167167

168168
lens = struct.unpack('<{}B'.format(cclass_count),
169-
''.join([chr(x) for x in re[i:i+cclass_count]]))
169+
b''.join([bytes([x]) for x in re[i:i+cclass_count]]))
170170
i += cclass_count
171171

172172
contents = [re[i+start:i+start+clen] for start, clen in zip(starts, lens)]
@@ -177,23 +177,23 @@ class RegexParser(object):
177177
@staticmethod
178178
def parse(re, i, regex_list):
179179
magic = struct.unpack('<I',
180-
''.join([chr(x) for x in re[i:i+0x4]]))[0]
180+
b''.join([bytes([x]) for x in re[i:i+0x4]]))[0]
181181
logger.debug('magic = {:#x}'.format(magic))
182182

183183
node_count = struct.unpack('<I',
184-
''.join([chr(x) for x in re[i+0x4:i+0x8]]))[0]
184+
b''.join([bytes([x]) for x in re[i+0x4:i+0x8]]))[0]
185185
logger.debug('node count = {:#x}'.format(node_count))
186186

187187
start_node = struct.unpack('<I',
188-
''.join([chr(x) for x in re[i+0x8:i+0xC]]))[0]
188+
b''.join([bytes([x]) for x in re[i+0x8:i+0xC]]))[0]
189189
logger.debug('start node = {:#x}'.format(start_node))
190190

191191
end_node = struct.unpack('<I',
192-
''.join([chr(x) for x in re[i+0xC:i+0x10]]))[0]
192+
b''.join([bytes([x]) for x in re[i+0xC:i+0x10]]))[0]
193193
logger.debug('end node = {:#x}'.format(end_node))
194194

195195
cclass_count = struct.unpack('<I',
196-
''.join([chr(x) for x in re[i+0x10:i+0x14]]))[0]
196+
b''.join([bytes([x]) for x in re[i+0x10:i+0x14]]))[0]
197197
logger.debug('character class count = {:#x}'.format(cclass_count))
198198
i += 0x14
199199

reverse-sandbox/regex_parser_v3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ class RegexParser(object):
146146

147147
@staticmethod
148148
def parse(re, i, regex_list):
149-
length = struct.unpack('<H', ''.join([chr(x) for x in re[i:i+2]]))[0]
149+
length = struct.unpack('<H', b''.join([bytes([x]) for x in re[i:i+2]]))[0]
150150
logger.debug("re.length: 0x%x", length)
151151
i += 2
152152
assert(length == len(re)-i)

reverse-sandbox/reverse_sandbox.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ def extract_string_from_offset(f, offset, ios_version):
3434
else:
3535
f.seek(offset * 8)
3636
len = struct.unpack("<I", f.read(4))[0]-1
37-
return '%s' % f.read(len)
37+
ret = f.read(len)
38+
try:
39+
ret = ret.decode()
40+
except:
41+
pass
42+
return ret
3843

3944

4045
def create_operation_nodes(infile, regex_list, num_operation_nodes,
@@ -43,10 +48,10 @@ def create_operation_nodes(infile, regex_list, num_operation_nodes,
4348
operation_nodes = operation_node.build_operation_nodes(infile,
4449
num_operation_nodes, ios_major_version)
4550
logger.info("operation nodes")
46-
51+
4752
for idx, node in enumerate(operation_nodes):
4853
logger.info("%d: %s", idx, node.str_debug())
49-
54+
5055
for n in operation_nodes:
5156
n.convert_filter(sandbox_filter.convert_filter_callback, infile,
5257
regex_list, ios_major_version, keep_builtin_filters,
@@ -211,7 +216,7 @@ def get_global_vars(f, vars_offset, num_vars, base_offset):
211216
len = struct.unpack("<I", f.read(4))[0]
212217
s = f.read(len-1)
213218
global_vars.append(s)
214-
logger.info("global variables are {:s}".format(", ".join(s for s in global_vars)))
219+
logger.info("global variables are {:s}".format(", ".join(s.decode() for s in global_vars)))
215220
return global_vars
216221

217222
def get_base_addr(f, ios_version):
@@ -304,7 +309,7 @@ def main():
304309
re_table_offset = 12
305310
else:
306311
re_table_offset = struct.unpack("<H", f.read(2))[0]
307-
312+
308313
if get_ios_major_version(args.release) >= 12:
309314
f.seek(8)
310315
re_table_count = struct.unpack("<H", f.read(2))[0]
@@ -319,7 +324,7 @@ def main():
319324
f.seek(re_table_offset)
320325
else:
321326
f.seek(re_table_offset * 8)
322-
327+
323328
re_offsets_table = struct.unpack("<%dH" % re_table_count, f.read(2 * re_table_count))
324329
for offset in re_offsets_table:
325330
if get_ios_major_version(args.release) >= 13:
@@ -328,7 +333,7 @@ def main():
328333
else:
329334
f.seek(offset * 8)
330335
re_length = struct.unpack("<I", f.read(4))[0]
331-
336+
332337
re = struct.unpack("<%dB" % re_length, f.read(re_length))
333338
logger.debug("total_re_length: 0x%x", re_length)
334339
re_debug_str = "re: [", ", ".join([hex(i) for i in re]), "]"
@@ -397,7 +402,7 @@ def main():
397402
break
398403
start = f.tell()
399404
end = re_table_offset * 8
400-
num_operation_nodes = (end - start) / 8
405+
num_operation_nodes = (end - start) // 8
401406
logger.info("number of operation nodes: %u" % num_operation_nodes)
402407

403408
operation_nodes = create_operation_nodes(f, regex_list,
@@ -489,7 +494,8 @@ def main():
489494
break
490495
start = f.tell()
491496
end = re_table_offset * 8
492-
num_operation_nodes = (end - start) / 8
497+
# has to be int and not float
498+
num_operation_nodes = (end - start) // 8
493499
logger.info("number of operation nodes: %d ; start: %#x" % (num_operation_nodes, start))
494500

495501
operation_nodes = create_operation_nodes(f, regex_list,

reverse-sandbox/reverse_string.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,14 @@ def get_length_minus_1(self):
146146
def read_token(self, substr_len):
147147
self.token_stack.append(self.token)
148148
self.token = self.binary_string[self.pos:self.pos+substr_len]
149-
logger.debug("got token \"{:s}\"".format(self.token))
149+
logger.debug("got token \"{:s}\"".format(self.token.decode()))
150150
self.pos += substr_len
151151

152152
def update_base(self):
153-
self.base += self.token
153+
if isinstance(self.token, bytes):
154+
self.base += self.token.decode()
155+
else:
156+
self.base += self.token
154157
self.token = ""
155158
logger.debug("update base to \"{:s}\"".format(self.base))
156159

@@ -168,14 +171,18 @@ def get_last_byte(self):
168171

169172
def get_substring(self, substr_len):
170173
substr = self.binary_string[self.pos:self.pos+substr_len]
171-
logger.debug(" ".join("0x{:02x}".format(ord(c)) for c in substr))
174+
logger.debug(" ".join("0x{:02x}".format(c) for c in substr))
172175
self.pos += substr_len
173176
return substr
174177

175178
def end_with_subtokens(self, subtokens):
176179
for s in subtokens:
177-
self.output_strings.append(self.base+self.token+s)
178-
logger.debug("output string with subtokens \"{:s}\"".format(self.base+self.token+s))
180+
if isinstance(self.token, bytes):
181+
self.output_strings.append(self.base+self.token.decode()+s)
182+
logger.debug("output string with subtokens \"{:s}\"".format(self.base+self.token.decode()+s))
183+
else:
184+
self.output_strings.append(self.base+self.token+s)
185+
logger.debug("output string with subtokens \"{:s}\"".format(self.base+self.token+s))
179186
self.token = ""
180187

181188
def is_end(self):
@@ -227,7 +234,7 @@ def parse_byte_string(self, s, global_vars):
227234
logger.debug("state is STATE_CONSTANT_READ")
228235
b = rss.get_last_byte()
229236
if b >= 0x10 and b < 0x3f:
230-
rss.token = "${" + global_vars[b-0x10] + "}"
237+
rss.token = b"${" + global_vars[b-0x10] + b"}"
231238
b = rss.get_next_byte()
232239
rss.update_state(b)
233240
elif rss.state == rss.STATE_CONCAT_BYTE_READ:
@@ -335,7 +342,7 @@ def parse_byte_string(self, s, global_vars):
335342
logger.warn("last state is not STATE_END_BYTE_READ ({:d})".format(rss.state))
336343
logger.warn("previous state ({:d})".format(rss.state_stack[len(rss.state_stack)-1]))
337344

338-
logger.info("initial string: " + " ".join("0x{:02x}".format(ord(c)) for c in s))
345+
logger.info("initial string: " + " ".join("0x{:02x}".format(c) for c in s))
339346
logger.info("output_strings (num: {:d}): {:s}".format(len(rss.output_strings), ",".join('"{:s}"'.format(s) for s in rss.output_strings)))
340347
return rss.output_strings
341348

reverse-sandbox/sandbox_filter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def get_filter_arg_string_by_offset(f, offset):
3737
if ios_major_version >= 10:
3838
f.seek(offset * 8)
3939
s = f.read(4+len)
40-
logger.info("binary string is " + s.encode("hex"))
40+
logger.info("binary string is " + s.hex())
4141
ss = reverse_string.SandboxString()
4242
myss = ss.parse_byte_string(s[4:], global_vars)
4343
actual_string = ""
@@ -72,7 +72,7 @@ def get_filter_arg_string_by_offset_with_type(f, offset):
7272
if ios_major_version >= 10:
7373
f.seek(base_addr + offset * 8)
7474
s = f.read(4+len)
75-
logger.info("binary string is " + s.encode("hex"))
75+
logger.info("binary string is " + s.hex())
7676
ss = reverse_string.SandboxString()
7777
myss = ss.parse_byte_string(s[4:], global_vars)
7878
append = "literal"

reverse-sandbox/sandbox_regex.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ def create_regex_list(re):
456456

457457
regex_list = []
458458

459-
version = struct.unpack('>I', ''.join([chr(x) for x in re[:4]]))[0]
459+
version = struct.unpack('>I', b''.join([bytes(chr(x), 'utf-8') for x in re[:4]]))[0]
460460
logger.debug("re.version: 0x%x", version)
461461

462462
i = 4

0 commit comments

Comments
 (0)