Reverse engineering a simple game archive
Sometimes I like to poke around in the files of the games I bought on Steam, just for fun or maybe to listen to the game music. Most game engines put the game files in their own proprietary archive files, so I started to reverse engineer those formats, at least to the point where I could extract the files. Some game archive file formats proved to be so simple that I was able to easily write the archives, too. So I thought I write down how I did it, maybe someone else finds this useful?
The main tools I use are a hex editor (bless) and Python.
Short detour: I'm not 100% satisfied with bless, though. Let me show you what I mean:
I would like those fields in the status bar to be text fields that I can edit, making it easy to select a certain area of the file when I know the size in bytes. If anyone knows a (Linux) hex editor that does that let me know!
But what I do like about bless is that it displays the bytes at the cursor in all possible number formats and it's dynamically resizable, making table structures in the file much more apparent. See:
So much for that. Now to the actual reverse engineering. Let's start with the simples game archive format that I saw: FEZ .pak files. Looking at the start of FEZ/Content/Essentials.pak we see this:
00000000: d700 0000 186f 7468 6572 2074 6578 7475 .....other textu 00000010: 7265 735c 6675 6c6c 7768 6974 65cb 0000 res\fullwhite... 00000020: 0058 4e42 7705 01cb 0000 0001 9401 4d69 .XNBw.........Mi 00000030: 6372 6f73 6f66 742e 586e 612e 4672 616d crosoft.Xna.Fram 00000040: 6577 6f72 6b2e 436f 6e74 656e 742e 5465 ework.Content.Te 00000050: 7874 7572 6532 4452 6561 6465 722c 204d xture2DReader, M 00000060: 6963 726f 736f 6674 2e58 6e61 2e46 7261 icrosoft.Xna.Fra 00000070: 6d65 776f 726b 2e47 7261 7068 6963 732c mework.Graphics, 00000080: 2056 6572 7369 6f6e 3d34 2e30 2e30 2e30 Version=4.0.0.0 00000090: 2c20 4375 6c74 7572 653d 6e65 7574 7261 , Culture=neutra 000000a0: 6c2c 2050 7562 6c69 634b 6579 546f 6b65 l, PublicKeyToke 000000b0: 6e3d 3834 3263 6638 6265 3164 6535 3035 n=842cf8be1de505 000000c0: 3533 0000 0000 0001 0000 0000 0200 0000 53.............. 000000d0: 0200 0000 0100 0000 1000 0000 ffff ffff ................ 000000e0: ffff ffff ffff ffff ffff ffff 1f6f 7468 .............oth 000000f0: 6572 2074 6578 7475 7265 735c 7472 616e er textures\tran 00000100: 7370 6172 656e 7477 6869 7465 cb00 0000 sparentwhite.... 00000110: 584e 4277 0501 cb00 0000 0194 014d 6963 XNBw.........Mic 00000120: 726f 736f 6674 2e58 6e61 2e46 7261 6d65 rosoft.Xna.Frame 00000130: 776f 726b 2e43 6f6e 7465 6e74 2e54 6578 work.Content.Tex 00000140: 7475 7265 3244 5265 6164 6572 2c20 4d69 ture2DReader, Mi 00000150: 6372 6f73 6f66 742e 586e 612e 4672 616d crosoft.Xna.Fram 00000160: 6577 6f72 6b2e 4772 6170 6869 6373 2c20 ework.Graphics, 00000170: 5665 7273 696f 6e3d 342e 302e 302e 302c Version=4.0.0.0, 00000180: 2043 756c 7475 7265 3d6e 6575 7472 616c Culture=neutral 00000190: 2c20 5075 626c 6963 4b65 7954 6f6b 656e , PublicKeyToken 000001a0: 3d38 3432 6366 3862 6531 6465 3530 3535 =842cf8be1de5055 000001b0: 3300 0000 0000 0100 0000 0002 0000 0002 3............... 000001c0: 0000 0001 0000 0010 0000 00ff ffff 00ff ................ 000001d0: ffff 00ff ffff 00ff ffff 0018 6f74 6865 ............othe ...
So there are 5 bytes that aren't printable ASCII character and then there is a bit of ASCII. Those first bytes don't look like a file signature/magic, so I guess it's already data. But denoting what and how? For the how: Most CPU architectures today are little-endian, Windows certainly only runs on little-endian systems (ignoring the XBox 360). Little- and big-endian are ways to encode integers in memory. It's the order of the bytes that make up the number. A 32bit integer consists of 4 bytes. For little-endian the least significant byte comes first, for big endian it's the other way around. Almost everyone has strong opinions about which encoding is the "correct" one, but let's not go into that.
As a first assumption (which I never had to overthrow so far) let's assume integers are stored in little-endian encoding. As a further assumption lets say that sizes of embedded files and things like file counts are stored as unsigned 32 bit values. Only if the game files would exceed 4 GB (or 2 GB for signed values) one would need 64 bit integers. So let's interpret the first 4 bytes as an unsigned 32 bit integer, what do we get? 215
That's a low enough number that it might be a file count. Let's run with that. But it's 5 bytes that are non-ASCII, what is the next one? Well, file names usually are quite short, so maybe they are limited to 255 characters in FEZ? Then we get 24
followed by 24 bytes of printable ASCII characters ("other textures\fullwhite"
) and then more non-ASCII bytes. So that seems right. How many non-ASCII bytes? 4! As 32 bit integer it's the number 203
. A file size? Maybe the next 203 bytes are the file data?
Coloring in the bytes of the file count and the first file you get this:
- number of files
- file name length
- file name
- file data size
- file data
00000000: d700 0000 186f 7468 6572 2074 6578 7475 .....other textu 00000010: 7265 735c 6675 6c6c 7768 6974 65cb 0000 res\fullwhite... 00000020: 0058 4e42 7705 01cb 0000 0001 9401 4d69 .XNBw.........Mi 00000030: 6372 6f73 6f66 742e 586e 612e 4672 616d crosoft.Xna.Fram 00000040: 6577 6f72 6b2e 436f 6e74 656e 742e 5465 ework.Content.Te 00000050: 7874 7572 6532 4452 6561 6465 722c 204d xture2DReader, M 00000060: 6963 726f 736f 6674 2e58 6e61 2e46 7261 icrosoft.Xna.Fra 00000070: 6d65 776f 726b 2e47 7261 7068 6963 732c mework.Graphics, 00000080: 2056 6572 7369 6f6e 3d34 2e30 2e30 2e30 Version=4.0.0.0 00000090: 2c20 4375 6c74 7572 653d 6e65 7574 7261 , Culture=neutra 000000a0: 6c2c 2050 7562 6c69 634b 6579 546f 6b65 l, PublicKeyToke 000000b0: 6e3d 3834 3263 6638 6265 3164 6535 3035 n=842cf8be1de505 000000c0: 3533 0000 0000 0001 0000 0000 0200 0000 53.............. 000000d0: 0200 0000 0100 0000 1000 0000 ffff ffff ................ 000000e0: ffff ffff ffff ffff ffff ffff 1f6f 7468 .............oth 000000f0: 6572 2074 6578 7475 7265 735c 7472 616e er textures\tran 00000100: 7370 6172 656e 7477 6869 7465 cb00 0000 sparentwhite.... 00000110: 584e 4277 0501 cb00 0000 0194 014d 6963 XNBw.........Mic 00000120: 726f 736f 6674 2e58 6e61 2e46 7261 6d65 rosoft.Xna.Frame 00000130: 776f 726b 2e43 6f6e 7465 6e74 2e54 6578 work.Content.Tex 00000140: 7475 7265 3244 5265 6164 6572 2c20 4d69 ture2DReader, Mi 00000150: 6372 6f73 6f66 742e 586e 612e 4672 616d crosoft.Xna.Fram 00000160: 6577 6f72 6b2e 4772 6170 6869 6373 2c20 ework.Graphics, 00000170: 5665 7273 696f 6e3d 342e 302e 302e 302c Version=4.0.0.0, 00000180: 2043 756c 7475 7265 3d6e 6575 7472 616c Culture=neutral 00000190: 2c20 5075 626c 6963 4b65 7954 6f6b 656e , PublicKeyToken 000001a0: 3d38 3432 6366 3862 6531 6465 3530 3535 =842cf8be1de5055 000001b0: 3300 0000 0000 0100 0000 0002 0000 0002 3............... 000001c0: 0000 0001 0000 0010 0000 00ff ffff 00ff ................ 000001d0: ffff 00ff ffff 00ff ffff 0018 6f74 6865 ............othe ...
Indeed it works to read the file like that (1 byte file name length, file name, 4 bytes file data length, file data, repeat). That was easy!
Using what we learned we can write a small Python script to list files of a FEZ game archive:
Which produces this output if you pass Essentials.pak
to it:
0 33 203 other textures\fullwhite 1 272 203 other textures\transparentwhite 2 504 211 other textures\fullblack 3 745 10150 other textures\smooth_ray 4 10928 44506 other textures\rainbow_flare 5 55465 3980 other textures\flare_alpha 6 59480 210 background planes\white_square 7 59726 251 background planes\dust_particle 8 60005 3016 effects\basicposteffect 9 63043 3928 effects\doteffect 10 67012 22804 effects\instancedanimatedplaneeffect 11 89848 10192 effects\animatedplaneeffect 12 100075 3680 effects\fakepointspriteseffect 13 103790 5760 effects\defaulteffect_textured 14 109590 5148 effects\defaulteffect_vertexcolored 15 114781 7288 effects\defaulteffect_litvertexcolored 16 122107 7624 effects\defaulteffect_littextured 17 129779 5644 effects\defaulteffect_texturedvertexcolored 18 135474 7672 effects\defaulteffect_littexturedvertexcolored 19 143183 17316 effects\instancedblackholeeffect 20 160527 128137 sounds\ui\menu\exitgame 21 288691 42781 sounds\ui\menu\confirm 22 331502 151699 sounds\nature\watersplash 23 483237 1197237 sounds\intro\zoomtofarawayplace 24 1680502 264705 sounds\zu\blackholebuzz 25 1945232 21014 resources\statictext 26 1966280 278 other textures\glyphs\abutton 27 1966597 278 other textures\glyphs\abutton_sony 28 1966912 308 other textures\glyphs\backbutton 29 1967262 334 other textures\glyphs\backbutton_sony 30 1967630 278 other textures\glyphs\bbutton 31 1967947 278 other textures\glyphs\bbutton_sony 32 1968260 260 other textures\glyphs\dpaddown 33 1968560 280 other textures\glyphs\dpaddown_sony 34 1968875 262 other textures\glyphs\dpadleft 35 1969177 284 other textures\glyphs\dpadleft_sony 36 1969497 260 other textures\glyphs\dpadright 37 1969798 284 other textures\glyphs\dpadright_sony 38 1970115 258 other textures\glyphs\dpadup 39 1970411 280 other textures\glyphs\dpadup_sony 40 1970727 316 other textures\glyphs\leftarrow 41 1971080 288 other textures\glyphs\leftbumper 42 1971409 294 other textures\glyphs\leftbumper_ps3 43 1971744 308 other textures\glyphs\leftbumper_ps4 44 1972088 302 other textures\glyphs\leftstick 45 1972431 264 other textures\glyphs\leftstick_sony 46 1972733 266 other textures\glyphs\lefttrigger 47 1973041 276 other textures\glyphs\lefttrigger_ps3 48 1973359 278 other textures\glyphs\lefttrigger_ps4 49 1973674 316 other textures\glyphs\rightarrow 50 1974028 298 other textures\glyphs\rightbumper 51 1974368 296 other textures\glyphs\rightbumper_ps3 52 1974706 314 other textures\glyphs\rightbumper_ps4 53 1975057 310 other textures\glyphs\rightstick 54 1975409 278 other textures\glyphs\rightstick_sony 55 1975726 276 other textures\glyphs\righttrigger 56 1976045 286 other textures\glyphs\righttrigger_ps3 57 1976374 292 other textures\glyphs\righttrigger_ps4 58 1976704 322 other textures\glyphs\startbutton 59 1977069 304 other textures\glyphs\startbutton_sony 60 1977407 280 other textures\glyphs\xbutton 61 1977726 278 other textures\glyphs\xbutton_sony 62 1978038 286 other textures\glyphs\ybutton 63 1978363 280 other textures\glyphs\ybutton_sony 64 1978682 276 other textures\keyboard_glyphs\p_0 65 1978997 266 other textures\keyboard_glyphs\p_1 66 1979302 284 other textures\keyboard_glyphs\p_2 67 1979625 278 other textures\keyboard_glyphs\p_3 68 1979942 278 other textures\keyboard_glyphs\p_4 69 1980259 282 other textures\keyboard_glyphs\p_5 70 1980580 282 other textures\keyboard_glyphs\p_6 71 1980901 278 other textures\keyboard_glyphs\p_7 72 1981218 274 other textures\keyboard_glyphs\p_8 73 1981531 280 other textures\keyboard_glyphs\p_9 74 1981850 280 other textures\keyboard_glyphs\p_a 75 1982174 352 other textures\keyboard_glyphs\p_arrows 76 1982574 272 other textures\keyboard_glyphs\p_arrow_down 77 1982894 280 other textures\keyboard_glyphs\p_arrow_left 78 1983223 280 other textures\keyboard_glyphs\p_arrow_right 79 1983549 276 other textures\keyboard_glyphs\p_arrow_up 80 1983864 270 other textures\keyboard_glyphs\p_b 81 1984181 366 other textures\keyboard_glyphs\p_backspace 82 1984586 280 other textures\keyboard_glyphs\p_c 83 1984908 358 other textures\keyboard_glyphs\p_caps 84 1985309 268 other textures\keyboard_glyphs\p_comma 85 1985616 274 other textures\keyboard_glyphs\p_d 86 1985934 346 other textures\keyboard_glyphs\p_delete 87 1986324 282 other textures\keyboard_glyphs\p_divide 88 1986645 278 other textures\keyboard_glyphs\p_e 89 1986964 312 other textures\keyboard_glyphs\p_end 90 1987319 358 other textures\keyboard_glyphs\p_enter 91 1987720 266 other textures\keyboard_glyphs\p_equal 92 1988027 320 other textures\keyboard_glyphs\p_esc 93 1988391 356 other textures\keyboard_glyphs\p_escape 94 1988789 334 other textures\keyboard_glyphs\p_esdf 95 1989162 278 other textures\keyboard_glyphs\p_f 96 1989480 290 other textures\keyboard_glyphs\p_f0 97 1989810 280 other textures\keyboard_glyphs\p_f1 98 1990131 290 other textures\keyboard_glyphs\p_f10 99 1990462 278 other textures\keyboard_glyphs\p_f11 100 1990781 290 other textures\keyboard_glyphs\p_f12 101 1991111 294 other textures\keyboard_glyphs\p_f2 102 1991445 288 other textures\keyboard_glyphs\p_f3 103 1991773 292 other textures\keyboard_glyphs\p_f4 104 1992105 288 other textures\keyboard_glyphs\p_f5 105 1992433 292 other textures\keyboard_glyphs\p_f6 106 1992765 286 other textures\keyboard_glyphs\p_f7 107 1993091 286 other textures\keyboard_glyphs\p_f8 108 1993417 290 other textures\keyboard_glyphs\p_f9 109 1993746 282 other textures\keyboard_glyphs\p_g 110 1994067 278 other textures\keyboard_glyphs\p_h 111 1994387 320 other textures\keyboard_glyphs\p_home 112 1994746 266 other textures\keyboard_glyphs\p_i 113 1995054 328 other textures\keyboard_glyphs\p_ijkl 114 1995426 360 other textures\keyboard_glyphs\p_insert 115 1995825 272 other textures\keyboard_glyphs\p_j 116 1996136 284 other textures\keyboard_glyphs\p_k 117 1996459 262 other textures\keyboard_glyphs\p_l 118 1996764 318 other textures\keyboard_glyphs\p_l_alt 119 1997129 272 other textures\keyboard_glyphs\p_l_bracket 120 1997448 352 other textures\keyboard_glyphs\p_l_control 121 1997845 348 other textures\keyboard_glyphs\p_l_shift 122 1998240 356 other textures\keyboard_glyphs\p_l_windows 123 1998635 286 other textures\keyboard_glyphs\p_m 124 1998964 268 other textures\keyboard_glyphs\p_minus 125 1999278 286 other textures\keyboard_glyphs\p_multiply 126 1999603 278 other textures\keyboard_glyphs\p_n 127 1999923 320 other textures\keyboard_glyphs\p_none 128 2000286 324 other textures\keyboard_glyphs\p_num_0 129 2000653 318 other textures\keyboard_glyphs\p_num_1 130 2001014 330 other textures\keyboard_glyphs\p_num_2 131 2001387 326 other textures\keyboard_glyphs\p_num_3 132 2001756 320 other textures\keyboard_glyphs\p_num_4 133 2002119 328 other textures\keyboard_glyphs\p_num_5 134 2002490 326 other textures\keyboard_glyphs\p_num_6 135 2002859 322 other textures\keyboard_glyphs\p_num_7 136 2003224 324 other textures\keyboard_glyphs\p_num_8 137 2003591 322 other textures\keyboard_glyphs\p_num_9 138 2003959 352 other textures\keyboard_glyphs\p_num_lock 139 2004350 276 other textures\keyboard_glyphs\p_o 140 2004665 274 other textures\keyboard_glyphs\p_p 141 2004986 356 other textures\keyboard_glyphs\p_page_down 142 2005387 312 other textures\keyboard_glyphs\p_page_up 143 2005743 266 other textures\keyboard_glyphs\p_period 144 2006051 260 other textures\keyboard_glyphs\p_pipe 145 2006353 278 other textures\keyboard_glyphs\p_plus 146 2006670 276 other textures\keyboard_glyphs\p_q 147 2006992 274 other textures\keyboard_glyphs\p_question 148 2007310 268 other textures\keyboard_glyphs\p_quotes 149 2007617 282 other textures\keyboard_glyphs\p_r 150 2007942 316 other textures\keyboard_glyphs\p_r_alt 151 2008305 270 other textures\keyboard_glyphs\p_r_bracket 152 2008622 352 other textures\keyboard_glyphs\p_r_control 153 2009019 342 other textures\keyboard_glyphs\p_r_shift 154 2009408 350 other textures\keyboard_glyphs\p_r_windows 155 2009797 280 other textures\keyboard_glyphs\p_s 156 2010121 354 other textures\keyboard_glyphs\p_scroll 157 2010522 268 other textures\keyboard_glyphs\p_semicolon 158 2010833 354 other textures\keyboard_glyphs\p_space 159 2011226 274 other textures\keyboard_glyphs\p_t 160 2011541 308 other textures\keyboard_glyphs\p_tab 161 2011892 280 other textures\keyboard_glyphs\p_tilde 162 2012211 272 other textures\keyboard_glyphs\p_u 163 2012522 282 other textures\keyboard_glyphs\p_v 164 2012843 276 other textures\keyboard_glyphs\p_w 165 2013161 340 other textures\keyboard_glyphs\p_wasd 166 2013540 288 other textures\keyboard_glyphs\p_x 167 2013867 282 other textures\keyboard_glyphs\p_y 168 2014188 284 other textures\keyboard_glyphs\p_z 169 2014514 358 other textures\keyboard_glyphs\p_zqsd 170 2014914 254 other textures\speech_bubble\b_prompt 171 2015216 238 other textures\speech_bubble\speechbubblene 172 2015502 234 other textures\speech_bubble\speechbubblenw 173 2015784 212 other textures\speech_bubble\speechbubblese 174 2016044 238 other textures\speech_bubble\speechbubblesw 175 2016332 230 other textures\speech_bubble\speechbubbletail 176 2016598 324 other textures\thought_glyphs\b 177 2016959 386 other textures\thought_glyphs\up 178 2017380 420 other textures\black_hole\rips 179 2017836 418 other textures\black_hole\stars 180 2018289 3460 other textures\splash\polytron 181 2021789 9470 other textures\splash\polytron_1440 182 2031294 9124 other textures\splash\trapdoor 183 2040452 5186 other textures\splash\trixels 184 2045677 9132 other textures\splash\trixels_1440 185 2054831 486056 fonts\chinese big 186 2540911 242918 fonts\chinese small 187 2783852 335020 fonts\japanese big 188 3118897 161618 fonts\japanese small 189 3280536 217780 fonts\korean big 190 3498339 114350 fonts\korean small 191 3612709 3860 fonts\latin big 192 3616591 3866 fonts\latin small 193 3620473 2572 fonts\zuish 194 3623075 2326381 sounds\intro\fezlogodrone 195 5949488 34247 sounds\intro\fezlogoglitch1 196 5983767 34247 sounds\intro\fezlogoglitch2 197 6018046 34247 sounds\intro\fezlogoglitch3 198 6052321 1428457 sounds\intro\hyperspeed 199 7480810 803029 sounds\intro\lensflaregleam 200 8283865 1157729 sounds\intro\logozoom 201 9441619 1411305 sounds\intro\pandown 202 10852956 482837 sounds\intro\polytronjingle 203 11335817 881177 sounds\intro\reboot 204 12217020 1625505 sounds\intro\starzoom 205 13842555 185953 sounds\intro\trixellogoin 206 14028539 185953 sounds\intro\trixellogoout 207 14214528 1197237 sounds\intro\zoomtofarawayplace 208 15411794 1182929 sounds\intro\zoomtohouse 209 16594746 103267 sounds\dot\comeout 210 16698038 78067 sounds\dot\heylisten 211 16776125 132405 sounds\dot\hide 212 16908550 112717 sounds\dot\idle 213 17021287 150125 sounds\dot\move 214 17171432 604905 sounds\dot\talk
And finally we can also dump files from a FEZ game archive like this:
With this knowledge I wrote a small Python program that can list, unpack, pack and even read-only mount FEZ game archives. See fezpak on GitHub.
Next time I'll talk about a more complicated file format, which I reverse engineered in order to make a simple sprite swap game mod.
Comments
Post a Comment