Added some new commands to other.py, fixed some non-matching capitalization styles (the new form for all future capitalizations is likeThis not LikeThis, nor like_this.) and removed the wx and png dependancies on quite a few modules. I'll add some interfacing to the other.py classes from nand.py some time soon. I have confirmed that CONF works 100%.

This commit is contained in:
Alex Marshall 2009-06-21 03:02:56 -07:00
parent f332764450
commit 7200db92c0
9 changed files with 131 additions and 72 deletions

2
U8.py
View File

@ -1,6 +1,4 @@
import os, hashlib, struct, subprocess, fnmatch, shutil, urllib, array
import wx
import png
from Crypto.Cipher import AES
from Struct import Struct

1
Wii.py
View File

@ -5,6 +5,7 @@ from savedata import *
from banner import *
from disc import *
from nand import *
from other import *
from title import *
from TPL import *
from U8 import *

View File

@ -1,6 +1,4 @@
import os, hashlib, struct, subprocess, fnmatch, shutil, urllib, array
import wx
import png
from Crypto.Cipher import AES
from Struct import Struct
@ -20,39 +18,39 @@ class Crypto:
def __init__(self):
self.align = 64
return
def DecryptData(self, key, iv, data, align = True):
def decryptData(self, key, iv, data, align = True):
"""Decrypts some data (aligns to 64 bytes, if needed)."""
if((len(data) % self.align) != 0 and align):
return AES.new(key, AES.MODE_CBC, iv).decrypt(data + ("\x00" * (self.align - (len(data) % self.align))))
else:
return AES.new(key, AES.MODE_CBC, iv).decrypt(data)
def EncryptData(self, key, iv, data, align = True):
def encryptData(self, key, iv, data, align = True):
"""Encrypts some data (aligns to 64 bytes, if needed)."""
if((len(data) % self.align) != 0 and align):
return AES.new(key, AES.MODE_CBC, iv).encrypt(data + ("\x00" * (self.align - (len(data) % self.align))))
else:
return AES.new(key, AES.MODE_CBC, iv).encrypt(data)
def DecryptContent(self, titlekey, idx, data):
def decryptContent(self, titlekey, idx, data):
"""Decrypts a Content."""
iv = struct.pack(">H", idx) + "\x00" * 14
return self.DecryptData(titlekey, iv, data)
def DecryptTitleKey(self, commonkey, tid, enckey):
return self.decryptData(titlekey, iv, data)
def decryptTitleKey(self, commonkey, tid, enckey):
"""Decrypts a Content."""
iv = struct.pack(">Q", tid) + "\x00" * 8
return self.DecryptData(commonkey, iv, enckey, False)
def EncryptContent(self, titlekey, idx, data):
return self.decryptData(commonkey, iv, enckey, False)
def encryptContent(self, titlekey, idx, data):
"""Encrypts a Content."""
iv = struct.pack(">H", idx) + "\x00" * 14
return self.EncryptData(titlekey, iv, data)
def CreateSHAHash(self, data): #tested WORKING (without padding)
return self.encryptData(titlekey, iv, data)
def createSHAHash(self, data): #tested WORKING (without padding)
return hashlib.sha1(data).digest()
def CreateSHAHashHex(self, data):
def createSHAHashHex(self, data):
return hashlib.sha1(data).hexdigest()
def CreateMD5HashHex(self, data):
def createMD5HashHex(self, data):
return hashlib.md5(data).hexdigest()
def CreateMD5Hash(self, data):
def createMD5Hash(self, data):
return hashlib.md5(data).digest()
def ValidateSHAHash(self, data, hash):
def validateSHAHash(self, data, hash):
"""Validates a hash. Not checking currently because we have some...issues with hashes."""
return 1 #hack
if((len(data) % self.align) != 0):

View File

@ -1,6 +1,4 @@
import os, hashlib, struct, subprocess, fnmatch, shutil, urllib, array
import wx
import png
import zlib
from Crypto.Cipher import AES

View File

