TZ-Strip Poker - Tutorial

Available from -

Another day begins and once again I'm declaring my intentions to wage war against a particular genre of software, I'm targetting specifically those commercial smut programs disguised as games, with zero playability and a few dirty pictures. TZ-Poker actually might prove interesting as it is pure 16-bit, (written with the Borland OWL libraries), as well as hiding a missing file protection.

Starting TZ-Poker will result in the registration screen appearing, just a single serial number would appear to be required, you can try using bpx's now if you like but unless you are a confident tracer and watch the code segments very closely, that might not be the easiest approach. In older 16-bit applications such as this we can certainly rule out the registry as a location for any good code so lets use File Monitor instead.

You should get a fair amount of file accessing (all the graphics files presence are checked), but an attempt to read cs2.cfg should alert you. Create that file now and insert some fake serial number inside. We'll try now to break in on the code that reads the cfg file, a disassembly listing will help you get a precise location.

:0003.229A PUSH 047C <-- "cs2".
:0003.229D PUSH WORD PTR [BP+08]
:0003.22A0 PUSH BX <-- A good place to start tracing from.
:0003.22A1 CALL 0007.03D5h <-- Protection routine.
:0003.22A9 CMP BYTE PTR [368E], 00 <-- Memory flag.
:0003.22AE JZ 22E7 <-- Jump_over_shareware_nag.

This code is fairly simple but I want to explain why I located it so easily, the cs2 reference was evidently the first indicator, however you'll notice that if JZ 22E7 doesn't happen we reach a few lines further on a String Ref "HINWEIS", this seems innocuous enough, but it isn't, have a look through the Dialog References and you'll realise that all of these PUSH's are in fact the shareware notice.

I advise you patch in an INT 3 at the PUSH BX instruction so that you can temporarily trace through the 03D5h call, this CALL displays the "insert serial number notice", although as you'll see it is easy to defeat the protection at this level. Tracing below you'll note the following code snippets, remember that we have 2 objectives, firstly that our nag doesn't get displayed and secondly [368E] must be returned 0.

:0007.03F3 CALL 049C <-- Does cfg file exist.
:0007.03F9 OR AX,AX <-- Logic OR (recall AX must be non-zero).
:0007.03FB JNZ 0436 <-- Jump_cfg_file_exists.
:0007.0441 CALL 0382 <-- Real_checking_is_done_beneath_here.
:0007.0447 OR AX,AX <-- Logic OR again (require AX=1).
:0007.0449 JNZ 047E <-- Good_guy_jump.
:0007.047E MOV BYTE PTR [368E], 00 <-- Good_guy_flag.

Once again, if you understand the simple logic OR operation you'll realise that AX needs to be non-zero (although as this is compiler generated, 1 is the likely required value). You could just patch JNZ --> JMP at this level, but lets trace lower once again inside 0382.

:0007.0398 CALL 023A <-- Trace this check.
:0007.039E OR AX,AX <-- Yet again.
:0007.03A0 JNZ 03A6 <-- Good_jump.
:0007.03A2 XOR AX,AX <-- Obviously_bad.

The 023A check is fairly interesting, though to actually get a chance to examine the real maths of the call you'll need to ensure that you have a hyphen (2D) somewhere in the bogus code (I recommend position 4 *hint*), the controlling division check is shown below.

:0007.02CA MOV EAX, [BP-0C] <-- Value computed from the code.
:0007.02CE MOV EBX, 000F4240 <-- 1,000,000 decimal.
:0007.02D4 XOR EDX,EDX <-- Clear EDX for division.
:0007.02D7 DIV EBX <-- Inevitable divide.
:0007.02DA CMP EDX,00000000 <-- Must be no remainder.
:0007.02DE JZ 02E2 <-- Jump_and_MOV_AX_1_good.

There is actually a fair amount of maths that goes on before we reach here, effectively the left hand side of the code acts as a sum, [BP-0C] is used as a store for this sum which is the result of basic multiplication of the digits and Shift Rights, (note that the SHR EAX,CL where CL=1 in this protection) is equivalent to decimal division by 2. There is actually an opportunity to fix this result because the right hand side of the code is used to polish the result from the left, so you need only trace and work out with simple arithmetic what value the right hand side needs to be.

There is however a snag, after successfully returning here and passing the simple 10 length check another function call (01C7) and below ruins our earlier work. I'd recommend that you fix your code so that the 1st 3 digits are cs2 and the 4rth a hyphen, then perform whatever tricks you must to balance the first check. One code I quickly found was cs2-926050 (needless to say there are certainly others).

In addition you may like to further improve your enjoyment of this game (if you have the willpower to even play it) by fixing the following code snippet thus guaranteeing your future success forever.

:0005.19CE CMP BYTE PTR [3882], 00 <-- Did_user_win_or_lose.
:0005.19D3 JZ 19FB <-- Oh_he_lost_and_I'd_better_NOP_this.
:0005.19D5 PUSH DS <-- "You win".

Return to Miscellaneous

© 1998 CrackZ. 28th October 1998.