├── .gitignore ├── README.md ├── iBoot64helper.py ├── screenshot-loader.png └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | notes 2 | *.pyc 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iBoot64helper 2 | 3 | ## Introduction 4 | 5 | **iBoot64helper** is now an IDA loader! 6 | 7 | Just copy *iBoot64helper.py* to your *~/.idapro/loaders/* (or your *IDA/loaders/*) 8 | directory, launch IDA, and open a *decrypted* iBoot, iBEC, or SecureROM binary image. 9 | 10 |

11 | 12 | This aims to become an IDAPython utility to help with iBoot and SecureROM reverse 13 | engineering. Currently it a) locates the image's proper loading address, b) rebases 14 | the image, c) identifies functions based on common AArch64 function prologues, and 15 | d) finds and renames some interesting functions. 16 | 17 | As you can see in the screenshot below, 3154 functions 18 | are recognized after running it on iBoot version 7459.100.504.0.1. 19 | 20 |

21 | 22 | I will be adding features to it, identifying more functions, etc. 23 | 24 | ## IDA support 25 | 26 | *iBoot64helper* now supports IDA 7.7 and lower versions (only tested with IDA's 27 | builtin IDAPython for Python 3). 28 | 29 | ## Decrypting images 30 | 31 | For decrypting images you should use [xerub's img4lib](https://github.com/xerub/img4lib); 32 | the ultimate IMG4 utility. 33 | 34 | ## SecureROM 35 | 36 | If you have a device vulnerable to [axi0mX's checkm8](https://github.com/axi0mX/ipwndfu), 37 | you can use ```./ipwndfu --dump-rom``` to get a dump of the SecureROM image from your device 38 | and use it with *iBoot64helper*. 39 | 40 | ## References 41 | * [iOS RE Wiki](https://github.com/kpwn/iOSRE/blob/master/wiki/iBoot-RE.md) 42 | * [img4lib](https://github.com/xerub/img4lib) 43 | * [checkm8](https://github.com/axi0mX/ipwndfu) 44 | -------------------------------------------------------------------------------- /iBoot64helper.py: -------------------------------------------------------------------------------- 1 | __author__ = "argp@CENSUS-labs.com" 2 | 3 | import idautils 4 | import idaapi 5 | import ida_idaapi 6 | import ida_search 7 | import ida_funcs 8 | import ida_segment 9 | import ida_bytes 10 | import ida_idp 11 | import ida_pro 12 | import ida_auto 13 | import idc 14 | import struct 15 | 16 | true = True 17 | false = False 18 | none = None 19 | 20 | kp_flag = false 21 | br_flag = false 22 | 23 | try: 24 | import keypatch 25 | kp_flag = true 26 | except: 27 | pass 28 | 29 | prologues = ["7F 23 03 D5", "BD A9", "BF A9"] 30 | 31 | def find_panic(base_ea): 32 | pk_ea = ida_search.find_text(base_ea, 1, 1, "double panic in ", ida_search.SEARCH_DOWN) 33 | 34 | if pk_ea != ida_idaapi.BADADDR: 35 | for xref in idautils.XrefsTo(pk_ea): 36 | func = idaapi.get_func(xref.frm) 37 | print("\t[+] _panic = 0x%x" % (func.start_ea)) 38 | idc.set_name(func.start_ea, "_panic", idc.SN_CHECK) 39 | return func.start_ea 40 | 41 | print("\t[-] _panic = not found") 42 | return ida_idaapi.BADADDR 43 | 44 | def find_do_ramdisk(base_ea): 45 | dr_ea = ida_search.find_text(base_ea, 1, 1, "Ramdisk image not valid\n", ida_search.SEARCH_DOWN) 46 | 47 | if dr_ea != ida_idaapi.BADADDR: 48 | for xref in idautils.XrefsTo(dr_ea): 49 | func = idaapi.get_func(xref.frm) 50 | print("\t[+] _do_ramdisk = 0x%x" % (func.start_ea)) 51 | idc.set_name(func.start_ea, "_do_ramdisk", idc.SN_CHECK) 52 | return func.start_ea 53 | 54 | print("\t[-] _do_ramdisk = not found") 55 | return ida_idaapi.BADADDR 56 | 57 | def find_prepare_and_jump(base_ea): 58 | paj_ea = ida_search.find_text(base_ea, 1, 1, "/boot/iBEC", ida_search.SEARCH_DOWN) 59 | 60 | if paj_ea != ida_idaapi.BADADDR: 61 | for xref in idautils.XrefsTo(paj_ea): 62 | func = idaapi.get_func(xref.frm) 63 | print("\t[+] _prepare_and_jump = 0x%x" % (func.start_ea)) 64 | idc.set_name(func.start_ea, "_prepare_and_jump", idc.SN_CHECK) 65 | return func.start_ea 66 | 67 | print("\t[-] _prepare_and_jump = not found") 68 | return ida_idaapi.BADADDR 69 | 70 | def find_arm_synchronous_exception(base_ea): 71 | ase_ea = ida_search.find_text(base_ea, 1, 1, "synchronous", ida_search.SEARCH_DOWN) 72 | 73 | if ase_ea != ida_idaapi.BADADDR: 74 | for xref in idautils.XrefsTo(ase_ea): 75 | func = idaapi.get_func(xref.frm) 76 | print("\t[+] _arm_synchronous_exception = 0x%x" % (func.start_ea)) 77 | idc.set_name(func.start_ea, "_arm_synchronous_exception", idc.SN_CHECK) 78 | return func.start_ea 79 | 80 | print("\t[-] _arm_synchronous_exception = not found") 81 | return ida_idaapi.BADADDR 82 | 83 | def find_chipid_get_chip_revision(base_ea, base_end_ea): 84 | seq_ea = ida_search.find_binary(base_ea, base_end_ea, "00 21 06 33", 16, ida_search.SEARCH_DOWN) 85 | 86 | if seq_ea != ida_idaapi.BADADDR: 87 | func = idaapi.get_func(seq_ea) 88 | print("\t[+] _chipid_get_chip_revision = 0x%x" % (func.start_ea)) 89 | idc.set_name(func.start_ea, "_chipid_get_chip_revision", idc.SN_CHECK) 90 | return func.start_ea 91 | 92 | print("\t[-] _chipid_get_chip_revision = not found") 93 | return ida_idaapi.BADADDR 94 | 95 | def find_platform_early_init(base_ea, base_end_ea): 96 | seq_ea = ida_search.find_binary(base_ea, base_end_ea, "A0 00 80 52 E1 03 80 52", 16, ida_search.SEARCH_DOWN) 97 | 98 | if seq_ea != ida_idaapi.BADADDR: 99 | func = idaapi.get_func(seq_ea) 100 | print("\t[+] _platform_early_init = 0x%x" % (func.start_ea)) 101 | idc.set_name(func.start_ea, "_platform_early_init", idc.SN_CHECK) 102 | return func.start_ea 103 | 104 | print("\t[-] _platform_early_init = not found") 105 | return ida_idaapi.BADADDR 106 | 107 | def find_image4_validate_property_callback(base_ea, base_end_ea, ptr_ea): 108 | seq_ea = ida_search.find_binary(base_ea, base_end_ea, "?? 77 00 51", 16, ida_search.SEARCH_DOWN) 109 | func = none 110 | 111 | if seq_ea != ida_idaapi.BADADDR: 112 | func = idaapi.get_func(seq_ea) 113 | else: 114 | for xref in idautils.XrefsTo(ptr_ea): 115 | func = idaapi.get_func(xref.frm) 116 | break 117 | 118 | if func: 119 | print("\t[+] _image4_validate_property_callback = 0x%x" % (func.start_ea)) 120 | idc.set_name(func.start_ea, "_image4_validate_property_callback", idc.SN_CHECK) 121 | return func.start_ea 122 | 123 | print("\t[-] _image4_validate_property_callback = not found") 124 | return ida_idaapi.BADADDR 125 | 126 | def find_image4_load(ea): 127 | ea_list = list(idautils.XrefsTo(ea)) 128 | 129 | if ea_list[0].frm != ida_idaapi.BADADDR: 130 | func_ea = ida_funcs.get_func(ea_list[0].frm).start_ea 131 | print("\t[+] _image4_load = 0x%x" % (func_ea)) 132 | idc.set_name(func_ea, "_image4_load", idc.SN_CHECK) 133 | return func_ea 134 | 135 | print("\t[-] _image4_load = not found") 136 | return ida_idaapi.BADADDR 137 | 138 | def find_image4_validate_property_callback_interposer(base_ea): 139 | ea_list = ida_search.find_imm(base_ea, ida_search.SEARCH_DOWN, 0x616E7264) 140 | 141 | if ea_list[0] == ida_idaapi.BADADDR or not ea_list: 142 | ea_list = ida_search.find_imm(base_ea, ida_search.SEARCH_DOWN, 0x4350524F) 143 | 144 | if ea_list[0] != ida_idaapi.BADADDR: 145 | func_ea = ida_funcs.get_func(ea_list[0]).start_ea 146 | print("\t[+] _image4_validate_property_callback_interposer = 0x%x" % (func_ea)) 147 | idc.set_name(func_ea, "_image4_validate_property_callback_interposer", idc.SN_CHECK) 148 | return func_ea 149 | 150 | print("\t[-] _image4_validate_property_callback_interposer = not found") 151 | return ida_idaapi.BADADDR 152 | 153 | def find_image4_validate_property_callback_interposer_ptr(ea): 154 | ea_list = list(idautils.XrefsTo(ea)) 155 | ptr_ea = ida_idaapi.BADADDR 156 | 157 | if len(ea_list) > 0: 158 | ptr_ea = ea_list[0].frm 159 | insn = idc.print_insn_mnem(ptr_ea) 160 | 161 | if not insn: 162 | print("\t[+] _image4_validate_property_callback_interposer_ptr = 0x%x" % (ptr_ea)) 163 | idc.set_name(ptr_ea, "_image4_validate_property_callback_interposer_ptr", idc.SN_CHECK) 164 | else: 165 | ptr_ea = ida_idaapi.BADADDR 166 | 167 | if ptr_ea != ida_idaapi.BADADDR: 168 | ptr_ea_list = list(idautils.XrefsTo(ptr_ea)) 169 | 170 | if len(ptr_ea_list) > 0: 171 | i4_hash_ptr_ea = ptr_ea_list[0].frm + 8 172 | 173 | src = idc.print_operand(i4_hash_ptr_ea, 1) 174 | i4_hash_ptr_ea = idc.get_name_ea_simple(src) 175 | print("\t[+] _image4_hash_init_ptr = 0x%x" % (i4_hash_ptr_ea)) 176 | idc.set_name(i4_hash_ptr_ea, "_image4_hash_init_ptr", idc.SN_CHECK) 177 | 178 | bl_ea = ptr_ea_list[0].frm + 8 + 16 179 | dst = idc.print_operand(bl_ea, 0) 180 | i4d_ea = idc.get_name_ea_simple(dst) 181 | print("\t[+] _Img4DecodePerformTrustEvaluation = 0x%x" % (i4d_ea)) 182 | idc.set_name(i4d_ea, "_Img4DecodePerformTrustEvaluation", idc.SN_CHECK) 183 | 184 | return ptr_ea 185 | 186 | if ptr_ea == ida_idaapi.BADADDR: 187 | print("\t[-] _image4_validate_property_callback_interposer_ptr = not found") 188 | 189 | print("\t[-] _image4_hash_init_ptr = not found") 190 | print("\t[-] _Img4DecodePerformTrustEvaluation = not found") 191 | return ida_idaapi.BADADDR 192 | 193 | def find_img4decodeinit(base_ea, base_end): 194 | ea_list = ida_search.find_imm(base_ea, ida_search.SEARCH_DOWN, 0x494D) 195 | 196 | if ea_list[0] == ida_idaapi.BADADDR: 197 | ea_list = ida_search.find_imm(base_ea, ida_search.SEARCH_DOWN, 0x494D0000) 198 | 199 | if ea_list[0] != ida_idaapi.BADADDR: 200 | try: 201 | func_ea = ida_funcs.get_func(ea_list[0]).start_ea 202 | except: 203 | print("\t[-] _Img4DecodeInit = not found") 204 | return ida_idaapi.BADADDR 205 | 206 | print("\t[+] _Img4DecodeInit = 0x%x" % (func_ea)) 207 | idc.set_name(func_ea, "_Img4DecodeInit", idc.SN_CHECK) 208 | 209 | return ida_idaapi.BADADDR 210 | 211 | def find_target_early_init(base_ea): 212 | ea_list = ida_search.find_imm(base_ea, ida_search.SEARCH_DOWN, 0x4A41) 213 | 214 | if ea_list[0] != ida_idaapi.BADADDR: 215 | try: 216 | func_ea = ida_funcs.get_func(ea_list[0]).start_ea 217 | except: 218 | print("\t[-] _target_early_init = not found") 219 | return ida_idaapi.BADADDR 220 | 221 | print("\t[+] _target_early_init = 0x%x" % (func_ea)) 222 | idc.set_name(func_ea, "_target_early_init", idc.SN_CHECK) 223 | tei_ea = func_ea 224 | 225 | str_ea = ida_search.find_text(tei_ea, 1, 1, "All pre", ida_search.SEARCH_DOWN) 226 | 227 | if str_ea != ida_idaapi.BADADDR: 228 | f_ea = idaapi.get_func(str_ea).start_ea 229 | 230 | if tei_ea != f_ea: 231 | print("\t[-] _platform_not_supported = not found") 232 | return tei_ea 233 | 234 | bl_ea = str_ea + 8 235 | dst = idc.print_operand(bl_ea, 0) 236 | pns_ea = idc.get_name_ea_simple(dst) 237 | print("\t[+] _platform_not_supported = 0x%x" % (pns_ea)) 238 | idc.set_name(pns_ea, "_platform_not_supported", idc.SN_CHECK) 239 | return tei_ea 240 | 241 | print("\t[-] _target_early_init = not found") 242 | return ida_idaapi.BADADDR 243 | 244 | def find_aes_crypto_cmd(base_ea, base_end_ea): 245 | aes_ea = ida_search.find_text(base_ea, 1, 1, "AES: bad arguments", ida_search.SEARCH_DOWN) 246 | 247 | if aes_ea != ida_idaapi.BADADDR: 248 | func = idaapi.get_func(aes_ea) 249 | print("\t[+] _aes_crypto_cmd = 0x%x" % (func.start_ea)) 250 | idc.set_name(func.start_ea, "_aes_crypto_cmd", idc.SN_CHECK) 251 | return func.start_ea 252 | 253 | print("\t[-] _aes_crypto_cmd = not found") 254 | return ida_idaapi.BADADDR 255 | 256 | def find_main_task(base_ea): 257 | du_ea = ida_search.find_text(base_ea, 1, 1, "debug-uarts", ida_search.SEARCH_DOWN) 258 | 259 | if du_ea != ida_idaapi.BADADDR: 260 | for xref in idautils.XrefsTo(du_ea): 261 | func = idaapi.get_func(xref.frm) 262 | mt_ea = 0 263 | 264 | if not func: 265 | mt_ea = ida_search.find_binary(xref.frm, base_ea, prologues[0], 16, ida_search.SEARCH_UP) 266 | 267 | if mt_ea == ida_idaapi.BADADDR: 268 | mt_ea = ida_search.find_binary(xref.frm, base_ea, "FF ?? ?? D1", 16, ida_search.SEARCH_UP) 269 | else: 270 | mt_ea = func.start_ea 271 | 272 | print("\t[+] _main_task = 0x%x" % (mt_ea)) 273 | idc.set_name(mt_ea, "_main_task", idc.SN_CHECK) 274 | return mt_ea 275 | 276 | print("\t[-] _main_task = not found") 277 | return ida_idaapi.BADADDR 278 | 279 | def find_boot_check_panic(base_ea, base_end_ea): 280 | seq_ea = ida_search.find_binary(base_ea, base_end_ea, "1F ?? 03 71", 16, ida_search.SEARCH_DOWN) 281 | 282 | if seq_ea != ida_idaapi.BADADDR: 283 | func = idaapi.get_func(seq_ea) 284 | print("\t[+] _boot_check_panic = 0x%x" % (func.start_ea)) 285 | idc.set_name(func.start_ea, "_boot_check_panic", idc.SN_CHECK) 286 | return func.start_ea 287 | 288 | print("\t[-] _boot_check_panic = not found") 289 | return ida_idaapi.BADADDR 290 | 291 | def find_update_device_tree(base_ea): 292 | udt_ea = ida_search.find_text(base_ea, 1, 1, "development-cert", ida_search.SEARCH_DOWN) 293 | 294 | if udt_ea != ida_idaapi.BADADDR: 295 | for xref in idautils.XrefsTo(udt_ea): 296 | func = idaapi.get_func(xref.frm) 297 | print("\t[+] _UpdateDeviceTree = 0x%x" % (func.start_ea)) 298 | idc.set_name(func.start_ea, "_UpdateDeviceTree", idc.SN_CHECK) 299 | return func.start_ea 300 | 301 | print("\t[-] _UpdateDeviceTree = not found") 302 | return ida_idaapi.BADADDR 303 | 304 | def find_record_memory_range(base_ea): 305 | rmr_ea = ida_search.find_text(base_ea, 1, 1, "chosen/memory-map", ida_search.SEARCH_DOWN) 306 | 307 | if rmr_ea != ida_idaapi.BADADDR: 308 | for xref in idautils.XrefsTo(rmr_ea): 309 | func = idaapi.get_func(xref.frm) 310 | print("\t[+] _record_memory_range = 0x%x" % (func.start_ea)) 311 | idc.set_name(func.start_ea, "_record_memory_range", idc.SN_CHECK) 312 | return func.start_ea 313 | 314 | print("\t[-] _record_memory_range = not found") 315 | return ida_idaapi.BADADDR 316 | 317 | def find_macho_valid(base_ea, base_end_ea): 318 | seq_ea = ida_search.find_binary(base_ea, base_end_ea, "0B 70 00 91", 16, ida_search.SEARCH_DOWN) 319 | 320 | if seq_ea != ida_idaapi.BADADDR: 321 | func = idaapi.get_func(seq_ea) 322 | print("\t[+] _macho_valid = 0x%x" % (func.start_ea)) 323 | idc.set_name(func.start_ea, "_macho_valid", idc.SN_CHECK) 324 | return func.start_ea 325 | 326 | print("\t[-] _macho_valid = not found") 327 | return ida_idaapi.BADADDR 328 | 329 | def find_stack_chk_fail(base_ea): 330 | ea_list = ida_search.find_imm(base_ea, ida_search.SEARCH_DOWN, 0x3CF) 331 | func = ida_funcs.get_func(ea_list[0]) 332 | 333 | if (ea_list[0] != ida_idaapi.BADADDR) and func: 334 | func_ea = func.start_ea 335 | print("\t[+] _stack_chk_fail = 0x%x" % (func_ea)) 336 | idc.set_name(func_ea, "_stack_chk_fail", idc.SN_CHECK) 337 | return func_ea 338 | else: 339 | str_ea = ida_search.find_text(base_ea, 1, 1, "__stack_chk_fail", ida_search.SEARCH_DOWN) 340 | 341 | if str_ea != ida_idaapi.BADADDR: 342 | for xref in idautils.XrefsTo(str_ea): 343 | func = idaapi.get_func(xref.frm) 344 | print("\t[+] _stack_chk_fail = 0x%x" % (func.start_ea)) 345 | idc.set_name(func.start_ea, "_stack_chk_fail", idc.SN_CHECK) 346 | return func.start_ea 347 | 348 | print("\t[-] _stack_chk_fail = not found") 349 | return ida_idaapi.BADADDR 350 | 351 | def find_platform_init_display(base_ea): 352 | str_ea = idc.get_name_ea_simple("aBacklightLevel") 353 | 354 | if str_ea != ida_idaapi.BADADDR: 355 | for xref in idautils.XrefsTo(str_ea): 356 | func = idaapi.get_func(xref.frm) 357 | print("\t[+] _platform_init_display = 0x%x" % (func.start_ea)) 358 | idc.set_name(func.start_ea, "_platform_init_display", idc.SN_CHECK) 359 | 360 | egu_ea = find_env_get_uint(xref.frm) 361 | 362 | if egu_ea != ida_idaapi.BADADDR: 363 | ege_ea = find_env_get_etc(egu_ea) 364 | 365 | return func.start_ea 366 | 367 | print("\t[-] _platform_init_display = not found") 368 | return ida_idaapi.BADADDR 369 | 370 | def find_env_get_uint(ea): 371 | bl_ea = ea + 12 372 | dst = idc.print_operand(bl_ea, 0) 373 | egu_ea = idc.get_name_ea_simple(dst) 374 | 375 | if egu_ea != ida_idaapi.BADADDR: 376 | print("\t[+] _env_get_uint = 0x%x" % (egu_ea)) 377 | idc.set_name(egu_ea, "_env_get_uint", idc.SN_CHECK) 378 | return egu_ea 379 | 380 | print("\t[-] _env_get_uint = not found") 381 | return ida_idaapi.BADADDR 382 | 383 | def find_env_get_etc(ea): 384 | cur_ea = ea + 4 385 | 386 | while true: 387 | insn = idc.print_insn_mnem(cur_ea) 388 | 389 | if insn == "BL": 390 | dst = idc.print_operand(cur_ea, 0) 391 | ege_ea = idc.get_name_ea_simple(dst) 392 | print("\t[+] _env_get_etc = 0x%x" % (ege_ea)) 393 | idc.set_name(ege_ea, "_env_get_etc", idc.SN_CHECK) 394 | return ege_ea 395 | 396 | if cur_ea > ea + (4 * 20): 397 | break 398 | 399 | cur_ea = cur_ea + 4 400 | 401 | print("\t[-] _env_get_etc = not found") 402 | return ida_idaapi.BADADDR 403 | 404 | def find_loaded_kernelcache(ea): 405 | ea_list = list(idautils.XrefsTo(ea)) 406 | 407 | if ea_list[0].frm != ida_idaapi.BADADDR: 408 | func_ea = ida_funcs.get_func(ea_list[0].frm).start_ea 409 | print("\t[+] _loaded_kernelcache = 0x%x" % (func_ea)) 410 | idc.set_name(func_ea, "_loaded_kernelcache", idc.SN_CHECK) 411 | return func_ea 412 | 413 | print("\t[-] _loaded_kernelcache = not found") 414 | return ida_idaapi.BADADDR 415 | 416 | def find_load_kernelcache(ea): 417 | ea_list = list(idautils.XrefsTo(ea)) 418 | func_ea = 0 419 | 420 | if len(ea_list) >= 1: 421 | if ea_list[0].frm != ida_idaapi.BADADDR: 422 | func = ida_funcs.get_func(ea_list[0].frm) 423 | 424 | if func != none: 425 | func_ea = func.start_ea 426 | print("\t[+] _load_kernelcache = 0x%x" % (func_ea)) 427 | idc.set_name(func_ea, "_load_kernelcache", idc.SN_CHECK) 428 | return func_ea 429 | 430 | print("\t[-] _load_kernelcache = not found") 431 | return ida_idaapi.BADADDR 432 | 433 | def find_load_kernelcache_object(base, ea): 434 | if ea != ida_idaapi.BADADDR: 435 | ea_list = list(idautils.XrefsTo(ea)) 436 | 437 | if ea_list[0].frm != ida_idaapi.BADADDR: 438 | func_ea = ida_funcs.get_func(ea_list[0].frm).start_ea 439 | print("\t[+] _load_kernelcache_object = 0x%x" % (func_ea)) 440 | idc.set_name(func_ea, "_load_kernelcache_object", idc.SN_CHECK) 441 | return func_ea 442 | 443 | print("\t[-] _load_kernelcache_object = not found") 444 | return ida_idaapi.BADADDR 445 | else: 446 | str_ea = ida_search.find_text(base, 1, 1, "Kernelcache too large", ida_search.SEARCH_DOWN) 447 | 448 | if str_ea != ida_idaapi.BADADDR: 449 | for xref in idautils.XrefsTo(str_ea): 450 | func = idaapi.get_func(xref.frm) 451 | print("\t[+] _load_kernelcache_object = 0x%x" % (func.start_ea)) 452 | idc.set_name(func.start_ea, "_load_kernelcache_object", idc.SN_CHECK) 453 | return func.start_ea 454 | 455 | print("\t[-] _load_kernelcache_object = not found") 456 | return ida_idaapi.BADADDR 457 | 458 | def find_do_go(base_ea): 459 | str_ea = idc.get_name_ea_simple("aCebilefciladrm") 460 | 461 | if str_ea != ida_idaapi.BADADDR: 462 | for xref in idautils.XrefsTo(str_ea): 463 | # IDA messes up this function, so I find it this way: 464 | func = idaapi.get_func(xref.frm) 465 | dg_ea = 0 466 | 467 | if func != none: 468 | dg_ea = ida_search.find_binary(xref.frm, func.start_ea, prologues[0], 16, ida_search.SEARCH_UP) 469 | 470 | if dg_ea == ida_idaapi.BADADDR: 471 | dg_ea = ida_search.find_binary(xref.frm, func.start_ea, "FF ?? ?? D1", 16, ida_search.SEARCH_UP) 472 | 473 | else: 474 | dg_ea = ida_search.find_binary(xref.frm, base_ea, "FF ?? ?? D1", 16, ida_search.SEARCH_UP) 475 | 476 | ida_funcs.add_func(dg_ea) 477 | print("\t[+] _do_go = 0x%x" % (dg_ea)) 478 | idc.set_name(dg_ea, "_do_go", idc.SN_CHECK) 479 | return dg_ea 480 | 481 | print("\t[-] _do_go = not found") 482 | return ida_idaapi.BADADDR 483 | 484 | def find_pmgr_binning_mode_get_value(base_ea): 485 | str_ea = ida_search.find_text(base_ea, 1, 1, "Invalid low", ida_search.SEARCH_DOWN) 486 | 487 | if str_ea != ida_idaapi.BADADDR: 488 | for xref in idautils.XrefsTo(str_ea): 489 | func = idaapi.get_func(xref.frm) 490 | print("\t[+] _pmgr_binning_mode_get_value = 0x%x" % (func.start_ea)) 491 | idc.set_name(func.start_ea, "_pmgr_binning_mode_get_value", idc.SN_CHECK) 492 | return func.start_ea 493 | 494 | print("\t[-] _pmgr_binning_mode_get_value = not found") 495 | return ida_idaapi.BADADDR 496 | 497 | def find_do_printf(base_ea, base_end_ea): 498 | str_ea = ida_search.find_text(base_ea, 1, 1, "", ida_search.SEARCH_DOWN) 499 | 500 | if str_ea != ida_idaapi.BADADDR: 501 | for xref in idautils.XrefsTo(str_ea): 502 | func = idaapi.get_func(xref.frm) 503 | 504 | if not func: 505 | break 506 | 507 | print("\t[+] _do_printf = 0x%x" % (func.start_ea)) 508 | idc.set_name(func.start_ea, "_do_printf", idc.SN_CHECK) 509 | return func.start_ea 510 | 511 | cmp_ea = ida_search.find_binary(base_ea, base_end_ea, "3F 94 00 71", 16, ida_search.SEARCH_DOWN) 512 | 513 | if cmp_ea != ida_idaapi.BADADDR: 514 | func = idaapi.get_func(cmp_ea) 515 | 516 | if not func: 517 | print("\t[-] _do_printf = not found") 518 | return ida_idaapi.BADADDR 519 | 520 | print("\t[+] _do_printf = 0x%x" % (func.start_ea)) 521 | idc.set_name(func.start_ea, "_do_printf", idc.SN_CHECK) 522 | return func.start_ea 523 | 524 | print("\t[-] _do_printf = not found") 525 | return ida_idaapi.BADADDR 526 | 527 | def find_image4_get_partial(base_ea): 528 | str_ea = idc.get_name_ea_simple("aImg4") 529 | 530 | if str_ea != ida_idaapi.BADADDR: 531 | aimg4_ea = list(idautils.XrefsTo(str_ea))[0].frm 532 | 533 | if aimg4_ea == ida_idaapi.BADADDR: 534 | return ida_idaapi.BADADDR 535 | 536 | func = idaapi.get_func(aimg4_ea) 537 | print("\t[+] _image4_get_partial = 0x%x" % (func.start_ea)) 538 | idc.set_name(func.start_ea, "_image4_get_partial", idc.SN_CHECK) 539 | return func.start_ea 540 | 541 | print("\t[-] _image4_get_partial = not found") 542 | return ida_idaapi.BADADDR 543 | 544 | def find_putchar(base_ea, base_end_ea): 545 | seq_ea = ida_search.find_binary(base_ea, base_end_ea, "A0 01 80 52 ?? ?? FF 97 E0", 16, ida_search.SEARCH_DOWN) 546 | 547 | if seq_ea != ida_idaapi.BADADDR: 548 | func = idaapi.get_func(seq_ea) 549 | print("\t[+] _putchar = 0x%x" % (func.start_ea)) 550 | idc.set_name(func.start_ea, "_putchar", idc.SN_CHECK) 551 | return func.start_ea 552 | 553 | print("\t[-] _putchar = not found") 554 | return ida_idaapi.BADADDR 555 | 556 | def find_macho_load(base_ea): 557 | pz_ea = idc.get_name_ea_simple("aPagezero") 558 | 559 | if pz_ea != ida_idaapi.BADADDR: 560 | if len(list(idautils.XrefsTo(pz_ea))) != 3: 561 | print("\t[-] _macho_load = not found") 562 | return ida_idaapi.BADADDR 563 | 564 | # in iBoot versions newer than 6603.x _macho_load seems inlined, 565 | # so the following heuristic isn't applicable 566 | func1_ea = idaapi.get_func(list(idautils.XrefsTo(pz_ea))[0].frm).start_ea 567 | func2_ea = idaapi.get_func(list(idautils.XrefsTo(pz_ea))[1].frm).start_ea 568 | func3_ea = idaapi.get_func(list(idautils.XrefsTo(pz_ea))[2].frm).start_ea 569 | 570 | if func2_ea != func3_ea: 571 | print("\t[-] _macho_load = not found") 572 | return ida_idaapi.BADADDR 573 | 574 | if func1_ea != func2_ea: 575 | print("\t[+] _macho_load = 0x%x" % (func2_ea)) 576 | idc.set_name(func2_ea, "_macho_load", idc.SN_CHECK) 577 | return func2_ea 578 | 579 | print("\t[-] _macho_load = not found") 580 | return ida_idaapi.BADADDR 581 | 582 | def find_interesting(base_ea, base_end): 583 | mv_ea = find_macho_valid(base_ea, base_end) 584 | 585 | if mv_ea != ida_idaapi.BADADDR: 586 | ldk_ea = find_loaded_kernelcache(mv_ea) 587 | 588 | if ldk_ea != ida_idaapi.BADADDR: 589 | lk_ea = find_load_kernelcache(ldk_ea) 590 | else: 591 | print("\t[-] _load_kernelcache = not found") 592 | else: 593 | print("\t[-] _loaded_kernelcache = not found") 594 | print("\t[-] _load_kernelcache = not found") 595 | 596 | pk_ea = find_panic(base_ea) 597 | go_ea = find_do_go(base_ea) 598 | pr_ea = find_do_printf(base_ea, base_end) 599 | 600 | i4i_ea = find_image4_validate_property_callback_interposer(base_ea) 601 | 602 | if i4i_ea != ida_idaapi.BADADDR: 603 | i4ip_ea = find_image4_validate_property_callback_interposer_ptr(i4i_ea) 604 | i4vc_ea = find_image4_validate_property_callback(base_ea, base_end, i4ip_ea) 605 | 606 | if i4vc_ea != ida_idaapi.BADADDR: 607 | i4l_ea = find_image4_load(i4vc_ea) 608 | else: 609 | print("\t[-] _image4_load = not found") 610 | 611 | else: 612 | print("\t[-] _image4_load = not found") 613 | print("\t[-] _image4_validate_property_callback = not found") 614 | print("\t[-] _image4_validate_property_callback_interposer_ptr = not found") 615 | 616 | dr_ea = find_do_ramdisk(base_ea) 617 | paj_ea = find_prepare_and_jump(base_ea) 618 | ase_ea = find_arm_synchronous_exception(base_ea) 619 | rmr_ea = find_record_memory_range(base_ea) 620 | i4d_ea = find_img4decodeinit(base_ea, base_end) 621 | scf_ea = find_stack_chk_fail(base_ea) 622 | aes_ea = find_aes_crypto_cmd(base_ea, base_end) 623 | udt_ea = find_update_device_tree(base_ea) 624 | ml_ea = find_macho_load(base_ea) 625 | lko_ea = find_load_kernelcache_object(base_ea, ml_ea) 626 | pgv_ea = find_pmgr_binning_mode_get_value(base_ea) 627 | i4p_ea = find_image4_get_partial(base_ea) 628 | mt_ea = find_main_task(base_ea) 629 | tei_ea = find_target_early_init(base_ea) 630 | bc_ea = find_boot_check_panic(base_ea, base_end) 631 | pei_ea = find_platform_early_init(base_ea, base_end) 632 | crv_ea = find_chipid_get_chip_revision(base_ea, base_end) 633 | pid_ea = find_platform_init_display(base_ea) 634 | pc_ea = find_putchar(base_ea, base_end) 635 | 636 | # just to be sure 637 | if br_flag == false: 638 | if pc_ea != ida_idaapi.BADADDR and mv_ea == ida_idaapi.BADADDR: 639 | # this is a SecureROM image 640 | segm = ida_segment.getseg(base_ea) 641 | 642 | if segm: 643 | idaapi.set_segm_name(segm, "SecureROM", 0) 644 | print("[+] Identified as a SecureROM image") 645 | 646 | def accept_file(fd, fname): 647 | global br_flag 648 | version = 0 649 | ret = 0 650 | 651 | if type(fname) == str: 652 | fd.seek(0x200) 653 | ver_bin = fd.read(0x30) 654 | 655 | try: 656 | ver_str = ver_bin.decode() 657 | ver_str = "%s" % (ver_str) 658 | except: 659 | return ret 660 | 661 | if ver_str[:9] == "SecureROM": 662 | ret = {"format" : "SecureROM (AArch64)", "processor" : "arm"} 663 | br_flag = true 664 | return ret 665 | 666 | fd.seek(0x280) 667 | ver_bin = fd.read(0x20) 668 | 669 | try: 670 | ver_str = ver_bin.decode() 671 | ver_str = "%s" % (ver_str) 672 | except: 673 | return ret 674 | 675 | if ver_str[:5] == "iBoot": 676 | version = ver_str[6:] # for later 677 | ret = {"format" : "iBoot (AArch64)", "processor" : "arm"} 678 | 679 | return ret 680 | 681 | def load_file(fd, neflags, format): 682 | global prologues 683 | global br_flag 684 | size = 0 685 | base_addr = 0 686 | ea = 0 687 | nfunc = 0 688 | 689 | idaapi.set_processor_type("arm", ida_idp.SETPROC_LOADER_NON_FATAL) 690 | idaapi.get_inf_structure().lflags |= idaapi.LFLG_64BIT 691 | 692 | if (neflags & idaapi.NEF_RELOAD) != 0: 693 | return 1 694 | 695 | fd.seek(0, idaapi.SEEK_END) 696 | size = fd.tell() 697 | 698 | segm = idaapi.segment_t() 699 | segm.bitness = 2 # 64-bit 700 | segm.start_ea = 0 701 | segm.end_ea = size 702 | 703 | if br_flag == false: 704 | idaapi.add_segm_ex(segm, "iBoot", "CODE", idaapi.ADDSEG_OR_DIE) 705 | else: 706 | idaapi.add_segm_ex(segm, "SecureROM", "CODE", idaapi.ADDSEG_OR_DIE) 707 | 708 | fd.seek(0) 709 | fd.file2base(0, 0, size, false) 710 | 711 | idaapi.add_entry(0, 0, "start", 1) 712 | ida_funcs.add_func(ea) 713 | 714 | print("[+] Marked as code") 715 | 716 | # heuristic 717 | while(true): 718 | mnemonic = idc.print_insn_mnem(ea) 719 | 720 | if "LDR" in mnemonic: 721 | base_str = idc.print_operand(ea, 1) 722 | base_addr = int(base_str.split("=")[1], 16) 723 | 724 | break 725 | 726 | if ea > (4 * 20): 727 | print("[!] Failed to identify base address") 728 | break 729 | 730 | ea += 4 731 | 732 | print("[+] Rebasing to address 0x%x" % (base_addr)) 733 | idaapi.rebase_program(base_addr, idc.MSF_NOFIX) 734 | 735 | segment_start = base_addr 736 | segment_end = idc.get_segm_attr(segment_start, idc.SEGATTR_END) 737 | 738 | ea = segment_start 739 | 740 | print("[+] Searching and defining functions") 741 | 742 | for prologue in prologues: 743 | while ea != ida_idaapi.BADADDR: 744 | ea = ida_search.find_binary(ea, segment_end, prologue, 16, ida_search.SEARCH_DOWN) 745 | 746 | if ea != ida_idaapi.BADADDR: 747 | if len(prologue) < 8: 748 | ea = ea - 2 749 | 750 | if (ea % 4) == 0 and ida_bytes.get_full_flags(ea) < 0x200: 751 | # print("[+] Defining a function at 0x%x" % (ea)) 752 | ida_funcs.add_func(ea) 753 | nfunc = nfunc + 1 754 | 755 | ea = ea + 4 756 | 757 | idc.plan_and_wait(segment_start, segment_end) 758 | 759 | print("[+] Identified %d new functions" % (nfunc)) 760 | 761 | print("[+] Looking for interesting functions") 762 | find_interesting(segment_start, segment_end) 763 | 764 | return 1 765 | 766 | # for easy testing 767 | if __name__ == "__main__": 768 | # ida_auto.auto_wait() 769 | print("[+] find_interesting():") 770 | 771 | for seg in idautils.Segments(): 772 | st = ida_segment.getseg(seg) 773 | name = idaapi.get_segm_name(st) 774 | 775 | if name == "iBoot" or name == "SecureROM" or name == "iBEC" \ 776 | or name == "BootRom" or name == "LLB": 777 | 778 | segm_start = st.start_ea 779 | segm_end = idc.get_segm_attr(segm_start, idc.SEGATTR_END) 780 | find_interesting(segm_start, segm_end) 781 | break 782 | 783 | # ida_pro.qexit(0) 784 | 785 | # EOF 786 | -------------------------------------------------------------------------------- /screenshot-loader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argp/iBoot64helper/83e5c319c8680b53e17de6c426c11578a05c2605/screenshot-loader.png -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argp/iBoot64helper/83e5c319c8680b53e17de6c426c11578a05c2605/screenshot.png --------------------------------------------------------------------------------