mirror of
https://github.com/qilingframework/qiling.git
synced 2026-10-12 02:21:40 +00:00
985 lines
33 KiB
Python
985 lines
33 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
|
#
|
|
|
|
import struct
|
|
|
|
|
|
from qiling.const import *
|
|
|
|
PT_DYNAMIC = 2
|
|
DT_NULL = 0
|
|
DT_NEEDED = 1
|
|
DT_PLTRELSZ = 2
|
|
DT_PLTGOT = 3
|
|
DT_HASH = 4
|
|
DT_STRTAB = 5
|
|
DT_SYMTAB = 6
|
|
DT_RELA = 7
|
|
DT_RELASZ = 8
|
|
DT_RELAENT = 9
|
|
DT_STRSZ = 10
|
|
DT_SYMENT = 11
|
|
DT_INIT = 12
|
|
DT_FINI = 13
|
|
DT_SONAME = 14
|
|
DT_RPATH = 15
|
|
DT_SYMBOLIC = 16
|
|
DT_REL = 17
|
|
DT_RELSZ = 18
|
|
DT_RELENT = 19
|
|
DT_PLTREL = 20
|
|
DT_DEBUG = 21
|
|
DT_TEXTREL = 22
|
|
DT_JMPREL = 23
|
|
DT_BIND_NOW = 24
|
|
DT_INIT_ARRAY = 25
|
|
DT_FINI_ARRAY = 26
|
|
DT_INIT_ARRAYSZ = 27
|
|
DT_FINI_ARRAYSZ = 28
|
|
DT_RUNPATH = 29
|
|
DT_FLAGS = 30
|
|
DT_ENCODING = 32
|
|
DT_GNU_HASH = 0x6ffffef5
|
|
|
|
DT_MIPS_LOCAL_GOTNO = 0x7000000a
|
|
DT_MIPS_SYMTABNO = 0x70000011
|
|
DT_MIPS_GOTSYM = 0x70000013
|
|
|
|
class HookFunc:
|
|
def __init__(self, ql, fn):
|
|
self.fucname = fn
|
|
self.hook_data_ptr = None
|
|
self.hook_onenter = []
|
|
self.hook_onexit = []
|
|
self.hook = []
|
|
self.ql = ql
|
|
self.ret_pc = None
|
|
self.exit_addr = None
|
|
|
|
def add_hook(self, cb, intercept, userdata):
|
|
if intercept == QL_INTERCEPT.ENTER:
|
|
self.hook_onenter.append((cb, userdata))
|
|
elif intercept == QL_INTERCEPT.EXIT:
|
|
self.hook_onexit.append((cb, userdata))
|
|
else:
|
|
self.hook.append((cb, userdata))
|
|
|
|
def get_ret_pc(self):
|
|
# ARM
|
|
if self.ql.arch.type == QL_ARCH.ARM:
|
|
return self.ql.arch.regs.lr
|
|
|
|
# MIPS32
|
|
elif self.ql.arch.type == QL_ARCH.MIPS:
|
|
return self.ql.arch.regs.ra
|
|
|
|
# ARM64
|
|
elif self.ql.arch.type == QL_ARCH.ARM64:
|
|
return self.ql.arch.regs.x30
|
|
|
|
# PPC
|
|
elif self.ql.arch.type== QL_ARCH.PPC:
|
|
return self.ql.arch.regs.lr
|
|
|
|
# X86
|
|
elif self.ql.arch.type == QL_ARCH.X86:
|
|
return self.ql.unpack(self.ql.mem.read(self.ql.arch.regs.esp, self.ql.arch.pointersize))
|
|
|
|
# X8664
|
|
elif self.ql.arch.type == QL_ARCH.X8664:
|
|
return self.ql.unpack(self.ql.mem.read(self.ql.arch.regs.rsp, self.ql.arch.pointersize))
|
|
else:
|
|
raise
|
|
|
|
def context_fixup(self):
|
|
# ARM
|
|
if self.ql.arch.type == QL_ARCH.ARM:
|
|
pass
|
|
|
|
# MIPS32
|
|
elif self.ql.arch.type == QL_ARCH.MIPS:
|
|
pass
|
|
|
|
# PPC
|
|
elif self.ql.arch.type== QL_ARCH.PPC:
|
|
pass
|
|
|
|
# ARM64
|
|
elif self.ql.arch.type == QL_ARCH.ARM64:
|
|
pass
|
|
|
|
# X86
|
|
elif self.ql.arch.type == QL_ARCH.X86:
|
|
self.ql.arch.regs.esp = self.ql.arch.regs.esp + self.ql.arch.pointersize
|
|
|
|
# X8664
|
|
elif self.ql.arch.type == QL_ARCH.X8664:
|
|
self.ql.arch.regs.rsp = self.ql.arch.regs.rsp + self.ql.arch.pointersize
|
|
else:
|
|
raise
|
|
|
|
def set_ret(self, addr):
|
|
# ARM
|
|
if self.ql.arch.type == QL_ARCH.ARM:
|
|
self.ql.arch.regs.lr = addr
|
|
|
|
# MIPS32
|
|
elif self.ql.arch.type == QL_ARCH.MIPS:
|
|
self.ql.arch.regs.ra = addr
|
|
|
|
# PPC
|
|
elif self.ql.arch.type== QL_ARCH.PPC:
|
|
self.ql.arch.regs.lr = addr
|
|
|
|
# ARM64
|
|
elif self.ql.arch.type == QL_ARCH.ARM64:
|
|
self.ql.arch.stack_write(0, addr)
|
|
|
|
# X86
|
|
elif self.ql.arch.type == QL_ARCH.X86:
|
|
self.ql.arch.stack_write(0, addr)
|
|
|
|
# X8664
|
|
elif self.ql.arch.type == QL_ARCH.X8664:
|
|
self.ql.arch.stack_write(0, addr)
|
|
else:
|
|
raise
|
|
|
|
def call_enter(self):
|
|
# if self.ql.arch.type == QL_ARCH.ARM or self.ql.arch.type == QL_ARCH.ARM64:
|
|
# self.ql.arch.regs.arch_pc = self.ql.arch.regs.arch_pc + 4
|
|
|
|
next_pc = self.ql.unpack(self.ql.mem.read(self.hook_data_ptr, self.ql.arch.pointersize))
|
|
self.ret_pc = self.get_ret_pc()
|
|
onenter_cb = None
|
|
onenter_userdata = None
|
|
|
|
if len(self.hook_onenter) != 0:
|
|
onenter_cb, onenter_userdata = self.hook_onenter[0]
|
|
|
|
cb = None
|
|
userdata = None
|
|
if len(self.hook) != 0:
|
|
cb, userdata = self.hook[0]
|
|
|
|
if onenter_cb == None:
|
|
ret = 0
|
|
else:
|
|
if onenter_userdata == None:
|
|
ret = onenter_cb(self.ql)
|
|
else:
|
|
ret = onenter_cb(self.ql, onenter_userdata)
|
|
|
|
if isinstance(ret, int) == False or ret & QL_CALL_BLOCK == 0:
|
|
if cb != None:
|
|
if userdata == None:
|
|
cb(self.ql)
|
|
else:
|
|
cb(self.ql, userdata)
|
|
else:
|
|
self.set_ret(self.exit_addr)
|
|
self.ql.arch.regs.arch_pc = next_pc
|
|
|
|
else:
|
|
self.context_fixup()
|
|
|
|
def call_exit(self):
|
|
# if self.ql.arch.type == QL_ARCH.ARM or self.ql.arch.type == QL_ARCH.ARM64:
|
|
# self.ql.arch.regs.arch_pc = self.ql.arch.regs.arch_pc + 4
|
|
onexit_cb = None
|
|
onexit_userdata = None
|
|
|
|
if len(self.hook_onexit) != 0:
|
|
onexit_cb, onexit_userdata = self.hook_onexit[0]
|
|
|
|
if onexit_cb != None:
|
|
if onexit_userdata == None:
|
|
onexit_cb(self.ql)
|
|
else:
|
|
onexit_cb(self.ql, onexit_userdata)
|
|
|
|
self.ql.arch.regs.arch_pc = self.ret_pc
|
|
|
|
|
|
class HookFuncRel(HookFunc):
|
|
def __init__(self, ql, fucname, r, load_base):
|
|
super(HookFuncRel, self).__init__(ql, fucname)
|
|
self.rel = r
|
|
self.idx = None
|
|
self.load_base = load_base
|
|
self.hook_fuc_ptr = None
|
|
self.ori_offest = None
|
|
self.ori_data = None
|
|
|
|
def _hook_fuc_enter(self, ql):
|
|
self.call_enter()
|
|
|
|
def _hook_fuc_exit(self, ql):
|
|
self.call_exit()
|
|
|
|
def enable(self):
|
|
if self.rel == None or self.hook_fuc_ptr == None or self.hook_data_ptr == None:
|
|
raise
|
|
|
|
self.exit_addr = self.hook_fuc_ptr + 8
|
|
|
|
self.ql.hook_address(self._hook_fuc_enter, self.hook_fuc_ptr)
|
|
self.ql.hook_address(self._hook_fuc_exit, self.hook_fuc_ptr + 8)
|
|
|
|
self.ori_offest = self.rel.r_offset
|
|
self.rel.r_offset = self.hook_data_ptr - self.load_base
|
|
self.ori_data = self.ql.mem.read(self.ori_offest + self.load_base, self.ql.arch.pointersize)
|
|
|
|
self.ql.mem.write(self.rel.ptr, self.rel.pack())
|
|
self.ql.mem.write_ptr(self.ori_offest + self.load_base, self.hook_fuc_ptr)
|
|
self.ql.mem.write(self.hook_data_ptr, bytes(self.ori_data))
|
|
|
|
class HookFuncMips(HookFunc):
|
|
def __init__(self, ql, fucname, got, gotidx, load_base):
|
|
super(HookFuncMips, self).__init__(ql, fucname)
|
|
self.load_base = load_base
|
|
self.got = got
|
|
self.gotidx = gotidx
|
|
|
|
def _hook_fuc_enter(self, ql):
|
|
self.ql.arch.regs.t9 = self.ql.unpack(self.ql.mem.read(self.hook_data_ptr, self.ql.arch.pointersize))
|
|
self.call_enter()
|
|
|
|
def _hook_fuc_exit(self, ql):
|
|
self.call_exit()
|
|
|
|
tmp = self.ql.unpack(self.ql.mem.read(self.got + self.load_base + self.gotidx * self.ql.arch.pointersize, self.ql.arch.pointersize))
|
|
if tmp != self.hook_fuc_ptr:
|
|
self.ql.mem.write_ptr(self.got + self.load_base + self.gotidx * self.ql.arch.pointersize, self.hook_fuc_ptr)
|
|
self.ql.mem.write_ptr(self.hook_data_ptr, tmp)
|
|
|
|
def _hook_got(self):
|
|
self.ori_data = self.ql.mem.read(self.got + self.load_base + self.gotidx * self.ql.arch.pointersize, self.ql.arch.pointersize)
|
|
|
|
self.ql.mem.write_ptr(self.got + self.load_base + self.gotidx * self.ql.arch.pointersize, self.hook_fuc_ptr)
|
|
self.ql.mem.write(self.hook_data_ptr, bytes(self.ori_data))
|
|
|
|
def enable(self):
|
|
if self.got == None or self.hook_fuc_ptr == None or self.hook_data_ptr == None:
|
|
raise
|
|
|
|
self.ql.os.register_function_after_load(self._hook_got)
|
|
|
|
self.exit_addr = self.hook_fuc_ptr + 8
|
|
|
|
self.ql.hook_address(self._hook_fuc_enter, self.hook_fuc_ptr)
|
|
self.ql.hook_address(self._hook_fuc_exit, self.hook_fuc_ptr + 8)
|
|
|
|
|
|
class ELF_Phdr:
|
|
def __init__(self, p_type, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_flags, p_align):
|
|
self.p_type = p_type
|
|
self.p_offset = p_offset
|
|
self.p_vaddr = p_vaddr
|
|
self.p_paddr = p_paddr
|
|
self.p_filesz = p_filesz
|
|
self.p_memsz = p_memsz
|
|
self.p_flags = p_flags
|
|
self.p_align = p_align
|
|
|
|
class ELF32_Phdr(ELF_Phdr):
|
|
Phdr_SIZE = 4 * 8
|
|
def __init__(self, buf, endian = 0):
|
|
if len(buf) != self.Phdr_SIZE:
|
|
raise
|
|
|
|
fmt = '<IIIIIIII' if endian == 0 else '>IIIIIIII'
|
|
|
|
p_type, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_flags, p_align = struct.unpack(fmt, buf)
|
|
super(ELF32_Phdr, self).__init__(p_type, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_flags, p_align)
|
|
|
|
class ELF64_Phdr(ELF_Phdr):
|
|
Phdr_SIZE = 8 * 7
|
|
def __init__(self, buf, endian = 0):
|
|
if len(buf) != self.Phdr_SIZE:
|
|
raise
|
|
|
|
fmt = '<IIQQQQQQ' if endian == 0 else '>IIQQQQQQ'
|
|
|
|
p_type, p_flags, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_align = struct.unpack(fmt, buf)
|
|
super(ELF64_Phdr, self).__init__(p_type, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_flags, p_align)
|
|
|
|
class ELF_Dyn:
|
|
def __init__(self, d_tag, d_un):
|
|
self.d_tag = d_tag
|
|
self.d_un = d_un
|
|
|
|
class ELF32_Dyn(ELF_Dyn):
|
|
Dyn_SIZE = 4 * 2
|
|
def __init__(self, buf, endian = 0):
|
|
if len(buf) != self.Dyn_SIZE:
|
|
raise
|
|
|
|
fmt = '<iI' if endian == 0 else '>iI'
|
|
|
|
d_tag, d_un = struct.unpack(fmt, buf)
|
|
super(ELF32_Dyn, self).__init__(d_tag, d_un)
|
|
|
|
class ELF64_Dyn(ELF_Dyn):
|
|
Dyn_SIZE = 8 * 2
|
|
def __init__(self, buf, endian = 0):
|
|
if len(buf) != self.Dyn_SIZE:
|
|
raise
|
|
|
|
fmt = '<qQ' if endian == 0 else '>qQ'
|
|
|
|
d_tag, d_un = struct.unpack(fmt, buf)
|
|
super(ELF64_Dyn, self).__init__(d_tag, d_un)
|
|
|
|
class ELF_Rel:
|
|
def __init__(self, r_offset, r_info):
|
|
self.r_offset = r_offset
|
|
self.r_info = r_info
|
|
|
|
class ELF32_Rel(ELF_Rel):
|
|
Rel_SIZE = 4 * 2
|
|
def __init__(self, buf, endian = 0, ptr = None):
|
|
if len(buf) != self.Rel_SIZE:
|
|
raise
|
|
|
|
self.ptr = ptr
|
|
self.fmt = '<II' if endian == 0 else '>II'
|
|
|
|
r_offset, r_info = struct.unpack(self.fmt, buf)
|
|
super(ELF32_Rel, self).__init__(r_offset, r_info)
|
|
|
|
@property
|
|
def r_type(self):
|
|
return self.r_info & 0xff
|
|
|
|
@property
|
|
def r_sym(self):
|
|
return self.r_info >> 8
|
|
|
|
def pack(self):
|
|
return struct.pack(self.fmt, self.r_offset, self.r_info)
|
|
|
|
class ELF64_Rel(ELF_Rel):
|
|
Rel_SIZE = 8 * 2
|
|
def __init__(self, buf, endian = 0, ptr = None):
|
|
if len(buf) != self.Rel_SIZE:
|
|
raise
|
|
|
|
self.ptr = ptr
|
|
self.fmt = '<QQ' if endian == 0 else '>QQ'
|
|
|
|
r_offset, r_info = struct.unpack(self.fmt, buf)
|
|
super(ELF64_Rel, self).__init__(r_offset, r_info)
|
|
|
|
@property
|
|
def r_type(self):
|
|
return self.r_info & 0xffffffff
|
|
|
|
@property
|
|
def r_sym(self):
|
|
return self.r_info >> 32
|
|
|
|
def pack(self):
|
|
return struct.pack(self.fmt, self.r_offset, self.r_info)
|
|
|
|
|
|
class ELF_Rela:
|
|
def __init__(self, r_offset, r_info, r_addend):
|
|
self.r_offset = r_offset
|
|
self.r_info = r_info
|
|
self.r_addend = r_addend
|
|
|
|
class ELF32_Rela(ELF_Rela):
|
|
Rela_SIZE = 4 * 3
|
|
def __init__(self, buf, endian = 0, ptr = None):
|
|
if len(buf) != self.Rela_SIZE:
|
|
raise
|
|
|
|
self.ptr = ptr
|
|
self.fmt = '<IIi' if endian == 0 else '>IIi'
|
|
|
|
r_offset, r_info, r_addend = struct.unpack(self.fmt, buf)
|
|
super(ELF32_Rela, self).__init__(r_offset, r_info, r_addend)
|
|
|
|
@property
|
|
def r_type(self):
|
|
return self.r_info & 0xff
|
|
|
|
@property
|
|
def r_sym(self):
|
|
return self.r_info >> 8
|
|
|
|
def pack(self):
|
|
return struct.pack(self.fmt, self.r_offset, self.r_info, self.r_addend)
|
|
|
|
class ELF64_Rela(ELF_Rela):
|
|
Rela_SIZE = 8 * 3
|
|
def __init__(self, buf, endian = 0, ptr = None):
|
|
if len(buf) != self.Rela_SIZE:
|
|
raise
|
|
|
|
self.ptr = ptr
|
|
self.fmt = '<QQq' if endian == 0 else '>QQq'
|
|
|
|
r_offset, r_info, r_addend = struct.unpack(self.fmt, buf)
|
|
super(ELF64_Rela, self).__init__(r_offset, r_info, r_addend)
|
|
|
|
@property
|
|
def r_type(self):
|
|
return self.r_info & 0xffffffff
|
|
|
|
@property
|
|
def r_sym(self):
|
|
return self.r_info >> 32
|
|
|
|
def pack(self):
|
|
return struct.pack(self.fmt, self.r_offset, self.r_info, self.r_addend)
|
|
|
|
class ELF_Sym:
|
|
def __init__(self, st_name ,st_value ,st_size ,st_info ,st_other ,st_shndx):
|
|
self.st_name = st_name
|
|
self.st_value = st_value
|
|
self.st_size = st_size
|
|
self.st_info = st_info
|
|
self.st_other = st_other
|
|
self.st_shndx = st_shndx
|
|
|
|
class ELF32_Sym(ELF_Sym):
|
|
Sym_SIZE = 4 * 4
|
|
def __init__(self, buf, endian = 0):
|
|
if len(buf) != self.Sym_SIZE:
|
|
raise
|
|
|
|
self.fmt = '<IIIBBH' if endian == 0 else '>IIIBBH'
|
|
|
|
st_name ,st_value ,st_size ,st_info ,st_other ,st_shndx = struct.unpack(self.fmt, buf)
|
|
super(ELF32_Sym, self).__init__(st_name ,st_value ,st_size ,st_info ,st_other ,st_shndx)
|
|
|
|
def pack(self):
|
|
struct.pack(self.fmt ,self.st_name ,self.st_value ,self.st_size ,self.st_info ,self.st_other ,self.st_shndx)
|
|
|
|
class ELF64_Sym(ELF_Sym):
|
|
Sym_SIZE = 8 * 3
|
|
def __init__(self, buf, endian = 0):
|
|
if len(buf) != self.Sym_SIZE:
|
|
raise
|
|
|
|
self.fmt = '<IBBHQQ' if endian == 0 else '>IBBHQQ'
|
|
|
|
st_name ,st_info ,st_other ,st_shndx ,st_value ,st_size = struct.unpack(self.fmt, buf)
|
|
super(ELF64_Sym, self).__init__(st_name ,st_value ,st_size ,st_info ,st_other ,st_shndx)
|
|
|
|
def pack(self):
|
|
struct.pack(self.fmt ,self.st_name ,self.st_info ,self.st_other ,self.st_shndx ,self.st_value ,self.st_size)
|
|
|
|
class ELF_Symtab:
|
|
def __init__(self, ql, symtab, endian = 0):
|
|
self.ql = ql
|
|
self.symtab = symtab
|
|
self.endian = endian
|
|
|
|
self.symclass = ELF32_Sym if self.ql.arch.bits == 32 else ELF64_Sym
|
|
|
|
def __getitem__(self, idx):
|
|
buf = self.ql.mem.read(self.symtab + idx * self.symclass.Sym_SIZE, self.symclass.Sym_SIZE)
|
|
return self.symclass(buf, self.endian)
|
|
|
|
def __setitem__(self, idx, sym):
|
|
self.ql.mem.write(self.symtab + idx * self.symclass.Sym_SIZE, sym.pack())
|
|
|
|
class ELF_Strtab:
|
|
def __init__(self, strtab):
|
|
self.strtab = bytes(strtab)
|
|
|
|
def __getitem__(self, idx):
|
|
return self.strtab[idx: self.strtab.index(b'\x00', idx)]
|
|
|
|
class FunctionHook:
|
|
def __init__(self, ql, phoff, phnum, phentsize, load_base, hook_mem):
|
|
self.ql = ql
|
|
self.hook_mem = hook_mem
|
|
self.phoff = phoff
|
|
self.phnum = phnum
|
|
self.phentsize = phentsize
|
|
self.load_base = load_base
|
|
self.add_function_hook = self.add_function_hook_default
|
|
|
|
self.dynamic = None
|
|
|
|
self.hash_nbucket = None
|
|
self.hash_nchain = None
|
|
self.hash_bucket = None
|
|
self.hash_chain = None
|
|
|
|
self.gnu_nbucket = None
|
|
self.gnu_symbias = None
|
|
self.gnu_maskwords = None
|
|
self.gnu_shift2 = None
|
|
self.gnu_bloom_filter = None
|
|
self.gnu_bucket = None
|
|
self.gnu_chain = None
|
|
|
|
self.strtab = None
|
|
self.strtab_size = None
|
|
|
|
self.symtab = None
|
|
self.syment = ELF32_Sym.Sym_SIZE if ql.arch.bits == 32 else ELF64_Sym.Sym_SIZE
|
|
|
|
self.plt_rel_size = None
|
|
self.plt_rel = None
|
|
self.plt_rel_type = DT_REL if ql.arch.bits == 32 else DT_RELA
|
|
|
|
self.rela = None
|
|
self.rela_size = None
|
|
self.relaent = ELF32_Rela.Rela_SIZE if ql.arch.bits == 32 else ELF64_Rela.Rela_SIZE
|
|
|
|
self.rel = None
|
|
self.rel_size = None
|
|
self.relent = ELF32_Rel.Rel_SIZE if ql.arch.bits == 32 else ELF64_Rel.Rel_SIZE
|
|
|
|
self.plt_got = None
|
|
self.mips_local_gotno = None
|
|
self.mips_symtabno = None
|
|
self.mips_gotsym = None
|
|
|
|
self.rel_list = []
|
|
self.endian = 0 if ql.arch.endian == QL_ENDIAN.EL else 1
|
|
|
|
# ARM
|
|
if self.ql.arch.type == QL_ARCH.ARM:
|
|
self.GLOB_DAT = 21
|
|
self.JMP_SLOT = 22
|
|
# orr r1, r1, r1
|
|
ins = b'\x01\x10\x81\xe1'
|
|
self.add_function_hook = self.add_function_hook_relocation
|
|
|
|
# MIPS32
|
|
elif self.ql.arch.type == QL_ARCH.MIPS:
|
|
self.GLOB_DAT = 21
|
|
self.JMP_SLOT = 22
|
|
# add $t9, $t9, $zero
|
|
ins = b' \xc8 \x03'
|
|
self.add_function_hook = self.add_function_hook_mips
|
|
|
|
# ARM64
|
|
elif self.ql.arch.type == QL_ARCH.ARM64:
|
|
self.GLOB_DAT = 1025
|
|
self.JMP_SLOT = 1026
|
|
# orr x1,x1,x1
|
|
ins = b'\x21\x00\x01\xaa'
|
|
self.add_function_hook = self.add_function_hook_relocation
|
|
|
|
# X86
|
|
elif self.ql.arch.type == QL_ARCH.X86:
|
|
self.GLOB_DAT = 6
|
|
self.JMP_SLOT = 7
|
|
# nop
|
|
ins = b'\x90'
|
|
self.add_function_hook = self.add_function_hook_relocation
|
|
|
|
# X8664
|
|
elif self.ql.arch.type == QL_ARCH.X8664:
|
|
self.GLOB_DAT = 6
|
|
self.JMP_SLOT = 7
|
|
# nop
|
|
ins = b'\x90'
|
|
self.add_function_hook = self.add_function_hook_relocation
|
|
|
|
elif self.ql.arch.type == QL_ARCH.RISCV:
|
|
self.GLOB_DAT = 21
|
|
self.JMP_SLOT = 22
|
|
|
|
# ADDI x0, x0, 0
|
|
ins = b'\x00\x01'
|
|
self.add_function_hook = self.add_function_hook_relocation
|
|
|
|
elif self.ql.arch.type == QL_ARCH.RISCV64:
|
|
self.GLOB_DAT = 21
|
|
self.JMP_SLOT = 22
|
|
|
|
# ADDI x0, x0, 0
|
|
ins = b'\x00\x01'
|
|
self.add_function_hook = self.add_function_hook_relocation
|
|
|
|
# PowerPC
|
|
elif self.ql.arch.type== QL_ARCH.PPC:
|
|
self.GLOB_DAT = 21
|
|
self.JMP_SLOT = 22
|
|
# nop
|
|
ins = b'\x60\x00\x00\x00'
|
|
self.add_function_hook = self.add_function_hook_relocation
|
|
|
|
self._parse()
|
|
if self.rel != None:
|
|
self.show_relocation(self.rel)
|
|
|
|
if self.rela != None:
|
|
self.rel_list += self.rela
|
|
self.show_relocation(self.rela)
|
|
|
|
if self.plt_rel != None:
|
|
self.rel_list += self.plt_rel
|
|
self.show_relocation(self.plt_rel)
|
|
|
|
if self.ql.arch.type == QL_ARCH.MIPS and self.plt_got != None and self.mips_gotsym != None and self.mips_local_gotno != None and self.mips_symtabno != None:
|
|
self.show_dynsym_name(self.mips_gotsym, self.mips_symtabno)
|
|
|
|
self.ql.mem.map(hook_mem, 0x2000, perms=7, info="[hook_mem]")
|
|
self.ql.mem.write(hook_mem, ins * (0x1000 // len(ins)))
|
|
|
|
self.free_list = [_ for _ in range(0, 0x1000, 0x10)]
|
|
self.use_list = {}
|
|
self.hook_list = {}
|
|
|
|
self.hook_int = False
|
|
|
|
def parse_program_header32(self):
|
|
# typedef struct elf32_phdr{
|
|
# Elf32_Word p_type;
|
|
# Elf32_Off p_offset;
|
|
# Elf32_Addr p_vaddr;
|
|
# Elf32_Addr p_paddr;
|
|
# Elf32_Word p_filesz;
|
|
# Elf32_Word p_memsz;
|
|
# Elf32_Word p_flags;
|
|
# Elf32_Word p_align;
|
|
# } Elf32_Phdr;
|
|
|
|
# /* 32-bit ELF base types. */
|
|
# typedef uint32_t Elf32_Addr;
|
|
# typedef uint16_t Elf32_Half;
|
|
# typedef uint32_t Elf32_Off;
|
|
# typedef int32_t Elf32_Sword;
|
|
# typedef uint32_t Elf32_Word;
|
|
|
|
Psize = int(self.phentsize)
|
|
Pnum = int(self.phnum)
|
|
Pdata = self.ql.mem.read(self.phoff, Pnum * Psize)
|
|
|
|
for i in range(Pnum):
|
|
buf = Pdata[i * ELF32_Phdr.Phdr_SIZE : (i + 1) * ELF32_Phdr.Phdr_SIZE]
|
|
P = ELF32_Phdr(buf, self.endian)
|
|
yield P
|
|
return
|
|
|
|
def parse_program_header64(self):
|
|
# typedef struct elf64_phdr {
|
|
# Elf64_Word p_type;
|
|
# Elf64_Word p_flags;
|
|
# Elf64_Off p_offset; /* Segment file offset */
|
|
# Elf64_Addr p_vaddr; /* Segment virtual address */
|
|
# Elf64_Addr p_paddr; /* Segment physical address */
|
|
# Elf64_Xword p_filesz; /* Segment size in file */
|
|
# Elf64_Xword p_memsz; /* Segment size in memory */
|
|
# Elf64_Xword p_align; /* Segment alignment, file & memory */
|
|
# } Elf64_Phdr;
|
|
|
|
# /* 64-bit ELF base types. */
|
|
# typedef uint64_t Elf64_Addr;
|
|
# typedef uint16_t Elf64_Half;
|
|
# typedef int16_t Elf64_SHalf;
|
|
# typedef uint64_t Elf64_Off;
|
|
# typedef int32_t Elf64_Sword;
|
|
# typedef uint32_t Elf64_Word;
|
|
# typedef uint64_t Elf64_Xword;
|
|
# typedef int64_t Elf64_Sxword;
|
|
|
|
Psize = int(self.phentsize)
|
|
Pnum = int(self.phnum)
|
|
Pdata = self.ql.mem.read(self.phoff, Pnum * Psize)
|
|
|
|
for i in range(Pnum):
|
|
buf = Pdata[i * ELF64_Phdr.Phdr_SIZE : (i + 1) * ELF64_Phdr.Phdr_SIZE]
|
|
P = ELF64_Phdr(buf, self.endian)
|
|
yield P
|
|
return
|
|
|
|
def parse_program_header(self):
|
|
if self.ql.arch.bits == 64:
|
|
return self.parse_program_header64()
|
|
elif self.ql.arch.bits == 32:
|
|
return self.parse_program_header32()
|
|
|
|
def parse_dynamic64(self):
|
|
# typedef struct
|
|
# {
|
|
# Elf64_Sxword d_tag; /* Dynamic entry type */
|
|
# union
|
|
# {
|
|
# Elf64_Xword d_val; /* Integer value */
|
|
# Elf64_Addr d_ptr; /* Address value */
|
|
# } d_un;
|
|
# } Elf64_Dyn;
|
|
|
|
# /* 64-bit ELF base types. */
|
|
# typedef uint64_t Elf64_Addr;
|
|
# typedef uint16_t Elf64_Half;
|
|
# typedef int16_t Elf64_SHalf;
|
|
# typedef uint64_t Elf64_Off;
|
|
# typedef int32_t Elf64_Sword;
|
|
# typedef uint32_t Elf64_Word;
|
|
# typedef uint64_t Elf64_Xword;
|
|
# typedef int64_t Elf64_Sxword;
|
|
|
|
Dsize = ELF64_Dyn.Dyn_SIZE
|
|
idx = 0
|
|
|
|
while True:
|
|
buf = self.ql.mem.read(self.dynamic + idx * Dsize, Dsize)
|
|
D = ELF64_Dyn(buf, self.endian)
|
|
yield D
|
|
idx += 1
|
|
if D.d_tag == DT_NULL:
|
|
break
|
|
return
|
|
|
|
def parse_dynamic32(self):
|
|
# typedef struct
|
|
# {
|
|
# Elf32_Sword d_tag; /* Dynamic entry type */
|
|
# union
|
|
# {
|
|
# Elf32_Word d_val; /* Integer value */
|
|
# Elf32_Addr d_ptr; /* Address value */
|
|
# } d_un;
|
|
# } Elf32_Dyn;
|
|
|
|
# /* 32-bit ELF base types. */
|
|
# typedef uint32_t Elf32_Addr;
|
|
# typedef uint16_t Elf32_Half;
|
|
# typedef uint32_t Elf32_Off;
|
|
# typedef int32_t Elf32_Sword;
|
|
# typedef uint32_t Elf32_Word;
|
|
Dsize = ELF32_Dyn.Dyn_SIZE
|
|
idx = 0
|
|
|
|
while True:
|
|
buf = self.ql.mem.read(self.dynamic + idx * Dsize, Dsize)
|
|
D = ELF32_Dyn(buf, self.endian)
|
|
yield D
|
|
idx += 1
|
|
if D.d_tag == DT_NULL:
|
|
break
|
|
return
|
|
|
|
def parse_dynamic(self):
|
|
if self.ql.arch.bits == 64:
|
|
return self.parse_dynamic64()
|
|
elif self.ql.arch.bits == 32:
|
|
return self.parse_dynamic32()
|
|
|
|
def _parse(self):
|
|
for p in self.parse_program_header():
|
|
if p.p_type == PT_DYNAMIC:
|
|
self.dynamic = p.p_vaddr + self.load_base
|
|
break
|
|
|
|
if self.dynamic == None:
|
|
return
|
|
|
|
for d in self.parse_dynamic():
|
|
if d.d_tag == DT_NULL:
|
|
break
|
|
elif d.d_tag == DT_HASH:
|
|
# self.hash_nbucket = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un, self.ql.arch.pointersize))
|
|
# self.hash_nchain = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize, self.ql.arch.pointersize))
|
|
# self.hash_bucket = self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize * 2, self.ql.arch.pointersize * self.hash_nbucket)
|
|
# self.hash_chain = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize * 2 + self.ql.arch.pointersize * self.hash_nbucket, self.ql.arch.pointersize))
|
|
pass
|
|
elif d.d_tag == DT_GNU_HASH:
|
|
# self.gnu_nbucket = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un, self.ql.arch.pointersize))
|
|
# self.gnu_symbias = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize, self.ql.arch.pointersize))))
|
|
# self.gnu_maskwords = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize * 2, self.ql.arch.pointersize))
|
|
# self.gnu_shift2 = self.ql.unpack(self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize * 3, self.ql.arch.pointersize))
|
|
# self.gnu_bloom_filter = self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize * 4, self.gnu_maskwords)
|
|
# self.gnu_bucket = self.ql.mem.read(self.load_base + d.d_un + self.ql.arch.pointersize * 4 + self.gnu_maskwords, self.ql.arch.pointersize * self.gnu_nbucket)
|
|
# self.gnu_chain = self.load_base + d.d_un + self.ql.arch.pointersize * 4 + self.gnu_maskwords + self.ql.arch.pointersize * self.gnu_nbucket - self.ql.arch.pointersize * self.gnu_symbias
|
|
pass
|
|
|
|
elif d.d_tag == DT_STRTAB:
|
|
self.strtab = d.d_un + self.load_base
|
|
elif d.d_tag == DT_STRSZ:
|
|
self.strtab_size = d.d_un
|
|
|
|
elif d.d_tag == DT_SYMTAB:
|
|
self.symtab = d.d_un + self.load_base
|
|
elif d.d_tag == DT_SYMENT:
|
|
if d.d_un != self.syment:
|
|
raise
|
|
|
|
elif d.d_tag == DT_PLTREL:
|
|
if d.d_un != self.plt_rel_type:
|
|
# FIXME: I don't why it is a error
|
|
# but it is triggered in riscv32
|
|
pass
|
|
elif d.d_tag == DT_PLTRELSZ:
|
|
self.plt_rel_size = d.d_un
|
|
elif d.d_tag == DT_JMPREL:
|
|
self.plt_rel = d.d_un + self.load_base
|
|
|
|
elif d.d_tag == DT_RELA:
|
|
self.rela = d.d_un + self.load_base
|
|
elif d.d_tag == DT_RELASZ:
|
|
self.rela_size = d.d_un
|
|
elif d.d_tag == DT_RELAENT:
|
|
if self.relaent != d.d_un:
|
|
raise
|
|
|
|
elif d.d_tag == DT_REL:
|
|
self.rel = d.d_un + self.load_base
|
|
elif d.d_tag == DT_RELSZ:
|
|
self.rel_size = d.d_un
|
|
elif d.d_tag == DT_RELENT:
|
|
if self.relent != d.d_un:
|
|
raise
|
|
|
|
elif d.d_tag == DT_PLTGOT:
|
|
self.plt_got = d.d_un
|
|
|
|
elif d.d_tag == DT_MIPS_LOCAL_GOTNO:
|
|
self.mips_local_gotno = d.d_un
|
|
elif d.d_tag == DT_MIPS_SYMTABNO:
|
|
self.mips_symtabno = d.d_un
|
|
elif d.d_tag == DT_MIPS_GOTSYM:
|
|
self.mips_gotsym = d.d_un
|
|
|
|
elif d.d_tag == DT_NEEDED:
|
|
pass
|
|
else:
|
|
pass
|
|
|
|
if self.strtab != None and self.strtab_size != None:
|
|
self.strtab = ELF_Strtab(self.ql.mem.read(self.strtab, self.strtab_size))
|
|
|
|
if self.rela != None and self.rela_size != None:
|
|
rela_buf = self.ql.mem.read(self.rela, self.rela_size)
|
|
rela_ptr = self.rela
|
|
if self.ql.arch.bits == 32:
|
|
self.rela = [ELF32_Rela(rela_buf[_ * self.relaent : (_ + 1) * self.relaent], self.endian, rela_ptr + _ * self.relaent) for _ in range(self.rela_size // self.relaent)]
|
|
elif self.ql.arch.bits == 64:
|
|
self.rela = [ELF64_Rela(rela_buf[_ * self.relaent : (_ + 1) * self.relaent], self.endian, rela_ptr + _ * self.relaent) for _ in range(self.rela_size // self.relaent)]
|
|
|
|
if self.rel != None and self.rel_size != None:
|
|
rel_buf = self.ql.mem.read(self.rel, self.rel_size)
|
|
rel_ptr = self.rel
|
|
if self.ql.arch.bits == 32:
|
|
self.rel = [ELF32_Rel(rel_buf[_ * self.relent : (_ + 1) * self.relent], self.endian, rel_ptr + _ * self.relent) for _ in range(self.rel_size // self.relent)]
|
|
elif self.ql.arch.bits == 64:
|
|
self.rel = [ELF64_Rel(rel_buf[_ * self.relent : (_ + 1) * self.relent], self.endian, rel_ptr + _ * self.relent) for _ in range(self.rel_size // self.relent)]
|
|
|
|
if self.plt_rel != None and self.plt_rel_size != None:
|
|
plt_rel_buf = self.ql.mem.read(self.plt_rel, self.plt_rel_size)
|
|
plt_rel_ptr = self.plt_rel
|
|
if self.plt_rel_type == DT_REL:
|
|
if self.ql.arch.bits == 32:
|
|
self.plt_rel = [ELF32_Rel(plt_rel_buf[_ * self.relent : (_ + 1) * self.relent], self.endian, plt_rel_ptr + _ * self.relent) for _ in range(self.plt_rel_size // self.relent)]
|
|
elif self.ql.arch.bits == 64:
|
|
self.plt_rel = [ELF64_Rel(plt_rel_buf[_ * self.relent : (_ + 1) * self.relent], self.endian, plt_rel_ptr + _ * self.relent) for _ in range(self.plt_rel_size // self.relent)]
|
|
else:
|
|
if self.ql.arch.bits == 32:
|
|
self.plt_rel = [ELF32_Rela(plt_rel_buf[_ * self.relaent : (_ + 1) * self.relaent], self.endian, plt_rel_ptr + _ * self.relaent) for _ in range(self.plt_rel_size // self.relaent)]
|
|
elif self.ql.arch.bits == 64:
|
|
self.plt_rel = [ELF64_Rela(plt_rel_buf[_ * self.relaent : (_ + 1) * self.relaent], self.endian, plt_rel_ptr + _ * self.relaent) for _ in range(self.plt_rel_size // self.relaent)]
|
|
|
|
if self.symtab != None:
|
|
self.symtab = ELF_Symtab(self.ql, self.symtab, self.endian)
|
|
|
|
|
|
def show_relocation(self, rel):
|
|
for r in rel:
|
|
if (r.r_type == self.JMP_SLOT or r.r_type == self.GLOB_DAT) and r.r_sym != 0:
|
|
rel_name = self.strtab[self.symtab[r.r_sym].st_name]
|
|
self.ql.log.debug('rel name ' + str(rel_name))
|
|
|
|
def show_dynsym_name(self, s, e):
|
|
for symidx in range(s, e):
|
|
rel_name = self.strtab[self.symtab[symidx].st_name]
|
|
self.ql.log.debug('dynsym name ' + str(rel_name))
|
|
|
|
def _hook_int(self, ql, intno):
|
|
idx = (self.ql.arch.regs.arch_pc - self.hook_mem) // 0x10
|
|
|
|
if idx not in self.use_list.keys():
|
|
raise
|
|
|
|
self.use_list[idx].call()
|
|
|
|
def _hook_function(self, fn, r, cb, pos, userdata):
|
|
if fn in self.hook_list.keys():
|
|
self.hook_list[fn].add_hook(cb, pos, userdata)
|
|
return
|
|
|
|
hf = HookFuncRel(self.ql, fn, r, self.load_base)
|
|
hf.add_hook(cb, pos, userdata)
|
|
|
|
if len(self.free_list) == 0:
|
|
raise
|
|
|
|
hf.idx = self.free_list[0]
|
|
del self.free_list[0]
|
|
|
|
hf.hook_fuc_ptr = hf.idx * 0x10 + self.hook_mem
|
|
hf.hook_data_ptr = hf.idx * 0x10 + self.hook_mem + 0x1000
|
|
|
|
self.use_list[hf.idx] = hf
|
|
self.hook_list[fn] = hf
|
|
|
|
hf.enable()
|
|
|
|
# if self.hook_int == False:
|
|
# if self.ql.arch.type == QL_ARCH.X86 or self.ql.arch.type == QL_ARCH.X8664:
|
|
# self.ql.hook_intno(self._hook_int, 0xa0)
|
|
# elif self.ql.arch.type == QL_ARCH.ARM or self.ql.arch.type == QL_ARCH.ARM64:
|
|
# self.ql.hook_intno(self._hook_int, 7)
|
|
# self.hook_int = True
|
|
|
|
|
|
def add_function_hook_relocation(self, funcname, cb, pos, userdata = None):
|
|
if type(funcname) != str:
|
|
raise
|
|
|
|
for r in self.rel_list:
|
|
if (r.r_type == self.JMP_SLOT or r.r_type == self.GLOB_DAT) and r.r_sym != 0:
|
|
tmp_name = self.strtab[self.symtab[r.r_sym].st_name]
|
|
if tmp_name == funcname.encode():
|
|
self._hook_function(tmp_name, r, cb, pos, userdata)
|
|
|
|
def add_function_hook_default(self, funcname, cb, userdata = None):
|
|
pass
|
|
|
|
def add_function_hook_mips(self, funcname, cb, pos, userdata = None):
|
|
self.add_function_hook_relocation(funcname, cb, userdata)
|
|
|
|
for symidx in range(self.mips_gotsym, self.mips_symtabno):
|
|
tmp_name = self.strtab[self.symtab[symidx].st_name]
|
|
if tmp_name == funcname.encode():
|
|
fn = tmp_name
|
|
if fn in self.hook_list.keys():
|
|
self.hook_list[fn].add_hook(cb, pos, userdata)
|
|
return
|
|
|
|
hf = HookFuncMips(self.ql, fn, self.plt_got, symidx - self.mips_gotsym + self.mips_local_gotno, self.load_base)
|
|
hf.add_hook(cb, pos, userdata)
|
|
|
|
if len(self.free_list) == 0:
|
|
raise
|
|
|
|
hf.idx = self.free_list[0]
|
|
del self.free_list[0]
|
|
|
|
hf.hook_fuc_ptr = hf.idx * 0x10 + self.hook_mem
|
|
hf.hook_data_ptr = hf.idx * 0x10 + self.hook_mem + 0x1000
|
|
|
|
self.use_list[hf.idx] = hf
|
|
self.hook_list[fn] = hf
|
|
|
|
hf.enable()
|
|
|
|
# if self.hook_int == False:
|
|
# self.ql.hook_intno(self._hook_int, 0xa0)
|
|
# self.hook_int = True
|
|
|
|
def _load_import(self):
|
|
pass |