miércoles, 6 de julio de 2022

Reversing a DOS Game (Part 1/3)

I would say, Indiana Jones and the Last crusade(arcade), Madshow, and Loom were very influential games in my life. The first, was the very first one I actually own. It cost my parents 500pts(equivalent today to 3€).

The game is divided in 4 chapters. The first represents when young Indiana, as in the film, is trying to escape with the Cross of Coronado. In the DOS version whenever your lives expired, you were done. Second chapter represent the catacombs of Rome together with the climb of the German castle where Indy father is retained. The third belongs to the Zeppelin scene. These two chapters, you could continue gaming and infinite number of times, there was plenty of oportunities to train in these levels. The fourth and last chapter represented the final scene of for the film, where you need to get of Holy Grail, to save Indy's father in the famous Jordanian temple. This as the first, you could not continue playing if your lifes expired, no training possiblities in this last level level.

1993, me being 11 years, my only owned game, and impossible to beat. Always dead at the fourth chapter, with no relevant progress. I eventually surrendered, but promised: "one day, my future self, will hack you, and then I will finally beat you".

During the following years there was unconsciously a plan in my head (or maybe just a fantasy): "which kind of hack, would help me beat the game without being too much intrusive at the same time..."

Almost 20 years later, 2012, and with enough ASM debugging experience behind:

The idea was to make the game to not decrease the LIFE bar. Locate the instruction performing the action and bypass it. The initial assumptions were the following:

  • There would be a x86 DEC instruction performing that action
  • Likely that DEC instruction is decreasing a memory position and not a register, not a stack variable.
  • That would be inside a middle to low size function similar to:
HandleDeadIndy() {
    ShowDeadIndy();
    FadeOut();
    DecreaseLife();
    RestartGame();
}
Some visual dissasembler like IDA made easier identify these kinds of candidate functions.

The number of DEC instructions:
# ndisasm -b 16 INDY.COM > Indy.asm
# cat Indy.asm | grep -i dec | wc -l
rounded 450.  Not impossible one by one, but I needed to apply the first two assumptions to filter out. Dissasembling the binary you can find that DEC instructions could take 1 2 3, and 4 bytes. Almost in the same the order in number of appearances, and those accessing memory are likely the 3 and 4 bytes ones, some examples:

00008B30  FE0EAA03          dec byte [0x3aa]
00008F80  FEC8              dec al
00009161  FE4C06            dec byte [si+0x6]
00009230  FEC8              dec al
000099A7  FEC9              dec cl
00009A02  4A                dec dx
00009A10  4A                dec dx

The number of candidates reduces to ~25. The strategy is to replace those bytes by the NOP instruction 0x90 (1 byte):
  • Create a copy of the binary (INDY.COM)
  • Replace that potential DEC instruction by the required number of NOPs with your favorite Hexadecimal editor
  • Play the game for testing
The expected result is an immediate crash of the game... but the mix of the assumptions taken and very good luck made the hit at the second attempt! LIFE bar was not decreasing after each dead!

That was it, the pattern FE0EF003 needs to be replaced by 90909090.

Next was remembering how to play again as I did in 1993. Where to jump, when to punch. The hacking exercise took the evening of that day, but finally beating that 4th chapter took the rest of the night... not easy. I was relieved that my younger self was wise enough to surrender and stop wasting time, because I could never beat it without that hack... 

If you read this and beat it in the same way, please post a screenshot in the comments...

That happened in 2012, still 10 years ago, but social networking allows you to find people with similar experiences and struggles, and decided to document a bit that experience.

However, now in 2022, 10 years older, I realize I did not pay any attention to the actual hacked instruction. LIFE bar not decreasing!! Time to BEAT!, just started playing after the achievement.

The method used was even inelegant, brute force maybe, and never stopped taking a careful look around that instruction. What was that instruction revealing? Could I do more?

FE0EF003          dec byte [0x3f0]

TO BE CONTINUED ...

No hay comentarios: