Add hash and size check for known unlaunch installer versions

Also check the installer to be valid at startup, rather than once the install process has started, so that the install option can be properly grayed out
This commit is contained in:
Edoardo Lolletti 2024-04-25 22:42:38 +02:00
parent ef82ae14d5
commit 71c1c21234
11 changed files with 214 additions and 22 deletions

View File

@ -31,7 +31,7 @@ CFLAGS := -g -Wall -O2\
$(ARCH)
CFLAGS += $(INCLUDE) -DARM9
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++23
ASFLAGS := -g $(ARCH) -march=armv5te -mtune=arm946e-s

View File

@ -56,7 +56,7 @@ static int mainMenu(int cursor)
"\nlow, of \x1B[41mbricking\x1B[47m your system"
"\nand should be done with caution!\n");
iprintf("\n\t \x1B[46mhttps://dsi.cfw.guide\x1B[47m\n");
iprintf("\x1b[23;0HJeff - 2018-2019");
iprintf("\x1b[21;0HJeff - 2018-2019");
iprintf("\x1b[22;0HPk11 - 2022-2023");
iprintf("\x1b[23;0Hedo9300 - 2024");
@ -68,7 +68,6 @@ static int mainMenu(int cursor)
sprintf(uninstallStr, "\x1B[%02omUninstall unlaunch", unlaunchFound ? 047 : 037);
sprintf(installStr, "\x1B[%02omInstall unlaunch", unlaunchInstallerFound ? 047 : 037);
addMenuItem(m, uninstallStr, NULL, 0);
addMenuItem(m, uninstallStr, NULL, 0);
addMenuItem(m, installStr, NULL, 0);
addMenuItem(m, "\x1B[47mExit", NULL, 0);
@ -139,7 +138,7 @@ int main(int argc, char **argv)
return 0;
}
unlaunchInstallerFound = fileExists("sd:/unlaunch.dsi");
unlaunchInstallerFound = loadUnlaunchInstaller("sd:/unlaunch.dsi");
if (!unlaunchInstallerFound)
{
messageBox("\x1B[41mWARNING:\x1B[47m unlaunch.dsi was not found in\n"

View File

@ -5,6 +5,10 @@
#include <fat.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
extern volatile bool programEnd;
extern bool charging;
extern u8 batteryLevel;
@ -14,4 +18,8 @@ extern PrintConsole bottomScreen;
void clearScreen(PrintConsole* screen);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -3,6 +3,10 @@
#include <nds/ndstypes.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ITEMS_PER_PAGE 20
typedef struct {
@ -34,4 +38,8 @@ void printMenu(Menu* m);
bool moveCursor(Menu* m);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -13,7 +13,7 @@ void keyWait(u32 key)
}
}
bool choiceBox(char* message)
bool choiceBox(const char* message)
{
const int choiceRow = 10;
int cursor = 0;
@ -53,7 +53,7 @@ bool choiceBox(char* message)
return (cursor == 0)? YES: NO;
}
bool choicePrint(char* message)
bool choicePrint(const char* message)
{
bool choice = NO;
@ -87,7 +87,7 @@ bool choicePrint(char* message)
const static u16 keys[] = {KEY_UP, KEY_DOWN, KEY_RIGHT, KEY_LEFT, KEY_A, KEY_B, KEY_X, KEY_Y};
const static char *keysLabels[] = {"\x18", "\x19", "\x1A", "\x1B", "<A>", "<B>", "<X>", "<Y>"};
bool randomConfirmBox(char* message)
bool randomConfirmBox(const char* message)
{
const int choiceRow = 10;
int sequencePosition = 0;
@ -137,13 +137,13 @@ bool randomConfirmBox(char* message)
return sequencePosition == sizeof(sequence);
}
void messageBox(char* message)
void messageBox(const char* message)
{
clearScreen(&bottomScreen);
messagePrint(message);
}
void messagePrint(char* message)
void messagePrint(const char* message)
{
iprintf("%s\n", message);
iprintf("\nOkay - [A]\n");

View File

@ -3,16 +3,24 @@
#include <nds/ndstypes.h>
#ifdef __cplusplus
extern "C" {
#endif
enum {
YES = true,
NO = false
};
void keyWait(u32 key);
bool choiceBox(char* message);
bool choicePrint(char* message);
bool randomConfirmBox(char* message);
void messageBox(char* message);
void messagePrint(char* message);
bool choiceBox(const char* message);
bool choicePrint(const char* message);
bool randomConfirmBox(const char* message);
void messageBox(const char* message);
void messagePrint(const char* message);
#ifdef __cplusplus
}
#endif
#endif

48
arm9/src/sha1digest.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef SHA1DIGEST_H
#define SHA1DIGEST_H
#include <array>
#include <string_view>
static constexpr int SHA1_LEN = 20;
class Sha1Digest {
public:
friend constexpr inline bool operator==(const Sha1Digest& lhs, const Sha1Digest& rhs);
Sha1Digest() {}
constexpr Sha1Digest(std::string_view digestString)
{
auto hex2int = [](char ch) {
if (ch >= '0' && ch <= '9') return ch - '0';
if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
return -1;
};
for(size_t i = 0; i < SHA1_LEN; ++i) {
auto c1 = digestString[i * 2];
auto c2 = digestString[(i * 2) + 1];
digest[i] = hex2int(c1) << 4 | hex2int(c2);
}
}
auto data()
{
return digest.data();
}
private:
std::array<uint8_t, SHA1_LEN> digest{};
};
constexpr inline bool operator==(const Sha1Digest& lhs, const Sha1Digest& rhs)
{
return lhs.digest == rhs.digest;
}
consteval Sha1Digest operator ""_sha1(const char* args, size_t len) {
return Sha1Digest{std::string_view{args, len}};
}
#endif

View File

@ -2,6 +2,7 @@
#include "main.h"
#include "message.h"
#include <errno.h>
#include <nds/sha1.h>
#include <dirent.h>
#define TITLE_LIMIT 39
@ -175,6 +176,40 @@ bool writeToFile(FILE* fd, const char* buffer, size_t size)
return toWrite == 0;
}
bool calculateFileSha1(FILE* f, void* digest)
{
fseek(f, 0, SEEK_SET);
swiSHA1context_t ctx;
ctx.sha_block = 0; //this is weird but it has to be done
swiSHA1Init(&ctx);
char buffer[512];
size_t n = 0;
while ((n = fread(buffer, sizeof(char), sizeof(buffer), f)) > 0)
{
swiSHA1Update(&ctx, buffer, n);
}
if (ferror(f) || !feof(f))
{
return false;
}
swiSHA1Final(digest, &ctx);
return true;
}
bool calculateFileSha1Path(const char* path, void* digest)
{
FILE* targetFile = fopen(path, "rb");
if (!targetFile)
{
return false;
}
bool res = calculateFileSha1(targetFile, digest);
fclose(targetFile);
return res;
}
bool safeCreateDir(const char* path)
{
if (((mkdir(path, 0777) == 0) || errno == EEXIST))

View File

@ -4,6 +4,10 @@
#include <nds/ndstypes.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
//Files
bool fileExists(char const* path);
int copyFile(char const* src, char const* dst);
@ -12,8 +16,14 @@ unsigned long long getFileSize(FILE* f);
unsigned long long getFileSizePath(char const* path);
bool toggleFileReadOnly(const char* path, bool readOnly);
bool writeToFile(FILE* fd, const char* buffer, size_t size);
bool calculateFileSha1(FILE* f, void* digest);
bool calculateFileSha1Path(const char* path, void* digest);
//Directories
bool safeCreateDir(const char* path);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,6 +1,10 @@
#include "message.h"
#include "sha1digest.h"
#include "storage.h"
#include "unlaunch.h"
#include <algorithm>
#include <array>
#include <nds/sha1.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
@ -9,6 +13,37 @@ static char unlaunchInstallerBuffer[0x30000];
static const char* hnaaTmdPath = "nand:/title/00030017/484e4141/content/title.tmd";
static const char* hnaaBackupTmdPath = "nand:/title/00030017/484e4141/content/title.tmd.bak";
enum UNLAUNCH_VERSION {
v1_8,
v1_9,
v2_0,
INVALID,
};
UNLAUNCH_VERSION installerVersion{INVALID};
size_t unlaunchInstallerSize{};
constexpr std::array knownUnlaunchHashes{
/*"9e6a8d95062533dfc422362f99ff3e24e7de9920"_sha1, // v0.8: blacklisted, doesn't like this install method*/
/*"fb0d0ffebda67b786f608bf5cbcb2efee6ab42bb"_sha1, // v0.9: blacklisted, doesn't like this install method*/
/*"d710ff585e321082b33456dd4e0568200c9adcc7"_sha1, // v1.0: blacklisted, doesn't like this install method*/
/*"4f3e455e0a752d35a219a3ff10ba14a6c98bff13"_sha1, // v1.1: blacklisted, doesn't like this install method*/
/*"25db1a47ba84748f911d9f4357bfc417533121c7"_sha1, // v1.2: blacklisted, doesn't like this install method*/
/*"068f1d56da02bb4f93fb76d7874e14010c7e7a3d"_sha1, // v1.3: blacklisted, doesn't like this install method*/
/*"43197370de74d302ef7c4420059c3ca7d50c4f3d"_sha1, // v1.4: blacklisted, doesn't like this install method*/
/*"0525b28cc59b6f7fc00ad592aebadd7257bf7efb"_sha1, // v1.5: blacklisted, doesn't like this install method*/
/*"9470a51fde188235052b119f6bfabf6689cb2343"_sha1, // v1.6: blacklisted, doesn't like this install method*/
/*"672c11eb535b97b0d32ff580d314a2ad6411d5fe"_sha1, // v1.7: blacklisted, doesn't like this install method*/
"b76c2b1722e769c6c0b4b3d4bc73250e41993229"_sha1, // v1.8
"f3eb41cba136a3477523155f8b05df14917c55f4"_sha1, // v1.9
"15f4a36251d1408d71114019b2825fe8f5b4c8cc"_sha1, // v2.0
};
bool isValidUnlaunchInstallerSize(size_t size)
{
return size == 163320 /*1.8*/ || size == 196088 /*1.9*/;
}
bool isLauncherTmdPatched(const char* path)
{
FILE* launcherTmd = fopen(path, "rb");
@ -148,7 +183,10 @@ static bool writeUnlaunchTmd(const char* path)
return false;
}
if(!writeToFile(targetTmd, unlaunchInstallerBuffer, sizeof(unlaunchInstallerBuffer)))
Sha1Digest expectedDigest, actualDigest;
swiSHA1Calc(expectedDigest.data(), unlaunchInstallerBuffer, unlaunchInstallerSize + 520);
if(!writeToFile(targetTmd, unlaunchInstallerBuffer, unlaunchInstallerSize + 520))
{
fclose(targetTmd);
remove(path);
@ -156,6 +194,14 @@ static bool writeUnlaunchTmd(const char* path)
return false;
}
fclose(targetTmd);
calculateFileSha1Path(path, actualDigest.data());
if(expectedDigest != actualDigest)
{
remove(path);
messageBox("\x1B[31mError:\x1B[33m Unlaunch tmd was not properly written\n");
return false;
}
return true;
}
@ -267,23 +313,23 @@ static bool installUnlaunchProtoConsole(void)
return true;
}
bool readUnlaunchInstaller(void)
bool readUnlaunchInstaller(const char* path)
{
FILE* unlaunchInstaller = fopen("sd:/unlaunch.dsi", "rb");
FILE* unlaunchInstaller = fopen(path, "rb");
if (!unlaunchInstaller)
{
messageBox("\x1B[31mError:\x1B[33m Failed to open unlaunch installer\n(sd:/unlaunch.dsi)\n");
return false;
}
int toRead = sizeof(unlaunchInstallerBuffer) - 520;
size_t installerFilesize = getFileSize(unlaunchInstaller);
if(installerFilesize != toRead)
unlaunchInstallerSize = getFileSize(unlaunchInstaller);
if(isValidUnlaunchInstallerSize(unlaunchInstallerSize))
{
messageBox("\x1B[31mError:\x1B[33m Unlaunch installer wrong file size\n");
return false;
}
int toRead = unlaunchInstallerSize;
char* buff = unlaunchInstallerBuffer;
// Pad the installer with 520 bytes, those being the size of a valid tmd
buff += 520;
@ -301,7 +347,22 @@ bool readUnlaunchInstaller(void)
return false;
}
fclose(unlaunchInstaller);
fclose(unlaunchInstaller);
return true;
}
bool verifyUnlaunchInstaller(void)
{
Sha1Digest digest;
swiSHA1Calc(digest.data(), unlaunchInstallerBuffer + 520, unlaunchInstallerSize);
auto it = std::ranges::find(knownUnlaunchHashes, digest);
if(it == knownUnlaunchHashes.end())
{
messageBox("\x1B[31mError:\x1B[33m Provided unlaunch installer has an unknown hash\n");
return false;
}
auto idx = std::distance(knownUnlaunchHashes.begin(), it);
installerVersion = static_cast<UNLAUNCH_VERSION>(idx);
return true;
}
@ -311,9 +372,14 @@ bool patchUnlaunchInstaller(void)
return true;
}
bool loadUnlaunchInstaller(const char* path)
{
return readUnlaunchInstaller(path) && verifyUnlaunchInstaller();
}
bool installUnlaunch(bool retailConsole, const char* retailLauncherTmdPath)
{
if (!readUnlaunchInstaller() || !patchUnlaunchInstaller())
if (installerVersion == INVALID || !patchUnlaunchInstaller())
return false;
// Treat protos differently

View File

@ -2,9 +2,19 @@
#define UNLAUNCH_H
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
bool uninstallUnlaunch(bool notProto, const char* retailLauncherTmdPath);
bool installUnlaunch(bool retailConsole, const char* retailLauncherTmdPath);
bool isLauncherTmdPatched(const char* path);
bool loadUnlaunchInstaller(const char* path);
#ifdef __cplusplus
}
#endif
#endif