HWInfo recovery by offsets

This commit is contained in:
Lillian Skinner 2024-11-22 03:53:42 -05:00
parent 1ece9ae6a7
commit e476985f68
No known key found for this signature in database
8 changed files with 330 additions and 120 deletions

View File

@ -193,6 +193,7 @@ int main(int argc, char **argv)
mountMain();
mountNitroFS();
agingMode = false;
mkdir("sd:/TwlNandTool", 0777);
//
swiWaitForVBlank();

View File

@ -185,6 +185,7 @@ bool readMbr(void) {
iprintf("\n>> MBR (Master Boot Record) ");
iprintf("\n--------------------------------");
memset(sector_buf, 0, 512);
if(nand_ReadSectors(0, 1, sector_buf) == false) {
iprintf("\nCouldn't read NAND!\n");
success = false;

226
arm9/src/nand/hwinfo.c Normal file
View File

@ -0,0 +1,226 @@
#include <nds.h>
#include <fat.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <nds/arm9/nand.h>
#include "f_xy.h"
#include "twltool/dsi.h"
#include "nandio.h"
#include "sector0.h"
#include "crypto.h"
#include "hwinfo.h"
#include "../message.h"
#include "../main.h"
#include "../video.h"
#include "../storage.h"
static size_t i;
hwsFormat hwsData = {0};
bool clearHWInfoStruct() {
// Dummy values are JPN. This seems to be the usual default.
for (int i = 0; i < 0x80; i++) {
hwsData.HWS_SIG[i] = 0xFF;
}
hwsData.HWS_HEADER = 0x01000000;
hwsData.HWS_SIZE = 0x1C000000;
hwsData.HWS_LANG = 0x01000000;
hwsData.HWS_PAD = 0x00000000;
hwsData.HWS_REGION = 0x00;
strcpy(hwsData.HWS_SERIAL, "AAAMP1234567"); // You're a real one if you understand this serial.
hwsData.HWS_TID = 0x50414E48;
return true;
}
bool loadHWInfoStruct() {
return true;
}
bool recoverHWInfo(bool simple) {
success = true;
bool hwinfofound = false;
clearScreen(cSUB);
iprintf("\n>> Recover HWInfo Secure ");
iprintf("\n--------------------------------");
if (false){//nandMounted == true) {
printf("\nChecking mounted TWL_MAIN...");
if (false){//fileExists("nand:/sys/HWINFO_S.dat")) {
printf("\nFound HWInfo.");
if (copyFile("nand:/sys/HWINFO_S.dat", "sd:/HWINFO_S_BACKUP.dat") != 0) {
success = false;
printf("\nCouldn't copy HWInfo!");
} else {
printf("\nCopied okay.");
// Read the file to file_buf
// Some verification here
// Dump the thing to the struct
hwinfofound = true;
}
} else {
printf("\nCouldn't find HWInfo!");
}
}
if (simple == false) {
agingMode = true;
if (recoverHWInfoOffset(HWS_OFFSET_OTHER)) {
hwinfofound = true;
} else if (recoverHWInfoOffset(HWS_OFFSET_BOX)) {
hwinfofound = true;
} else if (recoverHWInfoOffset(HWS_OFFSET_HANDHELD)) {
hwinfofound = true;
}
agingMode = false;
}
clearScreen(cSUB);
iprintf("\n>> Recover HWInfo Secure ");
iprintf("\n--------------------------------");
if (hwinfofound == true) {
iprintf("\nEverything was ok!");
} else {
iprintf("\nFailed to find HWInfo!");
}
exitFunction();
return success;
}
/*
Let's design recoverHWInfoOffset() around edge cases- meet the TWL-CPU-X4 (prototype).
The HWInfo is made up of one "real" HWInfo at the start, then a repeating secondary HWInfo to pad out to 16kb.
40E34ABB 0E81922C 0B2B24E5 CD4864D4 \
637F0199 D385B1AA A893F65E EDADE869 |
DA66D05D 5B31A897 E4AAF339 83FDD161 | "Real" HWInfo signature
128804B7 B7BA3C06 CD5304ED 9642181C |
A57B3B8B 695E1B37 6F7F220B 019C3932 |
8D45C9AE 99550547 71E77ECD 0C442E98 |
E2ECB154 BB4D7305 AF75D324 7231B36A |
A53677B1 EB4D0102 C8F31A43 EED93758 /
01000000 1C000000 01000000 00000000 <-- Region and language info
00414141 50503241 47303538 31000000 <-- Serial number
4A414E48 <-- Launcher TID
785E7A35 9A9B3C08 B9AAE1D5 02D5CD71 <-- No idea what this entire block is!
E7CFDC89 607EC36A 7A680E45 D0B30B50
BBD36599 99731FE3 91F61DDB 8788C2C1
50B19D58 ... and so on for 0x36C bytes.
64E1D65C 2649BBAA EDF4808A 3B5830A0 \
2E0C2E9A 481A0487 E9DC32DB A49BDA8C |
901B5647 2E3473CB 7317122A 2F7ECCE3 | "Real" HWInfo signature
C880187C 0EA2C230 DFFED67A D6AC7C54 |
98676C25 4B1726FF 3EAA6EE4 39A718CA |
EA2ECF98 9B41BA5F 3E32A49F 8262DE7E |
201585B4 E4D27B32 B4D501EE 2B098032 /
01000000 1C000000 01000000 00000000 <-- Region and language info
002E2E02 2E2E022E 2E022E59 02595902 <-- Serial number (completely broken in copies)
4A414E48 <-- Launcher TID
785E7A35 9A9B3C08 B9AAE1D5 02D5CD71 <-- This weird block appears again!
E7CFDC89 607EC36A 7A680E45 D0B30B50
BBD36599 99731FE3 91F61DDB 8788C2C1
50B19D58 ... and so on for 0x36C bytes.
I have no idea what testing this DSi went through. I can't say if this is unique to the X4 or not.
Anyways, the "weird block" is repeated between every HWInfo chunk. We can check for...
- The common part of the launcher TID (0x414E48)
- The first 13 bytes of the "weird block" data following the TID
Then to quickly verify this is a HWInfo, check if the "weird block" is repeated 0x36C + size of HWInfo later.
This will work on retail as well since the "weird block" here is 0xFF padding until 16kb, so it will always repeat.
Never let down the prototype enjoyers, even if this makes the process way more annoying!
Oh also here's another edge case!
The factory HWInfo Secure created by PRE_IMPORT will be entirely 0xFF.
It will only exist in units that did not leave the factory. This is less common, but there are hundreds of those DSis out there.
If you find 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF then do not proceed to recoverHWInfoDeep().
*/
bool recoverHWInfoOffset(int address) {
success = false;
clearScreen(cSUB);
iprintf("\n>> Recover HWInfo Secure Offset ");
iprintf("\n--------------------------------");
memset(sector_buf, 0, 512);
good_nandio_read(address + 0x91, 32, file_buf, true);
// 0x414E48
printf("\n ");
for (i = 0; i < 32; i++) {
printf("%02X", file_buf[i]);
if ((i + 1) % 2 == 0) {
printf(" ");
}
if ((i - 31) % 8 == 0 && i != 32) {
printf("\n ");
}
}
if (file_buf[16] == 0x41 && file_buf[17] == 0x4E && file_buf[18] == 0x48) {
success = true;
printf("\n\nLauncher TID found!");
good_nandio_read(address + 0x400 + 0x91, 32, file_buf2, true);
for (int i = 19; i < 32; i++) {
if (file_buf[i] != file_buf2[i]) {
success = false;
break;
}
}
if (success == true) {
iprintf("\nSecondary data matches!\nClearing buffer...");
memset(sector_buf, 0, 0xA4);
iprintf("\nLoading HWInfo...");
good_nandio_read(address, 0xA4, file_buf, true);
memcpy(&hwsData, file_buf, 0xA4);
success = saveHWInfoSDMC();
clearScreen(cSUB);
iprintf("\n>> Recover HWInfo Secure Offset ");
iprintf("\n--------------------------------");
if (success == true) {
iprintf("\nRecovery was ok!");
} else {
iprintf("\nFailed to back up HWInfo!");
}
}
// // Verify
// fopen fwrite fclose
}
exitFunction();
return success;
}
bool saveHWInfoSDMC(void) {
success = true;
clearScreen(cSUB);
iprintf("\n>> Save HWInfo Secure to SDMC ");
iprintf("\n--------------------------------");
// I need to do region checking (world/korea/china)
printf("\nRemoving old HWInfo...");
remove(HWS_PATH_SD);
printf("\nWriting HWInfo...");
FILE *file = fopen(HWS_PATH_SD, "wb");
if(file) {
fwrite(sector_buf, 1, 0xA4, file);
fclose(file);
iprintf("\nFile written.");
} else {
success = false;
iprintf("\nFile failed to open!");
}
exitFunction();
return success;
}

79
arm9/src/nand/hwinfo.h Normal file
View File

@ -0,0 +1,79 @@
#include <nds.h>
#include <fat.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <nds/arm9/nand.h>
#include "f_xy.h"
#include "twltool/dsi.h"
#include "nandio.h"
#include "sector0.h"
#include "crypto.h"
#include "../message.h"
#include "../main.h"
#include "../video.h"
// Language code
// Differences in display text between Europe and North America are determined by combining region and language code
typedef enum TWL_LANG
{
LANG_JAPANESE = 0, // Japanese
LANG_ENGLISH = 1, // English
LANG_FRENCH = 2, // French
LANG_GERMAN = 3, // German
LANG_ITALIAN = 4, // Italian
LANG_SPANISH = 5, // Spanish
LANG_SIMP_CHINESE = 6, // Simplified Chinese
LANG_KOREAN = 7, // Korean
LANG_CODE_MAX
} TWL_LANG;
#define LANG_BITMAP_JAPAN ( ( 0x0001 << LANG_JAPANESE ) )
#define LANG_BITMAP_AMERICA ( ( 0x0001 << LANG_ENGLISH ) | \
( 0x0001 << LANG_FRENCH ) | \
( 0x0001 << LANG_SPANISH ) )
#define LANG_BITMAP_EUROPE ( ( 0x0001 << LANG_ENGLISH ) | \
( 0x0001 << LANG_FRENCH ) | \
( 0x0001 << LANG_GERMAN ) | \
( 0x0001 << LANG_ITALIAN ) | \
( 0x0001 << LANG_SPANISH ) )
#define LANG_BITMAP_AUSTRALIA ( ( 0x0001 << LANG_ENGLISH ) )
#define LANG_BITMAP_CHINA ( ( 0x0001 << LANG_SIMP_CHINESE ) )
#define LANG_BITMAP_KOREA ( ( 0x0001 << LANG_KOREAN ) )
#define HWINFO_FILE_LENGTH ( 16 * 1024 )
#define HWN_PATH "nand:/sys/HWINFO_N.dat"
#define HWS_PATH "nand:/sys/HWINFO_S.dat"
#define HWS_PATH_SD "sd:/TwlNandTool/HWINFO_S.dat"
#define HWS_OFFSET_BOX 0x784000
#define HWS_OFFSET_HANDHELD 0x790000
#define HWS_OFFSET_OTHER 0x794000
#define HWN_VERSION 1 // HW information format version (start no.:1)
#define HWS_VERSION 1 // HW information format version (start no.:1)
// I think these are for HWInfo Normal? This is from the TwlSDK Secure7 private package
//#define HWS_MOVABLE_UNIQUE_ID_LEN 16 // Unique ID Transferable between bodies
//#define HWS_MOVABLE_UNIQUE_ID_MASK 0x9865f16bd375414c // Unique ID shall be XOR of this value with serial no.
typedef struct {
u32 HWS_SIG[0x80];
u32 HWS_HEADER; // Always 0x01000000
u32 HWS_SIZE; // Always 0x1C000000
u32 HWS_LANG;
u32 HWS_PAD;
u8 HWS_REGION;
char HWS_SERIAL[15]; // Last 3 bytes are zerofilled.
// USA serials are 11, and others are 12.
// Kinda neat, seems like this padding was in case they needed more serials?
u32 HWS_TID;
} hwsFormat;
extern hwsFormat hwsData;
bool clearHWInfoStruct();
bool loadHWInfoStruct();
bool recoverHWInfo(bool simple);
bool recoverHWInfoOffset(int address);
bool saveHWInfoSDMC(void);

View File

@ -52,10 +52,12 @@ static u32 fat_sig_fix_offset = 0;
static u32 sector_buf32[SECTOR_SIZE/sizeof(u32)];
u8 *sector_buf = (u8*)sector_buf32;
static u32 sector_buf232[SECTOR_SIZE/sizeof(u32)];
u8 *sector_buf2 = (u8*)sector_buf232;
static u32 sector_buf322[SECTOR_SIZE/sizeof(u32)];
u8 *sector_buf2 = (u8*)sector_buf322;
static u32 file_buf32[BUFFER_SIZE/sizeof(u32)];
u8 *file_buf = (u8*)file_buf32;
static u32 file_buf322[BUFFER_SIZE/sizeof(u32)];
u8 *file_buf2 = (u8*)file_buf322;
void nandio_set_fat_sig_fix(u32 offset)
{

View File

@ -46,6 +46,7 @@ typedef struct {
extern u8 *sector_buf;
extern u8 *file_buf;
extern u8 *file_buf2;
extern bool is3DS;
extern nandData nandInfo;
@ -57,6 +58,7 @@ extern u8 consoleIDfixed[8];
extern bool good_nandio_write(int inputAddress, int inputLength, u8 *buffer, bool crypt);
extern bool good_nandio_write_file(int inputAddress, int inputLength, FILE *fp, bool crypt);
extern bool good_nandio_read(int inputAddress, int inputLength, u8 *buffer, bool crypt);
extern bool nandio_startup();
extern bool nandio_shutdown();

View File

@ -9,6 +9,7 @@
#include "nandio.h"
#include "nandfirm.h"
#include "sysfile.h"
#include "hwinfo.h"
#include "sector0.h"
#include "crypto.h"
#include "../message.h"
@ -16,7 +17,7 @@
#include "../video.h"
#include "../storage.h"
static size_t i;
//static size_t i;
enum {
SYSFILEMENU_RECOVER,
@ -29,71 +30,6 @@ enum {
SYSFILEMENU_INIT_FONT
};
/*
I just need to plan things out:
- verifyHWInfo
- Just test input HWInfo buffer
- recoverHWInfoShallow
- Check for easy to find file (in mounted TWL_MAIN, then by common HWInfo offsets)
- recoverHWInfoDeep
- Scrap the entire TWL_MAIN byte by byte to find HWInfo (last resort)
- resignHWInfo
Let's design this around edge cases- meet the TWL-CPU-X4 (prototype).
The HWInfo is made up of one "real" HWInfo at the start, then a repeating secondary HWInfo to pad out to 16kb.
40E34ABB 0E81922C 0B2B24E5 CD4864D4 \
637F0199 D385B1AA A893F65E EDADE869 |
DA66D05D 5B31A897 E4AAF339 83FDD161 | "Real" HWInfo signature
128804B7 B7BA3C06 CD5304ED 9642181C |
A57B3B8B 695E1B37 6F7F220B 019C3932 |
8D45C9AE 99550547 71E77ECD 0C442E98 |
E2ECB154 BB4D7305 AF75D324 7231B36A |
A53677B1 EB4D0102 C8F31A43 EED93758 /
01000000 1C000000 01000000 00000000 <-- Region and language info
00414141 50503241 47303538 31000000 <-- Serial number
4A414E48 <-- Launcher TID
785E7A35 9A9B3C08 B9AAE1D5 02D5CD71 <-- No idea what this entire block is!
E7CFDC89 607EC36A 7A680E45 D0B30B50
BBD36599 99731FE3 91F61DDB 8788C2C1
50B19D58 ... and so on for 0x36C bytes.
64E1D65C 2649BBAA EDF4808A 3B5830A0 \
2E0C2E9A 481A0487 E9DC32DB A49BDA8C |
901B5647 2E3473CB 7317122A 2F7ECCE3 | "Real" HWInfo signature
C880187C 0EA2C230 DFFED67A D6AC7C54 |
98676C25 4B1726FF 3EAA6EE4 39A718CA |
EA2ECF98 9B41BA5F 3E32A49F 8262DE7E |
201585B4 E4D27B32 B4D501EE 2B098032 /
01000000 1C000000 01000000 00000000 <-- Region and language info
002E2E02 2E2E022E 2E022E59 02595902 <-- Serial number (completely broken)
4A414E48 <-- Launcher TID
785E7A35 9A9B3C08 B9AAE1D5 02D5CD71 <-- This weird block appears again!
E7CFDC89 607EC36A 7A680E45 D0B30B50
BBD36599 99731FE3 91F61DDB 8788C2C1
50B19D58 ... and so on for 0x36C bytes.
I have no idea what testing this DSi went through. I can't say if this is unique to the X4 or not.
Anyways, the "weird block" is repeated between every HWInfo chunk. We can check for...
- 0x414E48: the common part of the launcher TID
- 0x785E7A35 9A9B3C08 B9AAE1D5 02D5CD71: the "weird block" data after the TID
Then to verify this is a HWInfo, check if the "weird block" is repeated 0x36C + size of HWInfo later.
This will work on retail as well since the "weird block" here is 0xFF padding until 16kb, so it will always repeat.
Never let down the prototype enjoyers, even if this makes the process way more annoying!
Oh also here's another edge case!
The factory HWInfo Secure created by PRE_IMPORT will be entirely 0xFF.
It will only exist in uninitialized units (did not leave the factory).
If you find 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF then do not proceed to recoverHWInfoDeep.
*/
static int _sysfileMenu(int cursor)
{
@ -150,6 +86,7 @@ int sysfileMain(void)
{
case SYSFILEMENU_RECOVER:
recoverHWInfo(false);
break;
case SYSFILEMENU_RECOVER2:
@ -228,9 +165,9 @@ bool makeCertChain(void) {
printf("\nRemoving old cert.sys...");
char nitro_path[100];
snprintf(nitro_path, 100, "nitro:/import/%s/cert.sys", consoleSignName);
remove("nand:/sys/cert.sys");
remove(CERT_PATH);
printf("\nWriting cert.sys...");
if (copyFile(nitro_path, "nand:/sys/cert.sys") != 0) {
if (copyFile(nitro_path, CERT_PATH) != 0) {
success = false;
} else {
printf("\nDone!");
@ -257,9 +194,9 @@ bool makeFontTable(void) {
printf("\nRemoving old TWLFontTable...");
char nitro_path[100];
snprintf(nitro_path, 100, "nitro:/import/common/TWLFontTable.dat");
remove("nand:/sys/TWLFontTable.dat");
remove(FONT_PATH);
printf("\nWriting TWLFontTable (world)...");
if (copyFile(nitro_path, "nand:/sys/TWLFontTable.dat") != 0) {
if (copyFile(nitro_path, FONT_PATH) != 0) {
success = false;
} else {
printf("\nDone!");
@ -271,49 +208,4 @@ bool makeFontTable(void) {
exitFunction();
return success;
}
/*
bool recoverHWInfo(void) {
success = true;
bool hwinfofound = false;
clearScreen(cSUB);
iprintf("\n>> Recover HWInfo Secure ");
iprintf("\n--------------------------------");
if (nandMounted == true) {
printf("\nChecking mounted TWL_MAIN...");
if (fileExists("nand:/sys/HWINFO_S.dat")) {
printf("\nFound HWInfo.");
if (copyFile("nand:/sys/HWINFO_S.dat", "sd:/HWINFO_S_BACKUP.dat") != 0) {
success = false;
printf("\nCouldn't copy HWInfo!");
} else {
printf("\nCopied okay.");
// Some verification here
hwinfofound = true;
}
} else {
printf("\nCouldn't find HWInfo!");
}
} else {
iprintf("\nChecking common offsets...");
nandioread(offset + tidOffset, 16)
if (first 3 bytes == ANH)
nandioread(offset + tidOffset + 0x3CB, 16)
if (nandioread == nandioread)
// Verify
fopen fwrite fclose
}
exitFunction();
return success;
}
*/
}

View File

@ -13,12 +13,19 @@
#include "../main.h"
#include "../video.h"
#define CERT_PATH "nand:/sys/cert.sys"
#define DEVKP_PATH "nand:/sys/dev.kp"
#define HWID_PATH "nand:/sys/HWID.sgn"
#define FONT_PATH "nand:/sys/TWLFontTable.dat"
#define FLOG_PATH "nand:/sys/log/product.log"
#define CFG0_PATH "nand:/shared1/TWLCFG0.dat"
#define CFG1_PATH "nand:/shared1/TWLCFG1.dat"
#define WRAP_PATH "nand:/shared2/launcher/wrap.bin"
void wait(int ticks);
int sysfileMain();
bool recoverHWInfo();
bool recoverHWInfoDeep();
bool makeSystemFolders();
bool makeCertChain();
bool makeFontTable();