@ -1,6 +1,4 @@
import os, hashlib, struct, subprocess, fnmatch, shutil, urllib, array
import wx
import png
import time
from title import *
@ -124,7 +122,7 @@ class WOD: #WiiOpticalDisc
print 'IV %s (len %i)\n' % (hexdump(blockIV), len(blockIV))
blockData = block[0x0400:0x7FFF]
return Crypto().DecryptData(self.partitionKey, blockIV, blockData, True)
return Crypto().decryptData(self.partitionKey, blockIV, blockData, True)
def readPartition(self, offset, size):

View File

@ -1,6 +1,4 @@
import os, hashlib, struct, subprocess, fnmatch, shutil, urllib, array
import wx
import png
from binascii import *
from Crypto.Cipher import AES

136
other.py
View File

@ -1,6 +1,4 @@
import os, hashlib, struct, subprocess, fnmatch, shutil, urllib, array
import wx
import png
from binascii import *
from Crypto.Cipher import AES
@ -11,8 +9,7 @@ from common import *
from title import *
class CONF:
""" This class deal with setting.txt wich hold some wii info like game area and wii serial number """
"""This class deals with setting.txt which holds some important information like region and serial number """
def __init__(self, f):
self.conf = ''
self.keys = {}
@ -27,7 +24,7 @@ class CONF:
return
self.conf = self.fp.read(0x100)
self.conf = self.xorConf(self.conf)
self.conf = self.XORConf(self.conf)
self.fp.seek(0)
keys = self.conf.split('\r\n')
@ -43,19 +40,23 @@ class CONF:
self.keys[keyName] = keyVal
def getKeysCount(self):
"""Gets how many keys exist."""
return self.totalKeys
def getKeysName(self):
"""Returns the list of key names."""
return self.keyNames
def getKeyValue(self, key):
"""Returns the value of the key ``key''."""
try:
return self.keys[key.upper()]
except KeyError:
return 'Key not found'
def setKeyValue(self, key, value):
if self.getKeyValue(key.upper()) != 'Key not found':
"""Sets the value of key ``key'' to ``value''."""
if(self.keyExist(key)):
self.keys[key.upper()] = value.upper()
self.conf = ''
@ -67,18 +68,20 @@ class CONF:
self.conf += '\r\n'
self.fp.seek(0)
self.fp.write(self.xorConf(self.conf))
self.fp.write(self.XORConf(self.conf))
self.fp.write('\x00' * (0x100 - len(self.conf)))
self.lastKeyOffset = self.conf.rfind('\r\n') + 2
def keyExist(self, key):
"""Returns 1 if key ``key'' exists, 0 otherwise."""
if self.getKeyValue(key.upper()) != 'Key not found':
return 0
else:
return 1
def addKey(self, key, value):
"""Adds key ``key'' with value ``value'' to the list."""
if self.lastKeyOffset + len(key) + 1 + len(value) + 2 > 0x100:
return -1
if not self.keyExist(key):
@ -93,18 +96,20 @@ class CONF:
self.lastKeyOffset += len(key) + 1 + len(value) + 2
self.fp.seek(0)
self.fp.write(self.xorConf(self.conf))
self.fp.write(self.XORConf(self.conf))
def xorConf(self, conf):
xorKey = 0x73B5DBFA
def XORConf(self, conf):
"""Encrypts/decrypts the setting.txt file."""
XORKey = 0x73B5DBFA
out = ''
for x in range(len(conf)):
out += chr(ord(conf[x]) ^ xorKey & 0xFF)
xorKey = (xorKey << 1) | (xorKey >> 31)
out += chr(ord(conf[x]) ^ XORKey & 0xFF)
XORKey = (XORKey << 1) | (XORKey >> 31)
return out
def delKey(self, key):
def DeleteKey(self, key):
"""Deletes the key ``key''."""
try:
del self.keys[key.upper()]
self.keyNames.remove(key.upper())
@ -119,14 +124,16 @@ class CONF:
self.conf += '\r\n'
self.fp.seek(0)
self.fp.write(self.xorConf(self.conf))
self.fp.write(self.XORConf(self.conf))
self.fp.write('\x00' * (0x100 - len(self.conf)))
self.lastKeyOffset = self.conf.rfind('\r\n') + 2
except KeyError:
return 'Key not found'
def delKeyByValue(self, value):
# This function is fucking dangerous. It deletes all keys with that value. Really not a good idea.
def deleteKeyByValue(self, value):
"""Deletes all keys with value ``value''. WATCH OUT, YOU MIGHT ACCIDENTALLY DELETE WRONG KEYS."""
try:
for key in self.keys.keys():
if self.keys.get(key) == value:
@ -144,13 +151,71 @@ class CONF:
self.conf += '\r\n'
self.fp.seek(0)
self.fp.write(self.xorConf(self.conf))
self.fp.write(self.XORConf(self.conf))
self.fp.write('\x00' * (0x100 - len(self.conf)))
self.lastKeyOffset = self.conf.rfind('\r\n') + 2
except KeyError:
return 'Key not found'
def getRegion(self):
"""gets the Region key. (Shortcut for getKeyValue("GAME"))"""
return self.getKeyValue("GAME")
def getArea(self):
"""gets the Area key. (Shortcut for getKeyValue("AREA"))"""
return self.getKeyValue("AREA")
def getVideoMode(self):
"""gets the Video Mode key. (Shortcut for getKeyValue("VIDEO"))"""
return self.getKeyValue("VIDEO")
def getSerialCode(self):
"""gets the Serial Code key. (Shortcut for getKeyValue("CODE"))"""
return self.getKeyValue("CODE")
def getDVDModel(self): # Might not be model =/
"""gets the DVD Model (?) key. (Shortcut for getKeyValue("DVD"))"""
return self.getKeyValue("DVD")
def getHardwareModel(self):
"""gets the Hardware Model key. (Shortcut for getKeyValue("MODEL"))"""
return self.getKeyValue("MODEL")
def getSerialNumber(self):
"""gets the Serial Number key. (Shortcut for getKeyValue("SERNO"))"""
return self.getKeyValue("SERNO")
def setRegion(self, value):
"""sets the Region key. (Shortcut for setKeyValue("GAME", value))"""
return self.setKeyValue("GAME", value)
def setArea(self, value):
"""sets the Area key. (Shortcut for setKeyValue("AREA", value))"""
return self.setKeyValue("AREA", value)
def setVideoMode(self, value):
"""sets the Video Mode key. (Shortcut for setKeyValue("VIDEO", value))"""
return self.setKeyValue("VIDEO", value)
def setSerialCode(self, value):
"""sets the Serial Code key. (Shortcut for setKeyValue("CODE", value))"""
return self.setKeyValue("CODE", value)
def setDVDModel(self, value): # Might not be model =/
"""sets the DVD Model (?) key. (Shortcut for setKeyValue("DVD", value))"""
return self.setKeyValue("DVD", value)
def setHardwareModel(self, value):
"""sets the Hardware Model key. (Shortcut for setKeyValue("MODEL", value))"""
return self.setKeyValue("MODEL", value)
def setSerialNumber(self, value):
"""sets the Serial Number key. (Shortcut for setKeyValue("SERNO", value))"""
return self.setKeyValue("SERNO", value)
class iplsave:
"""This class performs all iplsave.bin related things. It includes functions to add a title to the list, remove a title based upon position or title, and move a title from one position to another."""
class IPLSAVE_Entry(Struct):
@ -201,17 +266,19 @@ class iplsave:
fp.close()
self.UpdateMD5()
def UpdateMD5(self):
def updateMD5(self):
"""Updates the MD5 hash in the iplsave.bin file. Used by other functions here."""
fp = open(self.f, "rb")
data = fp.read()
fp.close()
md5 = Crypto().CreateMD5Hash(data)
md5 = Crypto().createMD5Hash(data)
fp = open(self.f, "wb")
fp.write(data)
fp.write(md5)
fp.close()
def AddTitleBase(self, x, y, page, tid, movable, type, overwrite, clear, isdisc):
def addTitleBase(self, x, y, page, tid, movable, type, overwrite, clear, isdisc):
"""A base AddTitle function that is used by others. Don't use this."""
if((x + (y * 4) + (page * 12)) >= 0x30):
print "Too far!"
return None
@ -222,7 +289,7 @@ class iplsave:
baseipl_ent.type1 = fp.read(1)
fp.close()
if((baseipl_ent.type1 != "\0") and (not overwrite)):
return self.AddTitle(x + 1, y, page, tid, movable, type)
return self.addTitleBase(x + 1, y, page, tid, movable, type, overwrite, clear, isdisc)
fp = open(self.f, "wb")
fp.write(data)
fp.seek(16 + ((x + (y * 4) + (page * 12))) * 16)
@ -250,19 +317,23 @@ class iplsave:
fp.write(a2b_hex("%04X" % baseipl_ent.flags))
fp.write(a2b_hex("%016X" % baseipl_ent.titleid))
fp.close()
self.UpdateMD5()
self.updateMD5()
return (x + (y * 4) + (page * 12))
def AddTitle(self, x, y, page, tid, movable, type):
return self.AddTitleBase(x, y, page, tid, movable, type, 0, 0, 0)
def addTitle(self, x, y, page, tid, movable, type):
"""Adds a title with title ID ``tid'' at location (x,y) on page ``page''. ``movable'' specifies whether the title is movable, and ``type'' specifies the type of title (00 for most titles.)"""
return self.addTitleBase(x, y, page, tid, movable, type, 0, 0, 0)
def AddDisc(self, x, y, page, movable):
return self.AddTitleBase(x, y, page, 0, movable, 0, 0, 0, 1)
def addDisc(self, x, y, page, movable):
"""Adds the Disc Channel at location (x,y) on page ``page''. ``movable'' specifies whether it can be moved."""
return self.addTitleBase(x, y, page, 0, movable, 0, 0, 0, 1)
def DeletePosition(self, x, y, page):
return self.AddTitleBase(x, y, page, 0, 0, 0, 1, 1, 0)
def deletePosition(self, x, y, page):
"""Deletes the title at (x,y) on page ``page''"""
return self.addTitleBase(x, y, page, 0, 0, 0, 1, 1, 0)
def DeleteTitle(self, tid):
def deleteTitle(self, tid):
"""Deletes the title with title ID ``tid''"""
fp = open(self.f, "rb")
baseipl_ent = self.IPLSAVE_Entry
for i in range(0x30):
@ -273,10 +344,11 @@ class iplsave:
baseipl_ent.flags = fp.read(2)
baseipl_ent.titleid = fp.read(8)
if(baseipl_ent.titleid == a2b_hex("%016X" % tid)):
self.DeletePosition(i, 0, 0)
self.deletePosition(i, 0, 0)
fp.close()
def MoveTitle(self, x1, y1, page1, x2, y2, page2):
def moveTitle(self, x1, y1, page1, x2, y2, page2):
"""Moves a title from (x1,y1) on page ``page1'' to (x2,y2) on page ``page2''"""
fp = open(self.f, "rb")
baseipl_ent = self.IPLSAVE_Entry
fp.seek(16 + ((x1 + (y1 * 4) + (page1 * 12)) * 16))
@ -286,5 +358,5 @@ class iplsave:
baseipl_ent.flags = fp.read(2)
baseipl_ent.titleid = fp.read(8)
fp.close()
self.DeletePosition(x1, y1, page1)
return self.AddTitle(x2, y2, page2, baseipl_ent.titleid, (baseipl_ent.flags - 0xE) ^ 1, baseipl_ent.type2)
self.deletePosition(x1, y1, page1)
return self.addTitle(x2, y2, page2, baseipl_ent.titleid, (baseipl_ent.flags - 0xE) ^ 1, baseipl_ent.type2)

