Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from Tkinter import Tk
- def setClipboard(s):
- r = Tk()
- r.withdraw()
- r.clipboard_clear()
- r.clipboard_append(s)
- r.destroy()
- def functionReturn(funcea):
- func = idaapi.get_func(funcea)
- try:
- endea = func.endEA - 2
- while not isCode(GetFlags(endea)): endea -= ItemSize(endea)
- return endea
- except AttributeError:
- return idaapi.BADADDR
- def sniffFunctionParams(ea):
- func = idaapi.getfunc(ea)
- endea = functionReturn(ea)
- begea = func.startEA
- params = []
- sethere = []
- ea = begea
- while ea < endea:
- if isCode(GetFlags(ea)):
- pass
- # Shouldn't go further than first function call
- if GetMnem(ea) == 'BL':
- endea = ea
- idaapi.decode_insn(ea)
- ops = idaapi.cmd.Operands
- if GetMnem(ea) in ['LDR', 'MOV']:
- sethere.append(ops[0].reg)
- elif GetMnem(ea) in ['ADD', 'SUB', 'LSL', 'LSR']:
- if ops[1].type == idaapi.o_reg:
- if ops[1].reg != ops[0].reg:
- sethere.append(ops[0].reg)
- for i in xrange(0, 3):
- if i not in sethere:
- params.append(i)
- ea += ItemSize(ea)
- def findFreeReg(lower, upper):
- free = []
- old = lower
- # Move lower bound to earliest label or BL
- while not Name(lower):
- lower -= ItemSize(lower)
- if GetMnem(lower) == 'BL':
- funcea = GetOperandValue(lower, 0)
- funcret = functionReturn(funcea)
- # Function does BX R0, meaning it doesn't return anything
- if GetMnem(funcret) == 'BX' and GetOperandValue(funcret, 0) == 0:
- free.append(0)
- free += [1, 2, 3]
- break
- # Remove from free list if any were used
- while lower <= old and free:
- inslen = idaapi.decode_insn(lower)
- for i in xrange(3):
- op = idaapi.cmd.Operands[i]
- if op.type == idaapi.o_reg and op.reg in free:
- free.remove(op.reg)
- lower += inslen
- # If there were any found, exit early
- if free: return free[0]
- # Deep scan - exclude high registers
- lower = old
- used = list(range(8, 16))
- while lower < upper:
- inslen = idaapi.decode_insn(lower)
- mnem = GetMnem(lower).lower()
- ops = idaapi.cmd.Operands
- if mnem in ['mov', 'ldr']:
- # This opcodes always indicate Rd is free
- if ops[0].reg not in used:
- return ops[0].reg
- elif mnem in ['lsl', 'lsr', 'asr', 'add', 'sub']:
- # Rd is only free if Rd != Rs
- Rd = ops[0].reg
- Rs = ops[1].reg
- if Rd != Rs and Rd not in used: return Rd
- elif mnem == 'pop':
- r = int(GetDisasm(lower).split('{R')[1][0])
- if r not in used: return r
- elif mnem == 'bl':
- # Wost case, the function uses all of these
- worst = [0, 1, 2, 3]
- # Attempt to sniff arguments
- args = sniffFunctionParams(lower)
- if args != worst:
- unused = [x for x in worst if x not in args]
- return unused[0]
- used += worst
- else:
- # Forbid these registers
- for i in xrange(3):
- if ops[i].type and ops[i].reg not in used:
- used.append(ops[i].reg)
- lower += inslen
- if free:
- return free[0]
- def regRepr(reg):
- if reg == 13:
- return 'sp'
- if reg == 14:
- return 'lr'
- if reg == 15:
- return 'pc'
- return 'r' + str(reg)
- def opRepr(op):
- if op.type == idaapi.o_mem:
- return Name(Dword(op.addr))
- if op.type == idaapi.o_phrase:
- return '[{}, {}]'.format(regRepr(op.reg), regRepr(op.specflag1))
- if op.type == idaapi.o_displ:
- if op.addr:
- return '[{}, #0x{:X}]'.format(regRepr(op.reg), op.addr)
- else: return '[{}]'.format(regRepr(op.reg))
- if op.type == idaapi.o_reg:
- return regRepr(op.reg)
- if op.type == idaapi.o_imm:
- return '#' + hex(op.value)
- if op.type == idaapi.o_idpspec1:
- # Create register list
- reglist = []
- for i in range(16):
- if op.specval & (1 << i): reglist.append(i)
- # Why do you make me do this IDA
- text = ''
- last = 100
- inList = False
- for reg in reglist:
- if reg - last == 1:
- inList = True
- else:
- if inList:
- inList = False
- text += '-' + regRepr(last)
- if text: text += ', '
- text += regRepr(reg)
- last = reg
- else:
- if inList:
- text += '-' + regRepr(reg)
- return '{' + text + '}'
- def determineCallRegister(func):
- return 0
- def determineHookRegister(ea, length):
- x = findFreeReg(ea, ea + length)
- return x
- def getFuncHexLoc(func):
- return '0x{:08X} + 1'.format(func)
- def hookCode(register, unaligned):
- data = [0, 0x48 | register, register << 3, 0x47]
- if unaligned: data += [0, 0]
- data = list(map(lambda x: '{:02X}'.format(x), data))
- data += ['XX + 1', 'XX', 'XX', '08']
- return ' '.join(data)
- def realMnem(ea):
- mnem = GetDisasm(ea).split()[0].lower()
- if mnem in ['lsls', 'adds', 'subs', 'movs', 'lsrs', 'asrs', 'muls']:
- return mnem[:-1]
- return mnem
- def autoHook(ea=None):
- # Default to cursor position
- if ea == None: ea = ScreenEA()
- # Halfword align
- if ea & 1: ea -= 1
- # Figure out length of hook code
- if ea % 4: length = 10
- else: length = 8
- literals = {}
- asm = ['.align 2', '.thumb', '']
- branches = {}
- call_regs = []
- # Guess and ask user on failure
- hook_reg = determineHookRegister(ea, length)
- if hook_reg == None:
- prompt = 'Couldn\'t guess register. Please enter hook register from 0-7'
- while True:
- hook_reg = AskLong(0, prompt)
- if 0 <= hook_reg <= 7:
- break
- # Location comment
- hexstr = hookCode(hook_reg, length == 10)
- offset = ea - 0x08000000
- comment = '@ {} at 0x{:06X}'.format(hexstr, offset)
- comment += ' ({:08X} via r{})'.format(ea, hook_reg)
- asm.append(comment)
- # Start ASM
- asm.append('hook_name:')
- start = ea
- while length > 0:
- # Instruction information
- inslen = idaapi.decode_insn(ea)
- ops = idaapi.cmd.Operands
- mnem = GetMnem(ea).lower()
- flags = GetFlags(ea)
- if isAlign(flags) and length == 2:
- break
- if not isCode(flags):
- raise Exception('Not code')
- if Name(ea) and ea != start:
- raise Exception('Hook cannot go over label boundary')
- # Check for literals
- for i, op in enumerate(ops):
- if idaapi.o_mem == op.type:
- a = Dword(op.addr)
- literals[Name(a)] = '0x{:08X}'.format(a)
- # Work out instruction
- real_mnem = realMnem(ea)
- # Function call -- we need to make a long call
- if inslen == 4:
- if mnem == 'bl':
- funcea = ops[0].addr
- reg = determineCallRegister(funcea)
- call_regs.append(reg)
- name = GetFunctionName(funcea)
- literals[name] = getFuncHexLoc(funcea)
- asm.append('ldr r{}, {}'.format(reg, name))
- asm.append('bl call_via_r{}'.format(reg))
- else:
- # IDA fake
- pass
- elif mnem == 'b':
- locea = ops[0].addr
- name = Name(locea)
- branches[name] = locea
- asm.append('{} {}'.format(real_mnem, name))
- else:
- rep = (opRepr(idaapi.cmd.Operands[i]) for i in xrange(3))
- optext = filter(None, rep)
- asm.append('{} {}'.format(real_mnem, ', '.join(optext)))
- # Adjust position
- length -= inslen
- ea += inslen
- # We're going to unconditionally return, so remove unconditional branches
- # and set the correct return loc
- last_insn = asm[-1].split()
- if last_insn[0] == 'b':
- asm.pop()
- return_loc = branches[last_insn[1]]
- del branches[last_insn[1]]
- else:
- return_loc = ea
- # Find return location - there might be some cases where this isn't needed?
- if return_loc != functionReturn(return_loc - 2) + 2:
- literals['return'] = getFuncHexLoc(return_loc)
- asm.append('ldr r{}, return'.format(hook_reg))
- asm.append('bx r' + str(hook_reg))
- # Append branches
- # TODO: Fix registers
- for branch, loc in branches.items():
- asm += ['', branch + ':']
- asm.append('ldr r{}, =(0x{:08X} + 1)'.format(0, loc))
- asm.append('bx r{}'.format(0))
- # Append mini functions
- for call in call_regs:
- asm += ['', 'call_via_r' + str(call) + ':', 'bx r' + str(call)]
- # Append literals
- asm += ['', '.align 2']
- for literal, addr in literals.items():
- asm.append('{}: .word {}'.format(literal, addr))
- # Tabbed formatting
- for n, line in enumerate(asm):
- line = line.strip()
- if ':' in line or line.startswith('.') or line.startswith(';'):
- continue
- asm[n] = '\t' + line
- return '\n'.join(asm)
- def hookHelper():
- try:
- setClipboard(autoHook())
- print "Success! Hook code copied to clipboard."
- except:
- print "Something went wrong."
- print autoHook()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement