[Loader] A single plugin can be loaded via wiiload.

Now more sd swapping for ftpiiu while developing plugins!
Simply use the normal wiiload to send a plugin (.mod) file to the Wii U while the plugin loader in running.
This requires zlib, don't forget to add it to your portlib. It can be found in the "libs" folder.
This commit is contained in:
Maschell 2018-03-04 16:27:57 +01:00
parent 0b6d578e27
commit 36f4b1f350
15 changed files with 334 additions and 39 deletions

View File

@ -44,6 +44,7 @@ install:
- tar -xzvf libiosuhax.tar.gz - tar -xzvf libiosuhax.tar.gz
- tar -xzvf fs_wrapper.tar.gz - tar -xzvf fs_wrapper.tar.gz
- tar -xzvf controller_patcher.tar.gz - tar -xzvf controller_patcher.tar.gz
- 7z x -y ./libs/portlibs.zip -o${DEVKITPRO}
- 7z x -y ./dynamic_libs-lib/libs/portlibs.zip -o${DEVKITPRO} - 7z x -y ./dynamic_libs-lib/libs/portlibs.zip -o${DEVKITPRO}
- 7z x -y ./libgui-master/libs/portlibs.zip -o${DEVKITPRO} - 7z x -y ./libgui-master/libs/portlibs.zip -o${DEVKITPRO}
- (cd libiosuhax-master && make -j8 && make install) - (cd libiosuhax-master && make -j8 && make install)

View File

