juju.re/jujure/static/svartalfheim/interpreter.py
Julien CLEMENT 171c5040b2 add svartalheim writeup
Signed-off-by: Julien CLEMENT <julien.clement@epita.fr>
2024-04-11 10:33:38 +02:00

229 lines
6.2 KiB
Python
Executable File

#!/usr/bin/env python3
import struct
from typing import Optional
import copy
regs = {
0x42048: "DT_RELA",
0x42068: "DT_RELASZ",
0x42088: "r0",
0x420a0: "r1",
0x420b8: "r2",
0x420d0: "r3",
0x420e8: "r4",
0x42100: "r5",
0x42118: "r6",
0x42130: "r7",
0x42148: "r8",
0x42160: "r9",
0x42178: "r10",
0x42190: "r11",
0x421a8: "r12",
0x421c0: "r13",
0x421d8: "r14",
0x421f0: "r15",
0x42090: "r0.size",
0x420a8: "r1.size",
0x420c0: "r2.size",
0x420d8: "r3.size",
0x420f0: "r4.size",
0x42108: "r5.size",
0x42120: "r6.size",
0x42138: "r7.size",
0x42150: "r8.size",
0x42168: "r9.size",
0x42180: "r10.size",
0x42198: "r11.size",
0x421b0: "r12.size",
0x421c8: "r13.size",
0x421e0: "r14.size",
0x421f8: "r15.size"
}
class Stream:
i: int
buf: bytes
def __init__(self, buf) -> None:
self.pos = 0
self.buf = buf
def read_u8(self) -> Optional[int]:
try:
val = struct.unpack("<B", self.buf[self.pos:self.pos + 1])
self.pos += 1
return val[0]
except:
return None
def read_u16(self) -> Optional[int]:
try:
val = struct.unpack("<H", self.buf[self.pos:self.pos + 2])
self.pos += 2
return val[0]
except:
return None
def read_u32(self) -> Optional[int]:
try:
val = struct.unpack("<I", self.buf[self.pos:self.pos + 4])
self.pos += 4
return val[0]
except:
return None
def read_u64(self) -> Optional[int]:
try:
val = struct.unpack("<Q", self.buf[self.pos:self.pos + 8])
self.pos += 8
return val[0]
except:
return None
def is_done(self):
return self.pos >= len(self.buf)
class RelaEnt:
def __init__(self, stream):
self.addr = stream.read_u64()
self.type = stream.read_u32()
self.symbol = stream.read_u32()
self.addend = stream.read_u64()
class Rela:
def __init__(self, rela_addr, rela_size, elf):
self.rela_addr = rela_addr
self.rela_size = rela_size
self.elf = elf
def pop(self):
if self.rela_size <= 0:
return None
stream = Stream(self.elf.get_data(self.rela_addr, 0x18))
entry = RelaEnt(stream)
entry.offset = self.rela_addr
self.rela_addr += 0x18
self.rela_size -= 0x18
return entry
def peek(self):
if self.rela_size <= 0:
return None
stream = Stream(self.elf.get_data(self.rela_addr, 0x18))
entry = RelaEnt(stream)
entry.offset = self.rela_addr
return entry
class Symbol:
def __init__(self, data):
stream = Stream(data)
self.name = stream.read_u16()
self.info = stream.read_u8()
self.other = stream.read_u8()
self.shndx = stream.read_u32()
self.value = stream.read_u64()
self.size = stream.read_u64()
class Elf:
def __init__(self, path):
with open(path, 'rb') as f:
self._data = bytearray(f.read())
self.dynt_rela = 0x42048
self.dynt_relasz = 0x42068
self.symtab = 0x42080
self.breakpoints = {}
def dump(self, path):
with open(path, 'wb') as f:
f.write(self._data)
def get_data(self, addr, size):
addr -= 0x40000
return self._data[addr:addr + size]
def set_data(self, addr, data):
addr -= 0x40000
self._data[addr:addr + len(data)] = data
def set_flag(self, flag):
addr = 0x5a100
addr -= 0x40000
self._data[addr:addr + len(flag)] = flag
def get_rela(self):
rela_addr = Stream(self.get_data(self.dynt_rela, 8)).read_u64()
rela_size = Stream(self.get_data(self.dynt_relasz, 8)).read_u64()
return Rela(rela_addr, rela_size, self)
def get_symbol(self, i):
sym_data = self.get_data(self.symtab + 0x18 * i, 0x18)
return Symbol(sym_data)
def apply_relocs(self):
rela = self.get_rela()
patched_code = False
asm = ""
while True:
entry = rela.pop()
if not entry:
break
symbol = self.get_symbol(entry.symbol)
dst_name = regs[entry.addr] if entry.addr in regs else f'[{hex(entry.addr)}]'
line = f'{hex(entry.offset)}: '
if entry.type == 1:
data_int = (symbol.value + entry.addend) % 2**64
data = struct.pack('<Q', data_int)
line += f'add {dst_name}, r{entry.symbol}, ${hex(entry.addend)}'
elif entry.type == 5:
data = self.get_data(symbol.value + entry.addend, symbol.size)
line += f'mov {dst_name}, [r{entry.symbol}].{symbol.size}'
if entry.addend != 0:
raise
elif entry.type == 8:
data_int = entry.addend
data = struct.pack('<Q', data_int)
src_name = f'&{regs[entry.addend]}' if entry.addend in regs else f'${hex(entry.addend)}'
line += f'mov {dst_name}, {src_name}'
else:
print(hex(entry.type))
raise
line += ' ' * (50 - len(line)) + f'# {bytes(data).hex()}'
if entry.addr > entry.offset and entry.addr < 0x49000:
next_instr = rela.peek()
if not (entry.addr >= next_instr.offset and entry.addr < next_instr.offset + 0x18):
line += " PATCHING FAR"
else:
line += ' PATCHING CODE'
asm += line + '\n'
self.set_data(entry.addr, data)
if entry.addr >= 0x41000 and entry.addr < 0x41083:
patched_code = True
asm += '#End of block\n'
return asm, patched_code
def main():
elf = Elf('./svartalfheim')
asm = ""
n = 0
while n <= 7:
step_asm, patched_code = elf.apply_relocs()
if patched_code:
asm += '# NATIVE CODE LOADING:\n'
n += 1
elf.dump(f'./dumps/pydumped{n}')
asm += step_asm
print(asm)
if __name__ == "__main__":
main()