Guest User

Untitled

a guest
May 22nd, 2018
103
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.25 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # fsck secneo
  3.  
  4. from __future__ import print_function
  5. from unicorn import *
  6. from unicorn.arm_const import *
  7. from capstone import *
  8. import binascii
  9.  
  10. DEBUG = False
  11.  
  12. #
  13. # Utility functions
  14. #
  15. def info(formatted_string):
  16. print(formatted_string)
  17.  
  18. def error(formatted_string):
  19. print('ERROR - %s' % formatted_string)
  20.  
  21. def debug(formatted_string):
  22. if DEBUG:
  23. print('DEBUG - %s' % formatted_string)
  24.  
  25. # memory address where emulation starts
  26. ADDRESS = 0x1000
  27.  
  28. # Place for writing and playing with memory
  29. SCRATCH_ADDRESS = 0x1100
  30.  
  31. def disassemble_arm(arm_code):
  32. md = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
  33. for i in md.disasm(arm_code, 0x1000):
  34. info("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))
  35.  
  36. def emulate_arm(arm_code=None, ro_data=None, ro_data_start=None, ro_offset=None):
  37. debug(">>> Emulating ARM code")
  38. try:
  39. mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
  40.  
  41. # Memory for actual code
  42. mu.mem_map(ADDRESS, 2 * 1024 * 1024)
  43. mu.mem_write(ADDRESS, arm_code)
  44.  
  45. # RO Memory
  46. mu.mem_write(ro_data_start, ro_data)
  47.  
  48. # write dummy data to be emulated to memory
  49. mu.mem_write(SCRATCH_ADDRESS, "\x00"*64)
  50.  
  51. # Initialize some registers, automated by script
  52. mu.reg_write(UC_ARM_REG_R1, ro_offset)
  53. mu.reg_write(UC_ARM_REG_R3, ro_offset)
  54. mu.reg_write(UC_ARM_REG_R4, SCRATCH_ADDRESS)
  55. mu.reg_write(UC_ARM_REG_LR, SCRATCH_ADDRESS)
  56.  
  57. mu.reg_write(UC_ARM_REG_R0, 0x00)
  58. mu.reg_write(UC_ARM_REG_R12, 0x00)
  59.  
  60. if DEBUG:
  61. debug(">>> Before emulation ")
  62. for i in range(UC_ARM_REG_R0, UC_ARM_REG_R12):
  63. val = mu.reg_read(i)
  64. debug("\t %s = 0x%x" % ("R" + str(i-UC_ARM_REG_R0),val))
  65.  
  66. mu.emu_start(ADDRESS + 1, ADDRESS + len(arm_code))
  67.  
  68. debug(">>> Emulation done.")
  69.  
  70. if DEBUG:
  71. debug(">>> Emulation done. Below is the CPU context")
  72.  
  73. sp = mu.reg_read(UC_ARM_REG_SP)
  74. debug(">>> SP = 0x%x" %sp)
  75. val = mu.reg_read(UC_ARM_REG_PC)
  76. debug(">>> PC = 0x%x" %val)
  77. for i in range(UC_ARM_REG_R0, UC_ARM_REG_R12):
  78. val = mu.reg_read(i)
  79. debug(">>> %s = 0x%x" % ("R" + str(i-UC_ARM_REG_R0),val))
  80.  
  81. debug("Memory at addr 0x%X %s" % (SCRATCH_ADDRESS, binascii.hexlify(content)))
  82.  
  83. content = mu.mem_read(SCRATCH_ADDRESS, 100)
  84. info(">>> Decrypted text : %s" % content)
  85.  
  86.  
  87. except UcError as e:
  88. error("ERROR: %s" % e)
  89.  
  90. def get_ro_data():
  91. rodata_seg = idaapi.get_segm_by_name('.rodata')
  92. if rodata_seg is not None:
  93. return rodata_seg.startEA, idc.GetManyBytes(rodata_seg.startEA, rodata_seg.endEA - rodata_seg.startEA, False)
  94. return None, None
  95.  
  96. def get_decrypt_functions():
  97. funcs = []
  98. text_seg = idaapi.get_segm_by_name('.text')
  99. if text_seg is None:
  100. return 0, None
  101.  
  102. # This appears to be in all decryption functions
  103. # TODO : Assert this works across multiple compiles, etc
  104. # ADD.W R4, LR, R0
  105. hook_opcodes = '0E EB 00 04'
  106.  
  107. start = text_seg.startEA
  108. next_address = None
  109. while True:
  110. next_address = idaapi.find_binary(start, text_seg.endEA, hook_opcodes, 0, SEARCH_DOWN)
  111. if next_address == idaapi.BADADDR:
  112. break
  113.  
  114. func = idaapi.get_func(next_address)
  115. # TODO : There may be a case where it isn't a function /yet/, which we may want to cover
  116. if func is None:
  117. error("Hit an issue with 0x%x" % next_address)
  118. break
  119.  
  120. start = func.endEA
  121. funcs.append(func)
  122.  
  123. if func.endEA > text_seg.endEA:
  124. error('Odd case that should not be possible hit!')
  125. break
  126.  
  127. return len(funcs), funcs
  128.  
  129. # TODO : Beef this up, right now this is simple enough and easy to do
  130. # For tested binaries, this worked for 74/76 functions
  131. # Will likely need to validate new registers aren't used
  132. def check_func(func):
  133. if func.startEA + 0x38 > func.endEA:
  134. return False
  135.  
  136. if GetMnem(func.startEA + 0x38) == "ADDS":
  137. return True
  138.  
  139. return False
  140.  
  141. def seek_bound(func):
  142. if func is None or func.startEA is None or func.startEA + 0x3e > func.endEA:
  143. return None
  144.  
  145. addr = func.startEA + 0x3e
  146.  
  147. while True:
  148. addr = FindCode(addr, SEARCH_DOWN)
  149.  
  150. if GetMnem(addr) == "MOVS":
  151. return addr
  152.  
  153. # Unsure if the latter case could even be possible, but whatever - lets check?
  154. if addr > func.endEA or addr < func.startEA:
  155. return None
  156.  
  157. def get_offset_into_ro_data(func):
  158. # TODO : Better check
  159. if GetMnem(func.startEA + 0x30) != "LDR":
  160. return None
  161.  
  162. try:
  163. opnd = GetOpnd(func.startEA + 0x30, 1)
  164. if opnd.index("byte_") > 0:
  165. start = opnd.index("byte_") + len("byte_")
  166. end = opnd.index(" ", start)
  167. if end > 0:
  168. return int(opnd[start:end], 16)
  169. except ValueError as e:
  170. return None
  171. return None
  172.  
  173. def get_arm_code(func):
  174. lower_bound = func.startEA + 0x3e
  175. upper_bound = seek_bound(func)
  176.  
  177. if upper_bound is None:
  178. return None
  179.  
  180. arm_code = idc.GetManyBytes(lower_bound, upper_bound - lower_bound, False)
  181.  
  182. if arm_code is None or arm_code <= 0:
  183. return None
  184.  
  185. return arm_code
  186.  
  187. def decrypt_strings(func, ro_data, rodata_start):
  188. arm_code = get_arm_code(func)
  189.  
  190. offset = get_offset_into_ro_data(func)
  191. if offset is not None:
  192. debug("Using ro_data offset 0x%x for 0x%0x" % (offset, func.startEA))
  193. emulate_arm(arm_code, ro_data, rodata_start, offset)
  194. return True
  195.  
  196. return False
  197.  
  198. if __name__ == '__main__':
  199. rodata_start, ro_data = get_ro_data()
  200. if ro_data is None or ro_data <= 0:
  201. error("Issue finding '.rodata', bailing...")
  202. exit()
  203.  
  204. how_many, funcs = get_decrypt_functions()
  205. info("Found %d decryption functions..." % how_many)
  206.  
  207. for func in funcs:
  208. if check_func(func):
  209. if DEBUG:
  210. info("=" * 26)
  211. disassemble_arm(get_arm_code(func))
  212. info("=" * 26)
  213. if not decrypt_strings(func, ro_data, rodata_start):
  214. error("Decrypt failed..")
  215. else:
  216. error("0x%x doesn't seem correct" % func.startEA)
  217.  
  218. # TODO : relabel function names and ending pointer
Add Comment
Please, Sign In to add comment