├── .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
--------------------------------------------------------------------------------