Ruben has cracked QCRACK, and he kindly reached out to me once more with help on my own reversing efforts. I wrap up my own reversing efforts having felt I’ve learned all I could from this exercise.
The related posts and pages are:
- the original QCRACK post where I try to learn about the original DOS executable
- Continuing QCRACK
- flowlib in QCRACK
- The Super Advanced QCRACK ENCRYPT.EXE
- QCRACK SKU Encryption
(This is a recap, and captures some things I don’t think I have elsewhere.)
To just run QCRACK you’ll need the DJGPP files CWSDPMI.EXE, CWSDPRO.EXE, CWDSTUB.EXE, and CWSPARAM.EXE.
To use QCRACK, pass it a challenge string generated by the Quake Shareware program.
At this point it will complain about not having the sku.17 file that is used to find the game the challenge string is for based on a number generated from the challenge string.
If you already know the game, you can pass that to QCRACK
qcrack -g quake2 q12345678901
This bypasses the need for the sku file.
Regardless of how you get the game the challenge is for, it then uses that info to look up the .DOC file in flowlib.lib based on where flowlib.dir points to for that game. (You could also extract the .DOC files from flowlib.lib.)
So in addition to QCRACK.EXE and the DJGPP files, you should also have sku.17, flowlib.dir, and flowlib.lib from the Quake shareware CD.
I was stuck at where the last blog post left off because the disassembler seemed to have been breaking down. I believe radare2 was tripping over QCRACK using 32-bit extensions at those points.
Ruben made the breakthrough with basically a one-shot debugger script (the functions of which can be found in the link to his site I give above, my version that I made as a full script I used is found here). It injects a SIGTRAP signal that causes an error with a few bytes of memory displayed. He wrote it so it runs QCRACK to output those few bytes, parses that output and collects those bytes, then runs again to print a few more bytes and grabs those, etc, until you have all the bytes you need.
This helped me because in reversing with the disassembler it doesn’t actually run the executable so the block of memory that it builds up isn’t there, and the way I was getting around the mangling of the instructions is I navigate to each instruction. It displays the first few instructions in the display correctly, but then they get messed up.
One problem with my reversing is I found an instance of an input challenge string, Q76820422412, that my implementation gives a different result than QCRACK. Some thoughts on how this might be: It could be bit operations, such as a shift left in a 16-bit word in the original is done in 32 or more bits in my implementation, and shift back to the right those bits aren’t cleared out as in the original, or perhaps a bit mask is off. Regarless, the nature of the key gen is that the results of this difference rarely propagates to the final output.
More considerations that were beyond the scope of this work are to investigate if there’s any deeper understanding. For instance, for things like the validity check, is there an underlying equation? Because I’m wondering if there’s some rules that say how to generate a valid challenge, or if the operations are arbitrary and it just generates a challenge until it gets what it says is a good one.
That leads to other considerations. How are the challenge strings generated? How does the Test Drive scheme in general work? How does it encrypt files in general, and how are they fully unencrypted?
But this is where I leave it off. I have achieved my goal of a first reverse engineering project and seeing how QCRACK works. Thanks again to Ruben. I have worked on this on and off for several years now and am ready to move on.