created a little command line version

Added new features (FSGetStats, cd with "." or "..")
refactored some parts (file operations now in own operations file)
maybe serveral small bug fixes/improvements I don't remember
limiting read buffer instead of crashing the console
This commit is contained in:
Maschell 2016-11-10 23:39:18 +01:00
parent 8e6187ab0f
commit 020f1e185a
12 changed files with 448 additions and 55 deletions

1
.gitignore vendored
View File

@ -46,3 +46,4 @@ Network Trash Folder
Temporary Items
.apdisk
*.class
tickets/*

View File

@ -1,5 +1,41 @@
A port of smea's client (WUPClient.py) for the wupserver.
Open it with
```
java -jar jwupclient 192.168.x.x
```
The following commands are supported (in my ulgy written shell).
cd:
change directory to parent dir:
cd ..
change directory to relative dir:
cd code
change directory to absolute dir:
cd /vol/storage_mlc01
ls:
lists content of the directory
dl:
downloads a file.
dl filename [targetdir]
dlfp:
same as dl but keeps the absolute path of the wiiu
dldir:
downloads a directory
downloading the current dir
dldir
arguments:
-src <source folder on wiiu> (sets the source folder on the wiiu)
-dst <destination folder on wiiu> (sets the destination folder on your PC)
-fullpath (keeps the absolute path of the wiiu)
Server and smea's client.
https://github.com/smealum/iosuhax/tree/master/wupserver
Everything is from Smea, I'm just porting it!
Everything is from Smea, I'm just porting and extending it!

BIN
jwupclient.jar Normal file

Binary file not shown.

View File

@ -1,10 +1,11 @@
package de.mas.wupclient;
import java.io.IOException;
import java.util.Scanner;
import de.mas.wupclient.client.WUPClient;
import de.mas.wupclient.client.operations.DownloadUploadOperations;
import de.mas.wupclient.client.operations.SpecialOperations;
import de.mas.wupclient.client.operations.UtilOperations;
import de.mas.wupclient.client.utils.Logger;
public class Starter {
public static void main(String args[]){
@ -14,25 +15,113 @@ public class Starter {
}
WUPClient w = new WUPClient(ip);
try {
UtilOperations util = UtilOperations.UtilOperationsFactory(w);
DownloadUploadOperations dlul = DownloadUploadOperations.DownloadUploadOperationsFactory(w);
util.dump_syslog();
boolean exit = false;
Logger.logCmd("Lets into the " + w.getCwd() + "/sys/title/00050010/10040200/" + " folder!");
util.lsRecursive(w.getCwd() + "/sys/title/00050010/10040200/");
Logger.logCmd("And download the /code/app.xml to /test/app.xml");
dlul.downloadFile(w.getCwd() + "/sys/title/00050010/10040200/code", "app.xml", "test", null);
Logger.logCmd("done!");
System.out.println("JWUPClient. Please enter a command. Enter \"exit\" to exit.");
System.out.println();
System.out.print(w.getCwd() + " > ");
Scanner reader = new Scanner(System.in); // Reading from System.in
while(!exit){
String input = reader.nextLine();
if(input.equals("exit")){
exit = true;
break;
}
processCommand(input,w);
System.out.println();
System.out.print(w.getCwd() + " > ");
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
w.FSA_Close(w.get_fsa_handle());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
w.closeSocket();
}
}
private static void processCommand(String input,WUPClient w) throws IOException {
if(input == null || input.isEmpty()){
return;
}
UtilOperations util = UtilOperations.UtilOperationsFactory(w);
SpecialOperations special = SpecialOperations.SpecialOperationsFactory(w);
DownloadUploadOperations dlul = DownloadUploadOperations.DownloadUploadOperationsFactory(w);
String[] inputs = input.split(" ");
switch(inputs[0]){
case "ls":
if(inputs.length > 1){
util.ls(inputs[1]);
}else{
util.ls();
}
break;
case "lsr":
util.lsRecursive();
break;
case "sysdump":
util.dump_syslog();
break;
case "cd":
if(inputs.length > 1){
util.cd(inputs[1]);
}else{
util.cd();
}
break;
case "dldir":
String destination = null;
String source = w.getCwd();
boolean fullpath = false;
if(inputs.length > 1){
for(int i = 1;i < inputs.length;i++){
if(inputs[i].equals("-dst")){
if(inputs.length >= i+1){
destination = inputs[i+1];
i++;
}
}else if(inputs[i].equals("-src")){
if(inputs.length >= i+1){
source = inputs[i+1];
i++;
}
}else if(inputs[i].equals("-fullpath")){
fullpath = true;
}
}
}
dlul.downloadFolder(source,destination,fullpath);
break;
case "dl":
if(inputs.length == 2){
dlul.downloadFile("", inputs[1]);
}else if(inputs.length == 3){
dlul.downloadFile("", inputs[1], inputs[2]);
}
break;
case "dlfp": //download to full path
if(inputs.length == 2){
dlul.downloadFile("", inputs[1],w.getCwd());
}else if(inputs.length == 3){
dlul.downloadFile("", inputs[1],inputs[2] + "/" + w.getCwd());
}
break;
case "nandtickets": //download to full path
special.parseAndDownloadTickets();
break;
default:
System.out.println("Command not found!");
break;
}
}
}

View File

@ -13,6 +13,7 @@ import de.mas.wupclient.client.utils.Result;
import de.mas.wupclient.client.utils.Utils;
public class WUPClient {
public static final int MAX_READ_SIZE = 0x400;
private String IP;
private int fsaHandle = -1;
private String cwd = "";
@ -33,15 +34,19 @@ public class WUPClient {
Logger.logErr("send failed");
e.printStackTrace();
}
ByteBuffer destByteBuffer = ByteBuffer.allocate(0x600);
byte[] result = new byte[0x0600];
int size = getSocket().getInputStream().read(result);
destByteBuffer.put(result, 0, size);
int returnValue = destByteBuffer.getInt();
return new Result<byte[]>(returnValue,Arrays.copyOfRange(result, 4,result.length));
ByteBuffer destByteBuffer = ByteBuffer.allocate(0x04);
destByteBuffer.put(Arrays.copyOfRange(result, 0, 4));
int returnValue = destByteBuffer.getInt(0);
return new Result<byte[]>(returnValue,Arrays.copyOfRange(result, 4,size));
}
public byte[] read(int addr, int len) throws IOException{
if(len > WUPClient.MAX_READ_SIZE){
throw new IOException("read length > " + WUPClient.MAX_READ_SIZE);
}
Result<byte[]> result = send(1, Utils.m_packBE(addr,len));
if(result.getResultValue() == 0){
return result.getData();
@ -126,7 +131,7 @@ public class WUPClient {
e.printStackTrace();
}
setSocket(clientSocket);
Logger.log("Connected");
Logger.log("Connected to " + ip);
return clientSocket;
}
@ -151,7 +156,7 @@ public class WUPClient {
public String getCwd() {
return cwd;
}
private void setCwd(String cwd) {
public void setCwd(String cwd) {
this.cwd = cwd;
}
public Socket getSocket() {

View File

@ -32,31 +32,57 @@ public class DownloadUploadOperations extends Operations {
setFSA(FSAOperations.FSAOperationsFactory(client));
}
public boolean downloadFolder(String path) throws IOException{
return downloadFolder(path,null,false);
public boolean downloadFolder(String sourcePath) throws IOException{
return downloadFolder(sourcePath,null,false);
}
public boolean downloadFolder(String sourcePath, String targetPath,boolean fullpath) throws IOException {
String new_source_path = sourcePath;
if(!sourcePath.isEmpty() && !sourcePath.startsWith("/")){
new_source_path = getClient().getCwd() + "/"+ sourcePath;
}else if(sourcePath == null || sourcePath.isEmpty()){
new_source_path = getClient().getCwd();
}
public boolean downloadFolder(String sourcePath,String targetPath) throws IOException{
return downloadFolder(sourcePath,targetPath,false);
}
public boolean downloadFolder(String sourcePath, String targetPath,boolean useRelativPath) throws IOException {
List<FEntry> files = util.ls(sourcePath,true);
if(targetPath == null || targetPath.isEmpty()){
if(fullpath){
targetPath = new_source_path;
}else{
targetPath = "";
if(!sourcePath.isEmpty() && !sourcePath.startsWith("/")){
targetPath = sourcePath;
}
}
}else{
if(fullpath){
targetPath += new_source_path;
}else{
if(!sourcePath.isEmpty() && !sourcePath.startsWith("/")){
targetPath += "/" + sourcePath;
}
}
}
return _downloadFolder(new_source_path,targetPath);
}
private boolean _downloadFolder(String sourcePath, String targetPath) throws IOException {
Logger.logCmd("Downloading folder " + sourcePath);
List<FEntry> files = util.ls(sourcePath,true);
Utils.createSubfolder(targetPath);
for(FEntry f: files){
if(f.isFile()){
//System.out.println(targetPath);
downloadFile(sourcePath, f.getFilename(),targetPath);
}else{
downloadFolder(sourcePath + "/" + f.getFilename(), targetPath,useRelativPath);
_downloadFolder(sourcePath + "/" + f.getFilename(), targetPath + "/" + f.getFilename());
}
}
return false;
}
public byte[] downloadFileToByteArray(String path) throws IOException{
Logger.logCmd("Downloading " + path);
int fsa_handle = getClient().get_fsa_handle();
Result<Integer> res = fsa.FSA_OpenFile(fsa_handle, path, "r");
boolean success = false;
@ -65,6 +91,7 @@ public class DownloadUploadOperations extends Operations {
int block_size = 0x400;
ByteArrayOutputStream out = new ByteArrayOutputStream();
success = true;
int total_read = 0;
while(true){
Result<byte[]> read_result= fsa.FSA_ReadFile(fsa_handle, res.getData(), 0x1, block_size);
@ -73,30 +100,41 @@ public class DownloadUploadOperations extends Operations {
success = false;
break;
}
total_read += read_result.getResultValue();
out.write(Arrays.copyOf(read_result.getData(), read_result.getResultValue()));
if(read_result.getResultValue() <= 0)
break;
if((total_read /1024) % 50 == 0){
System.out.println(String.format("%.3f", (double)(total_read /1024.0)) + " kb done");
}
}
fsa.FSA_CloseFile(fsa_handle, res.getData());
if(success){
System.out.println("Download done: " + total_read+ " bytes");
result = out.toByteArray();
}
}
return result;
}
public boolean downloadFile(String sourcePath, String filename) throws IOException {
return downloadFile(sourcePath, filename, "");
}
public boolean downloadFile(String sourcePath, String filename, String targetPath) throws IOException {
return downloadFile(sourcePath, filename, targetPath,null);
}
public boolean downloadFile(String sourcePath,String sourceFilename,String targetPath,String targetFileName) throws IOException {
byte[] data = downloadFileToByteArray(sourcePath + "/" + sourceFilename);
if(data == null){
System.out.println("failed");
return false;
if(sourcePath == null || sourcePath.isEmpty()){
sourcePath = getClient().getCwd();
}
if(targetPath == null || targetPath.isEmpty()){
targetPath = "";
}
String subdir = "";
if(targetFileName == null){
@ -107,8 +145,17 @@ public class DownloadUploadOperations extends Operations {
if(subdir.startsWith("/")){
subdir = subdir.substring(1);
}
if(!targetPath.isEmpty()){
Utils.createSubfolder(subdir);
}
Logger.logCmd("Downloading " + sourcePath + "/" + sourceFilename + " to " + subdir);
byte[] data = downloadFileToByteArray(sourcePath + "/" + sourceFilename);
if(data == null){
System.out.println("failed");
return false;
}
FileOutputStream stream = new FileOutputStream(subdir);
try {
stream.write(data);

View File

@ -7,6 +7,7 @@ import java.util.Map;
import de.mas.wupclient.client.WUPClient;
import de.mas.wupclient.client.utils.FEntry;
import de.mas.wupclient.client.utils.FStats;
import de.mas.wupclient.client.utils.Logger;
import de.mas.wupclient.client.utils.Result;
import de.mas.wupclient.client.utils.Utils;
@ -95,6 +96,9 @@ public class FSAOperations extends Operations {
}
public Result<Integer> FSA_OpenFile(int handle, String path, String mode) throws IOException{
if(!path.startsWith("/")){
path = getClient().getCwd() + "/" + path;
}
byte[] inbuffer = new byte[0x520];
Utils.writeNullTerminatedStringToByteArray(inbuffer, path, 0x04);
Utils.writeNullTerminatedStringToByteArray(inbuffer, mode, 0x284);
@ -111,6 +115,10 @@ public class FSAOperations extends Operations {
}
public Result<byte[]> FSA_ReadFile(int handle, int file_handle, int size, int cnt) throws IOException{
if(size * cnt > WUPClient.MAX_READ_SIZE){
Logger.logErr("FSA_ReadFile error: size*cnt > " + WUPClient.MAX_READ_SIZE +"(" + (size * cnt) + ")");
return new Result<byte[]>(-228,new byte[0]);
}
byte[] inbuffer = new byte[0x520];
Utils.writeIntToByteArray(inbuffer, size, 0x08);
Utils.writeIntToByteArray(inbuffer, cnt, 0x0C);
@ -119,4 +127,22 @@ public class FSAOperations extends Operations {
return new Result<byte[]>(result.getResultValue(),result.getData()[0]);
}
public Result<FStats> FSA_StatFile(int fsa_handle, int handle) throws IOException{
byte[] inbuffer = new byte[0x520];
Utils.writeIntToByteArray(inbuffer, handle, 0x04);
Result<byte[]> result = system.ioctl(fsa_handle, 0x14, inbuffer, 0x293);
FStats stats = null;
if(result.getResultValue() == 0){
stats = new FStats(result.getData());
}
System.out.println(result.getResultValue());
return new Result<FStats>(result.getResultValue(), stats);
}
public Result<Integer> FSA_ReadFilePtr(int fsa_handle, int src_handle, int i, int block_size, int buffer_ptr) {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -0,0 +1,100 @@
package de.mas.wupclient.client.operations;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import de.mas.wupclient.client.WUPClient;
import de.mas.wupclient.client.utils.Logger;
import de.mas.wupclient.client.utils.Result;
public class FileOperations extends Operations {
private static Map<WUPClient,FileOperations> instances = new HashMap<>();
public static FileOperations FileOperationsFactory(WUPClient client){
if(!instances.containsKey(client)){
instances.put(client, new FileOperations(client));
}
return instances.get(client);
}
private UtilOperations util = null;
private FSAOperations fsa = null;
private SystemOperations system = null;
public FileOperations(WUPClient client) {
super(client);
setUtil(UtilOperations.UtilOperationsFactory(client));
setFsa(FSAOperations.FSAOperationsFactory(client));
setSystem(SystemOperations.SystemOperationsFactory(client));
}
public int mkdir(String path, int flags) throws IOException{
return createDir(path, flags);
}
public int createDir(String path, int flags) throws IOException{
int fsa_handle = getClient().get_fsa_handle();
return fsa.FSA_MakeDir(fsa_handle, path, 2);
}
/* FSA_ReadFilePtr needs to be implemented
public boolean cp(String source, String target) throws IOException{
return copyFile(source, target);
}
public boolean copyFile(String source, String destination) throws IOException{
int fsa_handle = getClient().get_fsa_handle();
Result<Integer> result_src = fsa.FSA_OpenFile(fsa_handle, source, "r");
if(result_src.getResultValue() != 0){
Logger.logErr("copyFile error: couldn't open " + source);
return false;
}
Result<Integer> result_dst = fsa.FSA_OpenFile(fsa_handle, destination, "r");
if(result_dst.getResultValue() != 0){
Logger.logErr("copyFile error: couldn't open " + destination);
fsa.FSA_CloseFile(fsa_handle, result_src.getData());
return false;
}
int block_size = 0x10000;
int buffer_ptr = system.alloc(block_size, 0x40);
int i =0;
int src_handle = result_src.getData();
int dst_handle = result_dst.getData();
boolean result = true;
while(true){
Result<Integer> result_read = fsa.FSA_ReadFilePtr(fsa_handle, src_handle, 0x1, block_size, buffer_ptr);
if(result_read.getResultValue() < 0){
Logger.log("copyFile error: reading source file failed.");
result = false;
break;
}
}
system.free(buffer_ptr);
fsa.FSA_CloseFile(fsa_handle, src_handle);
fsa.FSA_CloseFile(fsa_handle, dst_handle);
return result;
}*/
public UtilOperations getUtil() {
return util;
}
public void setUtil(UtilOperations util) {
this.util = util;
}
public FSAOperations getFsa() {
return fsa;
}
public void setFsa(FSAOperations fsa) {
this.fsa = fsa;
}
public SystemOperations getSystem() {
return system;
}
public void setSystem(SystemOperations system) {
this.system = system;
}
}

