Okay, I’ve spent a little bit of time trying to reconstruct the C code used to build the channel from my disassembler. The full IDA Pro output for those funcs is here:
http://static.hackmii.com/verifyzelda.html.
Below, you’ll find my C version. I’ve tried to make it function exactly like the one in the new system menu. Hopefully I did a good job, because I’d like to see people try to find bug in this that could lead to an exploit. There are at least two here, which we used in combination; can you find any more?
Don’t worry, I’ll give the answers if nobody gets them
// this helper function gets called during the NAND check
// for the TP hack files
int ipl::utility::ESMisc:
eleteSavedata(u32 titleid_h, u32 titleid_l) {
char pathname[0x100];
int deleted_files = 0;
sprintf(pathname, “/title/%08x/%08x/data/”, titleid_hi, titleid_lo);
int num_dir_entries = 0;
int retval = nandReadDir(pathname, 0, &num_dir_entries);
if (retval != 0 || num_dir_entries == 0) {
OSReportError(”iplESMisc.cpp:
eleteSavedata: ”
“Could not read1 %s: %d\n”, pathname);
goto done;
}
dirent_t *direntries=malloc(sizeof dirent_t * num_dir_entries);
if (direntries == NULL) {
OSReportError(”iplESMisc.cpp:
eleteSavedata:”
“Could not alloc: %d\n”);
goto done;
}
retval = nandReadDir(pathname, num_dir_entries, direntries);
if (retval != 0) {
OSReportError(”iplESMisc.cpp:
eleteSavedata: ”
“Could not read2 %s: %d\n”, pathname);
goto done;
}
int i;
for (i=0; i < num_dir_entries; i++) {
char buf[0x100];
strcpy(buf, pathname);
strcat(buf, direntries[i].filename);
retval = NANDPrivateDelete(buf);
if (retval != 0) {
OSReportError(”iplESMisc.cpp:
eleteSavedata: Failed to delete %s: %d\n”, buf);
goto done;
}
deleted_files = 1;
}
done:
if (direntries != NULL) free(direntries);
return deleted_files;
}
// this function is called upon boot or something
ipl::utility::ESMisc::VerifySavedataZD(u32 titleid_hi, u32 titleid_lo) {
int savegame_bad = 1;
char pathname[0x100];
sprintf(pathname, “/title/%08x/%08x/data/%s”, titleid_hi, titleid_lo, “zeldaTp.dat”);
if(ipl::utility::ESMisc::ChangeUid(titleid_hi, titleid_lo)==0) goto done;
int retval = nandPrivateOpen(pathname, &fd, O_RDWR);
if (retval == -ENOENT) {
OSReportError(”iplESMisc.cpp::VerifySavedataZD: Does not exist %s: %d\n”, pathname);
goto done;
}
if (retval == 0) {
OSReportError(”iplESMisc.cpp::VerifySavedataZD:O pen save data file failed: %d\n”);
goto done;
}
u32 file_length;
retval = NANDGetLength(fd, &file_length);
if (retval != 0) {
OSReportError(”iplESMisc.cpp::VerifySavedataZD:G et file length failed: %d\n”);
goto done;
}
char *buf = malloc(file_length);
if (buf == NULL) {
OSReportError(”iplESMisc.cpp::VerifySavedataZD: Alloc failed: %d\n”);
goto done;
}
int bytes_read;
bytes_read = NANDRead(fd, buf, _align_size(file_length, 32));
if (bytes_read != _align_size(file_length, 32)) {
OSReportError(”iplESMisc.cpp::VerifySavedataZD: Read file failed: %d\n”);
goto done;
}
if (WADCheckSavedataZD(buf) == 0) {
OSReport(”iplESMisc.cpp::VerifySavedataZD: Verify failed for %016llx\n”,
titleid_hi << 32 | titleid_lo);
NAND_Close(fd);
fd = 0;
ipl::utility::ESMisc:
eleteSavedata(titleid_h, titleid_l);
}
savegame_bad = 0;
done:
if (buf) free(buf);
if (fd) NANDClose(fd);
ipl::utility::ESMisc::ChangeUid(1,2);
return savegame_bad;
}
int _align_size(int value, int alignment) {
// round up value to next highest multiple of alignment
// e.g align_size(40, 32) = 64
return value + (alignment-1) & ~alignment;
}
int _check_strlen(char *string, int max) {
int i;
for (i=0; i< max; i++) if (string[i]==’\0′) return 1;
return 0;
}
int _check_save(char *buf) {
if (!_check_strlen(buf + 0×56, 8)) return 0; // random strings
if (!_check_strlen(buf + 0×60, 8)) return 0; // inside savegame
if (!_check_strlen(buf + 0×7A, 8)) return 0;
if (!_check_strlen(buf + 0×96, 8)) return 0;
if (!_check_strlen(buf + 0×1BC, 17)) return 0; // player name
if (!_check_strlen(buf + 0×1CD, 17)) return 0; // horse name
return 1;
}
int WADCheckSavedataZD(char *buf) {
int i;
// check 3 primary saveslots
for (i=0; i<3; i++) if (!_check_save(buf + i*0xA94)) return 0;
// check 3 backup saveslots
for (i=0; i< 3; i++) if (!_check_save(buf + 0×2008 + i*0xA94)) return 0;
return 1;
}
// this function is called when any savegame WAD is being
// installed (copied from SD)
int _wad_check_for_twilight_hack(WAD *wadfile) {
int i;
for (i=0; i < wadfile.numfiles; i++) {
// skip any leading directory names
char *p = strrchr(wadfile.filename[i], ‘/’);
if (p == NULL) p = wadfile.filename[i];
else p++;
if (strcmp(wadfile.filename[i], “zeldaTp.dat”)==0) {
return WADCheckSavedataZD(wadfile.filedata[i]);
}
}
}