Homebrew NDS Checksums

ChampionLeake

NTR/TWL Exploiter
OP
Member
Joined
Jan 19, 2016
Messages
209
Trophies
0
Age
25
XP
673
Country
United States
Hello everyone! I've recently got in reverse engineering and I'm actually studying about stack smashing. I picked FIFA06 for the US because it was already exploited and that there's only a EUR version of the exploit but not a US version of it. I'm trying edit a save file of FIFA06 US, but the checksum is a really big problem to me.

I tried making a program to look at a checksum but that didn't work out well for me. I do have some information about the game though.

The checksum is located at the offset of 0x0A or 0x0000000A
I found that out by comparing 2 save files together with VBinDiff

I then made a program to show the saves checksum in C(basically CTurt's example on his website: https://cturt.github.io/DS-exploit-finding.html)
Code:
#include <stdio.h>

int main(int argc, char **argv) {
    if(argc < 2) {
        printf("FIFA 06 DS (US) save checksum fixer\n");
        printf("Usage:\n");
        printf("checksumFix [save1.sav] [save2.sav] ...\n");
        return 1;
    }
 
    int i;
    for(i = 1; i < argc; i++) {
        FILE *f = fopen(argv[i], "rwb+");
     
        if(!f) {
            printf("Failed to open\n");
            fclose(f);
            return 1;
        }
     
        unsigned short checksum;
        fseek(f, 0x000000A, SEEK_SET);
        fread(&checksum, sizeof(unsigned short), 1, f);
     
        fclose(f);
     
        printf("%s:\n", argv[i]);
        printf("%p\n", checksum);
    }
 
    return 0;
}

So I made different save files with different names and got each result of the saves.
save1.sav (contains: AAAAAAAAAA) = 0x0000B043
save2.sav (contains: AAAAAAAAAB) = 0x0000B14F
save3.sav (contains: AAAAAAAAAC) = 0x0000B25B

Then I actually did some math to actually calculate the real checksum.

0x0000B14F - 0x0000B043 = 0x10C (save2.sav - save1.sav = 0x10C)
0x0000B25B - 0x0000B14F = 0x10C (save3.sav - save2.sav = 0x10C)

I've noticed that I've found a pattern with this and confirmed that byte 0x4D(which is the end of the profile name) changes the checksum by 0x10C


That was all the information I had on the the checksums.
If anyone can help me point to the next direction to please let me now because I'm stuck on what to do next.

EDIT: I'll provide all the saves I've observed and images of the savefiles to easily look the checksum below

Sincerely, ChampionLeake
 

Attachments

  • FIFA SOCCER 06.rar
    62.2 KB · Views: 116
Last edited by ChampionLeake,
  • Like
Reactions: u12345678

ChampionLeake

NTR/TWL Exploiter
OP
Member
Joined
Jan 19, 2016
Messages
209
Trophies
0
Age
25
XP
673
Country
United States
whatever you linked to already had a checksum calc for the E version, doesnt it work for this either?
https://github.com/CTurt/DARA/blob/master/CRCFix/source/main.c#L113-L122
also would be helpful if you maybe even linked the saves to look at, I'm sure if its not that exact crc calc its just a very tiny variation from it.
I actually tried compiling the crc program for the EUR version of FIFA to test but DevkitPro is acting up again for me. But I've provided the saves and images below the post if you're interested in looking into it
 
Last edited by ChampionLeake,

FIX94

Former Staff
Former Staff
Joined
Dec 3, 2009
Messages
7,284
Trophies
0
Age
29
Location
???
XP
11,238
Country
Germany
Why the hell devkitpro? This is regular PC stuff editing a single .sav so you just need gcc. You REALLY should work out very basic compiling before even doing exploits. Anyways as I guessed, its nearly the identical CRC with very slight variation, also the saves you uploaded didnt help me at all since they were broken and cut down so I made my own ones in no$gba with a forced bigger save to figure this out.
Code:
#include <stdio.h>
#include <malloc.h>
#include <inttypes.h>
int main()
{
    FILE *f = fopen("save5.sav","rb");
    fseek(f,0,SEEK_END);
    size_t fsize = ftell(f);
    rewind(f);
    uint8_t *data = malloc(fsize);
    fread(data,1,fsize,f);
    fclose(f);
    uint16_t crc = 11;
    uint32_t i;
    for(i = 0x000003B8; i < 0x00001150; i++) {
        crc += data[i] * (0x00001150 - (i & ~0x3));
    }
    if(crc == (uint16_t)(data[0x10]|(data[0x11]<<8)))
        printf("CRC Good\n");
    free(data);
    printf("%04x\n", crc);
}
All thats different from "fifa06emyclubcrc" in that existing exploit page is that "crc" in the US version is 11 instead of 21.
 
  • Like
Reactions: u12345678

ChampionLeake

NTR/TWL Exploiter
OP
Member
Joined
Jan 19, 2016
Messages
209
Trophies
0
Age
25
XP
673
Country
United States
Me and a buddy of mine read cturt's deduction on the checksum but we can't understand how is that translated to code. Can you help us understand it?
 
  • Like
Reactions: u12345678

FIX94

Former Staff
Former Staff
Joined
Dec 3, 2009
Messages
7,284
Trophies
0
Age
29
Location
???
XP
11,238
Country
Germany
I dont feel like spoon-feeding you, sorry, I've linked you to the exact functions of the existing exploits and even gave you adjusted code for the US version so really, if you want to port an exploit, just start learning basic coding and reading of existing code before thinking about more.
 

u12345678

Member
Newcomer
Joined
Aug 19, 2016
Messages
18
Trophies
0
XP
108
Country
Trinidad and Tobago
Why the hell devkitpro? This is regular PC stuff editing a single .sav so you just need gcc. You REALLY should work out very basic compiling before even doing exploits. Anyways as I guessed, its nearly the identical CRC with very slight variation, also the saves you uploaded didnt help me at all since they were broken and cut down so I made my own ones in no$gba with a forced bigger save to figure this out.
Code:
#include <stdio.h>
#include <malloc.h>
#include <inttypes.h>
int main()
{
    FILE *f = fopen("save5.sav","rb");
    fseek(f,0,SEEK_END);
    size_t fsize = ftell(f);
    rewind(f);
    uint8_t *data = malloc(fsize);
    fread(data,1,fsize,f);
    fclose(f);
    uint16_t crc = 11;
    uint32_t i;
    for(i = 0x000003B8; i < 0x00001150; i++) {
        crc += data[i] * (0x00001150 - (i & ~0x3));
    }
    if(crc == (uint16_t)(data[0x10]|(data[0x11]<<8)))
        printf("CRC Good\n");
    free(data);
    printf("%04x\n", crc);
}
All thats different from "fifa06emyclubcrc" in that existing exploit page is that "crc" in the US version is 11 instead of 21.

Hey, I've tried your program and it doesn't seem to calculate good checksums with no$gba savegames (yep, the 256 kb ones)
for an AAAAAAAAAA savegame, which checksum should be 0x43B0 the code calculates 0xCFBB.

Btw I'm also interested in knowing how did you figure and translate the results into code. I mean, anyone can copy-paste but the point is to understand and learn, don't you think? I tried what the OP said and got the 0xC01 variation too. But I think there's a lot of important explaination missed like how did you turned that into code.
 

FIX94

Former Staff
Former Staff
Joined
Dec 3, 2009
Messages
7,284
Trophies
0
Age
29
Location
???
XP
11,238
Country
Germany
Hey, I've tried your program and it doesn't seem to calculate good checksums with no$gba savegames (yep, the 256 kb ones)
for an AAAAAAAAAA savegame, which checksum should be 0x43B0 the code calculates 0xCFBB.

Btw I'm also interested in knowing how did you figure and translate the results into code. I mean, anyone can copy-paste but the point is to understand and learn, don't you think? I tried what the OP said and got the 0xC01 variation too. But I think there's a lot of important explaination missed like how did you turned that into code.
*sigh* the checksum at 0xA is irrelevant as that is only the profile, the one at 0x10 is the interesting one as it is the part that gets exploited so you're looking at the wrong thing entirely. Also I didnt even go into ASM for this, since I already saw it was all SO similar I just brute-forced the 65536 possitilites, confirmed the new number on 3 different saves and called it done.
edit: oh and also, if you actually look at the for loop; the interesting part that gets exploited goes from offset 0x3B8 to 0x1150, the stuff before that is irrelevant.
 
Last edited by FIX94,

u12345678

Member
Newcomer
Joined
Aug 19, 2016
Messages
18
Trophies
0
XP
108
Country
Trinidad and Tobago
*sigh* the checksum at 0xA is irrelevant as that is only the profile, the one at 0x10 is the interesting one as it is the part that gets exploited so you're looking at the wrong thing entirely. Also I didnt even go into ASM for this, since I already saw it was SO similar I just brute-forced the 65536 possitilites, confirmed the new number on 3 different saves and called it done.


Since the profile name is the only thing you can modify without going into ASM it is actually very important to figure out the algorithm, which in fact affects not only the profile name but 0x10 too.
As for the bruteforce thing could you explain what are your exactly talking about? I don't see how would you have to bruteforce anything here
 

FIX94

Former Staff
Former Staff
Joined
Dec 3, 2009
Messages
7,284
Trophies
0
Age
29
Location
???
XP
11,238
Country
Germany
Since the profile name is the only thing you can modify without going into ASM it is actually very important to figure out the algorithm, which in fact affects not only the profile name but 0x10 too.
As for the bruteforce thing could you explain what are your exactly talking about? I don't see how would you have to bruteforce anything here
I guess you didnt see the pattern then yet, anyways 0xA is practically identical to the other one.
Code:
#include <stdio.h>
#include <malloc.h>
#include <inttypes.h>
int main()
{
    FILE *f = fopen("save5.sav","rb");
    fseek(f,0,SEEK_END);
    size_t fsize = ftell(f);
    rewind(f);
    uint8_t *data = malloc(fsize);
    fread(data,1,fsize,f);
    fclose(f);
    uint16_t crc = 11;
    uint32_t i;
    for(i = 0x0000034; i < 0x0000158; i++) {
        crc += data[i] * (0x0000158 - (i & ~0x3));
    }
    if(crc == (uint16_t)(data[0xA]|(data[0xB]<<8)))
        printf("CRC Good\n");
    free(data);
    printf("%04x\n", crc);
}
Oh and if you wonder what I mean with brute-force:
Code:
#include <stdio.h>
#include <malloc.h>
#include <inttypes.h>
int main()
{
    FILE *f = fopen("save5.sav","rb");
    fseek(f,0,SEEK_END);
    size_t fsize = ftell(f);
    rewind(f);
    uint8_t *data = malloc(fsize);
    fread(data,1,fsize,f);
    fclose(f);
    uint16_t crc;
    uint32_t i;
    uint32_t t;
    for(t = 0; t < 0xFFFE; t++)
    {
        crc = t;
        for(i = 0x0000034; i < 0x0000158; i++) {
            crc += data[i] * (0x0000158 - (i & ~0x3));
        }
        if(crc == (uint16_t)(data[0xA]|(data[0xB]<<8)))
        {
            printf("CRC Good %04x\n", t);
            break;
        }
    }
    free(data);
    printf("%04x\n", crc);
}
You can see that brute-forces the start CRC using value "t" and can also depending on its value be a pretty good indicator if you are looking at the right data blocks because that should obviously be always the same start value for a game so if you have 2 saves and that value is different you know you're looking at a wrong data block.
edit: oh and also if you still didnt pick up its CRC pattern, it always loops through every byte, does an add-CRC inside a block using a specific start value as base.
A block is surrounded also by 0xFF so you can make out the boundaries of this check quite easily.
 
Last edited by FIX94,
  • Like
Reactions: u12345678

u12345678

Member
Newcomer
Joined
Aug 19, 2016
Messages
18
Trophies
0
XP
108
Country
Trinidad and Tobago
I guess you didnt see the pattern then yet, anyways 0xA is practically identical to the other one.
Code:
#include <stdio.h>
#include <malloc.h>
#include <inttypes.h>
int main()
{
    FILE *f = fopen("save5.sav","rb");
    fseek(f,0,SEEK_END);
    size_t fsize = ftell(f);
    rewind(f);
    uint8_t *data = malloc(fsize);
    fread(data,1,fsize,f);
    fclose(f);
    uint16_t crc = 11;
    uint32_t i;
    for(i = 0x0000034; i < 0x0000158; i++) {
        crc += data[i] * (0x0000158 - (i & ~0x3));
    }
    if(crc == (uint16_t)(data[0xA]|(data[0xB]<<8)))
        printf("CRC Good\n");
    free(data);
    printf("%04x\n", crc);
}
Oh and if you wonder what I mean with brute-force:
Code:
#include <stdio.h>
#include <malloc.h>
#include <inttypes.h>
int main()
{
    FILE *f = fopen("save5.sav","rb");
    fseek(f,0,SEEK_END);
    size_t fsize = ftell(f);
    rewind(f);
    uint8_t *data = malloc(fsize);
    fread(data,1,fsize,f);
    fclose(f);
    uint16_t crc;
    uint32_t i;
    uint32_t t;
    for(t = 0; t < 0xFFFE; t++)
    {
        crc = t;
        for(i = 0x0000034; i < 0x0000158; i++) {
            crc += data[i] * (0x0000158 - (i & ~0x3));
        }
        if(crc == (uint16_t)(data[0xA]|(data[0xB]<<8)))
        {
            printf("CRC Good %04x\n", t);
            break;
        }
    }
    free(data);
    printf("%04x\n", crc);
}
You can see that brute-forces the start CRC using value "t" and can also depending on its value be a pretty good indicator if you are looking at the right data blocks because that should obviously be always the same start value for a game so if you have 2 saves and that value is different you know you're looking at a wrong data block.
edit: oh and also if you still didnt pick up its CRC pattern, it always loops through every byte, does an add-CRC inside a block using a specific start value as base.
A block is surrounded also by 0xFF so you can make out the boundaries of this check quite easily.

Thank you!
 

ChampionLeake

NTR/TWL Exploiter
OP
Member
Joined
Jan 19, 2016
Messages
209
Trophies
0
Age
25
XP
673
Country
United States
Do you mind providing me your savefiles you generated? Since desmume kind of sucks generating them and that there are some changes.
 

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
  • No one is chatting at the moment.
  • BakerMan @ BakerMan:
    i said i was sleeping...
  • BakerMan @ BakerMan:
    sleeping with uremum
  • K3Nv2 @ K3Nv2:
    Even my mum slept on that uremum
  • TwoSpikedHands @ TwoSpikedHands:
    yall im torn... ive been hacking away at tales of phantasia GBA (the USA version) and have so many documents of reverse engineering i've done
  • TwoSpikedHands @ TwoSpikedHands:
    I just found out that the EU version is better in literally every way, better sound quality, better lighting, and there's even a patch someone made to make the text look nicer
  • TwoSpikedHands @ TwoSpikedHands:
    Do I restart now using what i've learned on the EU version since it's a better overall experience? or do I continue with the US version since that is what ive been using, and if someone decides to play my hack, it would most likely be that version?
  • Sicklyboy @ Sicklyboy:
    @TwoSpikedHands, I'll preface this with the fact that I know nothing about the game, but, I think it depends on what your goals are. Are you trying to make a definitive version of the game? You may want to refocus your efforts on the EU version then. Or, are you trying to make a better US version? In which case, the only way to make a better US version is to keep on plugging away at that one ;)
  • Sicklyboy @ Sicklyboy:
    I'm not familiar with the technicalities of the differences between the two versions, but I'm wondering if at least some of those differences are things that you could port over to the US version in your patch without having to include copyrighted assets from the EU version
  • TwoSpikedHands @ TwoSpikedHands:
    @Sicklyboy I am wanting to fully change the game and bend it to my will lol. I would like to eventually have the ability to add more characters, enemies, even have a completely different story if i wanted. I already have the ability to change the tilemaps in the US version, so I can basically make my own map and warp to it in game - so I'm pretty far into it!
  • TwoSpikedHands @ TwoSpikedHands:
    I really would like to make a hack that I would enjoy playing, and maybe other people would too. swapping to the EU version would also mean my US friends could not legally play it
  • TwoSpikedHands @ TwoSpikedHands:
    I am definitely considering porting over some of the EU features without using the actual ROM itself, tbh that would probably be the best way to go about it... but i'm sad that the voice acting is so.... not good on the US version. May not be a way around that though
  • TwoSpikedHands @ TwoSpikedHands:
    I appreciate the insight!
  • The Real Jdbye @ The Real Jdbye:
    @TwoSpikedHands just switch, all the knowledge you learned still applies and most of the code and assets should be the same anyway
  • The Real Jdbye @ The Real Jdbye:
    and realistically they wouldn't

    be able to play it legally anyway since they need a ROM and they probably don't have the means to dump it themselves
  • The Real Jdbye @ The Real Jdbye:
    why the shit does the shitbox randomly insert newlines in my messages
  • Veho @ Veho:
    It does that when I edit a post.
  • Veho @ Veho:
    It inserts a newline in a random spot.
  • The Real Jdbye @ The Real Jdbye:
    never had that i don't think
  • Karma177 @ Karma177:
    do y'all think having an sd card that has a write speed of 700kb/s is a bad idea?
    trying to restore emunand rn but it's taking ages... (also when I finished the first time hekate decided to delete all my fucking files :wacko:)
  • The Real Jdbye @ The Real Jdbye:
    @Karma177 that sd card is 100% faulty so yes, its a bad idea
  • The Real Jdbye @ The Real Jdbye:
    even the slowest non-sdhc sd cards are a few MB/s
  • Karma177 @ Karma177:
    @The Real Jdbye it hasn't given me any error trying to write things on it so I don't really think it's faulty (pasted 40/50gb+ folders and no write errors)
  • DinohScene @ DinohScene:
    run h2testw on it
  • DinohScene @ DinohScene:
    when SD cards/microSD write speeds drop below a meg a sec, they're usually on the verge of dying
    DinohScene @ DinohScene: when SD cards/microSD write speeds drop below a meg a sec, they're usually on the verge of dying