Last active
July 24, 2024 19:46
-
-
Save bazad/fe4e76a0a3b761d9fde7e74654ac14e4 to your computer and use it in GitHub Desktop.
Split a decrypted Apple SEP firmware image into individual Mach-O files.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
# | |
# sep_firmware_split.py | |
# Brandon Azad | |
# | |
# Split a decrypted Apple SEP firmware image into individual Mach-O files. | |
# | |
# iPhone11,8 17C5053a https://twitter.com/s1guza/status/1203550760102969345 | |
# iPhone11,8 17E255 https://twitter.com/s1guza/status/1244683851957522435 | |
# | |
import struct | |
import sys | |
def main(): | |
# Read the file. | |
filename = sys.argv[1] | |
with open(filename, 'rb') as f: | |
data = f.read() | |
# An assert that a file is a Mach-O. | |
def assert_macho(offset): | |
assert(struct.unpack('<I', data[offset:offset+4])[0] == 0xfeedfacf) | |
# Find the header. | |
offset = data.find(b'Built by legion2') | |
assert(offset > 0) | |
offset += 0x10 | |
offset = struct.unpack('<I', data[offset:offset+4])[0] | |
# Unpack the header, which describes 2 SEPOS components: the L4-based kernel and the SEPOS root | |
# process. | |
# 00000000 sep_fw_header struc ; (sizeof=0xC8, mappedto_25) | |
# [ 0] 00000000 kernel_uuid DCB 16 dup(?) | |
# [ 1] 00000010 field_10 DCQ ? | |
# [ 2] 00000018 kernel_text_offset DCQ ? ; offset | |
# [ 3] 00000020 kernel_data_offset DCQ ? ; offset | |
# [ 4] 00000028 start_of_text DCQ ? ; offset | |
# [ 5] 00000030 start_of_data DCQ ? ; offset | |
# [ 6] 00000038 sep_fw_size DCQ ? | |
# [ 7] 00000040 is_zero_1 DCQ ? | |
# [ 8] 00000048 is_zero_2 DCQ ? | |
# [ 9] 00000050 is_zero_3 DCQ ? | |
# [10] 00000058 root_text_offset DCQ ? ; offset | |
# [11] 00000060 root_text_size DCQ ? | |
# [12] 00000068 root_vm_size DCQ ? | |
# [13] 00000070 root_start DCQ ? | |
# [14] 00000078 is_zero_4 DCQ ? | |
# [15] 00000080 is_zero_5 DCQ ? | |
# [16] 00000088 is_zero_6 DCQ ? | |
# [17] 00000090 name DCB 16 dup(?) ; string(C) | |
# [18] 000000A0 root_uuid DCB 16 dup(?) | |
# [19] 000000B0 source_version DCQ ? | |
# [20] 000000B8 field_B8 DCQ ? | |
# [21] 000000C0 app_count DCQ ? | |
# 000000C8 sep_fw_header ends | |
# 0 10 20 30 40 50 60 70 80 90 a0 b0 c0 : c8 | |
sep_fw_header_format = '< 16s QQ QQ QQ QQ QQ QQ QQ QQ 16s 16s QQ Q' | |
sep_fw_header = struct.unpack(sep_fw_header_format, data[offset:offset+0xc8]) | |
kernel_text_offset = sep_fw_header[2] | |
root_text_offset = sep_fw_header[10] | |
name = sep_fw_header[17].decode('ascii').strip() | |
app_count = sep_fw_header[21] | |
offset += 0xc8 | |
# Extract the SEPOS kernel. | |
assert_macho(kernel_text_offset) | |
size_offset = data.find(b'__LINKEDIT', kernel_text_offset) | |
assert(size_offset > 0) | |
size_offset += 0x20 # segname -> fileoff | |
kernel_size = struct.unpack('<Q', data[size_offset:size_offset+8])[0] | |
print('{:#08x}-{:#08x} {}'.format(kernel_text_offset, | |
kernel_text_offset + kernel_size, name + '_kernel')) | |
with open(name + '_kernel', 'wb') as f: | |
f.write(data[kernel_text_offset:kernel_text_offset+kernel_size]) | |
# Extract the SEPOS root process. | |
assert_macho(root_text_offset) | |
size_offset = data.find(b'__LINKEDIT', root_text_offset) | |
assert(size_offset > 0) | |
size_offset += 0x20 # segname -> fileoff | |
root_size = struct.unpack('<Q', data[size_offset:size_offset+8])[0] | |
print('{:#08x}-{:#08x} {}'.format(root_text_offset, | |
root_text_offset + root_size, name + '_root')) | |
with open(name + '_root', 'wb') as f: | |
f.write(data[root_text_offset:root_text_offset+root_size]) | |
# 00000000 sep_fw_app struc ; (sizeof=0x78, mappedto_24) | |
# [ 0] 00000000 text_offset DCQ ? ; offset | |
# [ 1] 00000008 text_size DCQ ? | |
# [ 2] 00000010 data_offset DCQ ? ; offset | |
# [ 3] 00000018 data_size DCQ ? | |
# [ 4] 00000020 vm_base DCQ ? | |
# [ 5] 00000028 start DCQ ? | |
# [ 6] 00000030 is_zero_1 DCQ ? | |
# [ 7] 00000038 is_zero_2 DCQ ? | |
# [ 8] 00000040 is_zero_3 DCQ ? | |
# [ 9] 00000048 is_ffffffff DCQ ? | |
# [10] 00000050 name DCB 16 dup(?) ; string(C) | |
# [11] 00000060 uuid DCB 16 dup(?) | |
# [12] 00000070 source_version DCQ ? | |
# 00000078 sep_fw_app ends | |
# 0 10 20 30 40 50 60 70 : 78 | |
sep_fw_app_format = '< QQ QQ QQ QQ QQ 16s 16s Q' | |
# Process the individual apps. | |
for i in range(app_count): | |
# Unpack the entry for this app. | |
sep_fw_app = struct.unpack(sep_fw_app_format, data[offset:offset+0x78]) | |
text_offset = sep_fw_app[0] | |
text_size = sep_fw_app[1] | |
data_offset = sep_fw_app[2] | |
data_size = sep_fw_app[3] | |
name = sep_fw_app[10].decode('ascii').strip() | |
assert_macho(text_offset) | |
print('{:#08x}-{:#08x} {:#08x}-{:#08x} {}'.format( | |
text_offset, text_offset + text_size, | |
data_offset, data_offset + data_size, name)) | |
offset += 0x78 | |
# Reconstruct the app binary. | |
with open(name + '-{:02d}'.format(i), 'wb') as f: | |
f.write(data[text_offset:text_offset+text_size]) | |
f.write(data[data_offset:data_offset+data_size]) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment