-
-
Save V10lator/e79206a5cb8aae947a1ac009ad9b2317 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# may or may not be inspired by plutoo's ctrrpc | |
import errno | |
import socket | |
import os | |
import sys | |
import struct | |
import codecs | |
from time import sleep | |
def buffer(size): | |
return bytearray([0x00] * size) | |
def copy_string(buffer, s, offset): | |
s += "\0" | |
buffer[offset : (offset + len(s))] = bytearray(s, "ascii") | |
def copy_word(buffer, w, offset): | |
buffer[offset : (offset + 4)] = struct.pack(">I", w) | |
def get_string(buffer, offset): | |
s = buffer[offset:] | |
if b'\x00' in s: | |
return s[:s.index(b'\x00')].decode("utf-8") | |
else: | |
return s.decode("utf-8") | |
class wupclient: | |
s=None | |
def __init__(self, ip='192.168.178.27', port=1337): | |
self.s=socket.socket() | |
self.s.connect((ip, port)) | |
self.fsa_handle = self.open("/dev/fsa", 0) | |
self.cwd = "/vol/storage_mlc01" | |
def __del__(self): | |
self.FSA_Unmount(self.fsa_handle, "/vol/storage_sdcard", 2) | |
self.close(self.fsa_handle) | |
# fundamental comms | |
def send(self, command, data): | |
request = struct.pack('>I', command) + data | |
self.s.send(request) | |
response = self.s.recv(0x600) | |
ret = struct.unpack(">I", response[:4])[0] | |
return (ret, response[4:]) | |
# core commands | |
def read(self, addr, len): | |
data = struct.pack(">II", addr, len) | |
ret, data = self.send(1, data) | |
if ret == 0: | |
return data | |
else: | |
print("read error : %08X" % ret) | |
return None | |
def send_and_exit(self, command, data): | |
request = struct.pack('>I', command) + data | |
self.s.send(request) | |
self.s.close() | |
self.s = None | |
self.fsa_handle = None | |
exit() | |
def write(self, addr, data): | |
data = struct.pack(">I", addr) + data | |
ret, data = self.send(0, data) | |
if ret == 0: | |
return ret | |
else: | |
print("write error : %08X" % ret) | |
return None | |
def svc(self, svc_id, arguments): | |
data = struct.pack(">I", svc_id) | |
for a in arguments: | |
data += struct.pack(">I", a) | |
ret, data = self.send(2, data) | |
if ret == 0: | |
return struct.unpack(">I", data)[0] | |
else: | |
print("svc error : %08X" % ret) | |
return None | |
def svc_and_exit(self, svc_id, arguments): | |
data = struct.pack(">I", svc_id) | |
for a in arguments: | |
data += struct.pack(">I", a) | |
self.send_and_exit(2, data) | |
def kill(self): | |
ret, _ = self.send(3, bytearray()) | |
return ret | |
def memcpy(self, dst, src, len): | |
data = struct.pack(">III", dst, src, len) | |
ret, data = self.send(4, data) | |
if ret == 0: | |
return ret | |
else: | |
print("memcpy error : %08X" % ret) | |
return None | |
def repeatwrite(self, dst, val, n): | |
data = struct.pack(">III", dst, val, n) | |
ret, data = self.send(5, data) | |
if ret == 0: | |
return ret | |
else: | |
print("repeatwrite error : %08X" % ret) | |
return None | |
# derivatives | |
def alloc(self, size, align = None): | |
if size == 0: | |
return 0 | |
if align == None: | |
return self.svc(0x27, [0xCAFF, size]) | |
else: | |
return self.svc(0x28, [0xCAFF, size, align]) | |
def free(self, address): | |
if address == 0: | |
return 0 | |
return self.svc(0x29, [0xCAFF, address]) | |
def load_buffer(self, b, align = None): | |
if len(b) == 0: | |
return 0 | |
address = self.alloc(len(b), align) | |
self.write(address, b) | |
return address | |
def load_string(self, s, align = None): | |
return self.load_buffer(bytearray(s + "\0", "ascii"), align) | |
def open(self, device, mode): | |
address = self.load_string(device) | |
handle = self.svc(0x33, [address, mode]) | |
self.free(address) | |
return handle | |
def close(self, handle): | |
return self.svc(0x34, [handle]) | |
def ioctl(self, handle, cmd, inbuf, outbuf_size): | |
in_address = self.load_buffer(inbuf) | |
out_data = None | |
if outbuf_size > 0: | |
out_address = self.alloc(outbuf_size) | |
ret = self.svc(0x38, [handle, cmd, in_address, len(inbuf), out_address, outbuf_size]) | |
out_data = self.read(out_address, outbuf_size) | |
self.free(out_address) | |
else: | |
ret = self.svc(0x38, [handle, cmd, in_address, len(inbuf), 0, 0]) | |
self.free(in_address) | |
return (ret, out_data) | |
def iovec(self, vecs): | |
data = bytearray() | |
for (a, s) in vecs: | |
data += struct.pack(">III", a, s, 0) | |
return self.load_buffer(data) | |
def ioctlv(self, handle, cmd, inbufs, outbuf_sizes, inbufs_ptr = [], outbufs_ptr = []): | |
inbufs = [(self.load_buffer(b, 0x40), len(b)) for b in inbufs] | |
outbufs = [(self.alloc(s, 0x40), s) for s in outbuf_sizes] | |
iovecs = self.iovec(inbufs + inbufs_ptr + outbufs_ptr + outbufs) | |
out_data = [] | |
ret = self.svc(0x39, [handle, cmd, len(inbufs + inbufs_ptr), len(outbufs + outbufs_ptr), iovecs]) | |
for (a, s) in outbufs: | |
out_data += [self.read(a, s)] | |
for (a, _) in (inbufs + outbufs): | |
self.free(a) | |
self.free(iovecs) | |
return (ret, out_data) | |
# fsa | |
def FSA_Mount(self, handle, device_path, volume_path, flags): | |
inbuffer = buffer(0x520) | |
copy_string(inbuffer, device_path, 0x0004) | |
copy_string(inbuffer, volume_path, 0x0284) | |
copy_word(inbuffer, flags, 0x0504) | |
(ret, _) = self.ioctlv(handle, 0x01, [inbuffer, bytearray()], [0x293]) | |
return ret | |
def FSA_Unmount(self, handle, path, flags): | |
inbuffer = buffer(0x520) | |
copy_string(inbuffer, path, 0x4) | |
copy_word(inbuffer, flags, 0x284) | |
(ret, _) = self.ioctl(handle, 0x02, inbuffer, 0x293) | |
return ret | |
def FSA_RawOpen(self, handle, device): | |
inbuffer = buffer(0x520) | |
copy_string(inbuffer, device, 0x4) | |
(ret, data) = self.ioctl(handle, 0x6A, inbuffer, 0x293) | |
return (ret, struct.unpack(">I", data[4:8])[0]) | |
def FSA_OpenDir(self, handle, path): | |
inbuffer = buffer(0x520) | |
copy_string(inbuffer, path, 0x4) | |
(ret, data) = self.ioctl(handle, 0x0A, inbuffer, 0x293) | |
return (ret, struct.unpack(">I", data[4:8])[0]) | |
def FSA_ReadDir(self, handle, dir_handle): | |
inbuffer = buffer(0x520) | |
copy_word(inbuffer, dir_handle, 0x4) | |
(ret, data) = self.ioctl(handle, 0x0B, inbuffer, 0x293) | |
data = bytearray(data[4:]) | |
unk = data[:0x64] | |
if ret == 0: | |
return (ret, {"name" : get_string(data, 0x64), "is_file" : (unk[0] & 128) != 128, "unk" : unk}) | |
else: | |
return (ret, None) | |
def FSA_CloseDir(self, handle, dir_handle): | |
inbuffer = buffer(0x520) | |
copy_word(inbuffer, dir_handle, 0x4) | |
(ret, data) = self.ioctl(handle, 0x0D, inbuffer, 0x293) | |
return ret | |
def FSA_OpenFile(self, handle, path, mode): | |
inbuffer = buffer(0x520) | |
copy_string(inbuffer, path, 0x4) | |
copy_string(inbuffer, mode, 0x284) | |
(ret, data) = self.ioctl(handle, 0x0E, inbuffer, 0x293) | |
return (ret, struct.unpack(">I", data[4:8])[0]) | |
def FSA_MakeDir(self, handle, path, flags): | |
inbuffer = buffer(0x520) | |
copy_string(inbuffer, path, 0x4) | |
copy_word(inbuffer, flags, 0x284) | |
(ret, _) = self.ioctl(handle, 0x07, inbuffer, 0x293) | |
return ret | |
def FSA_ReadFile(self, handle, file_handle, size, cnt): | |
inbuffer = buffer(0x520) | |
copy_word(inbuffer, size, 0x08) | |
copy_word(inbuffer, cnt, 0x0C) | |
copy_word(inbuffer, file_handle, 0x14) | |
(ret, data) = self.ioctlv(handle, 0x0F, [inbuffer], [size * cnt, 0x293]) | |
return (ret, data[0]) | |
def FSA_WriteFile(self, handle, file_handle, data): | |
inbuffer = buffer(0x520) | |
copy_word(inbuffer, 1, 0x08) # size | |
copy_word(inbuffer, len(data), 0x0C) # cnt | |
copy_word(inbuffer, file_handle, 0x14) | |
(ret, data) = self.ioctlv(handle, 0x10, [inbuffer, data], [0x293]) | |
return (ret) | |
def FSA_ReadFilePtr(self, handle, file_handle, size, cnt, ptr): | |
inbuffer = buffer(0x520) | |
copy_word(inbuffer, size, 0x08) | |
copy_word(inbuffer, cnt, 0x0C) | |
copy_word(inbuffer, file_handle, 0x14) | |
(ret, data) = self.ioctlv(handle, 0x0F, [inbuffer], [0x293], [], [(ptr, size*cnt)]) | |
return (ret, data[0]) | |
def FSA_WriteFilePtr(self, handle, file_handle, size, cnt, ptr): | |
inbuffer = buffer(0x520) | |
copy_word(inbuffer, size, 0x08) | |
copy_word(inbuffer, cnt, 0x0C) | |
copy_word(inbuffer, file_handle, 0x14) | |
(ret, data) = self.ioctlv(handle, 0x10, [inbuffer], [0x293], [(ptr, size*cnt)], []) | |
return (ret) | |
def FSA_GetStatFile(self, handle, file_handle): | |
inbuffer = buffer(0x520) | |
copy_word(inbuffer, file_handle, 0x4) | |
(ret, data) = self.ioctl(handle, 0x14, inbuffer, 0x64) | |
return (ret, struct.unpack(">IIIIIIIIIIIIIIIIIIIIIIIII", data)) | |
def FSA_CloseFile(self, handle, file_handle): | |
inbuffer = buffer(0x520) | |
copy_word(inbuffer, file_handle, 0x4) | |
(ret, data) = self.ioctl(handle, 0x15, inbuffer, 0x293) | |
return ret | |
def FSA_ChangeMode(self, handle, path, mode): | |
mask = 0x777 | |
inbuffer = buffer(0x520) | |
copy_string(inbuffer, path, 0x0004) | |
copy_word(inbuffer, mode, 0x0284) | |
copy_word(inbuffer, mask, 0x0288) | |
(ret, _) = self.ioctl(handle, 0x20, inbuffer, 0x293) | |
return ret | |
def FSA_Rename(self, handle, oldpath, newpath): | |
inbuffer = buffer(0x520) | |
copy_string(inbuffer, oldpath, 0x4) | |
copy_string(inbuffer, newpath, 0x284) | |
(ret, _) = self.ioctl(handle, 0x09, inbuffer, 0x293) | |
return ret | |
def FSA_Remove(self, handle, path): | |
inbuffer = buffer(0x520) | |
copy_string(inbuffer, path, 0x04) | |
(ret, _) = self.ioctl(handle, 0x08, inbuffer, 0x293) | |
return ret | |
def FSA_FlushVolume(self, handle, path): | |
inbuffer = buffer(0x520) | |
copy_string(inbuffer, path, 0x04) | |
(ret, _) = self.ioctl(handle, 0x1B, inbuffer, 0x293) | |
return ret | |
# mcp | |
def MCP_InstallGetInfo(self, handle, path): | |
inbuffer = buffer(0x27F) | |
copy_string(inbuffer, path, 0x0) | |
(ret, data) = self.ioctlv(handle, 0x80, [inbuffer], [0x16]) | |
return (ret, struct.unpack(">IIIIIH", data[0])) | |
def MCP_Install(self, handle, path): | |
inbuffer = buffer(0x27F) | |
copy_string(inbuffer, path, 0x0) | |
(ret, _) = self.ioctlv(handle, 0x81, [inbuffer], []) | |
return ret | |
def MCP_InstallGetProgress(self, handle): | |
(ret, data) = self.ioctl(handle, 0x82, [], 0x24) | |
return (ret, struct.unpack(">IIIIIIIII", data)) | |
def MCP_DeleteTitle(self, handle, path, flush): | |
inbuffer = buffer(0x38) | |
copy_string(inbuffer, path, 0x0) | |
inbuffer2 = buffer(0x4) | |
copy_word(inbuffer2, flush, 0x0) | |
(ret, _) = self.ioctlv(handle, 0x83, [inbuffer, inbuffer2], []) | |
return ret | |
def MCP_CopyTitle(self, handle, path, dst_device_id, flush): | |
inbuffer = buffer(0x27F) | |
copy_string(inbuffer, path, 0x0) | |
inbuffer2 = buffer(0x4) | |
copy_word(inbuffer2, dst_device_id, 0x0) | |
inbuffer3 = buffer(0x4) | |
copy_word(inbuffer3, flush, 0x0) | |
(ret, _) = self.ioctlv(handle, 0x85, [inbuffer, inbuffer2, inbuffer3], []) | |
return ret | |
def MCP_InstallSetTargetDevice(self, handle, device): | |
inbuffer = buffer(0x4) | |
copy_word(inbuffer, device, 0x0) | |
(ret, _) = self.ioctl(handle, 0x8D, inbuffer, 0) | |
return ret | |
def MCP_InstallSetTargetUsb(self, handle, device): | |
inbuffer = buffer(0x4) | |
copy_word(inbuffer, device, 0x0) | |
(ret, _) = self.ioctl(handle, 0xF1, inbuffer, 0) | |
return ret | |
# syslog (tmp) | |
def dump_syslog(self): | |
syslog_address = struct.unpack(">I", self.read(0x05095ECC, 4))[0] + 0x10 | |
block_size = 0x400 | |
for i in range(0, 0x40000, block_size): | |
data = self.read(syslog_address + i, 0x400) | |
# if 0 in data: | |
# print(data[:data.index(0)].decode("ascii")) | |
# break | |
# else: | |
print(data.decode("ascii")) | |
def mkdir(self, path, flags): | |
if path[0] != "/": | |
path = self.cwd + "/" + path | |
ret = w.FSA_MakeDir(self.fsa_handle, path, flags) | |
if ret == 0: | |
return 0 | |
else: | |
print("mkdir error (%s, %08X)" % (path, ret)) | |
return ret | |
def chmod(self, filename, flags): | |
if filename[0] != "/": | |
filename = self.cwd + "/" + filename | |
ret = w.FSA_ChangeMode(self.fsa_handle, filename, flags) | |
print("chmod returned : " + hex(ret)) | |
def cd(self, path): | |
if path[0] != "/" and self.cwd[0] == "/": | |
return self.cd(self.cwd + "/" + path) | |
ret, dir_handle = self.FSA_OpenDir(self.fsa_handle, path if path != None else self.cwd) | |
if ret == 0: | |
self.cwd = path | |
self.FSA_CloseDir(self.fsa_handle, dir_handle) | |
return 0 | |
else: | |
print("cd error : path does not exist (%s)" % (path)) | |
return -1 | |
def ls(self, path = None, return_data = False): | |
if path != None and path[0] != "/": | |
path = self.cwd + "/" + path | |
ret, dir_handle = self.FSA_OpenDir(self.fsa_handle, path if path != None else self.cwd) | |
if ret != 0x0: | |
print("opendir error : " + hex(ret)) | |
return [] if return_data else None | |
entries = [] | |
while True: | |
ret, data = self.FSA_ReadDir(self.fsa_handle, dir_handle) | |
if ret != 0: | |
break | |
if not(return_data): | |
if data["is_file"]: | |
print(" %s" % data["name"]) | |
else: | |
print(" %s/" % data["name"]) | |
else: | |
entries += [data] | |
ret = self.FSA_CloseDir(self.fsa_handle, dir_handle) | |
return entries if return_data else None | |
def dldir(self, path): | |
if path[0] != "/": | |
path = self.cwd + "/" + path | |
entries = self.ls(path, True) | |
for e in entries: | |
if e["is_file"]: | |
print(e["name"]) | |
self.dl(path + "/" + e["name"],path[1:]) | |
else: | |
print(e["name"] + "/") | |
self.dldir(path + "/" + e["name"]) | |
def cpdir(self, srcpath, dstpath): | |
entries = self.ls(srcpath, True) | |
q = [(srcpath, dstpath, e) for e in entries] | |
while len(q) > 0: | |
_srcpath, _dstpath, e = q.pop() | |
_srcpath += "/" + e["name"] | |
_dstpath += "/" + e["name"] | |
if e["is_file"]: | |
print(e["name"]) | |
self.cp(_srcpath, _dstpath) | |
else: | |
self.mkdir(_dstpath, 0x600) | |
entries = self.ls(_srcpath, True) | |
q += [(_srcpath, _dstpath, e) for e in entries] | |
def pwd(self): | |
return self.cwd | |
def cp(self, filename_in, filename_out): | |
ret, in_file_handle = self.FSA_OpenFile(self.fsa_handle, filename_in, "r") | |
if ret != 0x0: | |
print("cp error : could not open " + filename_in) | |
return | |
ret, out_file_handle = self.FSA_OpenFile(self.fsa_handle, filename_out, "w") | |
if ret != 0x0: | |
print("cp error : could not open " + filename_out) | |
return | |
block_size = 0x10000 | |
buffer = self.alloc(block_size, 0x40) | |
k = 0 | |
while True: | |
ret, _ = self.FSA_ReadFilePtr(self.fsa_handle, in_file_handle, 0x1, block_size, buffer) | |
k += ret | |
ret = self.FSA_WriteFilePtr(self.fsa_handle, out_file_handle, 0x1, ret, buffer) | |
sys.stdout.write(hex(k) + "\r"); sys.stdout.flush(); | |
if ret < block_size: | |
break | |
self.free(buffer) | |
ret = self.FSA_CloseFile(self.fsa_handle, out_file_handle) | |
ret = self.FSA_CloseFile(self.fsa_handle, in_file_handle) | |
def df(self, filename_out, src, size): | |
ret, out_file_handle = self.FSA_OpenFile(self.fsa_handle, filename_out, "w") | |
if ret != 0x0: | |
print("df error : could not open " + filename_out) | |
return | |
block_size = 0x10000 | |
buffer = self.alloc(block_size, 0x40) | |
k = 0 | |
while k < size: | |
cur_size = min(size - k, block_size) | |
self.memcpy(buffer, src + k, cur_size) | |
k += cur_size | |
ret = self.FSA_WriteFilePtr(self.fsa_handle, out_file_handle, 0x1, cur_size, buffer) | |
sys.stdout.write(hex(k) + " (%f) " % (float(k * 100) / size) + "\r"); sys.stdout.flush(); | |
self.free(buffer) | |
ret = self.FSA_CloseFile(self.fsa_handle, out_file_handle) | |
def dl_buf(self, filename, show_progress = True): | |
if filename[0] != "/": | |
filename = self.cwd + "/" + filename | |
ret, file_handle = self.FSA_OpenFile(self.fsa_handle, filename, "r") | |
if ret != 0x0: | |
print("dl error : could not open " + filename) | |
return None | |
buf = bytearray() | |
block_size = 0x400 | |
while True: | |
ret, data = self.FSA_ReadFile(self.fsa_handle, file_handle, 0x1, block_size) | |
buf += data[:ret] | |
if show_progress: | |
sys.stdout.write(hex(len(buf)) + "\r") | |
sys.stdout.flush() | |
if ret < block_size: | |
break | |
self.FSA_CloseFile(self.fsa_handle, file_handle) | |
return buf | |
def dl(self, filename, directorypath = None, local_filename = None): | |
buf = self.dl_buf(filename) | |
if buf == None: | |
return -1 | |
if local_filename == None: | |
if "/" in filename: | |
local_filename = filename[[i for i, x in enumerate(filename) if x == "/"][-1]+1:] | |
else: | |
local_filename = filename | |
if directorypath == None: | |
open(local_filename, "wb").write(buf) | |
else: | |
dir_path = os.path.dirname(os.path.abspath(sys.argv[0])).replace('\\','/') | |
fullpath = dir_path + "/" + directorypath + "/" | |
fullpath = fullpath.replace("//","/") | |
mkdir_p(fullpath) | |
open(fullpath + local_filename, "wb").write(buf) | |
return 0 | |
def mkdir_p(path): | |
try: | |
os.makedirs(path) | |
except OSError as exc: # Python >2.5 | |
if exc.errno == errno.EEXIST and os.path.isdir(path): | |
pass | |
else: | |
raise | |
def fr(self, filename, offset, size): | |
if filename[0] != "/": | |
filename = self.cwd + "/" + filename | |
ret, file_handle = self.FSA_OpenFile(self.fsa_handle, filename, "r") | |
if ret != 0x0: | |
print("fr error : could not open " + filename) | |
return | |
buffer = bytearray() | |
block_size = 0x400 | |
while True: | |
ret, data = self.FSA_ReadFile(self.fsa_handle, file_handle, 0x1, block_size if (block_size < size) else size) | |
buffer += data[:ret] | |
sys.stdout.write(hex(len(buffer)) + "\r"); sys.stdout.flush(); | |
if len(buffer) >= size: | |
break | |
ret = self.FSA_CloseFile(self.fsa_handle, file_handle) | |
return buffer | |
def fw(self, filename, offset, buffer): | |
if filename[0] != "/": | |
filename = self.cwd + "/" + filename | |
ret, file_handle = self.FSA_OpenFile(self.fsa_handle, filename, "r+") | |
if ret != 0x0: | |
print("fw error : could not open " + filename) | |
return | |
block_size = 0x400 | |
k = 0 | |
while True: | |
cur_size = min(len(buffer) - k, block_size) | |
if cur_size <= 0: | |
break | |
sys.stdout.write(hex(k) + "\r"); sys.stdout.flush(); | |
ret = self.FSA_WriteFile(self.fsa_handle, file_handle, buffer[k:(k+cur_size)]) | |
k += cur_size | |
ret = self.FSA_CloseFile(self.fsa_handle, file_handle) | |
def stat(self, filename): | |
if filename[0] != "/": | |
filename = self.cwd + "/" + filename | |
ret, file_handle = self.FSA_OpenFile(self.fsa_handle, filename, "r") | |
if ret != 0x0: | |
print("stat error : could not open " + filename) | |
return | |
(ret, stats) = self.FSA_GetStatFile(self.fsa_handle, file_handle) | |
if ret != 0x0: | |
print("stat error : " + hex(ret)) | |
else: | |
print("flags: " + hex(stats[1])) | |
print("mode: " + hex(stats[2])) | |
print("owner: " + hex(stats[3])) | |
print("group: " + hex(stats[4])) | |
print("size: " + hex(stats[5])) | |
ret = self.FSA_CloseFile(self.fsa_handle, file_handle) | |
def askyesno(self): | |
yes = set(['yes', 'ye', 'y']) | |
no = set(['no','n', '']) | |
while True: | |
choice = input().lower() | |
if choice in yes: | |
return True | |
elif choice in no: | |
return False | |
else: | |
print("Please respond with 'y' or 'n'") | |
def rm(self, filename): | |
if filename[0] != "/": | |
filename = self.cwd + "/" + filename | |
ret, file_handle = self.FSA_OpenFile(self.fsa_handle, filename, "r") | |
if ret != 0x0: | |
print("rm error : could not open " + filename + " (" + hex(ret) + ")") | |
return | |
self.FSA_CloseFile(self.fsa_handle, file_handle) | |
print("WARNING: REMOVING A FILE CAN BRICK YOUR CONSOLE, ARE YOU SURE (Y/N)?") | |
if self.askyesno() == True: | |
ret = self.FSA_Remove(self.fsa_handle, filename) | |
print("rm : " + hex(ret)) | |
else: | |
print("rm aborted") | |
def rmdir(self, path): | |
if path[0] != "/": | |
path = self.cwd + "/" + path | |
ret, dir_handle = self.FSA_OpenDir(self.fsa_handle, path) | |
if ret != 0x0: | |
print("rmdir error : could not open " + path + " (" + hex(ret) + ")") | |
return | |
self.FSA_CloseDir(self.fsa_handle, dir_handle) | |
if len(self.ls(path, True)) != 0: | |
print("rmdir error : directory not empty!") | |
return | |
print("WARNING: REMOVING A DIRECTORY CAN BRICK YOUR CONSOLE, ARE YOU SURE (Y/N)?") | |
if self.askyesno() == True: | |
ret = self.FSA_Remove(self.fsa_handle, path) | |
print("rmdir : " + hex(ret)) | |
else: | |
print("rmdir aborted") | |
def mv(self, srcpath, dstpath): | |
if srcpath[0] != "/": | |
srcpath = self.cwd + "/" + srcpath | |
if dstpath[0] != "/": | |
dstpath = self.cwd + "/" + dstpath | |
print("WARNING: MOVING A FILE OR FOLDER CAN BRICK YOUR CONSOLE, ARE YOU SURE (Y/N)?") | |
if self.askyesno() == True: | |
ret = self.FSA_Rename(self.fsa_handle, srcpath, dstpath) | |
if ret == 0x0: | |
print("moved " + srcpath + " to " + dstpath) | |
else: | |
print("moving " + srcpath + " to " + dstpath + " failed : " + hex(ret)) | |
else: | |
print("mv aborted") | |
def up(self, local_filename, filename = None): | |
if filename == None: | |
if "/" in local_filename: | |
filename = local_filename[[i for i, x in enumerate(local_filename) if x == "/"][-1]+1:] | |
else: | |
filename = local_filename | |
if filename[0] != "/": | |
filename = self.cwd + "/" + filename | |
f = open(local_filename, "rb") | |
ret, file_handle = self.FSA_OpenFile(self.fsa_handle, filename, "w") | |
if ret != 0x0: | |
print("up error : could not open " + filename) | |
return | |
progress = 0 | |
block_size = 0x400 | |
while True: | |
data = f.read(block_size) | |
ret = self.FSA_WriteFile(self.fsa_handle, file_handle, data) | |
progress += len(data) | |
sys.stdout.write(hex(progress) + "\r"); sys.stdout.flush(); | |
if len(data) < block_size: | |
break | |
ret = self.FSA_CloseFile(self.fsa_handle, file_handle) | |
def mkdir_p(path): | |
try: | |
os.makedirs(path) | |
except OSError as exc: | |
if exc.errno == errno.EEXIST and os.path.isdir(path): | |
pass | |
else: | |
raise | |
def mount_sd(): | |
ret = w.FSA_Mount(w.fsa_handle, "/dev/sdcard01", "/vol/storage_sdcard", 2) | |
print(hex(ret)) | |
def unmount_sd(): | |
ret = w.FSA_Unmount(w.fsa_handle, "/vol/storage_sdcard", 2) | |
print(hex(ret)) | |
def mount_slccmpt01(): | |
ret = w.FSA_Mount(w.fsa_handle, "/dev/slccmpt01", "/vol/storage_slccmpt01", 2) | |
print(hex(ret)) | |
def unmount_slccmpt01(): | |
ret = w.FSA_Unmount(w.fsa_handle, "/vol/storage_slccmpt01", 2) | |
print(hex(ret)) | |
def mount_odd_content(): | |
ret = w.FSA_Mount(w.fsa_handle, "/dev/odd03", "/vol/storage_odd_content", 2) | |
print(hex(ret)) | |
def unmount_odd_content(): | |
ret = w.FSA_Unmount(w.fsa_handle, "/vol/storage_odd_content", 2) | |
print(hex(ret)) | |
def mount_odd_update(): | |
ret = w.FSA_Mount(w.fsa_handle, "/dev/odd02", "/vol/storage_odd_update", 2) | |
print(hex(ret)) | |
def unmount_odd_update(): | |
ret = w.FSA_Unmount(w.fsa_handle, "/vol/storage_odd_update", 2) | |
print(hex(ret)) | |
def mount_odd_tickets(): | |
ret = w.FSA_Mount(w.fsa_handle, "/dev/odd01", "/vol/storage_odd_tickets", 2) | |
print(hex(ret)) | |
def unmount_odd_tickets(): | |
ret = w.FSA_Unmount(w.fsa_handle, "/vol/storage_odd_tickets", 2) | |
print(hex(ret)) | |
def get_tik_keys(): | |
base_path = "/vol/system/rights/ticket/apps" | |
entries = w.ls(base_path, True) | |
#parse subfolder contents to get tik location | |
tikFiles = [] | |
for e in entries: | |
if not e["is_file"]: | |
path = base_path + "/" + e["name"] | |
subentries = w.ls(path, True) | |
for se in subentries: | |
if se["is_file"] and se["name"].endswith(".tik"): | |
tikFiles.append(path + "/" + se["name"]) | |
#go through all tiks | |
tikList = [] | |
for tikF in tikFiles: | |
tikContent = w.dl_buf(tikF, False) | |
if tikContent == None: | |
continue | |
checkTik = True | |
tikP = 0 | |
while checkTik == True: | |
checkTik = False | |
curTik = tikContent[tikP:] | |
if(curTik[0:4] != b'\x00\x01\x00\x04'): | |
print("Unhandled tik start at %i with ticket %s!" % (tikP, tikF)) | |
break | |
titleId = codecs.encode(curTik[0x1DC:0x1E4], 'hex').decode() | |
titleKey = codecs.encode(curTik[0x1BF:0x1CF], 'hex').decode() | |
tikFprint = tikF[tikF.rfind("apps/")+5:] | |
tikList.append(titleId + " " + titleKey + " (" + tikFprint + " @ " + hex(tikP) + ")") | |
if(len(curTik) > 0x354): | |
if(curTik[0x2B0:0x2B2] == b'\x00\x00' and curTik[0x2B8:0x2BC] == b'\x00\x01\x00\x04'): | |
tikP += 0x2B8 | |
checkTik = True | |
elif(curTik[0x2B0:0x2B2] == b'\x00\x01' and curTik[0x350:0x354] == b'\x00\x01\x00\x04'): | |
tikP += 0x350 | |
checkTik = True | |
else: | |
print("Unhandled packed tik at %i with ticket %s!" % (tikP, tikF)) | |
#print out all sorted and unique tiks | |
uniqueTiks = sorted(set(tikList)) | |
print("Found %i unique tickets" % len(uniqueTiks)) | |
for tikCnt in uniqueTiks: | |
print(tikCnt) | |
#path=root folder of installed/extracted title, only works if title is deleted | |
#on the destination device beforehand; path can also be a sd card location! | |
def copy_title(path, installToUsb = 0, flush = 0): | |
mcp_handle = w.open("/dev/mcp", 0) | |
print(hex(mcp_handle)) | |
ret = w.MCP_CopyTitle(mcp_handle, path, installToUsb, flush) | |
print(hex(ret)) | |
ret = w.close(mcp_handle) | |
print(hex(ret)) | |
#path=path to sd card folder on device root | |
def install_title(path, installToUsb = 0): | |
mcp_handle = w.open("/dev/mcp", 0) | |
print(hex(mcp_handle)) | |
ret, data = w.MCP_InstallGetInfo(mcp_handle, "/vol/storage_sdcard/"+path) | |
print("install info : " + hex(ret), [hex(v) for v in data]) | |
if ret != 0: | |
ret = w.close(mcp_handle) | |
print(hex(ret)) | |
return | |
ret = w.MCP_InstallSetTargetDevice(mcp_handle, installToUsb) | |
print("install set target device : " + hex(ret)) | |
if ret != 0: | |
ret = w.close(mcp_handle) | |
print(hex(ret)) | |
return | |
ret = w.MCP_InstallSetTargetUsb(mcp_handle, installToUsb) | |
print("install set target usb : " + hex(ret)) | |
if ret != 0: | |
ret = w.close(mcp_handle) | |
print(hex(ret)) | |
return | |
ret = w.MCP_Install(mcp_handle, "/vol/storage_sdcard/"+path) | |
print("install : " + hex(ret)) | |
ret = w.close(mcp_handle) | |
print(hex(ret)) | |
#path=full path, for example "/vol/storage_mlc01/usr/title/00050000/10179C00" | |
def delete_title(path, flush = 0): | |
mcp_handle = w.open("/dev/mcp", 0) | |
print(hex(mcp_handle)) | |
ret = w.MCP_DeleteTitle(mcp_handle, path, flush) | |
print("delete title : " + hex(ret)) | |
ret = w.close(mcp_handle) | |
print(hex(ret)) | |
def ios_shutdown(): | |
w.svc_and_exit(0x72,[0]) | |
def ios_reset(): | |
w.svc_and_exit(0x72,[1]) | |
def get_nim_status(): | |
nim_handle = w.open("/dev/nim", 0) | |
print(hex(nim_handle)) | |
inbuffer = buffer(0x80) | |
(ret, data) = w.ioctlv(nim_handle, 0x00, [inbuffer], [0x80]) | |
print(hex(ret), "".join("%02X" % v for v in data[0])) | |
ret = w.close(nim_handle) | |
print(hex(ret)) | |
def read_and_print(adr, size): | |
data = w.read(adr, size) | |
data = struct.unpack(">%dI" % (len(data) // 4), data) | |
for i in range(0, len(data), 4): | |
print(" ".join("%08X"%v for v in data[i:i+4])) | |
def read_and_dump(adr,size): | |
f=open("dump.bin","wb") | |
for i in range(0,size,1024): | |
data = w.read(adr+i,1024) | |
f.write(data) | |
f.close() | |
def flush_mlc(): | |
ret = w.FSA_FlushVolume(w.fsa_handle, "/vol/storage_mlc01") | |
print(hex(ret)) | |
if __name__ == '__main__': | |
w = wupclient() | |
mount_sd() | |
# mount_odd_content() | |
# print(w.pwd()) | |
# w.ls() | |
# w.dump_syslog() | |
# w.mkdir("/vol/storage_sdcard/usr", 0x600) | |
# install_title("test") | |
# get_nim_status() | |
# w.kill() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
agreed