@ -47,11 +47,23 @@ For building the loader you need:
- [libutils](https://github.com/Maschell/libutils) for common functions. - [libutils](https://github.com/Maschell/libutils) for common functions.
- [libgui](https://github.com/Maschell/libgui) for the gui elements. - [libgui](https://github.com/Maschell/libgui) for the gui elements.
Install the according to their readmes. Don't forget to install their dependencies.
**Dependencies**
All needed dependencies are in the "libs" folder of this repository. Extract the "portlibs.zip" archive into your devkitPro directory.
The archive includes:
- zlib
**Compiling**
Then call the following command in the "loader" directory. Then call the following command in the "loader" directory.
``` ```
make make
``` ```
This should create an "wiiupluginloader.elf" which can be loaded with the Homebrew Launcher. This should create an "wiiupluginloader.elf" which can be loaded with the Homebrew Launcher.
### Plugins ### Plugins
@ -70,6 +82,12 @@ You can also check out the travis script for needed dependencies of the library,
For logging (for example of the loader) you need to start the UdpDebugReader on a computer in the same network. For logging (for example of the loader) you need to start the UdpDebugReader on a computer in the same network.
This has been created by @dimok789 and can be found in the tools folder. This has been created by @dimok789 and can be found in the tools folder.
# Load plugin via network
While the loader is running, it's possible to load a single plugin via wiiload.
When using this feature, **only** this plugin will be loaded. The plugin will copied to the SDCard, this mean a SDCard is required.
A windows executable can be found in `tools/wiiload.exe`
More information about wiiload and alternatives can be found here: http://wiibrew.org/wiki/Wiiload
# Future # Future
Checkout the PLANS.MD for goals, issues and future plans. Checkout the PLANS.MD for goals, issues and future plans.
@ -78,3 +96,5 @@ Some files are based on brainslug by Chadderz:
https://github.com/Chadderz121/brainslug-wii https://github.com/Chadderz121/brainslug-wii
Much stuff also wouldn't be possible without dimok789. He made many great tools and homebrew this stuff in based on (Makefiles, Mocha, homebrew channel, udp logger, dynamic_libs etc.) Much stuff also wouldn't be possible without dimok789. He made many great tools and homebrew this stuff in based on (Makefiles, Mocha, homebrew channel, udp logger, dynamic_libs etc.)
Also thanks to everyone who made actual exploits. Also thanks to everyone who made actual exploits.
Thanks to dhewg for wiiload:
http://wiibrew.org/wiki/Wiiload

BIN
loader/libs/portlibs.zip Normal file

Binary file not shown.

View File

@ -25,6 +25,7 @@
#include <sounds/SoundHandler.hpp> #include <sounds/SoundHandler.hpp>
#include <utils/logger.h> #include <utils/logger.h>
#include "settings/CSettings.h" #include "settings/CSettings.h"
#include "myutils/TcpReceiver.h"
Application *Application::applicationInstance = NULL; Application *Application::applicationInstance = NULL;
bool Application::exitApplication = false; bool Application::exitApplication = false;
@ -169,6 +170,8 @@ void Application::executeThread(void) {
DEBUG_FUNCTION_LINE("Entering main loop\n"); DEBUG_FUNCTION_LINE("Entering main loop\n");
exitApplication = false; exitApplication = false;
//! main GX2 loop (60 Hz cycle with max priority on core 1) //! main GX2 loop (60 Hz cycle with max priority on core 1)
TcpReceiver pluginReceiver(4299);
while(!exitApplication && !reloadUIflag) { while(!exitApplication && !reloadUIflag) {
//! Read out inputs //! Read out inputs
for(s32 i = 0; i < 5; i++) { for(s32 i = 0; i < 5; i++) {

View File

@ -29,6 +29,9 @@ extern "C" {
#define DEFAULT_LANG_PATH DEFAULT_WUPSLOADER_PATH "/languages" #define DEFAULT_LANG_PATH DEFAULT_WUPSLOADER_PATH "/languages"
#define LANGUAGE_FILE_EXT ".lang" #define LANGUAGE_FILE_EXT ".lang"
#define WUPS_TEMP_PLUGIN_PATH SD_PATH WIIU_PATH "/plugins/temp"
#define WUPS_TEMP_PLUGIN_FILE WUPS_TEMP_PLUGIN_PATH "/temp.mod"
#define WUPS_SDUSB_MOUNTED_NONE 0 #define WUPS_SDUSB_MOUNTED_NONE 0
#define WUPS_SDUSB_MOUNTED_FAKE (1<<0) #define WUPS_SDUSB_MOUNTED_FAKE (1<<0)
#define WUPS_SDUSB_MOUNTED_OS_SD (1<<1) #define WUPS_SDUSB_MOUNTED_OS_SD (1<<1)

View File

@ -1,6 +1,8 @@
#include <string> #include <string>
#include <vector>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stddef.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <malloc.h> #include <malloc.h>
@ -10,7 +12,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include "dynamic_libs/os_functions.h" #include <dynamic_libs/os_functions.h>
#include "dynamic_libs/gx2_functions.h" #include "dynamic_libs/gx2_functions.h"
#include "dynamic_libs/ax_functions.h" #include "dynamic_libs/ax_functions.h"
#include "dynamic_libs/socket_functions.h" #include "dynamic_libs/socket_functions.h"
@ -19,7 +21,7 @@
#include "dynamic_libs/nn_nim_functions.h" #include "dynamic_libs/nn_nim_functions.h"
#include "dynamic_libs/vpad_functions.h" #include "dynamic_libs/vpad_functions.h"
#include "dynamic_libs/padscore_functions.h" #include "dynamic_libs/padscore_functions.h"
#include "dynamic_libs/proc_ui_functions.h" #include <dynamic_libs/proc_ui_functions.h>
#include <utils/logger.h> #include <utils/logger.h>
#include <fs/FSUtils.h> #include <fs/FSUtils.h>
@ -80,7 +82,7 @@ extern "C" int Menu_Main(int argc, char **argv) {
DEBUG_FUNCTION_LINE("Wii U Plugin System Loader %s\n",APP_VERSION); DEBUG_FUNCTION_LINE("Wii U Plugin System Loader %s\n",APP_VERSION);
//setup_os_exceptions(); setup_os_exceptions();
Init(); Init();

View File

@ -0,0 +1,236 @@
#include <algorithm>
#include <string>
#include <vector>
#include <string.h>
#include <zlib.h>
#include <libgen.h>
#include "common/common.h"
#include "TcpReceiver.h"
#include <dynamic_libs/os_functions.h>
#include <dynamic_libs/socket_functions.h>
#include <fs/CFile.hpp>
#include <fs/FSUtils.h>
#include <utils/logger.h>
#include <utils/StringTools.h>
#include <utils/net.h>
#include "Application.h"
#include "plugin/PluginLoader.h"
#include "plugin/PluginInformation.h"
TcpReceiver::TcpReceiver(int port)
: CThread(CThread::eAttributeAffCore0 | CThread::eAttributePinnedAff)
, exitRequested(false)
, serverPort(port)
, serverSocket(-1) {
resumeThread();
}
TcpReceiver::~TcpReceiver() {
exitRequested = true;
if(serverSocket > 0) {
shutdown(serverSocket, SHUT_RDWR);
}
}
void TcpReceiver::executeThread() {
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (serverSocket < 0)
return;
u32 enable = 1;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
struct sockaddr_in bindAddress;
memset(&bindAddress, 0, sizeof(bindAddress));
bindAddress.sin_family = AF_INET;
bindAddress.sin_port = serverPort;
bindAddress.sin_addr.s_addr = INADDR_ANY;
s32 ret;
if ((ret = bind(serverSocket, (struct sockaddr *)&bindAddress, sizeof(bindAddress))) < 0) {
socketclose(serverSocket);
return;
}
if ((ret = listen(serverSocket, 3)) < 0) {
socketclose(serverSocket);
return;
}
struct sockaddr_in clientAddr;
s32 addrlen = sizeof(struct sockaddr);
while(!exitRequested) {
s32 clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &addrlen);
if(clientSocket >= 0) {
u32 ipAddress = clientAddr.sin_addr.s_addr;
//serverReceiveStart(this, ipAddress);
int result = loadToMemory(clientSocket, ipAddress);
//serverReceiveFinished(this, ipAddress, result);
socketclose(clientSocket);
if(result > 0)
break;
} else {
os_usleep(100000);
}
}
socketclose(serverSocket);
}
int TcpReceiver::loadToMemory(s32 clientSocket, u32 ipAddress) {
DEBUG_FUNCTION_LINE("Loading file from ip %08X\n", ipAddress);
u32 fileSize = 0;
u32 fileSizeUnc = 0;
unsigned char haxx[8];
memset(haxx, 0, sizeof(haxx));
//skip haxx
recvwait(clientSocket, haxx, sizeof(haxx));
recvwait(clientSocket, (unsigned char*)&fileSize, sizeof(fileSize));
if (haxx[4] > 0 || haxx[5] > 4) {
recvwait(clientSocket, (unsigned char*)&fileSizeUnc, sizeof(fileSizeUnc)); // Compressed protocol, read another 4 bytes
}
u32 bytesRead = 0;
struct in_addr in;
in.s_addr = ipAddress;
DEBUG_FUNCTION_LINE("transfer start\n");
unsigned char* loadAddress = (unsigned char*)memalign(0x40, fileSize);
if(!loadAddress) {
os_sleep(1);
return NOT_ENOUGH_MEMORY;
}
// Copy rpl in memory
while(bytesRead < fileSize) {
u32 blockSize = 0x1000;
if(blockSize > (fileSize - bytesRead))
blockSize = fileSize - bytesRead;
int ret = recv(clientSocket, loadAddress + bytesRead, blockSize, 0);
if(ret <= 0) {
DEBUG_FUNCTION_LINE("Failure on reading file\n");
break;
}
bytesRead += ret;
}
if(bytesRead != fileSize) {
free(loadAddress);
DEBUG_FUNCTION_LINE("File loading not finished, %i of %i bytes received\n", bytesRead, fileSize);
os_sleep(1);
return FILE_READ_ERROR;
}
bool res = false;
// Do we need to unzip this thing?
if (haxx[4] > 0 || haxx[5] > 4) {
unsigned char* inflatedData = NULL;
// We need to unzip...
if (loadAddress[0] == 'P' && loadAddress[1] == 'K' && loadAddress[2] == 0x03 && loadAddress[3] == 0x04) {
//! TODO:
//! mhmm this is incorrect, it has to parse the zip
// Section is compressed, inflate
inflatedData = (unsigned char*)malloc(fileSizeUnc);
if(!inflatedData) {
free(loadAddress);
os_sleep(1);
return NOT_ENOUGH_MEMORY;
}
int ret = 0;
z_stream s;
memset(&s, 0, sizeof(s));
s.zalloc = Z_NULL;
s.zfree = Z_NULL;
s.opaque = Z_NULL;
ret = inflateInit(&s);
if (ret != Z_OK) {
free(loadAddress);
free(inflatedData);
os_sleep(1);
return FILE_READ_ERROR;
}
s.avail_in = fileSize;
s.next_in = (Bytef *)(&loadAddress[0]);
s.avail_out = fileSizeUnc;
s.next_out = (Bytef *)&inflatedData[0];
ret = inflate(&s, Z_FINISH);
if (ret != Z_OK && ret != Z_STREAM_END) {
free(loadAddress);
free(inflatedData);
os_sleep(1);
return FILE_READ_ERROR;
}
inflateEnd(&s);
fileSize = fileSizeUnc;
} else {
// Section is compressed, inflate
inflatedData = (unsigned char*)malloc(fileSizeUnc);
if(!inflatedData) {
free(loadAddress);
os_sleep(1);
return NOT_ENOUGH_MEMORY;
}
uLongf f = fileSizeUnc;
int result = uncompress((Bytef*)&inflatedData[0], &f, (Bytef*)loadAddress, fileSize);
if(result != Z_OK) {
DEBUG_FUNCTION_LINE("uncompress failed %i\n", result);
os_sleep(1);
return FILE_READ_ERROR;
}
fileSizeUnc = f;
fileSize = fileSizeUnc;
}
FSUtils::CreateSubfolder(WUPS_TEMP_PLUGIN_PATH);
res = FSUtils::saveBufferToFile(WUPS_TEMP_PLUGIN_FILE,inflatedData, fileSize);
free(inflatedData);
} else {
FSUtils::CreateSubfolder(WUPS_TEMP_PLUGIN_PATH);
res = FSUtils::saveBufferToFile(WUPS_TEMP_PLUGIN_FILE,loadAddress, fileSize);
free(loadAddress);
}
if(!res) {
os_sleep(1);
return NOT_ENOUGH_MEMORY;
}
PluginInformation * plugin = PluginInformation::loadPluginInformation(WUPS_TEMP_PLUGIN_FILE);
if(plugin == NULL) {
return NOT_A_VALID_PLUGIN;
}
PluginLoader * pluginLoader = PluginLoader::getInstance();
pluginLoader->resetPluginLoader();
std::vector<PluginInformation* > pluginList = pluginLoader->getPluginInformation(WUPS_TEMP_PLUGIN_PATH);
if(pluginList.size() == 0) {
return NOT_A_VALID_PLUGIN;
}
pluginLoader->loadAndLinkPlugins(pluginList);
Application::instance()->quit(APPLICATION_CLOSE_APPLY);
return fileSize;
}

View File

@ -0,0 +1,43 @@
#ifndef TCP_RECEIVER_H_
#define TCP_RECEIVER_H_
#include <vector>
#include <string>
#include <dynamic_libs/os_types.h>
#include <system/CThread.h>
#include <gui/sigslot.h>
#include <gui/gui.h>
class TcpReceiver : public CThread
{
public:
enum eLoadResults
{
SUCCESS = 0,
INVALID_INPUT = -1,
FILE_OPEN_FAILURE = -2,
FILE_READ_ERROR = -3,
NOT_ENOUGH_MEMORY = -4,
NOT_A_VALID_PLUGIN = -5,
};
TcpReceiver(int port);
~TcpReceiver();
sigslot::signal2<GuiElement *, u32> serverReceiveStart;
sigslot::signal3<GuiElement *, u32, int> serverReceiveFinished;
private:
void executeThread();
int loadToMemory(s32 clientSocket, u32 ipAddress);
bool saveFileToSDCard(const char * path, void * buffer,u32 size);
bool exitRequested;
s32 serverPort;
s32 serverSocket;
};
#endif

View File

@ -37,7 +37,7 @@ struct rpl_handling {
#define FUNCTION_PATCHER_METHOD_STORE_SIZE 7 #define FUNCTION_PATCHER_METHOD_STORE_SIZE 7
#define MAXIMUM_PLUGIN_PATH_NAME_LENGTH 256 #define MAXIMUM_PLUGIN_PATH_NAME_LENGTH 256
#define MAXIMUM_PLUGIN_NAME_LENGTH 51 #define MAXIMUM_PLUGIN_NAME_LENGTH 51
#define MAXIMUM_FUNCTION_NAME_LENGTH 51 #define MAXIMUM_FUNCTION_NAME_LENGTH 61
struct replacement_data_function_t { struct replacement_data_function_t {
u32 replaceAddr; /* [needs to be filled] Address of our replacement function */ u32 replaceAddr; /* [needs to be filled] Address of our replacement function */

View File

@ -105,6 +105,9 @@ void PluginLoader::loadAndLinkPlugins(std::vector<PluginInformation *> pluginInf
copyPluginDataIntoGlobalStruct(loadedPlugins); copyPluginDataIntoGlobalStruct(loadedPlugins);
clearPluginData(loadedPlugins); clearPluginData(loadedPlugins);
DCFlushRange((void*)this->startAddress,(u32)this->endAddress - (u32)this->startAddress);
ICInvalidateRange((void*)this->startAddress,(u32)this->endAddress - (u32)this->startAddress);
} }
void PluginLoader::clearPluginData(std::vector<PluginData *> pluginData) { void PluginLoader::clearPluginData(std::vector<PluginData *> pluginData) {
@ -392,11 +395,17 @@ void PluginLoader::copyPluginDataIntoGlobalStruct(std::vector<PluginData *> plug
for(size_t j = 0; j < function_data_list.size(); j++) { for(size_t j = 0; j < function_data_list.size(); j++) {
replacement_data_function_t * function_data = &plugin_data->functions[j]; replacement_data_function_t * function_data = &plugin_data->functions[j];
FunctionData * cur_function = function_data_list[j]; FunctionData * cur_function = function_data_list[j];
if(strlen(cur_function->getName().c_str()) > MAXIMUM_FUNCTION_NAME_LENGTH-1){
DEBUG_FUNCTION_LINE("Couldn not add function \"%s\" for plugin \"%s\" function name is too long.\n",cur_function->getName().c_str(),plugin_data->plugin_name);
continue;
}
DEBUG_FUNCTION_LINE("Adding function \"%s\" for plugin \"%s\"\n",cur_function->getName().c_str(),plugin_data->plugin_name); DEBUG_FUNCTION_LINE("Adding function \"%s\" for plugin \"%s\"\n",cur_function->getName().c_str(),plugin_data->plugin_name);
//TODO: Warning/Error if string is too long. //TODO: Warning/Error if string is too long.
strncpy(function_data->function_name,cur_function->getName().c_str(),MAXIMUM_FUNCTION_NAME_LENGTH-1); strncpy(function_data->function_name,cur_function->getName().c_str(),MAXIMUM_FUNCTION_NAME_LENGTH-1);
function_data->library = cur_function->getLibrary(); function_data->library = cur_function->getLibrary();
@ -424,4 +433,6 @@ void PluginLoader::copyPluginDataIntoGlobalStruct(std::vector<PluginData *> plug
plugin_index++; plugin_index++;
gbl_replacement_data.number_used_plugins++; gbl_replacement_data.number_used_plugins++;
} }
DCFlushRange((void*)&gbl_replacement_data,sizeof(gbl_replacement_data));
ICInvalidateRange((void*)&gbl_replacement_data,sizeof(gbl_replacement_data));
} }

View File

@ -107,6 +107,10 @@ public:
return getTotalSpace() - getAvailableSpace(); return getTotalSpace() - getAvailableSpace();
} }
void resetPluginLoader() {
this->currentStoreAddress = endAddress;
}
private: private:
PluginLoader(void * startAddress, void * endAddress) { PluginLoader(void * startAddress, void * endAddress) {
// TODO: Check if endAddress > startAddress. // TODO: Check if endAddress > startAddress.

View File

@ -2,6 +2,7 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <malloc.h> #include <malloc.h>
@ -9,40 +10,11 @@
#include <wups.h> #include <wups.h>
#include "utils.h" #include "utils.h"
#include <utils/logger.h>
#include "common/common.h" #include "common/common.h"
#include "common/retain_vars.h" #include "common/retain_vars.h"
#include "myutils/overlay_helper.h" #include "myutils/overlay_helper.h"
// https://gist.github.com/ccbrown/9722406
void dumpHex(const void* data, size_t size) {
char ascii[17];
size_t i, j;
ascii[16] = '\0';
for (i = 0; i < size; ++i) {
log_printf("%02X ", ((unsigned char*)data)[i]);
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
ascii[i % 16] = ((unsigned char*)data)[i];
} else {
ascii[i % 16] = '.';
}
if ((i+1) % 8 == 0 || i+1 == size) {
log_printf(" ");
if ((i+1) % 16 == 0) {
log_printf("| %s \n", ascii);
} else if (i+1 == size) {
ascii[(i+1) % 16] = '\0';
if ((i+1) % 16 <= 8) {
log_printf(" ");
}
for (j = (i+1) % 16; j < 16; ++j) {
log_printf(" ");
}
log_printf("| %s \n", ascii);
}
}
}
}
void CallHook(wups_loader_hook_type_t hook_type) { void CallHook(wups_loader_hook_type_t hook_type) {
CallHookEx(hook_type,-1); CallHookEx(hook_type,-1);
} }

View File

@ -7,8 +7,8 @@ extern "C" {
#endif #endif
#include <wups.h> #include <wups.h>
#include <stddef.h>
void dumpHex(const void* data, size_t size);
void CallHook(wups_loader_hook_type_t hook_type); void CallHook(wups_loader_hook_type_t hook_type);
void CallHookEx(wups_loader_hook_type_t hook_type, s32 plugin_index_needed); void CallHookEx(wups_loader_hook_type_t hook_type, s32 plugin_index_needed);

BIN
tools/wiiload.exe Normal file

Binary file not shown.