Twilight Hack/Exploit killer

From WiiBrew
Jump to navigation Jump to search
// 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) {
        OSReportError("iplESMisc.cpp::DeleteSavedata:"
        "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;
    }
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: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));
    
#ifdef 3_3
    if (bytes_read != _align_size(file_length, 32)) { // by making the file length not divisible by 32, bytes_read will be less than the aligned size, jumping to the end without deleting the save
#else
    if (bytes_read != file_length) {
#endif
        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::DeleteSavedata(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 + 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;
#ifdef 3_4
    int res = 1;
#endif
    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(p, "zeldaTp.dat")==0) {
#ifdef 3_3
            return WADCheckSavedataZD(wadfile.filedata[i]); // there should not be a "return" here; this means only the first zeldaTp.dat is checked
#else
#ifdef 3_4
            res = WADCheckSavedataZD(wadfile.filedata[i]); // this means only the sanity of the last zeldaTp.dat matters
#else
            if (!WADCheckSavedataZD(wadfile.filedata[i]))
                return 0;
#endif
#endif
        }
    }

#ifdef 3_4
    return res;
#else
#ifndef 3_3
    return 1;
#endif
#endif
}