View File

@ -1,6 +1,4 @@
import os, hashlib, struct, subprocess, fnmatch, shutil, urllib, array
import wx
import png
from hashlib import md5
from Crypto.Cipher import AES
@ -157,7 +155,7 @@ class Savegame():
print 'Extracted %s (%ib)' % (name, fileHdr.size)
fileBuffer = self.fd.read(fileHdr.size)
fileBuffer = Crypto().DecryptData(self.sdKey, fileIV, fileBuffer, True)
fileBuffer = Crypto().decryptData(self.sdKey, fileIV, fileBuffer, True)
try:
open(name, 'w+b').write(fileBuffer)
except:
@ -176,7 +174,7 @@ class Savegame():
def analyzeHeader(self):
headerBuffer = self.fd.read(0xF0C0)
headerBuffer = Crypto().DecryptData(self.sdKey, self.sdIv, headerBuffer, True)
headerBuffer = Crypto().decryptData(self.sdKey, self.sdIv, headerBuffer, True)
self.hdr = self.savegameHeader().unpack(headerBuffer[:0x20])

View File

@ -1,6 +1,4 @@
import os, struct, subprocess, fnmatch, shutil, urllib, array
import wx
import png
from Struct import Struct
@ -53,7 +51,7 @@ class Ticket:
if(self.tik.commonkey_index == 1): #korean, kekekekek!
commonkey = koreankey
self.titlekey = Crypto().DecryptTitleKey(commonkey, self.tik.titleid, self.tik.enctitlekey)
self.titlekey = Crypto().decryptTitleKey(commonkey, self.tik.titleid, self.tik.enctitlekey)
def getTitleKey(self):
"""Returns a string containing the title key."""
return self.titlekey
@ -87,7 +85,7 @@ class Ticket:
self.rsamod = self.rsamod = "\x00" * 256
for i in range(65536):
self.tik.unk2 = i
if(Crypto().CreateSHAHashHex(self.tik.pack())[:2] == "00"):
if(Crypto().createSHAHashHex(self.tik.pack())[:2] == "00"):
break
if(i == 65535):
raise ValueError("Failed to fakesign. Aborting...")
@ -204,7 +202,7 @@ class TMD:
data += self.tmd.pack()
for i in range(self.tmd.numcontents):
data += self.contents[i].pack()
if(Crypto().CreateSHAHashHex(data)[:2] == "00"):
if(Crypto().createSHAHashHex(data)[:2] == "00"):
break
if(i == 65535):
raise ValueError("Failed to fakesign! Aborting...")
@ -271,7 +269,7 @@ class NUS:
certs += rawtik[0x2A4 + 0x300:] #CA (tik)
certs += rawtmd[0x328:0x328 + 0x300] #CP
if(Crypto().CreateMD5HashHex(certs) != "7ff50e2733f7a6be1677b6f6c9b625dd"):
if(Crypto().createMD5HashHex(certs) != "7ff50e2733f7a6be1677b6f6c9b625dd"):
raise ValueError("Failed to create certs! MD5 mistatch.")
open("cert", "wb").write(certs)
@ -301,8 +299,8 @@ class NUS:
if(decrypt):
data = open("%08x.app" % output, "rb").read(content.size)
tmpdata = Crypto().DecryptContent(titlekey, content.index, data)
if(Crypto().ValidateSHAHash(tmpdata, content.hash) == 0):
tmpdata = Crypto().decryptContent(titlekey, content.index, data)
if(Crypto().validateSHAHash(tmpdata, content.hash) == 0):
raise ValueError("Decryption failed! SHA1 mismatch.")
open("%08x.app" % output, "wb").write(tmpdata)
@ -330,10 +328,10 @@ class WAD:
if(decrypted):
if(fakesign):
content.hash = str(Crypto().CreateSHAHash(tmpdata))
content.hash = str(Crypto().createSHAHash(tmpdata))
content.size = len(tmpdata)
encdata = Crypto().EncryptContent(titlekey, content.index, tmpdata)
encdata = Crypto().encryptContent(titlekey, content.index, tmpdata)
else:
encdata = tmpdata
@ -427,7 +425,7 @@ class WAD:
if(tmpsize % 16 != 0):
tmpsize += 16 - (tmpsize % 16)
tmptmpdata = fd.read(tmpsize)
tmpdata = Crypto().DecryptContent(titlekey, contents[i].index, tmptmpdata)
tmpdata = Crypto().decryptContent(titlekey, contents[i].index, tmptmpdata)
open("%08x.app" % contents[i].index, "wb").write(tmpdata)
if(tmpsize % 64 != 0):
fd.seek(64 - (tmpsize % 64), 1)