Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding which Checksum is used

Tags:

c++

checksum

crc

Together with some other people we are trying to make a Savegameeditor for a game, but we came accross some problems. The savegame files contain a kind of checksum,of which we can't seem to find which checksum is used for this. Till now all we know is:

  • The checksum is 32 bit
  • Between 9 different saved games, where the savegame data is exactly the same except for 5 bytes (which are spread accross the file), the checksum has been found to be between 1834565 - 1851372, when parsed as an unsinged long. Note that each save those 5 bytes each save are an increased number (mostly by around +8), but the checksum isn't lineair increased.
  • The checksum seems to position dependent, as the game declares the file as corrupt when 2 bytes are switched
  • I tried some checksums, and came to the conclusion that it didn't seem to be Sum32, addler32, DJB2 and CRC32, cause none of them seemed to come close to the checksums included in the savegames. It seems to be that the checksum which comes the closest to the checksum included in the savegames seems to be just adding all bytes to an unsigned long, which returns a value around ~2507737.

I was wondering if there is a better way to find which checksum is used for these files, or if someone knows any tips to find out which checksum is used. I'm currently just trying some checksums which I found on different sites in a C++ program. Maybe also important to know is that the game is from 2004, and in other files it used DJB2 for string-hashes. According to other people the .exe seems to be using a CRC32 check.

Edit 1: After some time I managed to get 924 different versions of the same file, except 2 bytes which vary each save, and I also got the checksums of these files to see how it reacted on those changes, and I made a list about this. (Note that I cannot manually make changes to the file and the game just makes a checksum for it, every time I saved the file it added +2 to the unsigned long containing the varying number, so that's how I created the list.)

See a part of the list below here (50 records out of 924):

>         The bytes         Checksum (as Hex and unsigned long)
>         -----------------------------
>         0x 0 0x18 0x 0    0x13DFA 81402
>         0x 0 0x19 0x 0    0x13F76 81782
>         0x 0 0x1A 0x 0    0x1406D 82029
>         0x 0 0x1B 0x 0    0x14114 82196
>         0x 0 0x1C 0x 0    0x13EC5 81605
>         0x 0 0x1D 0x 0    0x13790 79760
>         0x 0 0x1E 0x 0    0x143C1 82881
>         0x 0 0x1F 0x 0    0x13ED0 81616
>         0x 2 0x18 0x 0    0x13D02 81154
>         0x 2 0x19 0x 0    0x13ABD 80573
>         0x 2 0x1A 0x 0    0x14271 82545
>         0x 2 0x1B 0x 0    0x13E39 81465
>         0x 2 0x1C 0x 0    0x140FC 82172
>         0x 2 0x1D 0x 0    0x13FFE 81918
>         0x 2 0x1E 0x 0    0x1413B 82235
>         0x 2 0x1F 0x 0    0x13A5F 80479
>         0x 4 0x18 0x 0    0x138F2 80114
>         0x 4 0x19 0x 0    0x141AE 82350
>         0x 4 0x1A 0x 0    0x13E91 81553
>         0x 4 0x1B 0x 0    0x13F67 81767
>         0x 4 0x1C 0x 0    0x13C6C 81004
>         0x 4 0x1D 0x 0    0x13F4E 81742
>         0x 4 0x1E 0x 0    0x13BB8 80824
>         0x 4 0x1F 0x 0    0x1398D 80269
>         0x 6 0x18 0x 0    0x146C0 83648
>         0x 6 0x19 0x 0    0x139B5 80309
>         0x 6 0x1A 0x 0    0x13FAC 81836
>         0x 6 0x1B 0x 0    0x13E71 81521
>         0x 6 0x1C 0x 0    0x14162 82274
>         0x 6 0x1D 0x 0    0x13D55 81237
>         0x 6 0x1E 0x 0    0x13BE8 80872
>         0x 6 0x1F 0x 0    0x13B72 80754
>         0x 8 0x18 0x 0    0x142FE 82686
>         0x 8 0x19 0x 0    0x13E07 81415
>         0x 8 0x1A 0x 0    0x14923 84259
>         0x 8 0x1C 0x 0    0x13D3E 81214
>         0x 8 0x1D 0x 0    0x14420 82976
>         0x 8 0x1E 0x 0    0x13BEE 80878
>         0x 8 0x1F 0x 0    0x145F5 83445
>         0x 8 0x1F 0x 0    0x145F5 83445
>         0x A 0x18 0x 0    0x13CB6 81078
>         0x A 0x19 0x 0    0x142FB 82683
>         0x A 0x1A 0x 0    0x13EB2 81586
>         0x A 0x1B 0x 0    0x13C14 80916
>         0x A 0x1C 0x 0    0x13915 80149
>         0x A 0x1D 0x 0    0x14100 82176
>         0x A 0x1E 0x 0    0x14310 82704
>         0x A 0x1F 0x 0    0x13B34 80692
>         0x C 0x18 0x 0    0x142AE 82606
>         0x C 0x19 0x 0    0x14091 82065

I still cannot see a pattern between those varying bytes, and the checksum, so I was wondering if someone else maybe sees a pattern between those? Or maybe a technique of how to find patterns between them. If someone can help me out of this, I can also post a link to the full list (as Microsoft Excel or TXT format)

like image 822
Joey Avatar asked Sep 04 '11 09:09

Joey


1 Answers

The easiest way may, indeed, be to grab a debugger like OllyDbg, locate the checksumming code, and reverse-engineer it. Not to say that it's easy, because it is probably going to be rather hard. But in my opinion reverse-engineering even simple checksums just by looking at the numbers is bordering on impossibility, unless you have a high-functioning autist friend with superhuman abilities to see patterns – perhaps not even then.

You may of course be lucky to have a game which uses a standard, well-known checksum, to detect corruption. But if their goal was to prevent tampering (and that's fairly likely) then they wouldn't have used a standard checksum if they have any clue.

Here's a checksum algorithm from a crackme I'm entertaining myself with:

checksum algorithm

I certainly couldn't have guessed this code just by looking at its outputs:

uint sum = 0;
for (uint i = 0; i < 478; i++)
    sum = rol((((buf[i + 22] | 0xFFFFFF00u) + i) ^ (478 - i)) - sum + 0x272E4745u, 3);

There is a separate checksum algorithm in this code which uses sign-extension without completely obliterating it with an or 0xFFFFFF00 - would you have considered such an operation in your search? The search space is just too huge to guess it...

like image 91
Roman Starkov Avatar answered Sep 21 '22 12:09

Roman Starkov