Automatically save a OTP/SEEPROM dump to the sd card

This commit is contained in:
Maschell 2024-05-08 13:49:45 +02:00
parent cc04c1ae96
commit 3cd80d8cf3
12 changed files with 543 additions and 44 deletions

View File

@ -5,5 +5,6 @@ COPY --from=ghcr.io/wiiu-env/libnotifications:20240426 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/librpxloader:20240425 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libcurlwrapper:20240505 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libsdutils:20230621 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libmocha:20231127 /artifacts $DEVKITPRO
WORKDIR project

View File

@ -48,13 +48,13 @@ CXXFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
endif
LIBS := -lcurlwrapper -lnotifications -lrpxloader -lsdutils -lwups -lwut
LIBS := -lmocha -lcurlwrapper -lnotifications -lrpxloader -lsdutils -lwups -lwut
#-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level
# containing include and lib
#-------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(WUPS_ROOT) $(WUMS_ROOT) $(WUT_ROOT)
LIBDIRS := $(PORTLIBS) $(WUPS_ROOT) $(WUMS_ROOT) $(WUT_ROOT) $(WUT_ROOT)/usr
#-------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional

View File

@ -36,6 +36,7 @@ For building you need:
- [librpxloader](https://github.com/wiiu-env/librpxloader)
- [libcurlwrapper](https://github.com/wiiu-env/libcurlwrapper)
- [libsdutils](https://github.com/wiiu-env/libsdutils)
- [libmocha](https://github.com/wiiu-env/libmocha)
## Building using the Dockerfile

View File

@ -6,4 +6,8 @@
#define AROMA_UPDATER_OLD_PATH_FULL SD_CARD_PATH AROMA_UPDATER_OLD_PATH
#define AROMA_UPDATER_NEW_PATH_FULL SD_CARD_PATH AROMA_UPDATER_NEW_PATH
#define AROMA_UPDATER_NEW_DIRECTORY_FULL SD_CARD_PATH AROMA_UPDATER_NEW_DIRECTORY
#define AROMA_UPDATER_LAST_UPDATE_URL "https://aroma.foryour.cafe/api/latest_version"
#define AROMA_UPDATER_LAST_UPDATE_URL "https://aroma.foryour.cafe/api/latest_version"
#define BACKUPS_DIRECTORY "wiiu/backups"
#define BACKUPS_DIRECTORY_FULL SD_CARD_PATH BACKUPS_DIRECTORY

View File

@ -1,12 +1,13 @@
#include "main.h"
#include "Hints.h"
#include "UpdaterCheck.h"
#include "common.h"
#include "utils/DownloadUtils.h"
#include "utils/LatestVersion.h"
#include "utils/config.h"
#include "utils/utils.h"
#include <coreinit/title.h>
#include <malloc.h>
#include <mocha/mocha.h>
#include <nn/spm.h>
#include <notifications/notifications.h>
#include <rpxloader/rpxloader.h>
@ -72,44 +73,6 @@ bool InitConfigValuesFromStorage() {
return result;
}
/*
* Migrates wiiu/apps/AromaUpdater.wuhb to wiiu/apps/AromaUpdater/AromaUpdater.wuhb
*/
void MigrateAromaUpdater() {
struct stat st {};
bool oldExists = false;
bool newExists = false;
if (stat(AROMA_UPDATER_NEW_PATH_FULL, &st) >= 0 && S_ISREG(st.st_mode)) {
DEBUG_FUNCTION_LINE_VERBOSE("\"%s\" exists", AROMA_UPDATER_NEW_PATH_FULL);
newExists = true;
}
st = {};
if (stat(AROMA_UPDATER_OLD_PATH_FULL, &st) >= 0 && S_ISREG(st.st_mode)) {
DEBUG_FUNCTION_LINE_VERBOSE("\"%s\" exists", AROMA_UPDATER_OLD_PATH_FULL);
oldExists = true;
}
if (newExists) {
if (oldExists) {
if (remove(AROMA_UPDATER_OLD_PATH_FULL) < 0) {
DEBUG_FUNCTION_LINE_WARN("Failed to remove old Aroma Updater: %d", errno);
}
} else {
DEBUG_FUNCTION_LINE_VERBOSE("Only new AromaUpdater.wuhb exists");
}
return;
} else if (oldExists) {
if (stat(AROMA_UPDATER_NEW_DIRECTORY_FULL, &st) < 0 || !S_ISDIR(st.st_mode)) {
if (mkdir(AROMA_UPDATER_NEW_DIRECTORY_FULL, 0777) < 0) {
DEBUG_FUNCTION_LINE_WARN("Failed to create: \"%s\"", AROMA_UPDATER_NEW_DIRECTORY_FULL);
}
}
if (rename(AROMA_UPDATER_OLD_PATH_FULL, AROMA_UPDATER_NEW_PATH_FULL) < 0) {
DEBUG_FUNCTION_LINE_WARN("Failed to move Aroma Updater to new path");
}
} else {
DEBUG_FUNCTION_LINE_VERBOSE("No AromaUpdater.wuhb exists");
}
}
INITIALIZE_PLUGIN() {
initLogging();
@ -123,11 +86,17 @@ INITIALIZE_PLUGIN() {
DEBUG_FUNCTION_LINE_ERR("SDUtils_InitLibrary failed");
}
if (Mocha_InitLibrary() != MOCHA_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Mocha_InitLibrary failed");
}
InitConfigValuesFromStorage();
InitConfigMenu();
MigrateAromaUpdater();
Utils::MigrateAromaUpdater();
Utils::DumpOTPAndSeeprom();
}
ON_APPLICATION_START() {

View File

@ -1,5 +1,5 @@
#pragma once
#include "version.h"
#define PLUGIN_VERSION "v0.1.4"
#define PLUGIN_VERSION "v0.1.5"
#define PLUGIN_VERSION_FULL PLUGIN_VERSION PLUGIN_VERSION_EXTRA

173
src/utils/CFile.cpp Normal file
View File

@ -0,0 +1,173 @@
#include "CFile.hpp"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
CFile::CFile() {
iFd = -1;
mem_file = nullptr;
filesize = 0;
pos = 0;
}
CFile::CFile(const std::string &filepath, eOpenTypes mode) {
iFd = -1;
this->open(filepath, mode);
}
CFile::CFile(const uint8_t *mem, int32_t size) {
iFd = -1;
this->open(mem, size);
}
CFile::~CFile() {
this->close();
}
int32_t CFile::open(const std::string &filepath, eOpenTypes mode) {
this->close();
int32_t openMode = 0;
// This depend on the devoptab implementation.
// see https://github.com/devkitPro/wut/blob/master/libraries/wutdevoptab/devoptab_fs_open.c#L21 fpr reference
switch (mode) {
default:
case ReadOnly: // file must exist
openMode = O_RDONLY;
break;
case WriteOnly: // file will be created / zerod
openMode = O_TRUNC | O_CREAT | O_WRONLY;
break;
case ReadWrite: // file must exist
openMode = O_RDWR;
break;
case Append: // append to file, file will be created if missing. write only
openMode = O_CREAT | O_APPEND | O_WRONLY;
break;
}
//! Using fopen works only on the first launch as expected
//! on the second launch it causes issues because we don't overwrite
//! the .data sections which is needed for a normal application to re-init
//! this will be added with launching as RPX
iFd = ::open(filepath.c_str(), openMode);
if (iFd < 0)
return iFd;
filesize = ::lseek(iFd, 0, SEEK_END);
::lseek(iFd, 0, SEEK_SET);
return 0;
}
int32_t CFile::open(const uint8_t *mem, int32_t size) {
this->close();
mem_file = mem;
filesize = size;
return 0;
}
void CFile::close() {
if (iFd >= 0)
::close(iFd);
iFd = -1;
mem_file = NULL;
filesize = 0;
pos = 0;
}
int32_t CFile::read(uint8_t *ptr, size_t size) {
if (iFd >= 0) {
int32_t ret = ::read(iFd, ptr, size);
if (ret > 0)
pos += ret;
return ret;
}
int32_t readsize = size;
if (readsize > (int64_t) (filesize - pos))
readsize = filesize - pos;
if (readsize <= 0)
return readsize;
if (mem_file != NULL) {
memcpy(ptr, mem_file + pos, readsize);
pos += readsize;
return readsize;
}
return -1;
}
int32_t CFile::write(const uint8_t *ptr, size_t size) {
if (iFd >= 0) {
size_t done = 0;
while (done < size) {
int32_t ret = ::write(iFd, ptr, size - done);
if (ret <= 0)
return ret;
ptr += ret;
done += ret;
pos += ret;
}
return done;
}
return -1;
}
int32_t CFile::seek(long int offset, int32_t origin) {
int32_t ret = 0;
int64_t newPos = pos;
if (origin == SEEK_SET) {
newPos = offset;
} else if (origin == SEEK_CUR) {
newPos += offset;
} else if (origin == SEEK_END) {
newPos = filesize + offset;
}
if (newPos < 0) {
pos = 0;
} else {
pos = newPos;
}
if (iFd >= 0)
ret = ::lseek(iFd, pos, SEEK_SET);
if (mem_file != NULL) {
if (pos > filesize) {
pos = filesize;
}
}
return ret;
}
int32_t CFile::fwrite(const char *format, ...) {
char tmp[512];
tmp[0] = 0;
int32_t result = -1;
va_list va;
va_start(va, format);
if ((vsprintf(tmp, format, va) >= 0)) {
result = this->write((uint8_t *) tmp, strlen(tmp));
}
va_end(va);
return result;
}

71
src/utils/CFile.hpp Normal file
View File

@ -0,0 +1,71 @@
#ifndef CFILE_HPP_
#define CFILE_HPP_
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <unistd.h>
#include <wut_types.h>
class CFile {
public:
enum eOpenTypes {
ReadOnly,
WriteOnly,
ReadWrite,
Append
};
CFile();
CFile(const std::string &filepath, eOpenTypes mode);
CFile(const uint8_t *memory, int32_t memsize);
virtual ~CFile();
int32_t open(const std::string &filepath, eOpenTypes mode);
int32_t open(const uint8_t *memory, int32_t memsize);
BOOL isOpen() const {
if (iFd >= 0)
return true;
if (mem_file)
return true;
return false;
}
void close();
int32_t read(uint8_t *ptr, size_t size);
int32_t write(const uint8_t *ptr, size_t size);
int32_t fwrite(const char *format, ...);
int32_t seek(long int offset, int32_t origin);
uint64_t tell() {
return pos;
};
uint64_t size() {
return filesize;
};
void rewind() {
this->seek(0, SEEK_SET);
};
protected:
int32_t iFd;
const uint8_t *mem_file{};
uint64_t filesize{};
uint64_t pos{};
};
#endif

127
src/utils/FSUtils.cpp Normal file
View File

@ -0,0 +1,127 @@
#include "FSUtils.h"
#include "CFile.hpp"
#include "logger.h"
#include <fcntl.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int32_t FSUtils::CheckFile(const char *filepath) {
if (!filepath)
return 0;
struct stat filestat {};
char dirnoslash[strlen(filepath) + 2];
snprintf(dirnoslash, sizeof(dirnoslash), "%s", filepath);
while (dirnoslash[strlen(dirnoslash) - 1] == '/')
dirnoslash[strlen(dirnoslash) - 1] = '\0';
char *notRoot = strrchr(dirnoslash, '/');
if (!notRoot) {
strcat(dirnoslash, "/");
}
if (stat(dirnoslash, &filestat) == 0)
return 1;
return 0;
}
int32_t FSUtils::CreateSubfolder(const char *fullpath) {
if (!fullpath)
return 0;
int32_t result = 0;
char dirnoslash[strlen(fullpath) + 1];
strcpy(dirnoslash, fullpath);
int32_t pos = strlen(dirnoslash) - 1;
while (dirnoslash[pos] == '/') {
dirnoslash[pos] = '\0';
pos--;
}
if (CheckFile(dirnoslash)) {
return 1;
} else {
char parentpath[strlen(dirnoslash) + 2];
strcpy(parentpath, dirnoslash);
char *ptr = strrchr(parentpath, '/');
if (!ptr) {
//!Device root directory (must be with '/')
strcat(parentpath, "/");
struct stat filestat;
if (stat(parentpath, &filestat) == 0)
return 1;
return 0;
}
ptr++;
ptr[0] = '\0';
result = CreateSubfolder(parentpath);
}
if (!result)
return 0;
if (mkdir(dirnoslash, 0777) == -1) {
return 0;
}
return 1;
}
int32_t FSUtils::saveBufferToFile(const char *path, void *buffer, uint32_t size) {
CFile file(path, CFile::WriteOnly);
if (!file.isOpen()) {
DEBUG_FUNCTION_LINE_ERR("Failed to open %s\n", path);
return 0;
}
int32_t written = file.write((const uint8_t *) buffer, size);
file.close();
return written;
}
bool FSUtils::copyFile(const std::string &in, const std::string &out) {
// Using C++ buffers is **really** slow. Copying in 1023 byte chunks.
// Let's do it the old way.
size_t size;
int source = open(in.c_str(), O_RDONLY, 0);
if (source < 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to open source %s", in.c_str());
return false;
}
int dest = open(out.c_str(), 0x602, 0644);
if (dest < 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to open dest %s", out.c_str());
close(source);
return false;
}
auto bufferSize = 128 * 1024;
char *buf = (char *) malloc(bufferSize);
if (buf == nullptr) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc buffer");
return false;
}
while ((size = read(source, buf, bufferSize)) > 0) {
write(dest, buf, size);
}
free(buf);
close(source);
close(dest);
return true;
}

16
src/utils/FSUtils.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include <string>
#include <wut_types.h>
class FSUtils {
public:
//! todo: C++ class
static int32_t CreateSubfolder(const char *fullpath);
static int32_t CheckFile(const char *filepath);
static int32_t saveBufferToFile(const char *path, void *buffer, uint32_t size);
static bool copyFile(const std::string &in, const std::string &out);
};

116
src/utils/utils.cpp Normal file
View File

@ -0,0 +1,116 @@
#include "utils.h"
#include "FSUtils.h"
#include "common.h"
#include "logger.h"
#include <coreinit/mcp.h>
#include <mocha/mocha.h>
#include <string>
#include <sys/stat.h>
bool Utils::GetSerialId(std::string &serialID) {
bool result = false;
alignas(0x40) MCPSysProdSettings settings{};
auto handle = MCP_Open();
if (handle >= 0) {
if (MCP_GetSysProdSettings(handle, &settings) == 0) {
serialID = std::string(settings.code_id) + settings.serial_id;
result = true;
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to get SerialId");
}
MCP_Close(handle);
} else {
DEBUG_FUNCTION_LINE_ERR("MCP_Open failed");
}
return result;
}
static inline bool existsAsFile(std::string_view path) {
struct stat st {};
if (stat(path.data(), &st) >= 0 && S_ISREG(st.st_mode)) {
DEBUG_FUNCTION_LINE_VERBOSE("\"%s\" exists", path.data());
return true;
} else {
DEBUG_FUNCTION_LINE_VERBOSE("\"%s\" doesn't exists", path.data());
}
return false;
}
/*
* Migrates wiiu/apps/AromaUpdater.wuhb to wiiu/apps/AromaUpdater/AromaUpdater.wuhb
*/
void Utils::MigrateAromaUpdater() {
bool newExists = existsAsFile(AROMA_UPDATER_NEW_PATH_FULL);
bool oldExists = existsAsFile(AROMA_UPDATER_OLD_PATH_FULL);
if (newExists) {
if (oldExists) {
if (remove(AROMA_UPDATER_OLD_PATH_FULL) < 0) {
DEBUG_FUNCTION_LINE_WARN("Failed to remove old Aroma Updater: %d", errno);
}
} else {
DEBUG_FUNCTION_LINE_VERBOSE("Only new AromaUpdater.wuhb exists");
}
return;
} else if (oldExists) {
if (!FSUtils::CreateSubfolder(AROMA_UPDATER_NEW_DIRECTORY_FULL)) {
DEBUG_FUNCTION_LINE_WARN("Failed to create \"%s\"", AROMA_UPDATER_NEW_DIRECTORY_FULL);
}
if (rename(AROMA_UPDATER_OLD_PATH_FULL, AROMA_UPDATER_NEW_PATH_FULL) < 0) {
DEBUG_FUNCTION_LINE_WARN("Failed to move Aroma Updater to new path");
}
} else {
DEBUG_FUNCTION_LINE_VERBOSE("No AromaUpdater.wuhb exists");
}
}
/*
* Dumps the OTP and SEEPROM when if it's no exists.
*/
void Utils::DumpOTPAndSeeprom() {
std::string serialId;
if (!Utils::GetSerialId(serialId)) {
DEBUG_FUNCTION_LINE_WARN("Failed to get SerialId of the console, skip OTP/SEEPROM dumping");
return;
}
std::string backupPathConsole = string_format(BACKUPS_DIRECTORY_FULL "/%s", serialId.c_str());
std::string backupPathConsoleOtpPath = backupPathConsole + "/opt.bin";
std::string backupPathConsoleSeepromPath = backupPathConsole + "/seeprom.bin";
if (!FSUtils::CreateSubfolder(backupPathConsole.c_str())) {
DEBUG_FUNCTION_LINE_WARN("Failed to create \"%s\"", backupPathConsole.c_str());
}
bool seepromExists = FSUtils::CheckFile(backupPathConsoleSeepromPath.c_str());
bool optExists = FSUtils::CheckFile(backupPathConsoleOtpPath.c_str());
if (!seepromExists) {
uint8_t data[0x200] = {};
if (Mocha_SEEPROMRead(data, 0, sizeof(data)) != sizeof(data)) {
DEBUG_FUNCTION_LINE_WARN("Failed to read SEEPROM");
} else {
if (FSUtils::saveBufferToFile(backupPathConsoleSeepromPath.c_str(), (void *) data, sizeof(data)) != sizeof(data)) {
DEBUG_FUNCTION_LINE_WARN("Failed to write SEEPROM backup (\"%s\")", backupPathConsoleSeepromPath.c_str());
} else {
DEBUG_FUNCTION_LINE_INFO("Created SEEPROM backup: \"%s\"", backupPathConsoleSeepromPath.c_str());
}
}
} else {
DEBUG_FUNCTION_LINE_VERBOSE("SEEPROM backup already exists");
}
if (!optExists) {
WiiUConsoleOTP otp = {};
if (Mocha_ReadOTP(&otp) != MOCHA_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to read otp");
} else {
if (FSUtils::saveBufferToFile(backupPathConsoleOtpPath.c_str(), (void *) &otp, sizeof(otp)) != sizeof(otp)) {
DEBUG_FUNCTION_LINE_WARN("Failed to write otp backup (\"%s\")", backupPathConsoleOtpPath.c_str());
} else {
DEBUG_FUNCTION_LINE_INFO("Created OTP backup: \"%s\"", backupPathConsoleOtpPath.c_str());
}
}
} else {
DEBUG_FUNCTION_LINE_VERBOSE("OTP backup already exists");
}
}

21
src/utils/utils.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <cstdint>
#include <memory>
#include <string>
namespace Utils {
bool GetSerialId(std::string &serialID);
void MigrateAromaUpdater();
void DumpOTPAndSeeprom();
} // namespace Utils
template<typename... Args>
std::string string_format(const std::string &format, Args... args) {
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
auto size = static_cast<size_t>(size_s);
auto buf = std::make_unique<char[]>(size);
std::snprintf(buf.get(), size, format.c_str(), args...);
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
}