Notes from inside your Wii

HackMii header image 2

Wii menu TP-hack-killer analysis

June 17th, 2008 by bushing · 172 Comments

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::DeleteSavedata(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::DeleteSavedata: "
             "Could not read1 %s: %d\n", pathname);
        goto done;
    dirent_t *direntries=malloc(sizeof dirent_t * num_dir_entries);
    if (direntries == NULL) {
        "Could not alloc: %d\n");
        goto done;
    retval = nandReadDir(pathname, num_dir_entries, direntries);
    if (retval != 0) {
        OSReportError("iplESMisc.cpp::DeleteSavedata: "
        "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::DeleteSavedata: Failed to delete %s: %d\n", buf);
            goto done;
        deleted_files = 1;
    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:Open save data file failed: %d\n");
        goto done;

    u32 file_length;
    retval = NANDGetLength(fd, &file_length);
    if (retval != 0) {
        OSReportError("iplESMisc.cpp::VerifySavedataZD:Get 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);
        fd = 0;
        ipl::utility::ESMisc::DeleteSavedata(titleid_h, titleid_l);
    savegame_bad = 0;

    if (buf) free(buf);
    if (fd) NANDClose(fd);

    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 + 0x56, 8)) return 0; // random strings
    if (!_check_strlen(buf + 0x60, 8)) return 0; // inside savegame
    if (!_check_strlen(buf + 0x7A, 8)) return 0;
    if (!_check_strlen(buf + 0x96, 8)) return 0;
    if (!_check_strlen(buf + 0x1BC, 17)) return 0; // player name
    if (!_check_strlen(buf + 0x1CD, 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 + 0x2008 + 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]);



Tags: Wii

172 responses so far ↓

  • 1 WhoopJack // Jun 21, 2008 at 12:13 am

    Yeah, Nintendo did block the easy way in, but looks like mozy got it to work again a while ago (http://mozy.org/wii/#shop).

    I guess the save game hacks are easier since you bypass the default install process which likely brings asymmetric key encrypted files into the picture, where Nintendo holds the private key that you’d need. Oh well.

  • 2 jhark // Jun 21, 2008 at 7:11 am

    we’re at the 5 day mark now…

  • 3 Daverball // Jun 21, 2008 at 7:26 am

    So what?
    Just be patient. They owe you nothing. I wouldn’t even care if it took two months, they do a great job and great stuff takes its time.

  • 4 Neversoft // Jun 21, 2008 at 7:44 am

    I’m stunned at how impatient some of you people are, you don’t deserve the amount of effort that Bushing and the rest put into this (although I doubt they’re thinking about you when they’re hacking away anyway). More to the point, how come many of you appear to have had no previous interest in Wii homebrew (otherwise you’d have the HBC installed already and you wouldn’t need the updated Twilight hack) but it’s suddenly *very* important now that you’ve shoved a brand new firmware up your Wii without bothering to check for any possible repercussions… So you didn’t care for homebrew a week ago but it’s a life threatening situation now that you CAN’T install it? Grow up!

  • 5 Scoop // Jun 21, 2008 at 8:40 am

    I didnt even manage to get it installed. I was on for days trying different patches etc and all came up on my wii SD card as a ? in the blocks. I lost my temper and gave up installing the 3.3 patch. Next day when i calmed down I tried a 4th SD card and bloody typical the chain loaded showed up but obviously Nintendo have blocked me from saving the file to my Wii. Im hoping soon there will be a new patch for me to try now that I have sorted the SD card out. Great work and gods speed.


  • 6 rod // Jun 21, 2008 at 9:02 am

    well there be twilight hack for rzde j us

  • 7 rod // Jun 21, 2008 at 9:04 am

    i have 3.3u but i dont know how to do the new twilight hack you guys made for the pal wii

  • 8 rod // Jun 21, 2008 at 9:05 am

    can you help me

  • 9 Ark // Jun 21, 2008 at 10:22 am


    Realize some of us actually have not paid attention to the homebrew scene until recently, and did not know the repercussions of this update until researching Wii homebrew in general.

    Now, granted, I am as impatient as everyone else, but unless someone has actual input, then it’s best to just wait it out. Toy around with other games and save files, see what they may have. Take a look at the C output and look at anything. If you are not very technical, then just sit back and chill, play a damn game. Hell, mess around with DS homebrew if you want. Just be patient, and we will see progress over the next few weeks.

  • 10 Phredreeke // Jun 21, 2008 at 10:29 am

    > Sunrise: Can somebody clarify, do game ISOs that are obtained from say newsgroups all use Trucha?

    This isn’t a place for discussing piracy.

    People need to learn to research before updating their Wii systems. I hope this makes people think twice the next time they update their Wii.

  • 11 Nikayah // Jun 21, 2008 at 12:12 pm

    I have two ideas although i have no clue if they will work, i don’t even know very much about how the wii reads stuff or whatever, but what if you put a modified Mii on the wiimote with a program like transfer mii (its for linux as far as i know). Or even figure out how to send a mii from a computer to a wii (like you can with the mii channel, but with a computer to send it instead)

  • 12 bitflusher // Jun 21, 2008 at 1:28 pm

    impatient people, there are two approaches to solving a problem.

    approach 1 by someone @elotrolado.net forums:
    make something that has a slim chance it could possibly work. and release it the second you compiled it
    result: http://wiibrew.org/wiki/Talk:Twilight_Hack#3.3_T.Hack.3F
    something that got rushed, doesn’t work and is possibly full of other errors that could brick many wii’s

    approach 2 by team tweezers:
    do research, share it and have it verified, implement it and make it work, then test it and test it to ensure it does nothing it shouldn’t do. and when you are absolutely shure, release it

  • 13 Zelda // Jun 21, 2008 at 9:03 pm

    its out go to wiibrew.org and work with 3.3 update yay

  • 14 Putting the genie back into bottle? (MIOS) // Jun 22, 2008 at 12:09 am

    […] June 16th (”3.3″) Wii System Update did more than bring the death of the Twilight Hack (sort of) and a patched version of IOS30.  It also brought new versions of BC and MIOS — […]

  • 15 Wii firmware version 3.3 » Restart // Jun 22, 2008 at 3:25 am

    […] Update :- Some additional information here and interesting update here […]

  • 16 Sunrise // Jun 22, 2008 at 7:46 am

    I’ll discuss what I want, when I want and where I want until such time as I agree to some posting rules.

    My question was valid and probably a lot more helpful that your 100% useless post, which incidentally by reposting my question makes you guilty of the same.

    I’d suggest trying MSN or similar to find some youngsters who’ll bow to your bullying attempts ..maybe try
    DON’T spell that like that blah or
    DON’T you know correct grammar blah or
    DON’T you search before asking blah blah.

    Finally, I find it ironic that you mention piracy, I presume from a legal aspect, and we’re all discussing decompiling (and not just for education but for vulnerability) encrypted and copyrighted code.

  • 17 Darksyntax // Jun 22, 2008 at 8:38 am

    that zant guy is annoying. This is why i make no effort to contribute to such projects — the ungrateful, illinformed consumer mass that see it as “easy”.

  • 18 Phredreeke // Jun 23, 2008 at 4:58 am


    There’s no need to be rude about it. Bushing is very much against piracy, so I’d say it’s impolite to ask about ISOs downloaded from newsgroups in his blog.

    The vulnerabilities we’re looking for is not for running pirated games (which has been possible almost since the release of the console anyway) but for running our own code.

  • 19 hochniveau: Das offizielle Blog der h8u.de Gemeinde // Jun 24, 2008 at 2:47 pm

    […] der das Verwenden des modifizierten Speicherstandes analysiert und auch da ein h

  • 20 Creg // Jun 25, 2008 at 2:26 pm

    I have finally discovered the flaw.

  • 21 Wii ISO Loader? « nooblog // Aug 3, 2008 at 12:35 pm

    […] nicht verwendet, doch war es nur eine Frage der Zeit bis Dies geschehen sollte. Es geschah dann am 16 Juni mit der Firmware Version “3.3X”, die jedoch nicht nur modifizierten Spielen, sondern […]

  • 22 Korean Wii // Sep 13, 2008 at 3:17 pm

    […] (It identifies itself as 3.3K, and it does have the anti-Twilight Hack code.) […]

You must log in to post a comment.