Join the discord

Soldier of Fortune - bugfix

03 Apr, 2013 19:50
I'm continuing to play outdated games, and after completing Max Payne, I've remember about another first person shooter classic - Soldier of Fortune.

With times goes by, running old games on a modern computer is becoming an art. There's no longer technical support, official sites and forums of the games are long time dead, not many people are playing these games anymore, so no one can give you and advice, and last but not least - Windows's back compatibility will most of the time fail to resolve your problem.
Then again, there are lots of sweet games from the old days that deserve replaying for the sake of nostalgia.

In this case your only solution is the good old debugger, and the even older - beer.

Now, the game is quite old as i said before. It was released back in 2000, and at that time was a hit. Good thing about the old games is that they doesn't have too advanced protections like the games now days. So when you try to modify an old executable, you will most probably wouldn't need to bypass any software protections, which is hard, time consuming and depending on your current location - illegal.

When i first started the game, everything seems to be in order... until the screen blinks and the game just exit to my desktop. Trying to run it in compatibility mode for Windows XP or Windows 98 didn't work either, so I throw the exe in the debugger.

Little playing around, reloading, setting breakpoints and tracing through the code lead me here:


Executing this call will crash the game, so let's see what's going on here...
The bad function itself contains this:


Before that call, there's another one executing glGetString function from the opengl32 library, that returns a pretty large string:


That string is a list of OpenGL capabilities provided by the video card. This information is (i think) returned from the OpenGL and the video card drivers, so depending on the PC configuration it may vary.
Well, times passes by, and since year 2000 things get changed, modern video cards provide more and more new functionalities, so logically, there will be more capabilities in that list returned by glGetString.

Why I'm writing this?
The glGetString function gets the video card capabilities, and the next call is actually a sprintf-like function, that format and (depending on the game configuration) can display a debug message.
That technique is often used by game developers for faster debugging of their products. I'm not saying, that only game devs use debug functions of course, but i've seen this mostly in game executables - containing debugging functionalities, that most of the time are activated by specific debug parameter to the executable, hidden configuration value or in-game key combination - all hidden for the regular user (well, unless the regular user decide to dig into the game's guts).
So, when the game development ends, the debug functions will stay in the code and depending on the design, they may execute without notice to the user or for example, write their result in a log file. Sometimes developers just comment the exact "sprint" function that is located in one place, instead of removing all the sprintf wrappers, spread around the entire game code.

That leads to executing a dead function, that technically do something, but basically doesn't.

Get that code for example:
pseudocodemain() {

    result = do_something1();
    if (result == false) {
        debug("Cannot do something 1...");
    }
    
    result = do_something2();
    if (result == false) {
        debug("Cannot do something 2...");
    }
    
    result = do_something3();
    if (result == false) {
        debug("Cannot do something 3...");
    }

}

debug(msg) {
    print(msg);
}

When the code is tested and it works, we can easily remove the "print(msg);" line, without needing to remove all the checks from the main() procedure.

The problem is that, there's something called buffer and another thing that most of the time can be defined as bitch to the buffer, called buffer overflow.

In this case with Soldier of Fortune, the buffer for the debug function is defined to be N bytes long, but since the times goes by, and new OpenGL functionalities were added to the modern video cards, the original buffer got too short to handle the full capabilities list.

So, when the function is executed it writes the list into the memory, and unfortunately at some point the buffer size is exceeded - it starts writing into other parts of the game memory (sometimes crucial parts), and in my case - tries to write to addresses exceeding the memory map, leading to a blink-and-exit situation.

What can be done in this case?
I can think of two options:
1. Find where the buffer is located and if it's a dynamic one, change it to a larger size or if it's a static place in the code, make some ninja code-cave relocations, change code sections sizes, fix offsets... OR, or...
2. Strip out the function that's useless anyway and go pwning bad guys.

The decision is not hard, so number two it is!
For that i can change to code to RETN at the very beginning of the debug function, from this:


to this:


This way, every time the program tries to execute that function, it will immediately return, without executing the one inside, that has the buffer overflow problem.

Doing this fixed the game for me, so the lazy solution was a win. I'm not going to release patch for this one, since it's pretty simple solution that probably anyone can apply it by itself.

And that's that.

Comments

* You have an opinion? Let us all hear it!

Guest 26 Aug, 2019 20:58
I'm having a hard time trying to run SoF.exe on Linux (xubuntu 16.04 - Radeon HD4650 driver) The catch is: I can play all other Quake 2 engine games fine on Wine (Sin, Daikatana, GzDoom, ports of Quake1, ports of Quake2, you name it...) except for Soldier of Fortune. The log file tells me the system is not finding OpenGL and it's falling back to "software rasterizer". I already tried making symlinks to "libGL.so.1" in many locations, I tried other tricks as well, nothing... How exactly is the executable looking for OpenGL files? This seems to be a problem with AMD cards in general, for Nvidia a driver update solves it. (same problem is observed for people trying to run the Steam client, because it is dependant on 32-bit Opengl libraries)
XpoZed 19 Oct, 2016 09:38
Interestingly enough, there's actually someone who read my site... ;)

Alright, I'll take a note for the next time.
Guest 04 Oct, 2016 12:48
"And that's that."

some of us are dumb and dumber and really appreciate it when guys like you release this stuff.
© nullsecurity.org 2011-2024 | legal | terms & rules | contacts