Decompiler Installshield Error

0 views
Skip to first unread message
Message has been deleted

Ronald

unread,
Jul 14, 2024, 2:53:49 PM7/14/24
to compresaces

Abandoned Witch here. Apperently the official video game of Salt Lake 2002 Winter Olympics has problems when running on modern hardware (like Windows 7). First when installing it gives a not enough memory error, and second, after installing, it crashes to desktop after starting the game (no opening logos). Any solutions?
It did work on Vista but not on 7.

Hi!
I own an original, US copy of this game and while looking through my old CDs a few weeks ago, I wanted to take it for a spin, having played this game the last time many-many years ago.
When I tried installing and running the game now, I encountered exactly the same issues as you did.
As someone who's interested in old software, disassembly and reverse engineering, I've decided to embark on a bit of a journey and try to debug and find out exactly what causes these errors on modern systems. While I'm there, I figured I'd also make a post about it here for anyone who ever runs into these exact problems in the future and is interested about their root cause.

Decompiler Installshield Error


Download https://ssurll.com/2yMd2S



When the game installer queries your system for how much physical RAM it has, it uses an ancient, long deprecated Windows API called GlobalMemoryStatus that basically cannot count above 4GiB due to storing the result in a 32-bit integer.

So if you have more RAM than 4GiB, this API will return a negative number due to integer overflow, as even Microsoft mentions in their API docs. In turn, when the installer script receives this negative number from Windows and then compares it to 64MB (as that is the minimum memory requirement for the game), it will find that this negative amount of memory is less than that and will abort installation with the "There is not enough memory available to install this application. Setup will now exit." error message.

The installer script was coded in a way that it unfortunately doesn't recognize when it has received an obviously bogus negative number for the available RAM. The game developers most probably didn't expect that the game would be run on systems using more than 4GiB of memory. And even if they did - and could have used the more future-proof API (GlobalMemoryStatusEx) which at the time was already available - they most probably opted for this one instead, because the newer API is not supported on Windows 95 and 98.

Here's a snippet of how this installer script looks like - it has to be decompiled from the setup.inx file using an InstallShield script decompiler tool, I've used SID (sexy installshield decompiler by sn00pee):

The GlobalMemoryStatus call populates a struct (local_object1) with information about the machine's RAM. The nTotalPhys/dwTotalPhys struct member contains the total physical memory present in the machine in bytes. The code divides it by 1024 to get the total RAM of the machine in kibibytes and then returns that value.
Back to the code on line 926 (or offset @00004D99:0007), the total installed RAM in KiB gets stored inside the local_number1 variable here:

Since this comparison is done with signed numbers - and as already mentioned, GlobalMemoryStatus returns negative numbers if there are more than 4GiB of RAM - this if-statement will evaluate to true on modern machines with high RAM capacity. This will in turn show the message box with the "not enough memory available" message and abort the installation:

Fixing this can be done easily with SID. Changing the comparison into something that would be far less likely to happen on an actual (modern) PC is what I've opted for. The way I've patched it is to change the less-than operator to an equals operator:

This way the setup will only abort if someone has exactly 64000 KiB RAM available, which is very unlikely, as even if you were to have 64 megs of system memory installed, function_190 would most probably return around 65536 KiB, thus it would pass the check.
You can find my patched setup.inx file here in the attachments or at the link above in the TL;DR.

The crash itself is of type EXCEPTION_ACCESS_VIOLATION (error code 0xC0000005), which means the program is trying to read or write memory at an invalid memory location. The crash logs also disclose what the address of the problematic instruction is: 0x00000000. This means the application expects CPU instructions to be at memory address 0x00000000, but since that is zero, it won't be able to execute any instructions from there and is going to just shut down itself.

Figuring this one out seemed harder, so I've utilized the help of VirtualBox to create a clean virtualized environment for researching what exactly causes the crash. What also helped tremendously is that I've noticed early on that beha_r's fixed EXE does not crash; the game can be played flawlessly when using it.

Initially I thought - like maybe yourself - that something about the OS version itself is the trigger for the crash. To confirm this assumption, I created a virtual machine with Windows XP SP3, and tried running the game in it. To my surprise, it still crashed!
Just to confirm it's not the DRM causing it, I've also downloaded an old crack for the game (by DN $iMoN) and it crashed even with that one both in the VM and also on my modern Windows 10 machine.

