Skip to content

Commit 1c719f3

Browse files
committed
First implem of arm64.assemble()
1 parent 998382b commit 1c719f3

File tree

1 file changed

+144
-1
lines changed

1 file changed

+144
-1
lines changed

windows/native_exec/simple_arm64.py

Lines changed: 144 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,4 +246,147 @@ class Subs(Instruction):
246246
### C6.2.307 RET (page 2203) (11010110010111110000000000000000)
247247

248248
class Ret(Instruction):
249-
encoding = [RetEncoding]
249+
encoding = [RetEncoding]
250+
251+
252+
253+
class MultipleInstr(object):
254+
INSTRUCTION_SIZE = 4
255+
256+
def __init__(self, init_instrs=()):
257+
self.instrs = {}
258+
self.labels = {}
259+
self.expected_labels = {}
260+
# List of all labeled jump already resolved
261+
# Will be used for 'relocation'
262+
self.computed_jump = []
263+
self.size = 0
264+
for i in init_instrs:
265+
self += i
266+
267+
def get_code(self):
268+
if self.expected_labels:
269+
raise ValueError("Unresolved labels: {0}".format(self.expected_labels.keys()))
270+
return b"".join([x[1].get_code() for x in sorted(self.instrs.items())])
271+
272+
def add_instruction(self, instruction):
273+
# if isinstance(instruction, Label):
274+
# return self.add_label(instruction)
275+
# # Change DelayedJump to LabeledJump ?
276+
# if isinstance(instruction, DelayedJump):
277+
# return self.add_delayed_jump(instruction)
278+
if isinstance(instruction, Instruction):
279+
self.instrs[self.size] = instruction
280+
self.size += self.INSTRUCTION_SIZE
281+
return
282+
raise ValueError("Don't know what to do with {0} of type {1}".format(instruction, type(instruction)))
283+
284+
def add_label(self, label):
285+
if label.name not in self.expected_labels:
286+
# Label that have no jump before definition
287+
# Just registed the address of the label
288+
self.labels[label.name] = self.size
289+
return
290+
# Label with jmp before definition
291+
# Lot of stuff todo:
292+
# Find all delayed jump that refer to this jump
293+
# Replace them with real jump
294+
# If size of jump < JUMP_SIZE: relocate everything we can
295+
# Update expected_labels
296+
for jump_to_label in self.expected_labels[label.name]:
297+
if jump_to_label.offset in self.instrs:
298+
raise ValueError("WTF REPLACE EXISTING INSTR...")
299+
distance = self.size - jump_to_label.offset
300+
real_jump = jump_to_label.type(distance)
301+
self.instrs[jump_to_label.offset] = real_jump
302+
self.computed_jump.append((jump_to_label.offset, self.size))
303+
for i in range(self.JUMP_SIZE - len(real_jump.get_code())):
304+
self.instrs[jump_to_label.offset + len(real_jump.get_code()) + i] = _NopArtifact()
305+
del self.expected_labels[label.name]
306+
self.labels[label.name] = self.size
307+
if not self.expected_labels:
308+
# No more un-resolved label (for now): time to reduce the shellcode
309+
self._reduce_shellcode()
310+
311+
def add_delayed_jump(self, jump):
312+
dst = jump.label
313+
if dst in self.labels:
314+
# Jump to already defined labels
315+
# Nothing fancy: get offset of label and jump to it !
316+
distance = self.size - self.labels[dst]
317+
jump_instruction = jump.type(-distance)
318+
self.computed_jump.append((self.size, self.labels[dst]))
319+
return self.add_instruction(jump_instruction)
320+
# Jump to undefined label
321+
# Add label to expected ones
322+
# Add jump info -> offset of jump | type
323+
# Reserve space for call !
324+
jump.offset = self.size
325+
self.expected_labels.setdefault(dst, []).append(jump)
326+
self.size += self.JUMP_SIZE
327+
return
328+
329+
def merge_shellcode(self, other):
330+
shared_labels = set(self.labels) & set(other.labels)
331+
if shared_labels:
332+
raise ValueError("Cannot merge shellcode: shared labels {0}".format(shared_labels))
333+
for offset, instr in sorted(other.instrs.items()):
334+
for label_name in [name for name, label_offset in other.labels.items() if label_offset == offset]:
335+
self.add_instruction(Label(label_name))
336+
self.add_instruction(instr)
337+
338+
def __iadd__(self, other):
339+
if isinstance(other, MultipleInstr):
340+
self.merge_shellcode(other)
341+
elif isinstance(other, basestring):
342+
self.assemble(other)
343+
else:
344+
self.add_instruction(other)
345+
return self
346+
347+
def assemble(self, code):
348+
for instr in assemble_instructions_generator(code):
349+
self.add_instruction(instr)
350+
351+
352+
def split_in_instruction(str):
353+
for line in str.split("\n"):
354+
if not line:
355+
continue
356+
for instr in line.split(";"):
357+
if not instr:
358+
continue
359+
yield instr.strip()
360+
361+
def assemble_instructions_generator(str):
362+
for instr in split_in_instruction(str):
363+
data = instr.split(" ", 1)
364+
mnemo, args_raw = data[0], data[1:]
365+
try:
366+
instr_object = globals()[mnemo.capitalize()]
367+
except:
368+
raise ValueError("Unknow mnemonic <{0}>".format(mnemo))
369+
370+
# if issubclass(instr_object, Raw):
371+
# # Raw should received the raw buffer as it expect encoded hex
372+
# # The transformation may transform 'raw 9090' (nopnop) as 0n9090
373+
# # If other fake-instr need this : make a class attribute
374+
# yield instr_object(*args_raw)
375+
# continue
376+
377+
args = []
378+
if args_raw:
379+
for arg in args_raw[0].split(","):
380+
arg = arg.strip()
381+
try:
382+
arg = int(arg, 0)
383+
except ValueError:
384+
pass
385+
args.append(arg)
386+
yield instr_object(*args)
387+
388+
def assemble(str):
389+
"""Play test"""
390+
shellcode = MultipleInstr()
391+
shellcode += str
392+
return shellcode.get_code()

0 commit comments

Comments
 (0)