I’ve got a handheld laptop that I use to play games during my daily train commute. I thought about trying Tomb Raider Anniversary, since I had enjoyed Tomb Raider Underworld in that same trilogy. When I tried to run the game however, it couldn’t create the save game profile. Attempting to start the game anyway would crash with a disk errror. Part of me shrugged it off as just something that happens with old games, but part of me thought I could figure out the problem and fix it. I haven’t fixed it yet, but I’ve learned a lot from the attempt.
The first thing I tried was googling around for a solution. I found many references to profile problems, but they seemed related to problems losing save files after successfully making one. I did find people with my problem, but no one knew had to fix it. Most tellingly, someone had the same laptop as me. The first useful clue I found was in the save file directory. I could see that it could make folders for each of my profiles, but the actual file would be completely empty. At this time I cracked open a book I had bought about hacking games, and started with the tools it recommended. I tried to use CheatEngine to discover how the game was writing the file. But it wasn’t until I got out ProcessMonitor that I could actually capture the file system call.
This also told me where in the game’s code the calls were coming from.
I attached a debugger and found the code
I could see the code calling CreateFileW and WriteFile which are Windows systems calls. And I could see what options they were passing in. I ran these tools directly on the broken laptop, and was able to follow the code to GetLastError which returned error code 0x87 – Invalid Parameter. I found from StackOverflow that this could be caused by a bad file size, and I verified that the broken laptop had a physical hard drive sector size of 4092 (rather than 512). I reasoned that it would be hard to change the game to write a file with the correct size, and instead tried to enable file buffering.
Trying to Patch the game
I learned from the assembly code that it was pushing a value of 0x6000000 which FILE_FLAG_NO_BUFFERING. I thought if I could remove that flag and replace it with 0x2000000 everything would work. I used the debugger to find and replace the instances of 0x6000000 connected with CreateFileW and then tried to save. And it couldn’t save. Over the next few hours I tried following the code to see where it was coming from. I learned that the code I saw in the picture only existed in memory, and it wasn’t part of the executable file. I tried walking through the code and learning how it was built, but I’m still pretty stuck. My theory is that this is some sort of scripting language (.net? lua?) and as the game starts up it defines the functions that it is going to use. I’ve never worked with this before, so I’m not sure what I’m doing. I think that somewhere in the games files is the data it uses to generate the code. There are about 4-5 data files that get loaded into memory, and I can see where the game requests a page of memory. But I’m definitely in over my head.
Trying to understand the bug
I was also suspicious about what was going on. I tried to create a test program to expose the flaw. I wrote the following Python program that calls the same function:
file_path = "tra_test.txt"
handle = win32file.CreateFile(
win32con.GENERIC_WRITE | win32con.GENERIC_READ,
win32file.WriteFile(handle, "a" * 512)
if __name__ == "__main__":
My main desktop had two harddrives. The first was a small SSD with a 512 sector size, the other a large HDD with a 4092 size. I reasoned that this script would work on the first and not the second. Surprisingly, it worked on both. Then I ran the script on the broken laptop. And it failed, with an error 0x87. But even more suprisingly, I couldn’t get it to work with any file size. Not 512, not 4092, nothing. Which means that the file size isn’t the issue. Instead, something else is wrong with the harddrive causing it to refuse non-buffered writes. If I remove the FILE_FLAG_NO_BUFFERING, then everything works, which means my earlier plan of patching the game is still a good one. I just can’t figure out what to do next.
After writing most of this post, I decided to look at the game code again. I could see that the file loading function was being written in dynamically, and I spent some time with this loading code. I ended up being some kind of anti-tamper system. It was essentially calculating each byte of code bit by bit while reading and writing from a table. I haven’t spent any time figuring out where this table comes from, but my assumption is that to alter the value of this flag, I would need to alter this table. And I imagine this table is used for decoding the everything else, so a single alteration could change other unrelated instructions. I really don’t want to do that – because I don’t know how far this encryption scheme goes. It appears that there are more than one function that derives code. I haven’t found the code that loads the table. And in any case, this code is ALSO loaded dynamically, which means there’s at least one more layer of indirection that could trip me up.
My new goal is to try to understand why my handheld laptop won’t handle unbuffered reads. I’m not optimistic I can fix that either, but I would also learn a lot from that experience.