sábado, 23 de julio de 2022

Reversing a DOS Game (Part 2/3)

This is a series of 3 posts explaining the experience of reverse engineering a DOS game. The method used is far from sophisticated. Basically disassembling and inspecting the instructions. While live debuggers or live memory inspection through some emulator would bring you faster to this same conclusions, in this exercise we are only using ndiasmgrep and a hex editor to make modifications.

So if you read the previous entry, This x86 DOS instruction:

FE0EF003          dec byte [0x3f0]

Closed one of my childhood traumas, years later, from a more critic point of view we can make some considerations:

  • How great when everything just fits in 64KBs
  • So LIFE was stored in a single byte at 0x3f0... What other operations are done with that address location:
$ cat Indy.asm | grep -i \\[0x3f0
0000598F  C606F00305        mov byte [0x3f0],0x5
00005C3B  FE0EF003          dec byte [0x3f0]
00005C42  803EF00300        cmp byte [0x3f0],0x0
00005CB4  C606F00305        mov byte [0x3f0],0x5
00005D1C  FE0EF003          dec byte [0x3f0]
00008649  FE06F003          inc byte [0x3f0]
0000A669  A0F003            mov al,[0x3f0]
0000A679  A0F003            mov al,[0x3f0]

I just always saw a LIFE bar(never actually cared about the count of that bar), but in here I actually see that there were 5 lives. As we can see how this address location is initialized with 0x5 in two locations.

C606F00305        mov byte [0x3f0],0x5

You would be tempted to replace that 0x5 with a higher number, but notice that black rectangle in the UI. This is most likely too intrusive. I would suspect the code is trying to dump as many sprites in the screen depending on the integer represented by 0x3f0. So most likely we are reading invalid memory.


Here we see a comparison with 0, so this is likely the only place in the game where LIFE is checked to decide if Game is Over or not.

803EF00300        cmp byte [0x3f0],0x0

Here, as far as I remember, each time you completed a chapter, you get a new life. So this instruction must be it:

FE06F003          inc byte [0x3f0]
  • What other memory locations are decreasing or getting subtracted around 0x3f0?

$ cat Indy.asm | grep -i dec.*\\[0x3
00005C3B  FE0EF003          dec byte [0x3f0]
00005D1C  FE0EF003          dec byte [0x3f0]
000081D8  FE0EED03          dec byte [0x3ed]
00008A86  FE0EAA03          dec byte [0x3aa]
00008AAB  FE0EAA03          dec byte [0x3aa]
00008B14  FE0EAA03          dec byte [0x3aa]
00008B30  FE0EAA03          dec byte [0x3aa]
$ cat Indy.asm | grep -i sub.*\\[0x3
00007A71  2A06AA03          sub al,[0x3aa]
00007A9F  2A06AA03          sub al,[0x3aa]
00007AD1  2A06AA03          sub al,[0x3aa]
00007F47  2B069403          sub ax,[0x394]
00007F80  2B069403          sub ax,[0x394]
0000813C  802EEF030A        sub byte [0x3ef],0xa
00008659  2806EF03          sub [0x3ef],al
00008854  832E9D0308        sub word [0x39d],byte +0x8
00008859  832E9F0328        sub word [0x39f],byte +0x28
00008979  2B06FF03          sub ax,[0x3ff]
00008983  2B1EFF03          sub bx,[0x3ff]
00008C98  812E9403D000      sub word [0x394],0xd0
00009D83  2A06E203          sub al,[0x3e2]

So applying the same method to these instructions, We can see that:
  • 0x3AA is storing the time. Each chapter has a limited amount of time which can be increased by collecting torches ... or you can replace FE0EAA03 by 90909090 and don't rush anymore... 
  • 0x3ED is the number of lashes. Whenever Indy catches the whiplash artifact, he can use it 5 times. If we check operations on this address:
$ cat Indy.asm | grep -i \\[0x3ed
0000597A  A2ED03            mov [0x3ed],al
00005A2E  C606ED0300        mov byte [0x3ed],0x0
000081D1  803EED0300        cmp byte [0x3ed],0x0
000081D8  FE0EED03          dec byte [0x3ed]
0000877C  C606ED0305        mov byte [0x3ed],0x5
0000B80E  8A16ED03          mov dl,[0x3ed]
0000B857  8A16ED03          mov dl,[0x3ed]
0000B887  8A16ED03          mov dl,[0x3ed]

We can make the same assumptions as with LIFE. We can see how it gets initialized to 0, or to 5 (presumably when the artifact is caught). Likewise, we see in the same way comparison to 0 (to detect that Indy cannot use the whiplash anymore). And finally, the decreasing instruction.
So replacing FE0EED03 by 90909090 will give you infinite whiplash hits... But I am not sure how this can affect the gameplay in other chapters.

You could still change the 0x5 of:

C606ED0305        mov byte [0x3ed],0x5

To a number bigger than 0x5...like 0xFF(255) But the game might not be able as well to render that number on the dash, so this could be conflicting (not tested).

The actions described here might be seeing as hack & cheat a video game. But the message pretended is that by checking the operations performed over the memory locations, we are identifying what are doing whole portions of ASM code. So we can see this as a reverse engineering process.

  • 0x3EF is maybe the most interesting one: The ENERGY bar. Although Indy dies immediately in contact with other opponents or falling in uncontrolled areas of the level. Handling this memory portion properly will make Indy immortal by not receiving any damage during the gameplay(not only not decreasing lives as my initial intention...). Someone can beat the game even faster with this one... But this is not as easy as replacing by NOPs...

    ... TO BE CONTINUED...

No hay comentarios: