Advertisement
Guest User

Untitled

a guest
Feb 7th, 2016
59
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.37 KB | None | 0 0
  1. from Tkinter import Tk
  2.  
  3. def setClipboard(s):
  4. r = Tk()
  5. r.withdraw()
  6. r.clipboard_clear()
  7. r.clipboard_append(s)
  8. r.destroy()
  9.  
  10. def functionReturn(funcea):
  11. func = idaapi.get_func(funcea)
  12. try:
  13. endea = func.endEA - 2
  14. while not isCode(GetFlags(endea)): endea -= ItemSize(endea)
  15. return endea
  16. except AttributeError:
  17. return idaapi.BADADDR
  18.  
  19. def sniffFunctionParams(ea):
  20. func = idaapi.getfunc(ea)
  21. endea = functionReturn(ea)
  22. begea = func.startEA
  23. params = []
  24. sethere = []
  25.  
  26. ea = begea
  27. while ea < endea:
  28. if isCode(GetFlags(ea)):
  29. pass
  30.  
  31. # Shouldn't go further than first function call
  32. if GetMnem(ea) == 'BL':
  33. endea = ea
  34.  
  35. idaapi.decode_insn(ea)
  36. ops = idaapi.cmd.Operands
  37.  
  38. if GetMnem(ea) in ['LDR', 'MOV']:
  39. sethere.append(ops[0].reg)
  40. elif GetMnem(ea) in ['ADD', 'SUB', 'LSL', 'LSR']:
  41. if ops[1].type == idaapi.o_reg:
  42. if ops[1].reg != ops[0].reg:
  43. sethere.append(ops[0].reg)
  44.  
  45. for i in xrange(0, 3):
  46. if i not in sethere:
  47. params.append(i)
  48.  
  49. ea += ItemSize(ea)
  50.  
  51. def findFreeReg(lower, upper):
  52. free = []
  53. old = lower
  54.  
  55. # Move lower bound to earliest label or BL
  56. while not Name(lower):
  57. lower -= ItemSize(lower)
  58. if GetMnem(lower) == 'BL':
  59. funcea = GetOperandValue(lower, 0)
  60. funcret = functionReturn(funcea)
  61.  
  62. # Function does BX R0, meaning it doesn't return anything
  63. if GetMnem(funcret) == 'BX' and GetOperandValue(funcret, 0) == 0:
  64. free.append(0)
  65.  
  66. free += [1, 2, 3]
  67. break
  68.  
  69. # Remove from free list if any were used
  70. while lower <= old and free:
  71. inslen = idaapi.decode_insn(lower)
  72.  
  73. for i in xrange(3):
  74. op = idaapi.cmd.Operands[i]
  75. if op.type == idaapi.o_reg and op.reg in free:
  76. free.remove(op.reg)
  77.  
  78. lower += inslen
  79.  
  80. # If there were any found, exit early
  81. if free: return free[0]
  82.  
  83. # Deep scan - exclude high registers
  84. lower = old
  85. used = list(range(8, 16))
  86.  
  87. while lower < upper:
  88. inslen = idaapi.decode_insn(lower)
  89. mnem = GetMnem(lower).lower()
  90. ops = idaapi.cmd.Operands
  91.  
  92. if mnem in ['mov', 'ldr']:
  93. # This opcodes always indicate Rd is free
  94. if ops[0].reg not in used:
  95. return ops[0].reg
  96. elif mnem in ['lsl', 'lsr', 'asr', 'add', 'sub']:
  97. # Rd is only free if Rd != Rs
  98. Rd = ops[0].reg
  99. Rs = ops[1].reg
  100. if Rd != Rs and Rd not in used: return Rd
  101. elif mnem == 'pop':
  102. r = int(GetDisasm(lower).split('{R')[1][0])
  103. if r not in used: return r
  104. elif mnem == 'bl':
  105. # Wost case, the function uses all of these
  106. worst = [0, 1, 2, 3]
  107.  
  108. # Attempt to sniff arguments
  109. args = sniffFunctionParams(lower)
  110.  
  111. if args != worst:
  112. unused = [x for x in worst if x not in args]
  113. return unused[0]
  114.  
  115. used += worst
  116. else:
  117. # Forbid these registers
  118. for i in xrange(3):
  119. if ops[i].type and ops[i].reg not in used:
  120. used.append(ops[i].reg)
  121.  
  122. lower += inslen
  123.  
  124. if free:
  125. return free[0]
  126.  
  127. def regRepr(reg):
  128. if reg == 13:
  129. return 'sp'
  130. if reg == 14:
  131. return 'lr'
  132. if reg == 15:
  133. return 'pc'
  134. return 'r' + str(reg)
  135.  
  136. def opRepr(op):
  137. if op.type == idaapi.o_mem:
  138. return Name(Dword(op.addr))
  139. if op.type == idaapi.o_phrase:
  140. return '[{}, {}]'.format(regRepr(op.reg), regRepr(op.specflag1))
  141. if op.type == idaapi.o_displ:
  142. if op.addr:
  143. return '[{}, #0x{:X}]'.format(regRepr(op.reg), op.addr)
  144. else: return '[{}]'.format(regRepr(op.reg))
  145. if op.type == idaapi.o_reg:
  146. return regRepr(op.reg)
  147. if op.type == idaapi.o_imm:
  148. return '#' + hex(op.value)
  149. if op.type == idaapi.o_idpspec1:
  150. # Create register list
  151. reglist = []
  152. for i in range(16):
  153. if op.specval & (1 << i): reglist.append(i)
  154.  
  155. # Why do you make me do this IDA
  156. text = ''
  157. last = 100
  158. inList = False
  159. for reg in reglist:
  160. if reg - last == 1:
  161. inList = True
  162. else:
  163. if inList:
  164. inList = False
  165. text += '-' + regRepr(last)
  166.  
  167. if text: text += ', '
  168. text += regRepr(reg)
  169.  
  170. last = reg
  171. else:
  172. if inList:
  173. text += '-' + regRepr(reg)
  174.  
  175. return '{' + text + '}'
  176.  
  177. def determineCallRegister(func):
  178. return 0
  179.  
  180. def determineHookRegister(ea, length):
  181. x = findFreeReg(ea, ea + length)
  182. return x
  183.  
  184. def getFuncHexLoc(func):
  185. return '0x{:08X} + 1'.format(func)
  186.  
  187. def hookCode(register, unaligned):
  188. data = [0, 0x48 | register, register << 3, 0x47]
  189. if unaligned: data += [0, 0]
  190.  
  191. data = list(map(lambda x: '{:02X}'.format(x), data))
  192. data += ['XX + 1', 'XX', 'XX', '08']
  193.  
  194. return ' '.join(data)
  195.  
  196. def realMnem(ea):
  197. mnem = GetDisasm(ea).split()[0].lower()
  198. if mnem in ['lsls', 'adds', 'subs', 'movs', 'lsrs', 'asrs', 'muls']:
  199. return mnem[:-1]
  200.  
  201. return mnem
  202.  
  203. def autoHook(ea=None):
  204. # Default to cursor position
  205. if ea == None: ea = ScreenEA()
  206.  
  207. # Halfword align
  208. if ea & 1: ea -= 1
  209.  
  210. # Figure out length of hook code
  211. if ea % 4: length = 10
  212. else: length = 8
  213.  
  214. literals = {}
  215. asm = ['.align 2', '.thumb', '']
  216. branches = {}
  217. call_regs = []
  218.  
  219. # Guess and ask user on failure
  220. hook_reg = determineHookRegister(ea, length)
  221. if hook_reg == None:
  222. prompt = 'Couldn\'t guess register. Please enter hook register from 0-7'
  223. while True:
  224. hook_reg = AskLong(0, prompt)
  225. if 0 <= hook_reg <= 7:
  226. break
  227.  
  228. # Location comment
  229. hexstr = hookCode(hook_reg, length == 10)
  230. offset = ea - 0x08000000
  231. comment = '@ {} at 0x{:06X}'.format(hexstr, offset)
  232. comment += ' ({:08X} via r{})'.format(ea, hook_reg)
  233. asm.append(comment)
  234.  
  235. # Start ASM
  236. asm.append('hook_name:')
  237. start = ea
  238. while length > 0:
  239. # Instruction information
  240. inslen = idaapi.decode_insn(ea)
  241. ops = idaapi.cmd.Operands
  242. mnem = GetMnem(ea).lower()
  243.  
  244. flags = GetFlags(ea)
  245. if isAlign(flags) and length == 2:
  246. break
  247.  
  248. if not isCode(flags):
  249. raise Exception('Not code')
  250.  
  251. if Name(ea) and ea != start:
  252. raise Exception('Hook cannot go over label boundary')
  253.  
  254. # Check for literals
  255. for i, op in enumerate(ops):
  256. if idaapi.o_mem == op.type:
  257. a = Dword(op.addr)
  258. literals[Name(a)] = '0x{:08X}'.format(a)
  259.  
  260. # Work out instruction
  261. real_mnem = realMnem(ea)
  262.  
  263. # Function call -- we need to make a long call
  264. if inslen == 4:
  265. if mnem == 'bl':
  266. funcea = ops[0].addr
  267. reg = determineCallRegister(funcea)
  268. call_regs.append(reg)
  269. name = GetFunctionName(funcea)
  270. literals[name] = getFuncHexLoc(funcea)
  271. asm.append('ldr r{}, {}'.format(reg, name))
  272. asm.append('bl call_via_r{}'.format(reg))
  273. else:
  274. # IDA fake
  275. pass
  276. elif mnem == 'b':
  277. locea = ops[0].addr
  278. name = Name(locea)
  279. branches[name] = locea
  280. asm.append('{} {}'.format(real_mnem, name))
  281. else:
  282. rep = (opRepr(idaapi.cmd.Operands[i]) for i in xrange(3))
  283. optext = filter(None, rep)
  284. asm.append('{} {}'.format(real_mnem, ', '.join(optext)))
  285.  
  286. # Adjust position
  287. length -= inslen
  288. ea += inslen
  289.  
  290. # We're going to unconditionally return, so remove unconditional branches
  291. # and set the correct return loc
  292. last_insn = asm[-1].split()
  293. if last_insn[0] == 'b':
  294. asm.pop()
  295. return_loc = branches[last_insn[1]]
  296. del branches[last_insn[1]]
  297. else:
  298. return_loc = ea
  299.  
  300. # Find return location - there might be some cases where this isn't needed?
  301. if return_loc != functionReturn(return_loc - 2) + 2:
  302. literals['return'] = getFuncHexLoc(return_loc)
  303. asm.append('ldr r{}, return'.format(hook_reg))
  304. asm.append('bx r' + str(hook_reg))
  305.  
  306. # Append branches
  307. # TODO: Fix registers
  308. for branch, loc in branches.items():
  309. asm += ['', branch + ':']
  310. asm.append('ldr r{}, =(0x{:08X} + 1)'.format(0, loc))
  311. asm.append('bx r{}'.format(0))
  312.  
  313. # Append mini functions
  314. for call in call_regs:
  315. asm += ['', 'call_via_r' + str(call) + ':', 'bx r' + str(call)]
  316.  
  317. # Append literals
  318. asm += ['', '.align 2']
  319. for literal, addr in literals.items():
  320. asm.append('{}: .word {}'.format(literal, addr))
  321.  
  322. # Tabbed formatting
  323. for n, line in enumerate(asm):
  324. line = line.strip()
  325. if ':' in line or line.startswith('.') or line.startswith(';'):
  326. continue
  327. asm[n] = '\t' + line
  328.  
  329. return '\n'.join(asm)
  330.  
  331. def hookHelper():
  332. try:
  333. setClipboard(autoHook())
  334. print "Success! Hook code copied to clipboard."
  335. except:
  336. print "Something went wrong."
  337.  
  338. print autoHook()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement