Skip to content

Instantly share code, notes, and snippets.

@swarzesherz
Last active April 26, 2016 02:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save swarzesherz/447fafcb73e2f5f3f7bf to your computer and use it in GitHub Desktop.
Save swarzesherz/447fafcb73e2f5f3f7bf to your computer and use it in GitHub Desktop.
ExInjector.py
#!/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={}
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)
@mid-kid
Copy link

mid-kid commented Feb 4, 2015

Please consider incorporating this patch in order for this to work when ctrtool is in path

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment