The good old days: cracking 16-bit DOS games

5.00 avg. rating (96% score) - 2 votes

I recently wanted to play one of my favorite old games again, Zentris for DOS by Zensoft and realized how much things have changed. First, my 64-bit version of Windows 7 doesn’t like 16-bit DOS apps and refuse to run the game:

Attempting to run on another computer with 32-bit Windows 7 fails because Windows Vista and above no longer supports 16-bit apps in full screen mode required by the game. Although there are various hacks to remove the restriction – some of which may prevent Windows Aero from working, the proper solution is to use DOSBox, which helps me get the game running:


However, because the version I have is a demo version, I was soon irritated by the numerous registration prompts:


Since the full version is no longer available for purchase/download either from the official website or from various abandonware sites, with some time at hand, I decided to disassemble the source code in order to remove the registration prompts.

Decompiling the code

My first attempt was to use the Interactive Disassembler to analyze ZENTRIS.EXE. However, this failed with error “sp-analysis failed” and showed an incomplete disassembly:

sp is short for stack pointer. In other words, IDA was unable to identify where the sub-functions start or end, presumably due to unexpected changes in the value of the sp pointer, and stopped analyzing the EXE file. I found this post which suggests manually identifying the sub functions. Not an easy task!

The proper solution is to be aware of the fact that the executable has been packed to save disk space, preventing IDA from disassembling it. Using my favorite DOS packer analyzer, unp411, I was soon able to detect that ZENTRIS.EXE has been compressed using PKLITE and unpack it:

IDA now disassembled the file properly:

However, now the unpacked game didn’t even seem to run but instead complained “File ZENTRIS.EXE has been illegally modified”. The author has implemented integrity check of the executable file to prevent exactly what I am attempting! What a hassle, but in the end I successfully used Turbo Debugger (IDA does not support debugging 16-bit apps) to locate the various checks, and use HIEW to replace these instructions with 90 NOP, allowing the unpacked game to run.

Removing the registration prompts

It’s only now that the real fun began – removing the various registration prompts. Using the same technique as above, I was able to remove the text-mode prompts before and after the game, and the modified game runs flawlessly till the end, when an error message “Divide Error” is shown upon exit:

I am unable to identify where exactly this error is coming from. From Turbo Debugger disassembly it seems that the error occurs after a RET instruction – most likely due to some previous instructions overflowing some segment registers. Ignoring the error since it does not seem to affect the game functionality, I decided to proceed to try to remove the graphical registration notice.

For a moment I was not able to find any references to the main game logic in ZENTRIS.EXE. It was only then that the structure of the game is clear to me. ZENTRIS.EXE is only used as a loader that loads ZENTRIS.OVL as an overlay. ZENTRIS.OVL is then responsible for the main game logic.

ZENTRIS.OVL is also compressed, surprisingly twice. It is first packed with PKlite and then linked with the EXEPACK option:

Luckily there are no other integrity checks on ZENTRIS.OVL and the game still runs perfectly with the unpacked version of ZENTRIS.OVL.

The challenge

However, I never found a way to make Turbo Debugger step into the unpacked ZENTRIS.OVL in order to locate the call to display the graphics registration prompt. In fact, under Turbo Debugger, the main game wouldn’t even start, complaining “out of memory” when trying to load the overlay. It is unclear to me why the game has to be designed this way, perhaps to limit the main executable size to less than 64K (the unpacked ZENTRIS.EXE is already 60K), or more likely, to make disassembling the game a hassle.

The only approach was to use IDA to make an educated guess on which instruction is responsible for the prompt, and then use HIEW to replace them with NOP. Inside ZENTRIS.OVL, I was able to identify a few calls to DOS INT 16h, responsible for keyboard monitoring. Replacing them with NOP and the game was indeed affected, either stopped responding at the menu, or showed the registration prompt and stopped responding without accepting keyboard input. This proved that I was on the right track.

Unfortunately, the actual counter to how long the prompt should be displayed or the correct way to remove the prompt was never found. In fact, some functions around the “suspected” area are not disassembled properly by IDA, with some instructions still showing as data bytes. It seems as if the author has obfuscated the source code to prevent disassembling. Unless I find a way to step into ZENTRIS.OVL at runtime, at this point it is not worth the time and efforts for me to proceed further.

The modified ZENTRIS.EXE with the text mode registration prompts removed, which is good enough for me :), can be downloaded here. The original game can be downloaded here. I hope someone with the right expertise can give me some hints on how to step into ZENTRIS.OVL and complete the hacking job :)

UPDATE (May 2013)
Thanks to an anonymous reader, the hacking challenge has been completed and the modified version of the game with all the registration prompts removed can be downloaded here. The complete removal of all the demo prompts (including the graphics registration screen) is done by applying a six-byte patch to the decompressed ZENTRIS.EXE and ZENTRIS.OVL files, detailed below:

File integrity check – ZENTRIS.EXE – Offset: 0x8ca8 – patch to 0xcb
First registration prompt – ZENTRIS.EXE – Offset: 0x94e2 – patch to 0xe9 0xb5
Graphics registration screen – ZENTRIS.OVL – Offset: 0x9e5f – patch to 0x0d
Closing demo prompt – ZENTRIS.EXE – Offset: 0x926a – patch to 0xeb 0x5c

Refer to the comments section of this article for several techniques that are useful in debugging old DOS games. Take note that the download link and all information provided in this article as well as the comments that followed are strictly for educational purposes only. Whenever possible, please support the author by always purchasing paid software via official means.

See also:
Programming Nostalgia: revisiting Mike Wiering’s Mario game written in Pascal
USB flash drives on 8-bit ISA bus using CH375 ISB to USB adapter

5.00 avg. rating (96% score) - 2 votes


A tough developer who likes to work on just about anything, from software development to electronics, and share his knowledge with the rest of the world.

17 thoughts on “The good old days: cracking 16-bit DOS games

  • June 11, 2012 at 9:39 pm

    I have no clue if this will work, or how experienced you are with programming. But i guess you can modify dosbox emulator to give you a great hint.

    i am not familiar with the codebase or what functionality that already exist there.. but i assume you can somehow get a raw\dump of the game/app running. Including the obfuscated area when it is decoded…

    One approach to find the graphic registration call could be to add a modification to dosbox so you can push a button, right before the game shows the registration screen, to make the emulator set a point at that address it processed at that time or to start dumping from that time.

  • June 11, 2012 at 10:23 pm

    Thanks for the suggestions of modifying DOSBox. Right now I have tried to compile DOSBox several times successfully, but I have never attempted any major modifications before. I will try and see if I could get it to provide me any useful hints. :)

  • July 18, 2012 at 7:39 am

    Still working on it? or having a break? :)

  • July 18, 2012 at 8:59 am

    Haha, I have stopped it shortly after writing the article. If you have any good suggestions, leave it here and I will continue :)

  • April 6, 2013 at 2:34 am

    If anybody is still interested in the cosmetic changes needed for this shareware DOS game, here goes…

    Like already mentioned the file ZENTRIS.EXE is packed with PKlite and ZENTRIS.OVL is packed with PKlite and linked with the EXEPACK option. First you need to unpack both files with UNP for DOS. ZENTRIS.OVL needs a second pass through UNP to remove the EXEPACK. This is needed before making the changes below.

    File integrity check – ZENTRIS.EXE – Offset: 0x8ca8 – patch to 0xcb.
    Closing nag screen – ZENTRIS.EXE – Offset: 0x926a – patch to 0xeb 0x5c.
    Starting nag screen – ZENTRIS.EXE – Offset: 0x94e2 – patch to 0xe9 0xb5.
    Registration nag screen – ZENTRIS.OVL – Offset: 0x9e5f – patch to 0x0d.

    In total a six byte patch which removes all the nags.

  • April 7, 2013 at 10:31 am

    Thanks a lot for sharing :)

    Let me attempt the modification and update the article with your patch to remove the registration prompts :)

  • March 17, 2014 at 7:30 am

    Hello MD.

    Can you find some time and write to me your e-mail at: oles[no-sp@m]o2[dot]pl I have problem to fix old DOS game "Summer Challenge" to do not quit with disable debugger error when DosBOX have too much cycles. I unpacked executable by unp ofcourse and find and patch check, which caused quit to DOS suddenly without any message. I can send you my archive with Debugger and other tools and my notes about suspect places on e-mail. I tried wrote to author of text about unpacking execs with DosBOX debugger on some blog entry, but he do not reply my over more than two weeks. So probable he do not have time.

    I like to patch patched old games which I like, to bypass docs checks completly if it possible and sometimes lazy crackers leave it, but do not have such a great tool like DosBOX in old time, which allow to see whole game screen changes in background when moving on assembly instructions. I hope you can help me, because I see you have experience. Sorry for my bad English. Best regards: olesio :)

    P.S.: if you like you can use Jabber to contact me instead e-mail my JID is: olesio[change-it-to-@t] or you can visit TheCompany forum from url above. Thanks in advance and sorry for long comment. Just please let me know can you find some free time to help me. Maybe not now, but maybe in a short future :)

  • March 17, 2014 at 10:39 am

    Hi olesio,

    Thanks for sharing! Already replied to your email address. Hope I can help with the Summer Challenge DOS game.


  • June 16, 2014 at 7:12 am

    There is more than one way to go about beating the protections used by Summer Challenge. Decompressing the executable isn't necessary. I'll explain why. The executable has overlay code which is read from disk and loaded on the fly by the overlay manager. The overlay manager is called through interrupt 0x3f. After each int 0x3f instruction you'll find a byte and a word which tells the overlay manager which overlay segment to load from disk and at what offset execution should continue within the loaded overlay segment. The first byte is used to determine which overlay segment to load. The second word is the offset in the overlay segment in memory where the overlay manager returns to execute the loaded overlay code. The overlay manager first checks if the overlay segment is already loaded. If it is it transfers control to it using the offset after the int 0x3f instruction. If it's not in memory it reads it from disk in memory and executes it.

    Now knowing this there are a few ways to take advantage of this. The things we need to defeat are the debugger check and the copy protection wheel. The game uses compression and can be uncompressed using UNP. However the game stops running properly and silently quits to DOS. The game does check where the overlay code is in the file by looking for the overlay signature 'MB'. That's not the reason though, that the game quits without warning. The game quits when the overlay manager tries to load a segment from the overlay on disk. I didn't investigate further because I didn't have to. I found out that the code to patch is in the overlay it self. This means you can edit the patches in without the need to decompress the file first. The reason overlays are untouched most of the times is that the compressor used often has no understanding of how the game handles loading overlay code in memory. Even if it knows by hooking file reads and checking weather the file pointer is within the overlay, it still needs to decompress the overlay first, then judging by the file pointer returning the overlay code the game requires. This defeats the purpose of overlay code and introduces overhead since it would need to do this every time an overlay segment need to be read from disk.

    Since the code to patch is in the overlay we can simply do a binary string search and patch the necessary bytes. The included crack SUMCRACK.EXE does not fully remove the copy protection as in you still need to put in a ticket number to progress. Most of the times copy protection schemes use a boolean flag which indicates if the user already answered successfully once. Summer Challenge is no different here as you can find the boolean at offset 0xc4c in the data segment. To beat both protections you need to patch two bytes. Use the search strings below.

    Copy protection flag check – SUMMER.EXE – Offset: 0x13ef30 – patch to 0x1
    Anti debugger check – SUMMER.EXE – Offset: 0x13ef57 – patch to 0x33

  • June 16, 2014 at 7:13 am

    Making a loader was fun as well since the programmer really didn't like anyone debugging the game. It uses instruction sequence timing and it has 0xcc (one byte opcode int 03) breakpoints inserted at certain points which do nothing if no debugger is attached to the program, but cause a problem when debugging read in overlay code. I hooked calls to the overlay manager and patched the read in code and transferred control to the requested offset in the overlay segment. I had to rewrite the debugger handler to only debug code from the breakpoint at the end of the overlay manager interrupt handler to defeat that. It also tries to re-hook the breakpoint interrupt at launch, but does so in a nice manner. It doesn't try to modify the IVT directly. Oh and the debugger present check is done by timing instruction sequences. An often used trick to catch a software debugger.

    Some fun game copy protections to debug and beat are "The Humans" and "Alone in the Dark 2". The last one has a really nasty surprise when you try to modify the copy protect successful flag before having answered the copy protection successfully. The Humans is one of those exceptions that doesn't use a copy protection flag and forces the copy protection to be answered at game launch. The protection used is also a nice change compared to the many boring and uninspired copy protection schemes used.

  • June 16, 2014 at 10:44 am


    Thanks so much for updating me on the progress of cracking this game and thanks for sharing your knowledge on the various copyright protection scheme use in these early DOS games. Do you perhaps have the link to download the mentioned SUMCRACK.EXE and the various executables produced as part of the cracking process? I will spend some time trying them out for a better understanding.

  • June 16, 2014 at 6:26 pm

    It includes the source code for the loader and the compiled loader executable. To directly patch SUMMER.EXE just use the offsets I provided in a Hex Editor and patch them.
    When debugging I only used DOSBox. I use the latest SVN version with heavy debug options enabled. Below are the notes I made while debugging.


    ds:0c4c copy protection flag
    ds:5c6a pointer to quit game context data

    11a8:6523 quits game

    patch 1b3:df62 interrupt return from int 0x3f
    set interrupt 0x3f ISR call @ 01b3:c157

    overlay (copy protection function and anti debugging check function) int call @ 01b3:175a with params 01 00 00
    overlay (clear copy protection flag function) int call @ 41be:00c2 with params 31 20 00

    41be:1e56 copy protection
    41be:1e8c debugger present check
    41be:1e8c push bp 55
    41be:1e8d mov bp,sp 8bec
    41be:1e8f or ax,ax 0bc0 patch 0b -> 33
    41be:1e91 je 1eab 7418
    0173:0000 funcion that clears copy protection flag
    0173:0014 mov byte [0c4c],00 c6064c0c00 patch 00 -> 01

    When the game intro starts break into the DOSBox debugger and put a breakpoint on int 0x3f. Continue execution and press enter to skip the intro. The DOSBox will break on the overlay manager call we need. Look at the byte and word after the overlay manager call int 0x3f instruction. So in the disassembly you should see the hex string cd3f010000. The 010000 being the params for the overlay manager. Now trace through the int 0x3f. Do not step over, but use step into. After the iret instruction you'll be in the overlay code segment that contains the functions we're after. Now you'll find the debugger check at offset 0x1e8c and the copy protection at offset 0x1e56 in the overlay segment just loaded. Now look at the loader source code how to use this to create a loader that patches the code on the fly.

  • June 16, 2014 at 6:53 pm

    The segment values will vary depending on what you have loaded in DOSBox. If you follow the steps it'll be easy to get to the code you're after. Disabling the copy protection in the loader is done a little bit different than the plain patching. The plain patching pretty much reverses the meaning of the boolean value of the copy protection flag. Meaning zero (false) will be a successful copy protection answered state. In the loader I set the copy protection flag instead of clearing it in the code that is loaded by the overlay manager through int 0x3f with params 31 20 00. 31 is the overlay code segment to load and 0x20 is the offset to continue execution. At offset 0x14 in that segment you'll find the game clears the copy protection flag. The loader simply sets the copy protection flag instead of clearing it.

    This assume you already know what code is responsible for the debugging check and copy protection. To find the debugging check put a breakpoint ont interrupt 0x21 and function 0x4c (ah). Then use the stack to look through the activation records (local function stack frames) to find why the game exits. You can simply do a d ss:bp to find the next activation record. Some functions will not have a stack frame and simply return. These functions will not change register bp to creat a local stack frame. You can use the same technique to find the copy protection caller when breaking into the code where the copy protection function waits for user input. Remember though that this is not a fixed way of doing things. For example you can also get to code that is part of the debugging check by breaking on interrupt 0x21 function 0x2c (gettime). It uses the seconds and 1/100 seconds field returned as a reference point to measure how long it takes to execute a sequence of instructions. If it takes too long it assumes a debugger is present. It also checks for handles used by well known debuggers in DOS. It checks if it can open NU-MEGA, SOFTICE1 and TDHDEBUG through interrupt 0x21 function 0x3d (open existing file).

  • June 16, 2014 at 8:37 pm


    Thanks again for the detailed comments. They are of great value to those who are still interested in these old but fascinating DOS games. MS-DOS is still alive but not many nowadays are still knowledgeable enough to use it – let alone investigating and cracking DOS applications.

    Btw, I am not able to download from the link you provide (HTTP 404 Not Found). Can you help me check the link? I will be trying out the loader and writing another article on how to use DOSBox debugger to locate and crack copy protection in these early DOS games following your instructions.

  • March 15, 2016 at 5:29 am

    Hi MD,
    Could you please contact me at mags(et)kultmags(d0tt)com.
    I have an old game and the permission of the programmer to crack it!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>