I then started looking at different parameters I could change. After some experimenting, I found that the crash can be controlled with the amount of VRAM that is allocated for the virtual machine. When I allocated 128MiB, it was always crashing. With 64MiB, it pretty much never did.

Any value at or above these VRAM values at the given resolution will crash the game. So basically if you want to run the game at 1024x768, just set your VM's VRAM capacity to 73MiB, and you'll be able to run the game just fine.

Since beha_r's fix has been available for a while now, I started looking for answers to the second question first.
I contacted beha_r and he was kind enough to share a before and after EXE with me so that I could see exactly what he changed in the original executable.

Besides obviously patching out the function call responsible for the CD check, he also changed another function's stack. By reverting his CD check patch to only include the stack modification in the executable, I was able to confirm that this stack trick was indeed the one that actually fixed the crash.
The change he's done is just simply to increase the stack size by 4 bytes for the function at address 0x00531C30:

So it seems more space is given to this function on the stack.
Since this function is clearly the main point of interest in the quest to figure out why the crash occurs, I've decided to go through the painstaking process of decompiling it. Fortunately it isn't that huge and it didn't take that long, but it still did take some time to properly name, annotate and explain everything.

As you can see, this function is basically a VRAM capacity test. It returns the usable amount of video memory in MiB.
This leads us back to the first question - how does a large amount of VRAM crash the game here? Well, there's a bug in this function. See if you can spot it yourself.

The first mistake: apparently they never expected (or didn't care) that GPUs could go beyond 512 * 128K == 64MiB VRAM capacity. They relied on DirectX to eventually throw a D3DERR_OUTOFVIDEOMEMORY result when creating up to 512 surfaces and textures to properly calculate how much VRAM is available for them to use,
basically always expecting a failure, an eventual exhaustion of video memory.

They thought 64MiB would be plenty for a VRAM overload test as most consumer graphics cards at the time of development (2001) had around 16-32MiB VRAM. Even the game's system requirements mention a 32MiB GPU for optimal performance. Interestingly, higher-end cards that had 128MiB VRAM had already come out in 2002, so this bug could have popped up for some people already only mere months after release.

Funnily enough, the usable VRAM amount this function calculates doesn't even appear to influence anything; it does get saved, but doesn't affect gameplay or playability/game stability whatsoever, even if it's 0. In fact, beha_r's fix makes the game believe it has 0 MiB usable VRAM, yet it still works fine.

However the other mistake is a much more severe one: they messed up the bounds checks for BOTH arrays. The dummy texture and surface arrays were both defined as capable of holding 512 pointers each, however their respective loops overrun both buffers with one extra iteration when too much VRAM is available.
This is because the devs were supposed to use

Arrays in C/C++ are zero-indexed, meaning that dummySurfaces' first item is dummySurfaces[0], and its last item is dummySurfaces[511], not dummySurfaces[512]; that one points beyond the end of the array. With each successful texture/surface creation, this index is incremented, however it gets incremented one additional time - so 513 times instead of the intended 512 - due to the less-or-equal (

For the dummyTextures array, this isn't too big of a deal, the item dummyTextures[512] - which actually points to the 513th element of the array, thus is outside of it - only points to the next array's first item in memory, dummySurfaces[0]. That is overwritten by its own loop anyway later on.

The problem is with dummySurfaces[512], as dummySurfaces is the last array in the function's stack. When this last array is overrun, beyond its very last item, the function's own return address on the stack is overwritten with a pointer to a blank surface.

Due to how the stack is structured, the return address goes after all the variables and buffers defined on the stack. It just so happens that the very last buffer defined on the stack is overrun, corrupting the return address that comes just right after it.

While this last +1 surface is NULL'ed out, the original return address (that would tell the function where to go back after it finished executing) is lost forever and has been replaced with zeroes. That's why when the function finishes, it thinks it was called from 0x00000000, so it tries to go there to continue execution, but as that is invalid memory, the game crashes with an EXCEPTION_ACCESS_VIOLATION.

7fc3f7cf58
Reply all
Reply to author
Forward
0 new messages