View File

@ -39,11 +39,12 @@ public class UtilOperations extends Operations {
return ls(targetPath,false);
}
public List<FEntry> ls(String targetPath,boolean return_data) throws IOException{
if(!return_data){
if(targetPath != null){
Logger.logCmd("ls(" + targetPath + ")");
//Logger.logCmd("ls(" + targetPath + ")");
}else{
Logger.logCmd("ls()");
//Logger.logCmd("ls()");
}
}
List<FEntry> results = new ArrayList<>();
@ -51,6 +52,8 @@ public class UtilOperations extends Operations {
String path = targetPath;
if(targetPath == null || targetPath.isEmpty()){
path = getClient().getCwd();
}else if(!targetPath.startsWith("/")){
path = getClient().getCwd() + "/" + targetPath;
}
Result<Integer> res = fsa.FSA_OpenDir(fsa_handle, path);
@ -82,6 +85,7 @@ public class UtilOperations extends Operations {
if((result = fsa.FSA_CloseDir(fsa_handle, dirhandle)) != 0){
Logger.logErr("CloseDir failed!" + result);
}
getClient().FSA_Close(getClient().get_fsa_handle());
return results;
}
@ -101,6 +105,7 @@ public class UtilOperations extends Operations {
}
}
}
public void dump_syslog() throws IOException {
int syslog_address = Utils.bigEndianByteArrayToInt(getClient().read(0x05095ECC, 4)) + 0x10;
int block_size = 0x400;
@ -113,11 +118,41 @@ public class UtilOperations extends Operations {
break;
}
}
getClient().FSA_Close(getClient().get_fsa_handle());
}
public boolean cd() throws IOException {
return cd("");
}
public boolean cd(String path) throws IOException {
if(path.equals(".")){
return true;
}
if(path.equals("..")){
path = Utils.getParentDir(getClient().getCwd());
}
if(!path.startsWith("/")&& getClient().getCwd().startsWith("/")){
return cd(getClient().getCwd() + "/" + path);
}
public int mkdir(String path, int flags) throws IOException{
int fsa_handle = getClient().get_fsa_handle();
return fsa.FSA_MakeDir(fsa_handle, path, 2);
String usedPath = path;
if(path.equals("..")){
usedPath = Utils.getParentDir(getClient().getCwd());
System.out.println(usedPath +" dd");
}else if (path.isEmpty()){
usedPath = getClient().getCwd();
}
boolean final_result = false;
Result<Integer> result = fsa.FSA_OpenDir(fsa_handle,usedPath);
if(result.getResultValue() == 0){
getClient().setCwd(usedPath);
fsa.FSA_CloseDir(fsa_handle, result.getData());
final_result = true;
}else{
Logger.logErr("path does not exists");
final_result = false;
}
getClient().FSA_Close(getClient().get_fsa_handle());
return final_result;
}
public int mount(String device_path, String volume_path, int flags) throws IOException{

View File

@ -0,0 +1,39 @@
package de.mas.wupclient.client.utils;
import java.nio.ByteBuffer;
import java.util.Arrays;
public class FStats {
int[] unk1 = new int[0x4];
int size; // size in bytes
int physsize; // physical size on disk in bytes
int[] unk2 = new int[0x13];
public FStats(byte[] data) {
if(data == null || data.length < 0x64){
return;
}
ByteBuffer buffer = ByteBuffer.allocate(0x64);
buffer.put(Arrays.copyOfRange(data, 0x00, 0x64));
int offset = 0x00;
for(int i = 0;i<unk1.length;i++){
unk1[i] = buffer.getInt(offset);
offset +=0x04;
}
this.size = buffer.getInt(offset);;
offset += 4;
this.physsize = buffer.getInt(offset);;
offset += 4;
for(int i = 0;i<unk2.length;i++){
unk2[i] = buffer.getInt(offset);
offset +=0x04;
}
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
return sb.append("Size: " + size + " Physicalsize:" + physsize).toString();
}
}

View File

@ -5,7 +5,7 @@ public class Logger {
System.out.println(log);
}
public static void logCmd(String log){
log(">>>" + log);
log("" + log);
}
public static void logErr(String log){
System.err.println("> Error: " + log);

View File

@ -44,7 +44,7 @@ public class Utils {
public static String getStringFromByteArray(byte[] data){
int i = 0;
while(data[i] != 0 && (i) < data.length){
while(data[i] != 0 && (i) < data.length-1){
i++;
}
String string = "";
@ -145,8 +145,23 @@ public class Utils {
return null;
}
public static void createSubfolder(String folder){
public static String getParentDir(String path){
if(path == null || path.isEmpty() || path.equals("/")){
return "/";
}
String new_path = "";
String [] pathpath = path.split("/");
for(int i = 0;i<pathpath.length-1;i++){
if(!pathpath[i].equals("")){
new_path += "/" + pathpath[i] ;
}
}
return new_path;
}
public static void createSubfolder(String folder){
if(folder.endsWith("/")) folder += "_";
String [] path = folder.split("/");
File folder_ = null;
String foldername = new String();