News: 11 March 2016 - Forum Rules

Author Topic: Editing GBA Save Files via HxD?  (Read 807 times)

merelylegend

  • Jr. Member
  • **
  • Posts: 3
    • View Profile
Editing GBA Save Files via HxD?
« on: October 20, 2021, 12:47:36 pm »
Hey y'all, as a bored college student, I've been messing around with the GBA and I've recently been trying to create a "unit editor" for Fire Emblem 7. I've already half written a python program that writes Codebreaker codes to change values for the first character slot, however it would be a chore to add 15 other slots to that program, along with the fact that you'd have to turn the codes off once the changes actually take effect. I figured it would be much more effective to either edit the game's save file directly or edit VBA's ram dump (tried this and for some reason the character values stayed the same even after loading the edited RAM dump, so the former it is.) However, I cannot for the life of me find where character stats are stored. I've tried comparing save files where I manually edited one stat (ie Eliwood's strength is 21 in save 1 and 25 in save 2,) however searching for hex value 15 still gives the same number of hits in both saves. So I'm a bit lost here, is there something I'm missing about the structure of GBA saves? If anyone is able to help me out, it'd be greatly appreciated! Thanks!

Cyneprepou4uk

  • Hero Member
  • *****
  • Posts: 734
  • I am the baldest romhacker
    • View Profile
Re: Editing GBA Save Files via HxD?
« Reply #1 on: October 20, 2021, 02:25:35 pm »
Quote
I've tried comparing save files where I manually edited one stat, however searching for hex value 15 still gives the same number of hits in both saves.

I don't get it, are you comparing files or searching inside them? File comparsion is a good idea, and also a better idea than searching, if you don't know exactly how the game stores data.

merelylegend

  • Jr. Member
  • **
  • Posts: 3
    • View Profile
Re: Editing GBA Save Files via HxD?
« Reply #2 on: October 20, 2021, 02:39:28 pm »
I don't get it, are you comparing files or searching inside them? File comparsion is a good idea, and also a better idea than searching, if you don't know exactly how the game stores data.

I'm using HxD's "Find" feature to search both files for a hex value of 15. Theoretically, the file which Eliwood's strength is 25 should have at least one less 15 value than the file where Eliwood's strength is 21. However,  I get the exact same amount of 15 values in both files.

FAST6191

  • Hero Member
  • *****
  • Posts: 3358
    • View Profile
Re: Editing GBA Save Files via HxD?
« Reply #3 on: October 20, 2021, 02:47:17 pm »
The GBA should be fancy enough to have activator codes (press this combo to enable sort of thing). Slide codes is also what most use to edit repetitive data.

Curious that the RAM dump does not edit either, though that might be what you meant by disable after they take effect, and could be a mild anti cheat effect. Just to check you did edit it say in normal screen and then go to stats or indeed back in and out of stats?

There is no special GBA save format (for handhelds that would be the DSi and 3ds before that came along for Nintendo stuff). Everything is just a binary blob however the devs did it, which will include their own custom hash that is likely to be the bigger problem here.
As far as data then nothing says it has to be the same in save game as it is in RAM (or ROM). Compression, different encodings, calculations

On comparing then try seeing what changed* rather than searching for specific values.

*there is a possibility that the save would have two slots for one game (alternating them for backup purposes). Also I can't remember which Fire Emblem game it was but in one of them they also constantly save (was an issue for one of the newer flash carts that emulated save types). Easy enough to figure out said slots though and then adjust accordingly.

But yeah the main problem is going to be the hash. http://www.romhacking.net/forum/index.php?topic=33218.0 is a discussion on the matter from the other month, and while it is was one of the more obtuse ones I have seen it is still within normal ranges for fun and games with such matters. Though I should also note the "for the purposes of programming an editor" route of disable the check in the game and have it sort it on the write side of things.

merelylegend

  • Jr. Member
  • **
  • Posts: 3
    • View Profile
Re: Editing GBA Save Files via HxD?
« Reply #4 on: October 20, 2021, 03:21:44 pm »

Curious that the RAM dump does not edit either, though that might be what you meant by disable after they take effect, and could be a mild anti cheat effect. Just to check you did edit it say in normal screen and then go to stats or indeed back in and out of stats?


It's weird, the .DMP file from VBA's memory dump is editable from HxD, and is simply an exact copy of the current RAM value for value, so it was very easy to find what I needed. However, when loading that edited file back into VBA, the values simply... don't change? It reverts it to the exact same as before it was ever edited.



There is no special GBA save format (for handhelds that would be the DSi and 3ds before that came along for Nintendo stuff). Everything is just a binary blob however the devs did it, which will include their own custom hash that is likely to be the bigger problem here.
As far as data then nothing says it has to be the same in save game as it is in RAM (or ROM). Compression, different encodings, calculations

On comparing then try seeing what changed* rather than searching for specific values.


I'm a little new to this, so forgive me if I'm wrong, but the hash is like an encryption key for the save file, right? So the different values that I'm looking for could be anything? How would I go about trying to find it?

October 20, 2021, 07:04:53 pm - (Auto Merged - Double Posts are not allowed before 7 days.)
Am I allowed to double post here? I managed to make two very similar save files, with only 5 bytes being different from each other. The first three bytes seem irrelevant, as long as they stay the same the save will not corrupt so I've just been leaving them alone. The last two different values are where I believe the stat I edited, Eliwood's Strength stat, are located, being 0x00003F2C and 0x00003F7A. Save one's values are 5E and 52, giving Eliwood a strength stat of 20. Save two's values are 56 and 5A, giving Eliwood a strength stat of 22.  Changing these values independently will corrupt the save file, and changing them to anything but the other save's value will also corrupt the save file. I'm guessing this is an issue with the checksum/hash. Are there any resources that I can use to find out what values to put without corrupting the save file?
« Last Edit: October 20, 2021, 07:04:53 pm by merelylegend »

Cyneprepou4uk

  • Hero Member
  • *****
  • Posts: 734
  • I am the baldest romhacker
    • View Profile
Re: Editing GBA Save Files via HxD?
« Reply #5 on: October 20, 2021, 09:19:20 pm »
Check emulator settings, does it use any kind of compression for save files? A save file should contain current RAM state from the game, besides other important data. Normally one is be able to copy several bytes from RAM and find them inside the file, in order to determine where RAM starts.

Try to make more saves with different stat values, maybe you'll start noticing a pattern. The only thing I've noticed from your save values is changes in bit3, but that doesn't make much sense.

FAST6191

  • Hero Member
  • *****
  • Posts: 3358
    • View Profile
Re: Editing GBA Save Files via HxD?
« Reply #6 on: October 21, 2021, 07:34:36 am »
It's weird, the .DMP file from VBA's memory dump is editable from HxD, and is simply an exact copy of the current RAM value for value, so it was very easy to find what I needed. However, when loading that edited file back into VBA, the values simply... don't change? It reverts it to the exact same as before it was ever edited.


I'm a little new to this, so forgive me if I'm wrong, but the hash is like an encryption key for the save file, right? So the different values that I'm looking for could be anything? How would I go about trying to find it?

October 20, 2021, 07:04:53 pm - (Auto Merged - Double Posts are not allowed before 7 days.)
Am I allowed to double post here? I managed to make two very similar save files, with only 5 bytes being different from each other. The first three bytes seem irrelevant, as long as they stay the same the save will not corrupt so I've just been leaving them alone. The last two different values are where I believe the stat I edited, Eliwood's Strength stat, are located, being 0x00003F2C and 0x00003F7A. Save one's values are 5E and 52, giving Eliwood a strength stat of 20. Save two's values are 56 and 5A, giving Eliwood a strength stat of 22.  Changing these values independently will corrupt the save file, and changing them to anything but the other save's value will also corrupt the save file. I'm guessing this is an issue with the checksum/hash. Are there any resources that I can use to find out what values to put without corrupting the save file?

Other than user error the usual reasons for reversion to older values would be some kind of internal anti cheat type verification/restoration (a weak one like I have yet to encounter if leaving a basic naive cheat on for a while sorts it, could be a coding error in anti cheat though), or the values living on the stack somewhere and being written back down afterwards again (one of the reasons I suggested going to a different part of the game -- if on the stats screen the game is doing all its calculations then that is one thing, if it is in something else it is probably not concerned with stats and might not write them back down afterwards).

Hashes, checksums and encryption can be closely related but for the most part you will be spared any encryption aspects in this.
The most basic hash is you add everything up, is it odd or is it even. The weaknesses of that approach should be evident but go a bit further and do bytesums, or further still and do more fun things with trapdoor functions that you can't easily reverse (what two numbers did I multiply together to get 30?) and you get into more useful checksums/hashes.

As far as changing numbers then by and large you don't for stuff like this. Any time you change a file and it produces the same hash (generally a mathematical possibility if not certainty) it is called a collision. Chances of it happening here for changing a few stats is minimal, and even if you have some extra junk data you can fiddle with (if the end of the file is normally all 00 or something, still covered by the hash but unused by the game then that is prime ground for many to brute force things).
It is far easier to either figure out how the hash works (see that link for an example of it being done for a specific game, though the approach is similar enough -- find where the save gets loaded into RAM, find the hash, find what reads that and how it in turn calculates it, https://www.romhacking.net/documents/361/ though go with no$gba debug these days or maybe https://wrongbaud.github.io/posts/ghidra-debugger/ ) or disable the hash* until you want to return to the former. There are reasons in some scenarios to brute force a matching hash, usually when you want to bypass security and have to have a specific payload, but this is not that and frankly to do that you will almost certainly need to understand the hash** and at that point you are done as you can just sort it yourself.

*somewhere in the game will be the hash function mentioned before, however after it will be some variation on the theme of "compare result to one in save, if equal then jump to save good and carry on with life, if not equal then say bad save and do what you do there". You change the thing to always take the save good path and it will dutifully load the save, and then fix it for you next time it saves such that will likely work on an unmodified game unless you broke something when fiddling (deleted all the characters, set stats to 0, straight up corrupted it such that the game can't handle it...).

**you technically could do an oracle attack using the game and an emulator as a black box but this is just being silly.

As far as finding the hash location then sounds like you just did. If you know two values are stats you edited then the other change will be the hash. 3 bytes is an odd length (while far from impossible I would still have expected an even amount, could be coincidence that one byte remained the same between files so maybe try a radically different save and see what goes for that location).

phonymike

  • Jr. Member
  • **
  • Posts: 69
    • View Profile
Re: Editing GBA Save Files via HxD?
« Reply #7 on: October 26, 2021, 01:17:49 am »
Hey, I checked out the save routine and bad news: it looks really bad. I just set breakpoints on writes to the sram in no$gba pressing ctrl+b and typing
Code: [Select]
[0E000000..0E00FFFF]!!
That will be for 64KB of sram but no$gba doesn't seem to like that, and it creates a 128KB save file and can't read it afterwards. But it seems the game only uses 32KB of sram. So I just kept using a savestate before saving since after resetting the file shows up as new game. But that's only the beginning.

I found it copies some chunks of data to the sram when saving a file like this
Code: [Select]
size  from:    to:
0x10 3007CE4 E000094
0x10 3007CE4 E0000A4
0x48 202BBF8 E003F2C

Then does 0x33 more copies like this (first 6 are listed)
Code: [Select]
size  from:    to:
0x24 3007C6C E003F74
0x24 3007C6C E003F98
0x24 3007C6C E003FBC
0x24 3007C6C E003FE0
0x24 3007C6C E004004
0x24 3007C6C E004028

Then there's a large section of over 0x200 bytes copied and I think that's it.



But what are those 0x24 byte chunks all about? It looks like a temporary buffer at 3007C6C. Maybe I'm wrong, but it looks like a long unrolled loop, a small example below (not for the weak of heart)
Spoiler:
Code: [Select]
080A0A9E 4668     mov     r0,r13                                  ;2  501
080A0AA0 7504     strb    r4,[r0,14h]                             ;7  508
080A0AA2 4669     mov     r1,r13                                  ;2  510
080A0AA4 1C28     adds    r0,r5,0                                 ;2  512
080A0AA6 780C     ldrb    r4,[r1]                                 ;12 524
080A0AA8 4020     ands    r0,r4                                   ;2  526
080A0AAA 7008     strb    r0,[r1]                                 ;13 539
080A0AAC 466A     mov     r2,r13                                  ;2  541
080A0AAE 2108     movs    r1,8h                                   ;2  543
080A0AB0 5679     ldsb    r1,[r7,r1]                              ;4  547
080A0AB2 251F     movs    r5,1Fh                                  ;2  549
080A0AB4 46A8     mov     r8,r5                                   ;2  551
080A0AB6 4646     mov     r6,r8                                   ;2  553
080A0AB8 4031     ands    r1,r6                                   ;2  555
080A0ABA 01C9     lsls    r1,r1,7h                                ;2  557
080A0ABC 4BEB     ldr     r3,=Lxx_0FFFFF07Fh                      ;9  566
080A0ABE 1C18     adds    r0,r3,0                                 ;2  568
080A0AC0 8814     ldrh    r4,[r2]                                 ;12 580
080A0AC2 4020     ands    r0,r4                                   ;2  582
080A0AC4 4308     orrs    r0,r1                                   ;2  584
080A0AC6 8010     strh    r0,[r2]                                 ;13 597
080A0AC8 257F     movs    r5,7Fh                                  ;2  599
080A0ACA 46A9     mov     r9,r5                                   ;2  601
080A0ACC 4649     mov     r1,r9                                   ;2  603
080A0ACE 7A7E     ldrb    r6,[r7,9h]                              ;6  609
080A0AD0 4031     ands    r1,r6                                   ;2  611
080A0AD2 0309     lsls    r1,r1,0Ch                               ;2  613
080A0AD4 9800     ldr     r0,[sp]                                 ;4  617
080A0AD6 4AE6     ldr     r2,=Lxx_0FFF80FFFh                      ;9  626
080A0AD8 4010     ands    r0,r2                                   ;2  628
080A0ADA 4308     orrs    r0,r1                                   ;2  630
080A0ADC 9000     str     r0,[sp]                                 ;5  635
080A0ADE 466C     mov     r4,r13                                  ;2  637
080A0AE0 2110     movs    r1,10h                                  ;2  639
080A0AE2 5679     ldsb    r1,[r7,r1]                              ;4  643
080A0AE4 203F     movs    r0,3Fh                                  ;2  645
080A0AE6 4001     ands    r1,r0                                   ;2  647
080A0AE8 00C9     lsls    r1,r1,3h                                ;2  649
080A0AEA 8862     ldrh    r2,[r4,2h]                              ;12 661
080A0AEC 48E1     ldr     r0,=Lxx_0FFFFFE07h                      ;9  670
080A0AEE 4010     ands    r0,r2                                   ;2  672
080A0AF0 4308     orrs    r0,r1                                   ;2  674
080A0AF2 8060     strh    r0,[r4,2h]                              ;13 687
080A0AF4 2111     movs    r1,11h                                  ;2  689
080A0AF6 5679     ldsb    r1,[r7,r1]                              ;4  693
080A0AF8 203F     movs    r0,3Fh                                  ;2  695
080A0AFA 4001     ands    r1,r0                                   ;2  697
080A0AFC 0049     lsls    r1,r1,1h                                ;2  699
080A0AFE 78E2     ldrb    r2,[r4,3h]                              ;12 711

and it goes on for a while.... I don't want to flood the forum

So if you want to try Ghidra I think you'll get better results. Maybe someone recognizes what's going on here. I think they obfuscated the save data. I have no idea what it's doing to the data before storing it in the sram (it does set the hardware wait state to 8 each time before writing the 0x24 bytes and verifies them afterwards too so that's nice.)