Skip to content

Instantly share code, notes, and snippets.

@mid-kid
Forked from swarzesherz/ExInjector.py
Created February 4, 2015 15:45
Show Gist options
  • Save mid-kid/5e1565684a509c178530 to your computer and use it in GitHub Desktop.
Save mid-kid/5e1565684a509c178530 to your computer and use it in GitHub Desktop.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import argparse
import os
import random
import string
import tempfile
import binascii
import sys
import subprocess
import hashlib
import errno
parser = argparse.ArgumentParser()
parser.add_argument('-rom', required=True, action='store', help='<your rom>')
parser.add_argument('-exheader', required=True, action='store', help='<origianl decrypted exheader>')
parser.add_argument('-sd', action='store_true', default=False, dest='sd', help='')
parser.add_argument('-fwspoof', action='store_true', default=False, dest='fwspoof', help='Spoof FW')
arguments = parser.parse_args()
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[34m'
OKGREEN = '\033[32m'
WARNING = '\033[33m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
def randomString(length):
return ''.join(random.choice(string.lowercase) for i in range(length))
def binRead(rfile, length=0, offset=0):
data=bytes()
with open(rfile, 'rb') as f:
f.seek(offset)
data = f.read(length)
return {'rbytes':len(data), 'data':data}
def binWrite(wfile, data, length=0, offset=0):
wbytes=0
mode='wb'
if os.path.isfile(wfile):
mode='rb+'
with open(wfile, mode) as f:
f.seek(offset)
for i in range(0, length):
f.write(data[i])
wbytes = wbytes+1
return {'wbytes': wbytes}
def xor(data, keystream):
result = bytes()
for i in range(len(data)):
result = result+chr( ord(data[i]) ^ ord(keystream[i]) )
return result
def cleanup(errcode):
if os.path.isfile(exheadercopypath):
os.remove(exheadercopypath)
if os.path.isfile(xorpadfilepath):
os.remove(xorpadfilepath)
exit(errcode)
romfilepath=os.path.abspath(arguments.rom)
exheaderfilepath=os.path.abspath(arguments.exheader)
useSD=arguments.sd
fwSpoof=arguments.fwspoof
ctrtoolPath=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'ctrtool')
container=""
zerostring = '\x00' * 0x800
tempdir=tempfile.gettempdir()
exheadercopypath=os.path.join(tempdir, randomString(9)+'exhcopy')
xorpadfilepath=os.path.join(tempdir, randomString(9)+'xorpad')
result={}
# Use ctrtool from path if it does not exist in script dir
if not os.path.isfile(ctrtoolPath):
ctrtoolPath = 'ctrtool'
result = binRead(exheaderfilepath, 0x400, 0)
if result['rbytes'] != 0x400:
print bcolors.FAIL + "Could not read exheader file!"
cleanup(errno.EIO)
exheader = binRead(exheaderfilepath, 0x800, 0)
if exheader['rbytes'] != 0x800:
print bcolors.FAIL + "Could not read exheader file!"
cleanup(errno.EIO)
result = binWrite(exheadercopypath, exheader['data'], 0x800, 0)
if result['wbytes'] != 0x800:
print bcolors.FAIL + "Could not write exheader copy!"
cleanup(errno.EIO)
magic = binRead(romfilepath, 0x4, 0x100)
if magic['data'] not in ("NCCH", "NCSD"):
print bcolors.FAIL + "Invalid file!"
cleanup(errno.EBFONT)
container = magic['data']
offset = {'NCCH': 0x18F, 'NCSD': 0x418F}
ncchflags = binRead(romfilepath, 0x1, offset[container])
if ncchflags['rbytes'] != 0x1:
print bcolors.FAIL + "Could not read ncch flags!"
cleanup(errno.ENODATA)
if not(ord(ncchflags['data']) & 0x4) and not(ord(ncchflags['data']) & 0x1):
print bcolors.FAIL + "This is a retail rom!\nYou need a repacked one."
cleanup(errno.EBFONT)
if ord(ncchflags['data']) & 0x4:
syscontrolflags = binRead(exheadercopypath, 0x1,0x0D)
if syscontrolflags['rbytes'] != 0x1:
print bcolors.FAIL + "Could not read system control flags!"
if not(ord(syscontrolflags['data']) & 0x02) and not(arguments.fwspoof or arguments.sd):
print bcolors.WARNING + "Do you want to install this game to the SD card (y,n)? ",
userinput = sys.stdin.readline(1)
if userinput == 'y':
useSD = True
fw = binRead(exheadercopypath, 0x1, 0x39C)
if not(arguments.fwspoof or arguments.sd) and fw > 0x21:
print bcolors.WARNING + "This game requires a firmware version higher than 4.5.\nSpoof firmware version so it runs on the CFW?\n(May not work with all games) (y,n)? ",
userinput = sys.stdin.readline(1)
if userinput == 'y':
fwSpoof = True
if not(ord(syscontrolflags['data']) & 0x02) and useSD:
newflags = chr(ord(syscontrolflags['data']) ^ 0x02)
result = binWrite(exheadercopypath, newflags, 0x1, 0x0D)
if result['wbytes'] != 0x1:
print bcolors.FAIL + "Could not write system control flags!"
cleanup(errno.EIO)
if fwSpoof:
result = binWrite(exheadercopypath, "\x21", 0x1, 0x39C)
if result['wbytes'] != 0x1:
print bcolors.FAIL + "Could not write kernel release version!"
cleanup(errno.EIO)
result = binWrite(exheadercopypath, "\x21", 0x1, 0x79C)
if result['wbytes'] != 0x1:
print bcolors.FAIL + "Could not write kernel release version!"
cleanup(errno.EIO)
if container == "NCSD":
result = binWrite(romfilepath, zerostring, 0x800, 0x4200)
elif container == "NCCH":
result = binWrite(romfilepath, zerostring, 0x800, 0x200)
subprocess.call([ctrtoolPath, "--exheader", xorpadfilepath, romfilepath], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
xorpad = binRead(xorpadfilepath, 0x800,0x0)
exhcopy = binRead(exheadercopypath, 0x800,0x0)
vhex = binRead(exheadercopypath, 0x400,0x0)
newexheader = xor(exhcopy['data'], xorpad['data'])
if container == "NCSD":
result = binWrite(romfilepath, newexheader, 0x800, 0x4200)
elif container == "NCCH":
result = binWrite(romfilepath, newexheader, 0x800, 0x200)
vhash = hashlib.sha256(vhex['data']).digest()
if container == "NCSD":
result = binWrite(romfilepath, vhash, 0x20, 0x1160)
result = binWrite(romfilepath, vhash, 0x20, 0x4160)
elif container == "NCCH":
result = binWrite(romfilepath, vhash, 0x20, 0x160)
ctrtool = subprocess.call([ctrtoolPath, romfilepath], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if ctrtool != 0:
print bcolors.FAIL + "Could not inject properly."
cleanup(errno.EBFONT)
print bcolors.OKGREEN + "Extended header successfully injected!"
cleanup(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment