diff --git a/tools/ps3py/Makefile b/tools/ps3py/Makefile index abefea40..0300b2ad 100644 --- a/tools/ps3py/Makefile +++ b/tools/ps3py/Makefile @@ -17,7 +17,7 @@ endif all: pkgcrypt.so pkgcrypt.so: crypt.c setup.py - @`./find-python2` setup.py build_ext $(COMPILER) + @python3 setup.py build_ext $(COMPILER) @cp build/lib.*/pkgcrypt.* . clean: @@ -27,5 +27,5 @@ clean: install: all @[ -d $(PS3DEV)/bin ] || mkdir -p $(PS3DEV)/bin @echo Installing ICON0.PNG sfo.xml pkgcrypt.* fself.py Struct.py sfo.py pkg.py - @install -m 644 ICON0.PNG sfo.xml pkgcrypt.* $(PS3DEV)/bin - @./install-scripts $(PS3DEV)/bin/ fself.py Struct.py sfo.py pkg.py + @install -m 644 ICON0.PNG sfo.xml pkgcrypt.* Struct.py $(PS3DEV)/bin + @install -m 755 fself.py sfo.py pkg.py $(PS3DEV)/bin/ diff --git a/tools/ps3py/Struct.py b/tools/ps3py/Struct.py index ab221cf1..54ce0ac3 100755 --- a/tools/ps3py/Struct.py +++ b/tools/ps3py/Struct.py @@ -13,7 +13,7 @@ class StructException(Exception): pass class Struct(object): - __slots__ = ('__attrs__', '__baked__', '__defs__', '__endian__', '__next__', '__sizes__', '__values__') + __slots__ = ('__attrs__', '__baked__', '__defs__', '__next__', '__sizes__', '__values__') int8 = StructType(('b', 1)) uint8 = StructType(('B', 1)) @@ -49,7 +49,7 @@ def __init__(self, func=None, unpack=None, **kwargs): else: sys.settrace(self.__trace__) func() - for name in func.func_code.co_varnames: + for name in func.__code__.co_varnames: value = self.__frame__.f_locals[name] self.__setattr__(name, value) @@ -231,7 +231,7 @@ def unpack(self, data, pos=0): def pack(self): arraypos, arrayname = None, None - ret = '' + ret = b'' for i in range(len(self.__defs__)): sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i] diff --git a/tools/ps3py/crypt.c b/tools/ps3py/crypt.c index 6186abb1..c8240877 100644 --- a/tools/ps3py/crypt.c +++ b/tools/ps3py/crypt.c @@ -1,3 +1,4 @@ +#define PY_SSIZE_T_CLEAN #include static PyObject *sha1_callback = NULL; @@ -25,32 +26,46 @@ static void manipulate(uint8_t *key) { } static PyObject* pkg_crypt(PyObject *self, PyObject *args) { - uint8_t *key, *input, *ret; - int key_length, input_length, length; - int remaining, i, offset=0; + const uint8_t *rokey, *input; + uint8_t *ret; + uint8_t key[0x40]; + Py_ssize_t key_length, input_length, length; + Py_ssize_t remaining, i, offset=0; PyObject *arglist; PyObject *result; - if (!PyArg_ParseTuple(args, "s#s#i", &key, &key_length, &input, &input_length, &length)) + (void) self; + + if (!PyArg_ParseTuple(args, "y#y#n", &rokey, &key_length, &input, &input_length, &length)) + return NULL; + + if (key_length != 0x40) return NULL; + + if (input_length < length) + return NULL; + + memcpy(key, rokey, sizeof(key)); + ret = malloc(length); remaining = length; - + while (remaining > 0) { - int bytes_to_dump = remaining; + Py_ssize_t bytes_to_dump = remaining; if (bytes_to_dump > 0x10) bytes_to_dump = 0x10; // outhash = SHA1(listToString(key)[0:0x40]) uint8_t *outHash; { - arglist = Py_BuildValue("(s#)", key, 0x40); + arglist = Py_BuildValue("(y#)", key, 0x40); result = PyObject_CallObject(sha1_callback, arglist); Py_DECREF(arglist); if (!result) return NULL; - int outHash_length; - if (!PyArg_Parse(result, "s#", &outHash, &outHash_length)) return NULL; + Py_ssize_t outHash_length; + if (!PyArg_Parse(result, "y#", &outHash, &outHash_length)) return NULL; + if (outHash_length < 0x10) return NULL; } for(i = 0; i < bytes_to_dump; i++) { ret[offset] = outHash[i] ^ input[offset]; @@ -60,9 +75,9 @@ static PyObject* pkg_crypt(PyObject *self, PyObject *args) { manipulate(key); remaining -= bytes_to_dump; } - + // Return the encrypted data - PyObject *py_ret = Py_BuildValue("s#", ret, length); + PyObject *py_ret = Py_BuildValue("y#y#", ret, length, key, sizeof(key)); free(ret); return py_ret; } @@ -71,6 +86,8 @@ static PyObject *register_sha1_callback(PyObject *self, PyObject *args) { PyObject *result = NULL; PyObject *temp; + (void) self; + if (PyArg_ParseTuple(args, "O:set_callback", &temp)) { if (!PyCallable_Check(temp)) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); @@ -92,8 +109,20 @@ static PyMethodDef cryptMethods[] = { {NULL, NULL, 0, NULL} }; -PyMODINIT_FUNC initpkgcrypt(void) { - (void) Py_InitModule("pkgcrypt", cryptMethods); +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "pkgcrypt", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + cryptMethods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; + +PyMODINIT_FUNC PyInit_pkgcrypt(void) { + return PyModule_Create(&moduledef); } diff --git a/tools/ps3py/find-python2 b/tools/ps3py/find-python2 deleted file mode 100755 index 17351295..00000000 --- a/tools/ps3py/find-python2 +++ /dev/null @@ -1,5 +0,0 @@ -#! /bin/sh -for python in python2.7 python2.6 python2 python; do - which "$python" >/dev/null 2>&1 && break -done -echo "$python" diff --git a/tools/ps3py/fself.py b/tools/ps3py/fself.py index 80967835..f1d79809 100755 --- a/tools/ps3py/fself.py +++ b/tools/ps3py/fself.py @@ -1,5 +1,4 @@ -#!/usr/bin/env python -from __future__ import with_statement +#!/usr/bin/env python3 from Struct import Struct import struct import getopt @@ -115,7 +114,7 @@ def align(address, alignment): def padding(address, alignment): padding = alignment - (address % alignment) - return "\0" * padding + return b"\0" * padding def readElf(infile): with open(infile, 'rb') as fp: @@ -243,11 +242,11 @@ def createFself(npdrm, infile, outfile="EBOOT.BIN"): def usage(): - print """fself.py usage: + print("""fself.py usage: fself.py [options] input.elf output.self If output file is not specified, fself.py will default to EBOOT.BIN Options: - --npdrm: will output a file for use with pkg.py.""" + --npdrm: will output a file for use with pkg.py.""") def main(): try: opts, args = getopt.getopt(sys.argv[1:], "hn", ["help", "npdrm"]) diff --git a/tools/ps3py/install-scripts b/tools/ps3py/install-scripts deleted file mode 100755 index 239413e7..00000000 --- a/tools/ps3py/install-scripts +++ /dev/null @@ -1,10 +0,0 @@ -#! /bin/sh -target="$1" -shift -find_python=`dirname "$0"`/find-python2 -python=`"$find_python"` -for file; do - basefile=`basename "$file"` - sed -e '1s!python.*!'"$python"'!' < "$file" > "$target"/"$basefile" - test -x "$file" && chmod +x "$target"/"$basefile" -done diff --git a/tools/ps3py/pkg.py b/tools/ps3py/pkg.py index 855bc9d8..16854a85 100755 --- a/tools/ps3py/pkg.py +++ b/tools/ps3py/pkg.py @@ -1,5 +1,4 @@ -#!/usr/bin/env python -from __future__ import with_statement +#!/usr/bin/env python3 from Struct import Struct from fself import SelfHeader, AppInfo @@ -8,7 +7,6 @@ import hashlib import os import getopt -import ConfigParser import io import glob @@ -104,7 +102,7 @@ def __repr__(self): return self.fileName + (" Size: 0x%016x" % self.fileSize) def __init__(self): Struct.__init__(self) - self.fileName = "" + self.fileName = b"" def doWork(self, decrypteddata, context = None): if context == None: self.fileName = nullterm(decrypteddata[self.fileNameOff:self.fileNameOff+self.fileNameLength]) @@ -113,12 +111,12 @@ def doWork(self, decrypteddata, context = None): def dump(self, directory, data, header): if self.flags & 0xFF == 0x4: try: - os.makedirs(directory + "/" + self.fileName) - except Exception, e: - print + os.makedirs(directory + b"/" + self.fileName) + except Exception as e: + print() else: - tFile = open(directory + "/" + self.fileName, "wb") + tFile = open(directory + b"/" + self.fileName, "wb") tFile.write(data[self.fileOff:self.fileOff+self.fileSize]) @@ -161,29 +159,27 @@ def __str__(self): out += "[X] Data Offset: %016x\n" % self.dataOff out += "[X] Data Size: %016x\n" % self.dataSize - out += "[X] ContentID: '%s'\n" % (nullterm(self.contentID)) + out += "[X] ContentID: '%s'\n" % (nullterm(self.contentID).decode('ascii')) out += "[X] QA_Digest: %s\n" % (nullterm(self.QADigest, True)) - out += "[X] K Licensee: %s\n" % licensee.encode('hex') + out += "[X] K Licensee: %s\n" % licensee.hex() return out def listToString(inlist): if isinstance(inlist, list): - return ''.join(["%c" % el for el in inlist]) + return bytes(inlist) else: - return "" + return b"" def nullterm(str_plus, printhex=False): if isinstance(str_plus, list): - if printhex: - str_plus = ''.join(["%X" % el for el in str_plus]) - else: - str_plus = listToString(str_plus) - z = str_plus.find('\0') + str_plus = listToString(str_plus) + z = str_plus.find(b'\0') if z != -1: - return str_plus[:z] - else: - return str_plus + str_plus = str_plus[:z] + if printhex: + str_plus = str_plus.hex() + return str_plus def keyToContext(key): if isinstance(key, list): @@ -191,13 +187,13 @@ def keyToContext(key): key = key[0:16] largekey = [] for i in range(0, 8): - largekey.append(ord(key[i])) + largekey.append(key[i]) for i in range(0, 8): - largekey.append(ord(key[i])) + largekey.append(key[i]) for i in range(0, 8): - largekey.append(ord(key[i+8])) + largekey.append(key[i+8]) for i in range(0, 8): - largekey.append(ord(key[i+8])) + largekey.append(key[i+8]) for i in range(0, 0x20): largekey.append(0) return largekey @@ -217,25 +213,20 @@ def manipulate(key): def setContextNum(key, tmpnum): tmpchrs = struct.pack('>Q', tmpnum) - key[0x38] = ord(tmpchrs[0]) - key[0x39] = ord(tmpchrs[1]) - key[0x3a] = ord(tmpchrs[2]) - key[0x3b] = ord(tmpchrs[3]) - key[0x3c] = ord(tmpchrs[4]) - key[0x3d] = ord(tmpchrs[5]) - key[0x3e] = ord(tmpchrs[6]) - key[0x3f] = ord(tmpchrs[7]) + key[0x38:0x40] = tmpchrs[0:8] import pkgcrypt def crypt(key, inbuf, length): if not isinstance(key, list): - return "" + return b"" # Call our ultra fast c implemetation - return pkgcrypt.pkgcrypt(listToString(key), inbuf, length); + data, new_key = pkgcrypt.pkgcrypt(listToString(key), inbuf, length); + key[:] = new_key + return data # Original python (slow) implementation - ret = "" + ret = b"" offset = 0 while length > 0: bytes_to_dump = length @@ -243,10 +234,11 @@ def crypt(key, inbuf, length): bytes_to_dump = 0x10 outhash = SHA1(listToString(key)[0:0x40]) for i in range(0, bytes_to_dump): - ret += chr(ord(outhash[i]) ^ ord(inbuf[offset])) + ret += bytes([outhash[i] ^ inbuf[offset]]) offset += 1 manipulate(key) length -= bytes_to_dump + return ret def SHA1(data): m = hashlib.sha1() @@ -261,14 +253,14 @@ def listPkg(filename): offset = 0 header = Header() header.unpack(data[offset:offset+len(header)]) - print header - print + print(header) + print() assert header.type == 0x00000001, 'Unsupported Type' if header.itemCount > 0: - print 'Listing: "' + filename + '"' - print "+) overwrite, -) no overwrite" - print + print('Listing: "' + filename + '"') + print("+) overwrite, -) no overwrite") + print() dataEnc = data[header.dataOff:header.dataOff+header.dataSize] context = keyToContext(header.QADigest) @@ -295,10 +287,9 @@ def listPkg(filename): else: out += "-" out += "%11d: " % fileD.fileSize - out += fileD.fileName - print out, - print - #print fileD + out += str(fileD.fileName) + print(out) + #print(fileD) def unpack(filename): with open(filename, 'rb') as fp: data = fp.read() @@ -306,8 +297,8 @@ def unpack(filename): header = Header() header.unpack(data[offset:offset+len(header)]) if debug: - print header - print + print(header) + print() assert header.type == 0x00000001, 'Unsupported Type' if header.itemCount > 0: @@ -318,7 +309,7 @@ def unpack(filename): directory = nullterm(header.contentID) try: os.makedirs(directory) - except Exception, e: + except Exception as e: pass fileDescs = [] for i in range(0, header.itemCount): @@ -327,7 +318,7 @@ def unpack(filename): fileD.doWork(decData) fileDescs.append(fileD) if debug: - print fileD + print(fileD) fileD.dump(directory, decData, header) def getFiles(files, folder, original): oldfolder = folder @@ -341,7 +332,7 @@ def getFiles(files, folder, original): sortedList.append(filepath) for filepath in sortedList: newpath = filepath.replace("\\", "/") - newpath = newpath[len(original):] + newpath = newpath[len(original):].encode('ascii') if os.path.isdir(filepath): folder = FileHeader() folder.fileName = newpath @@ -362,7 +353,7 @@ def getFiles(files, folder, original): file.fileOff = 0 file.fileSize = os.path.getsize(filepath) file.flags = TYPE_OVERWRITE_ALLOWED | TYPE_RAW - if newpath == "USRDIR/EBOOT.BIN": + if newpath == b"USRDIR/EBOOT.BIN": file.fileSize = ((file.fileSize - 0x30 + 63) & ~63) + 0x30 file.flags = TYPE_OVERWRITE_ALLOWED | TYPE_NPDRMSELF @@ -370,6 +361,9 @@ def getFiles(files, folder, original): files.append(file) def pack(folder, contentid, outname=None): + contentid = contentid.encode('ascii') + # Force / at the end of path + folder = os.path.join(folder, '') qadigest = hashlib.sha1() @@ -421,7 +415,7 @@ def pack(folder, contentid, outname=None): files = [] getFiles(files, folder, folder) header.itemCount = len(files) - dataToEncrypt = "" + dataToEncrypt = b"" fileDescLength = 0 fileOff = 0x20 * len(files) for file in files: @@ -435,17 +429,17 @@ def pack(folder, contentid, outname=None): for file in files: alignedSize = (file.fileNameLength + 0x0F) & ~0x0F dataToEncrypt += file.fileName - dataToEncrypt += "\0" * (alignedSize-file.fileNameLength) + dataToEncrypt += b"\0" * (alignedSize-file.fileNameLength) fileDescLength = len(dataToEncrypt) for file in files: if not file.flags & 0xFF == TYPE_DIRECTORY: - path = os.path.join(folder, file.fileName) + path = os.path.join(folder, file.fileName.decode('ascii')) fp = open(path, 'rb') fileData = fp.read() qadigest.update(fileData) fileSHA1 = SHA1(fileData) fp.close() - if fileData[0:9] == "SCE\0\0\0\0\x02\x80": + if fileData[0:9] == b"SCE\0\0\0\0\x02\x80": fselfheader = SelfHeader() fselfheader.unpack(fileData[0:len(fselfheader)]) appheader = AppInfo() @@ -471,9 +465,9 @@ def pack(folder, contentid, outname=None): meta.drmType = metaBlock.drmType meta.unk2 = 1 for i in range(0,min(len(contentid), 0x30)): - meta.contentID[i] = ord(contentid[i]) + meta.contentID[i] = contentid[i] for i in range(0,0x10): - meta.fileSHA1[i] = ord(fileSHA1[i]) + meta.fileSHA1[i] = fileSHA1[i] meta.notSHA1[i] = (~meta.fileSHA1[i]) & 0xFF if i == 0xF: meta.notXORKLSHA1[i] = (1 ^ meta.notSHA1[i] ^ 0xAA) & 0xFF @@ -487,7 +481,7 @@ def pack(folder, contentid, outname=None): else: dataToEncrypt += fileData - dataToEncrypt += '\0' * (((file.fileSize + 0x0F) & ~0x0F) - len(fileData)) + dataToEncrypt += b'\0' * (((file.fileSize + 0x0F) & ~0x0F) - len(fileData)) header.dataSize = len(dataToEncrypt) metaBlock.dataSize = header.dataSize header.packageSize = header.dataSize + 0x1A0 @@ -497,17 +491,17 @@ def pack(folder, contentid, outname=None): QA_Digest = qadigest.digest() for i in range(0, 0x10): - header.QADigest[i] = ord(QA_Digest[i]) + header.QADigest[i] = QA_Digest[i] for i in range(0, min(len(contentid), 0x30)): - header.contentID[i] = ord(contentid[i]) + header.contentID[i] = contentid[i] context = keyToContext(header.QADigest) setContextNum(context, 0xFFFFFFFFFFFFFFFF) licensee = crypt(context, listToString(header.KLicensee), 0x10) for i in range(0, min(len(contentid), 0x10)): - header.KLicensee[i] = ord(licensee[i]) + header.KLicensee[i] = licensee[i] if outname != None: outFile = open(outname, 'wb') @@ -520,12 +514,12 @@ def pack(folder, contentid, outname=None): metaData = metaBlock.pack() metaBlockSHA = SHA1(metaData)[3:19] - metaBlockSHAPad = '\0' * 0x30 + metaBlockSHAPad = b'\0' * 0x30 - context = keyToContext([ord(c) for c in metaBlockSHA]) + context = keyToContext(metaBlockSHA) metaBlockSHAPadEnc = crypt(context, metaBlockSHAPad, 0x30) - context = keyToContext([ord(c) for c in headerSHA]) + context = keyToContext(headerSHA) metaBlockSHAPadEnc2 = crypt(context, metaBlockSHAPadEnc, 0x30) outFile.write(metaBlockSHAPadEnc2) outFile.write(metaData) @@ -535,12 +529,12 @@ def pack(folder, contentid, outname=None): context = keyToContext(header.QADigest) encData = crypt(context, dataToEncrypt, header.dataSize) outFile.write(encData) - outFile.write('\0' * 0x60) + outFile.write(b'\0' * 0x60) outFile.close() - print header + print(header) def usage(): - print """usage: [based on revision 1061] + print("""usage: [based on revision 1061] python pkg.py target-directory [out-file] @@ -550,10 +544,10 @@ def usage(): python pkg.py [options] --version print revision. - --help print this message.""" + --help print this message.""") def version(): - print """pky.py 0.5""" + print("""pky.py 0.5""") def main(): global debug diff --git a/tools/ps3py/ps3listen.py b/tools/ps3py/ps3listen.py index 320e7df8..364d363c 100755 --- a/tools/ps3py/ps3listen.py +++ b/tools/ps3py/ps3listen.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import socket UDP_IP='' UDP_PORT=18194 @@ -8,4 +8,4 @@ while True: data, addr = sock.recvfrom( 1024 ) # buffer size is 1024 bytes - print data + print(data) diff --git a/tools/ps3py/sfo.py b/tools/ps3py/sfo.py index a769223b..e7c9f0bb 100755 --- a/tools/ps3py/sfo.py +++ b/tools/ps3py/sfo.py @@ -1,5 +1,4 @@ -#!/usr/bin/env python -from __future__ import with_statement +#!/usr/bin/env python3 from xml.dom.minidom import Document, parse, parseString from Struct import Struct import struct @@ -12,11 +11,11 @@ SFO_STRING = 2 SFO_INT = 4 def nullterm(str_plus): - z = str_plus.find('\0') + z = str_plus.find(b'\0') if z != -1: - return str_plus[:z] + return str_plus[:z].decode('ascii') else: - return str_plus + return str_plus.decode('ascii') class Header(Struct): __endian__ = Struct.LE @@ -70,11 +69,11 @@ def PrettyPrint(self, data, key_off, value_off): return out def usage(): - print """usage: - python sfo.py""" + print("""usage: + python sfo.py""") def version(): - print """sfo.py 0.2""" + print("""sfo.py 0.2""") def listSFO(file): global debug @@ -86,22 +85,22 @@ def listSFO(file): header = Header() header.unpack(data[offset:offset+len(header)]) if debug: - print header - print + print(header) + print() assert header.magic == SFO_MAGIC assert header.unk1 == 0x00000101 offset += len(header) off1 = header.KeyOffset off2 = header.ValueOffset - for x in xrange(header.PairCount): + for x in range(header.PairCount): entry = Entry() entry.unpack(data[offset:offset+len(entry)]) if debug and not pretty: - print entry - print + print(entry) + print() if debug and pretty: - print entry.PrettyPrint(data, off1, off2) - print + print(entry.PrettyPrint(data, off1, off2)) + print() key = nullterm(data[off1+entry.key_off:]) if entry.value_type == SFO_STRING: value = nullterm(data[off2+entry.value_off:]) @@ -110,7 +109,7 @@ def listSFO(file): stuff[key] = value offset += len(entry) if not debug: - print stuff + print(stuff) def convertToXml(sfofile, xml): doc = Document() sfo = doc.createElement("sfo") @@ -123,22 +122,22 @@ def convertToXml(sfofile, xml): header = Header() header.unpack(data[offset:offset+len(header)]) if debug: - print header - print + print(header) + print() assert header.magic == SFO_MAGIC assert header.unk1 == 0x00000101 offset += len(header) off1 = header.KeyOffset off2 = header.ValueOffset - for x in xrange(header.PairCount): + for x in range(header.PairCount): entry = Entry() entry.unpack(data[offset:offset+len(entry)]) if debug and not pretty: - print entry - print + print(entry) + print() if debug and pretty: - print entry.PrettyPrint(data, off1, off2) - print + print(entry.PrettyPrint(data, off1, off2)) + print() key = nullterm(data[off1+entry.key_off:]) valuenode = doc.createElement("value") valuenode.setAttribute("name", key) @@ -154,10 +153,10 @@ def convertToXml(sfofile, xml): stuff[key] = value offset += len(entry) if not debug: - print stuff + print(stuff) doc.appendChild(sfo) - file = open(xml, "wb" ) + file = open(xml, "w" ) doc.writexml(file, '', '\t', '\n' ) file.close() @@ -231,8 +230,8 @@ def convertToSFO(xml, sfofile, forcetitle, forceappid): for entry in entries: file.write(entry.pack()) for k,v in kvs: - file.write(k + '\0') - file.write('\0' * keypad) + file.write(k.encode('ascii') + b'\0') + file.write(b'\0' * keypad) for k,v in kvs: if isinstance(v, int): file.write(struct.pack('