Initial public commit.

This commit is contained in:
Cody Brocious 2017-05-16 14:29:03 -06:00
commit bd045eeea7
9 changed files with 32919 additions and 0 deletions

22
bundler.py Normal file
View file

@ -0,0 +1,22 @@
import struct
from glob import glob
mainaddr = raw_input('Enter main module address: ')
if mainaddr.startswith('0x'):
mainaddr = mainaddr[2:]
mainaddr = int(mainaddr, 16)
print 'Main at 0x%016x' % mainaddr
wkcaddr = raw_input('Enter wkc module address: ')
if wkcaddr.startswith('0x'):
wkcaddr = wkcaddr[2:]
wkcaddr = int(wkcaddr, 16)
print 'WKC at 0x%016x' % wkcaddr
with file('membundle.bin', 'wb') as fp:
files = glob('memdumps/*.bin')
fp.write(struct.pack('<IQQ', len(files), mainaddr, wkcaddr))
for fn in files:
addr = int(fn[11:].split(' ')[0], 16)
data = file(fn, 'rb').read()
fp.write(struct.pack('<QI', addr, len(data)))
fp.write(data)

291
ceval.py Normal file
View file

@ -0,0 +1,291 @@
from pycparser.c_parser import CParser
from pycparser.c_ast import *
import struct
class AstTranslator(object):
def process(self, ast):
node = ast.__class__.__name__
if hasattr(self, node):
func = getattr(self, node)
fcode = func.im_func.func_code
argnames = fcode.co_varnames[1:fcode.co_argcount]
args = [getattr(ast, name) for name in argnames]
return func(*args)
else:
print 'Unhandled AST node:'
ast.show()
return '?unknown?'
def UnaryOp(self, op, expr):
opmap = {'*' : 'deref', '-' : 'neg', '!' : 'not', '~' : 'bnot'}
return opmap[op], self.process(expr)
def BinaryOp(self, op, left, right):
return op, self.process(left), self.process(right)
def Cast(self, to_type, expr):
return 'cast', self.process(to_type), self.process(expr)
def ID(self, name):
return 'register', name
def Typename(self, type):
return self.process(type)
def TypeDecl(self, type):
return self.process(type)
def IdentifierType(self, names):
return ' '.join(names)
def PtrDecl(self, type):
return 'ptr', self.process(type)
def ArrayRef(self, name, subscript):
return '[]', self.process(name), self.process(subscript)
def Constant(self, type, value):
if type == 'int':
if value.startswith('0x'):
return int(value[2:], 16)
elif value.startswith('0b'):
return int(value[2:], 2)
elif value.startswith('0'):
return int(value, 8)
else:
return int(value)
elif type == 'float':
return float(value)
else:
print 'Unknown constant type:', type, `value`
return '?unkconst?'
def Assignment(self, op, lvalue, rvalue):
lvalue = self.process(lvalue)
rvalue = self.process(rvalue)
if op != '=':
rvalue = op[0], lvalue, rvalue
return '=', lvalue, rvalue
dispatchers = {}
def ddp(name):
def sub(func):
dispatchers[name] = func
return func
if callable(name):
dispatchers[name.func_name] = name
return name
return sub
class Register(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name
class TypedValue(object):
def __init__(self, type, value):
self.type, self.value = type, value
while isinstance(self.value, TypedValue):
self.value = self.value.value
def __repr__(self):
return '%s:%r' % (self.type, self.value)
@property
def stride(self):
return int(self.type[1:].rstrip('*')) / 8
@property
def pointer(self):
return '*' in self.type
def bare(value):
if isinstance(value, TypedValue):
return value.value
else:
return value
def autotype(value):
if isinstance(value, TypedValue):
return value
elif isinstance(value, float):
return TypedValue('f64', value)
elif isinstance(value, str):
return value
else:
return TypedValue('i64', value)
class SexpRunner(object):
def __init__(self, ctu):
self.ctu = ctu
def run(self, sexp, rvalue=None):
if not isinstance(sexp, tuple):
return autotype(sexp)
if sexp[0] in dispatchers:
if rvalue is None:
return dispatchers[sexp[0]](self, *sexp[1:])
else:
return dispatchers[sexp[0]](self, *tuple(list(sexp[1:]) + [rvalue]))
else:
print 'Unhandled S-exp:', sexp
return None
@ddp('=')
def assign(self, left, right):
return self.run(left, self.run(right))
@ddp('[]')
def subscript(self, base, sub, ass=None):
base, sub = self.run(base), self.run(sub)
addr = bare(base) + bare(sub) * base.stride
return self.deref(TypedValue(base.type, addr), ass)
@ddp
def deref(self, ptr, ass=None):
ptr = self.run(ptr)
assert ptr.pointer
fmtmap = dict(u8='B', i8='b', u16='H', i16='h', u32='I', i32='i', u64='L', i64='l', f32='f', f64='d')
fmt = fmtmap[ptr.type.rstrip('*')]
size = struct.calcsize(fmt)
if ass is None:
data = self.ctu.readmem(bare(ptr), size)
return TypedValue(ptr.type[:-1], struct.unpack(fmt, data)[0])
else:
self.ctu.writemem(bare(ptr), struct.pack(fmt, bare(ass)))
@ddp
def register(self, name, ass=None):
name = name.upper()
if name == 'PC':
if ass is None:
return TypedValue('u64', self.ctu.pc)
else:
self.ctu.pc = bare(ass)
else:
typemap = dict(X='u64', W='u32', D='f64', Q='f128')
assert name[0] in 'XW' # XXX: Add float support
if ass is None:
type = typemap[name[0]]
value = self.ctu.reg(int(name[1:]))
if type == 'u32':
value &= 0xFFFFFFFF
return TypedValue(type, value)
else:
self.ctu.reg(int(name[1:]), bare(ass))
@ddp
def cast(self, type, value):
return TypedValue(self.run(type), self.run(value))
@ddp
def ptr(self, type):
return self.run(type) + '*'
@ddp('+')
def add(self, a, b):
a, b = self.run(a), self.run(b)
if b.pointer and not a.pointer:
a, b = b, a
if a.pointer and not b.pointer:
return TypedValue(a.type, bare(a) + bare(b) * a.stride)
return TypedValue(a.type, bare(a) + bare(b))
@ddp('-')
def sub(self, a, b):
a, b = self.run(a), self.run(b)
if b.pointer and not a.pointer:
return TypedValue(b.type, bare(a) * b.stride - bare(b))
elif a.pointer and not b.pointer:
return TypedValue(a.type, bare(a) - bare(b) * a.stride)
return TypedValue(a.type, bare(a) + bare(b))
@ddp('*')
def mul(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue(a.type, bare(a) * bare(b))
@ddp('/')
def div(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue(a.type, bare(a) / bare(b))
@ddp('==')
def eq(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if a.value == b.value else 0)
@ddp('!=')
def ne(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if a.value != b.value else 0)
@ddp('>')
def gt(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if a.value > b.value else 0)
@ddp('>=')
def ge(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if a.value >= b.value else 0)
@ddp('<')
def lt(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if a.value < b.value else 0)
@ddp('<=')
def le(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if a.value <= b.value else 0)
@ddp('&&')
def booland(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if bool(a.value) and bool(b.value) else 0)
@ddp('||')
def boolor(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if bool(a.value) or bool(b.value) else 0)
def compile(code):
parser = CParser()
stypes = 'u8 i8 u16 i16 u32 i32 u64 i64 f32 f64 f128'
code = 'void runner() { ' + code + ' ; }'
for type in stypes.split(' '):
code = 'typedef void %s; %s' % (type, code)
ast = parser.parse(code)
found = None
for _, child in ast.children():
if isinstance(child, FuncDef):
found = child
break
assert found is not None
assert len(found.body.children()) == 1
ast = found.body.children()[0][1]
sexp = AstTranslator().process(ast)
def run(ctu):
return bare(SexpRunner(ctu).run(sexp))
return run
def ceval(code, ctu):
return compile(code)(ctu)

617
ctu.py Normal file
View file

@ -0,0 +1,617 @@
import gzip, math, os, re, struct, sys
from cmd import Cmd
import colorama
from colorama import Fore, Back, Style
from unicorn import *
from unicorn.arm64_const import *
from capstone import *
from ceval import ceval, compile
from util import *
from svc import SvcHandler
TRACE_NONE = 0
TRACE_INSTRUCTION = 1
TRACE_BLOCK = 2
TRACE_FUNCTION = 4
def colorDepth(depth):
colors = [Fore.RED, Fore.WHITE, Fore.GREEN, Fore.YELLOW, Style.BRIGHT + Fore.BLUE, Fore.MAGENTA, Fore.CYAN]
return colors[depth % len(colors)]
class CTU(Cmd, object):
def __init__(self, flags=0):
Cmd.__init__(self)
colorama.init()
self.initialized = False
self.flags = 0
self.sublevel = 0
self.breakpoints = set()
self.watchpoints = []
self.mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM)
self.md = Cs(CS_ARCH_ARM64, CS_MODE_ARM)
self.mu.hook_add(UC_HOOK_CODE, self.hook_insn_bytes)
self.mu.hook_add(UC_HOOK_BLOCK, self.trace_block)
self.insnhooks = {}
self.termaddr = 1 << 61 # Pseudoaddress upon which to terminate execution
self.mu.mem_map(self.termaddr, 0x1000)
self.mu.mem_write(self.termaddr, '\x1F\x20\x03\xD5') # NOP
self.stacktop = 1 << 48
self.stacksize = 8 * 1024 * 1024 # 8MB
self.mu.mem_map(self.stacktop - self.stacksize, self.stacksize)
self.heapbase = 1 << 50
self.heapsize = 32 * 1024 * 1024 # 32MB
self.heapoff = 0
self.mu.mem_map(self.heapbase, self.heapsize)
self.tlsbase = 1 << 49
self.tlssize = 1024 * 1024 # 1MB
self.mu.mem_map(self.tlsbase, self.tlssize)
for i in xrange(30):
self.hookinsn(0xD53BD060 + i, (lambda i: lambda _, __: self.tlshook(i))(i))
self.svch = SvcHandler(self)
self.reset()
self.enableFP()
self.execfunc = None
self.initialized = True
def reset(self):
self.debugging = False
self.started = False
self.restarting = False
self.singlestep = False
self.skipbp = False
self.lastinsn = None
self.blx = False
self.callstack = []
self.loadmemory()
if not self.initialized:
mapLoader('main.map', MainAddress, 0x19e5006000)
mapLoader('webkit_wkc.map', WKCAddress, 0x3C0D91E000)
self.heapoff = 0
self.mu.mem_write(self.heapbase, '\0' * self.heapsize)
self.mu.mem_write(self.stacktop - self.stacksize, '\0' * self.stacksize)
self.mu.mem_write(self.tlsbase + 0x1F8, struct.pack('<Q', 0x5AF9C2D000 + 0xFD3840))
for i in xrange(32):
self.reg(i, 0)
def setup(self, func):
self.execfunc = func
def run(self, flags=0):
self.reset()
assert self.execfunc is not None
self.flags = flags
self.execfunc(self)
def enableFP(self):
addr = 1 << 62
self.mu.mem_map(addr, 0x1000)
self.mu.mem_write(addr, '\x41\x10\x38\xd5\x00\x00\x01\xaa\x40\x10\x18\xd5\x40\x10\x38\xd5\xc0\x03\x5f\xd6')
assert (self.call(addr, 3 << 20) >> 20) & 3 == 3
def loadmemory(self):
if not os.path.isfile('membundle.bin'):
with gzip.GzipFile('membundle.bin.gz', 'rb') as ifp:
with file('membundle.bin', 'wb') as ofp:
print 'Decompressing membundle'
ofp.write(ifp.read())
print 'Done!'
with file('membundle.bin', 'rb') as fp:
regions, mainbase, wkcbase = struct.unpack('<IQQ', fp.read(20))
if not self.initialized:
rmap = []
for i in xrange(regions):
addr, dlen = struct.unpack('<QI', fp.read(12))
data = fp.read(dlen)
if not self.initialized:
self.mu.mem_map(addr, dlen)
rmap.append((addr, dlen))
self.mu.mem_write(addr, data)
if not self.initialized:
MainAddress.realbase = mainbase
WKCAddress.realbase = wkcbase
MainAddress.realsize = WKCAddress.realsize = 0
inMain = inWKC = False
last = 0
rmap.sort(key=lambda x: x[0])
for (addr, dlen) in rmap:
if addr == mainbase:
inMain = True
last = addr
elif addr == wkcbase:
inWKC = True
last = addr
if (inMain or inWKC) and last != addr:
inMain = inWKC = False
elif inMain:
MainAddress.realsize += dlen
last = addr + dlen
elif inWKC:
WKCAddress.realsize += dlen
last = addr + dlen
def trace_insn(self, mu, addr, size, user_data):
if not self.initialized:
return
for ins in self.md.disasm(str(mu.mem_read(addr, size)), addr):
print("0x%08x: %s %s" % (ins.address, ins.mnemonic, ins.op_str))
def trace_block(self, mu, addr, size, user_data):
if not self.initialized or (self.flags & TRACE_BLOCK) == 0:
return
print 'Block at %s - %s' % (raw(addr, pad=True), raw(addr + size, pad=True))
if addr == MainAddress(0x1ec928) or addr == MainAddress(0x1ec9e0) or addr == MainAddress(0x1ecab4):
print '\nFATAL:\n%s\n%s\n%s\n' % (
self.readmem(self.reg(0), 0x100).split('\0', 1)[0],
self.readmem(self.reg(1), 0x100).split('\0', 1)[0],
self.readmem(self.reg(2), 0x100).split('\0', 1)[0]
)
self.stop()
def trace_func(self, mu, addr, size, user_data):
if not self.initialized:
return
if self.blx:
self.callstack.append(addr)
print ' ' * len(self.callstack) + colorDepth(len(self.callstack)) + '-> %s' % raw(addr), Style.RESET_ALL
self.blx = False
insn, = struct.unpack('<I', self.mu.mem_read(addr, 4))
bl_mask = 0b11101100 << 24
bl_match = 0b10000100 << 24
blr_mask = 0b011011111010 << 20
blr_match = 0b010001100010 << 20
ret_mask = 0b011011110110 << 20
ret_match = 0b010001100100 << 20
if (insn & bl_mask) == bl_match or (insn & blr_mask) == blr_match:
self.blx = True
elif (insn & ret_mask) == ret_match:
print ' ' * len(self.callstack) + colorDepth(len(self.callstack)) + '<- %s' % raw(self.callstack.pop()), Style.RESET_ALL
def hook_insn_bytes(self, mu, addr, size, user_data):
if self.restarting:
return
if self.skipbp and not self.singlestep:
self.skipbp = False
elif self.singlestep or addr in self.breakpoints:
if self.singlestep:
self.singlestep = False
else:
print 'Breakpoint at %s' % raw(addr)
self.skipbp = True
self.debugbreak()
else:
for code, func in self.watchpoints:
if func(self):
print 'Watchpoint %s triggered at %s' % (code, raw(addr))
self.skipbp = True
self.debugbreak()
break
if self.flags & TRACE_INSTRUCTION and self.flags & TRACE_FUNCTION:
if self.blx:
self.trace_func(mu, addr, size, user_data)
self.trace_insn(mu, addr, size, user_data)
else:
self.trace_insn(mu, addr, size, user_data)
self.trace_func(mu, addr, size, user_data)
elif self.flags & TRACE_INSTRUCTION:
self.trace_insn(mu, addr, size, user_data)
elif self.flags & TRACE_FUNCTION:
self.trace_func(mu, addr, size, user_data)
self.lastinsn = addr
insn, = struct.unpack('<I', self.mu.mem_read(addr, 4))
if insn in self.insnhooks:
if self.insnhooks[insn](self, addr) == False:
self.pc += 4
def hookinsn(self, insn, func=None):
def sub(func):
assert insn not in self.insnhooks
self.insnhooks[insn] = func
if func is None:
return sub
sub(func)
def tlshook(self, reg):
self.reg(reg, self.tlsbase)
return False
def call(self, pc, *args):
self.started = True
self.callstack.append(pc)
self.mu.reg_write(UC_ARM64_REG_X30, self.termaddr)
self.mu.reg_write(UC_ARM64_REG_SP, self.stacktop)
for i, v in enumerate(args):
self.mu.reg_write(UC_ARM64_REG_X0 + i, v)
try:
self.mu.emu_start(native(pc), self.termaddr + 4)
except:
import traceback
traceback.print_exc()
print 'Exception at %s' % raw(self.lastinsn)
self.dumpregs()
self.started = False
if self.restarting:
self.restarting = False
raise Restart()
return self.mu.reg_read(UC_ARM64_REG_X0)
def stop(self):
self.mu.reg_write(UC_ARM64_REG_PC, self.termaddr)
def malloc(self, size):
self.heapoff += size
assert self.heapoff <= self.heapsize
return self.heapbase + self.heapoff - size
def writemem(self, addr, data):
try:
self.mu.mem_write(addr, data)
return True
except unicorn.UcError:
return False
def readmem(self, addr, size):
try:
return self.mu.mem_read(native(addr), size)
except unicorn.UcError:
return None
def reg(self, i, val=None):
sr = {'LR': 30, 'SP': 31}
for ri in xrange(32):
sr['X%i' % ri] = ri
if isinstance(i, str) and i.upper() in sr:
i = sr[i.upper()]
if i <= 28:
c = UC_ARM64_REG_X0 + i
elif i == 29 or i == 30:
c = UC_ARM64_REG_X29 + i - 29
elif i == 31:
c = UC_ARM64_REG_SP
else:
return None
if val is None:
return self.mu.reg_read(c)
else:
self.mu.reg_write(c, native(val))
return True
@property
def pc(self):
return self.mu.reg_read(UC_ARM64_REG_PC)
@pc.setter
def pc(self, val):
self.mu.reg_write(UC_ARM64_REG_PC, val)
def dumpregs(self):
sr = {30: 'LR', 31: 'SP'}
print '-' * 52
for i in xrange(0, 32, 2):
an = sr[i] if i in sr else 'X%i' % i
bn = sr[i + 1] if i + 1 in sr else 'X%i' % (i + 1)
an += ' ' * (3 - len(an))
bn += ' ' * (3 - len(bn))
print '%s - 0x%016x %s - 0x%016x' % (
an, self.reg(i),
bn, self.reg(i + 1)
)
print '-' * 52
print
def dumpmem(self, addr, size):
addr = native(addr)
data = self.readmem(addr, size)
if data is None:
print 'Unmapped memory at %s' % raw(addr)
return
fmt = '%%0%ix |' % (int(math.log(addr + size, 16)) + 1)
for i in xrange(0, len(data), 16):
print fmt % (addr + i),
ascii = ''
for j in xrange(16):
if i + j < len(data):
print '%02x' % data[i + j],
if 0x20 <= data[i+j] <= 0x7E:
ascii += chr(data[i+j])
else:
ascii += '.'
else:
print ' ',
ascii += ' '
if j == 7:
print '',
ascii += ' '
print '|', ascii
def reprompt(self):
if self.started:
self.prompt = 'ctu %s> ' % raw(self.mu.reg_read(UC_ARM64_REG_PC))
else:
self.prompt = 'ctu> '
def debug(self, sub=False):
self.debugging = True
self.reprompt()
try:
self.sublevel += 1
while True:
try:
self.cmdloop()
break
except KeyboardInterrupt:
print
finally:
self.sublevel -= 1
if self.sublevel == 1:
self.prompt = 'ctu> '
def debugbreak(self):
try:
self.debug(sub=True)
except Restart:
self.restarting = True
return self.stop()
def print_topics(self, header, cmds, cmdlen, maxcol):
nix = 'EOF', 'b', 'c', 's', 'r', 't'
if header is not None:
Cmd.print_topics(self, header, [cmd for cmd in cmds if cmd not in nix], cmdlen, maxcol)
def do_EOF(self, line):
print
try:
if raw_input('Really exit? y/n: ').startswith('y'):
sys.exit()
except EOFError:
print
sys.exit()
def do_exit(self, line):
"""exit
Exit the debugger."""
sys.exit()
def do_start(self, line):
"""s/start
Start or restart the code."""
if self.sublevel != 1:
raise Restart()
while True:
self.reset()
try:
self.execfunc(self)
break
except Restart:
print 'got restart at', self.sublevel
continue
do_s = do_start
def do_trace(self, line):
"""t/trace (i/instruction | b/block | f/function)
Toggles tracing of instructions, blocks, or functions."""
if line.startswith('i'):
self.flags ^= TRACE_INSTRUCTION
print 'Instruction tracing', 'on' if self.flags & TRACE_INSTRUCTION else 'off'
elif line.startswith('b'):
self.flags ^= TRACE_BLOCK
print 'Block tracing', 'on' if self.flags & TRACE_BLOCK else 'off'
elif line.startswith('f'):
self.flags ^= TRACE_FUNCTION
print 'Function tracing', 'on' if self.flags & TRACE_FUNCTION else 'off'
else:
print 'Unknown trace flag'
do_t = do_trace
def do_break(self, addr):
"""b/break [name]
Without `name`, list breakpoints.
Given a symbol name or address, toggle breakpoint."""
if addr == '':
print 'Breakpoints:'
for addr in self.breakpoints:
print '*', addr
return
try:
addr = raw(addr)
except BadAddr:
print 'Invalid address/symbol'
return
if addr in self.breakpoints:
print 'Removing breakpoint at %s' % addr
self.breakpoints.remove(addr)
else:
print 'Breaking at %s' % addr
self.breakpoints.add(addr)
do_b = do_break
def complete_break(self, text, line, begidx, endidx):
ftext = line.split(' ', 1)[1] if ' ' in line else ''
cut = len(ftext) - len(text)
return [sym[cut:] for sym in symbols.keys() if sym.startswith(ftext)]
complete_b = complete_break
def do_sym(self, name):
"""sym <name>
Prints the address of a given symbol."""
try:
print raw(name)
except BadAddr:
print 'Invalid address/symbol'
complete_sym = complete_break
def do_continue(self, line):
"""c/continue
Continues execution of the code."""
if self.sublevel == 1:
print 'Not running'
else:
return True
do_c = do_continue
def do_next(self, line):
"""n/next
Step to the next instruction."""
if self.sublevel == 1:
print 'Not running'
else:
self.singlestep = True
return True
do_n = do_next
def do_regs(self, line):
"""r/reg/regs [reg [value]]
No parameters: Display registers.
Reg parameter: Display one register.
Otherwise: Assign a value (always hex, or a symbol) to a register."""
if line == '':
return self.dumpregs()
elif ' ' in line:
r, v = line.split(' ', 1)
try:
v = raw(v)
if self.reg(r, v) is None:
print 'Invalid register'
except BadAddr:
print 'Invalid address/Symbol'
else:
v = self.reg(line)
if v is False:
print 'Invalid register'
else:
print '0x%016x' % v
do_r = do_reg = do_regs
def do_exec(self, line):
"""x/exec <code>
Evaluates a given line of C."""
try:
val = ceval(line, self)
except:
import traceback
traceback.print_exc()
print 'Execution failed'
return
if val is not None:
print '0x%x' % val
do_x = do_exec
def do_dump(self, line):
"""dump <address> [size]
Dumps `size` (default: 0x100) bytes of memory at an address.
If the address takes the form `*register` (e.g. `*X1`) then the value of that register will be used."""
line = list(line.split(' '))
if len(line[0]) == 0:
print 'No address'
elif len(line) <= 2:
if len(line[0]) and line[0][0] == '*':
line[0] = self.reg(line[0][1:])
if line[0] is None:
print 'Invalid register'
return
else:
try:
line[0] = raw(line[0])
except BadAddr:
print 'Invalid address/symbol'
return
if len(line) == 2:
line[1] = parseInt(line[1])
if line[1] is None or line[1] >= 0x10000:
print 'Invalid size'
return
self.dumpmem(line[0], 0x100 if len(line) == 1 else line[1])
else:
print 'Too many parameters'
def do_ad(self, line):
"""ad
Toggle address display specialization."""
Address.display_specialized = not Address.display_specialized
print '%s specialized address display' % ('Enabled' if Address.display_specialized else 'Disabled')
self.reprompt()
def do_watch(self, line):
"""w/watch <expression>
Breaks when expression evaluates to true.
Without an expression, list existing watchpoints."""
if line == '':
print 'Watchpoints:'
for code, _ in self.watchpoints:
print '*', code
return
if line in [code for code, _ in self.watchpoints]:
self.watchpoints = [(code, func) for code, func in self.watchpoints if code != line]
print 'Watchpoint deleted'
else:
self.watchpoints.append((line, compile(line)))
print 'Watchpoint added'
do_w = do_watch
def debug(func):
ctu = CTU()
ctu.setup(func)
ctu.debug()
return func
def run(flags=TRACE_NONE):
def sub(func):
ctu = CTU()
ctu.setup(func)
ctu.run(flags)
return func
if callable(flags):
func = flags
flags = TRACE_NONE
return sub(func)
else:
return sub

12501
main.map Normal file

File diff suppressed because it is too large Load diff

28
main.py Normal file
View file

@ -0,0 +1,28 @@
from ctu import *
from util import *
from ceval import ceval
@run
@debug
def main(ctu):
fPath = ctu.malloc(36)
ctu.writemem(fPath, 'blacklist:/blacklist.txt\0')
fOption = ctu.malloc(16)
ctu.writemem(fOption, 'rb\0')
print '%016x' % ctu.call(MainAddress(0x43ddb4), fPath, fOption)
#ctu.dumpmem(WKCAddress(0x887828), 0x1000)
#ctu.call(WKCAddress(0x397B3C))
"""sbuf = ctu.malloc(32)
ctu.writemem(sbuf + 4, struct.pack('<I', 0xAABCDDEF))
obuf = ctu.malloc(0x1000)
print 'obuf at %x' % obuf
ctu.call(MainAddress(0x397c68), sbuf, obuf, 0xDEADBEEF01234567, 0xCAFEBABE)"""
"""dname = ctu.malloc(64)
ctu.writemem(dname, '/dev/nvmap')
print '%016x' % ctu.call(MainAddress(0x1a49c4), dname)"""
#print '%016x' % ctu.call(MainAddress(0x1a4b10), 0xdeadbeef, 0xcafebabe, 0x0123456789, 0xf0e0d0c0)
#print '%016x' % ctu.call(MainAddress(0x1a4ae8), 0x6b0001)

4
requirements.txt Normal file
View file

@ -0,0 +1,4 @@
unicorn>=1.0.0
capstone>=3.0.0
colorama>=0.3.7
pycparser>=2.17

47
svc.py Normal file
View file

@ -0,0 +1,47 @@
from util import *
import struct
handlers = {}
def handler(num):
def sub(func):
handlers[num] = func
return func
return sub
class SvcHandler(object):
def __init__(self, ctu):
self.ctu = ctu
for i in xrange(0x60):
ctu.hookinsn(0xD4000001 | (i << 5), (lambda i: lambda _, __: self.svcDispatch(i))(i))
def svcDispatch(self, svc):
if svc in handlers:
print 'svc %x' % svc
handlers[svc](self)
return False
print 'Unhandled: SVC 0x%02x @ %s' % (svc, raw(self.ctu.pc))
self.ctu.debugbreak()
return False
def ipcDispatcher(self, handle, addr, size):
print 'IPC! Handle: %08x' % handle
self.ctu.dumpmem(addr, size)
@handler(0x1D)
def SignalEvent(self):
self.ctu.reg(0, 0)
@handler(0x21)
def SendSyncRequest(self):
return self.ipcDispatcher(self.ctu.reg(0), self.ctu.tlsbase, 0x100)
@handler(0x22)
def SendSyncRequestEx(self):
return self.ipcDispatcher(self.ctu.reg(2), self.ctu.reg(0), self.ctu.reg(1))
@handler(0x25)
def GetThreadId(self):
self.ctu.writemem(self.ctu.reg(0), struct.pack('<Q', 0xf00))
self.ctu.reg(0, 0)

109
util.py Normal file
View file

@ -0,0 +1,109 @@
import re
class Restart(Exception):
pass
class BadAddr(Exception):
pass
symbols = {}
invsyms = {}
def mapLoader(fn, acls, base):
cut = 'nullsub_', 'def_%s' % ('%x' % base)[:2]
with file(fn, 'r') as fp:
for line in fp:
if not line.startswith(' 00000001:'):
continue
addr = acls(int(line[10:26], 16) - base)
name = line[33:].strip().split('(', 1)[0]
if any(name.startswith(x) for x in cut):
continue
symbols[name] = addr
invsyms[addr] = name
class Address(object):
display_specialized = True
def __init__(self, addr, pad=False, raw=False):
self.addr = addr + (0 if raw else self.baseaddr)
self.pad = pad
def __hash__(self):
return self.addr.__hash__()
def __eq__(self, b):
b = raw(b)
return self.addr == b.addr
@property
def baseaddr(self):
return type(self).realbase
@property
def offset(self):
return self.addr - self.baseaddr
def to(self, cls):
if isinstance(self, cls):
return self
return cls(self.addr, pad=self.pad, raw=True)
@property
def symbol(self):
if self in invsyms:
return invsyms[self]
def __str__(self):
if not Address.display_specialized and not isinstance(self, RawAddress):
return self.to(RawAddress).__str__()
sym = self.symbol
if sym is None:
return self.mname % (('0x%016x' if self.pad else '0x%x') % self.offset)
else:
return self.mname % (('%s @ 0x%016x' if self.pad else '%s @ 0x%x') % (sym, self.offset))
def __add__(self, b):
return type(self)(self.addr + b, pad=self.pad, raw=True)
def specialize(self):
if not isinstance(self, RawAddress):
return self.to(RawAddress).specialize()
if WKCAddress.realbase <= self.addr < WKCAddress.realbase + WKCAddress.realsize:
return self.to(WKCAddress)
elif MainAddress.realbase <= self.addr < MainAddress.realbase + MainAddress.realbase:
return self.to(MainAddress)
return self
class RawAddress(Address):
mname = '%s'
realbase = 0
realsize = 1 << 64
class WKCAddress(Address):
mname = 'WKC(%s)'
# realbase/size assigned at runtime
class MainAddress(Address):
mname = 'Main(%s)'
# realbase/size assigned at runtime
def raw(addr, pad=False):
if isinstance(addr, str) or isinstance(addr, unicode):
if addr in symbols:
return symbols[addr]
else:
raise BadAddr()
elif isinstance(addr, Address):
return addr.to(RawAddress)
return RawAddress(addr, pad=pad).specialize()
def native(addr):
if isinstance(addr, Address):
return addr.addr
else:
return addr
def parseInt(addr, implicitHex=False):
if (implicitHex or addr.startswith('0x')) and re.match(r'^(0x)?[0-9a-fA-F]+$', addr):
return int(addr[2:] if addr.startswith('0x') else addr, 16)
elif re.match(r'^[0-9]+$', addr):
return int(addr)
else:
return None

19300
webkit_wkc.map Normal file

File diff suppressed because it is too large Load diff