Cheating in games. Lesson #4a

by

Indian_Trail

SOME CRACKING PRACTICE

Since fall is usually filled with rainy grey days I decided to study some basic cracking techniques. One of the best cracking sitez on the Web (next to Fravia's) is Lord Caligo's site. He has a lot of crack_me's and try_me's. These are usually .COM files and contain some protection written by crackers. Since the protectioners of commercial software are too lazy to write real protection schemes, some crackers write their own for others to try. Let us download some of these files, fire Softice and start cracking them.

Here are the files you should download for this lesson:

The best thing with these files is that they contain variations of different techniques. So if you wanna learn how to crack paper protections (password protection schemes) the fastest way to get a grasp of the variation of those protection schemes is to crack these try_me files.

Let's begin with PW.COM. The first thing we should do is to run PW. So now you know that it displays:

Enter Password:

Switch to Softice and examine the memory by using the "MAP" command. Look for segment:offset where PW dwells. In my computer it dwells at 10F2:0000 and ends at 10F2:8F0E.

Write that address down and switch back to PW. Type in your name, switch to Softice and search for your inputs in the segment where PW dwells:

S 10F2:0 L FFFF 'your input'

Softice will give you the address where your input is stored:

10F2:010B (in my computer).

Remember that segment addresses are always different but the offset is always the same.

Let's display that memory location and see that our inputs are really there:

D 10F2:010B

You should see your input at that address. Now, we figure out that our input is not the correct password (unless we are lucky as hell) and that PW compares our input with the correct password. To do that it has to read our characters from the memory location where our input is stored and compare them with the correct password chars. So let's put a breakpoint on our chars in memory.

BPR 10F2:010B 10F2:010B + 6 RW (assuming you typed 6 chars)

Now switch back to PW and press enter. Softice will pop up as soon as PW reads from the memory locations where our input is stored. It will pop up in this routine:

MOV     BX,DX            ; DX is the sixth char user password
MOV     AX,DI
MOV     [BX],AL
RET
------------------
XOR     CH,CH
MOV     CL,[0103]        ; What is at address 0103?
MOV     CX,BX
MOV     AL,[BX+103]
CMP     AL,[BX+010A]     ; [BX+10A] = The sixth char of user password
JNZ     01BF             ; Bad jump
Now you should know what you have found (if you have read the preceding lessons). If you dump address 0103 you'll see the correct password. So the JNZ is a "bad password" jump. How should we crack it? Well, there are at least two ways. One way is to change the JNZ into a JZ. That way we will get the "correct password" everytime we type in a FALSE password. The second way is to make the program compare the correct password with itself instead of with the user password. That way it will always print "correct password" no matter what we typed in. To change it that way we simply change the CMP line to:

CMP AL,AL

You will notice if you reassemble that line in Softice that different instructions come out after our CMP AL,AL. That's because the original instructions were 4 bytes and we changed it with our new instruction into 2 bytes. Like this:

CMP AL,AL
OR AL,[BX+DI]

So place 2 NOP (no operation) instructions and it will work fine. This is how it should be:

CMP AL,AL
NOP
NOP
JNZ 01BF

If you view PW.COM with a hex editor you'll be able to see the correct password. If you take a commercial game protected with a manual scheme and hex-view it you might see the correct passwords. If you don't, it generally means one of two things:

  1. The passwords are ENCRYPTED or
  2. the program generates the passwords itself.
The last one is quite common with Windows software that has a registering option, where you enter a serial number to register the program. We will study Windows cracking in Lesson #5. It's still raining in Sweden today, so let's move on to the next try_me file.

TEST1.COM

TEST1.COM is 673 bytes and a quick hex-view reveals that it has been packed with PKLITE. You can see the PKLITE string inside the file, which means that the file is compressed (it is uncompressed at run time). Therefore you won't be able to see any strings of possible passwords, you will see letters that have been arranged by a specific algorithm that PKLITE use.

So before we go on let's unpack it with a .EXE file unpacker like UNP. I'm using UNP 4.11, you'll easily find it on the Web:

UNP TEST1.COM

The new file size (of the uncompressed TEST1.COM) is 1039. Alright, let's hex-view the file now and see if there are any strings that could be possible passwords. We can see strings like:

  1. Please enter password:
  2. Right password...congratulations...ask Lordbyte for next job
  3. Wrong password....Try again..>:)
Then it follows some strange looking chars in 3 to 5 rows (I didn't count them), but no possible password, unless the password is "enter" or "password" or any combination of the words that we saw in the file. So I'd say it's probably encrypted. I gather that from the strange rows of chars that were in the file. Those strange chars are probably used to decrypt the password in the decryption routine. They could also be used to generate a password, it almost the same thing. So let's run it and see what it does. As expected, it prompts for a password. Type in a false password, switch to Softice and dump the memory using the "MAP" command (very handy).

183D:0 -6574 TEST1

Now search for your input in that segment as we did with PW.COM. Softice doesn't find our input. That means that the user password is copied into TEST1's data segment as we press return. For us that means that we can't breakpoint on our input string with BPR because we don't know where that string is stored. How do we find out the location where our input is stored? Well, two approaches are possible:

  1. Run the program and wait for the "Wrong password....Try again..>:)" to appear. Then search the main memory regions of the program for the string that you put in as password. If Softice finds it you should run a new attempt to see if the same location is used for storing the user input. If it is, you can use the same technique as we used with PW (BPR xxxx:yyyy xxxx:yyyy + length of input RW).
  2. Since the program reads our input from the buffer (in this case the DOS keyboard buffer) and stores it somewhere in TEST1's offset memory, we could BPR on the whole data segment of TEST1 with write to find out the offset. This approach is very secure, since our input has to be written to TEST1's data segment. This approach is quite common with Windows programs (as you'll see in Lesson #5).
One side effect with this last method is that data is written all the time to the segment range of our main program (TEST1). We are only interested in the part that copies our input from the keyboard buffer to the data segment of TEST1. Therefore we will set this breakpoint just before we press enter. DOS programs often use interrupts to do basic tasks such as copy text from one place to another. So with this breakpoint set we can expect to land in a part of an interrupt routine that copies the text from the buffer to TEST1. Let's try:

BPR 183D:0 183D:6574 W

Here's where we land:
FF03:5468 MOV    [DI-01],DH
          INC    DH
          PUSH   DS
          PUSH   ES
          POP    DS
          POP    ES
          MOV    SI,01FB     ; 01FB is the DOS buffer where our string is
          MOV    CL,DL
          REPZ   MOVSB       ; Move our input to ES:DI (data of TEST1)
          RET
          --------------
          IRET               ; Return from interrupt
The IRET means returning from the interrupt service back to the main program. Here's what follows:

MOV   SI,01E6              ; SI = encrypted password
MOV   AL,C3
XOR   SI,AL                ; Decrypt a char
CMP   BYTE PTR [SI],00     ; Last char?
JZ    END OF STRING
SUB   AL,25
INC   SI                   ; Get next char
JMP   NEXT CHAR TO DECRYPT
If you dump SI you'll see that the correct password appear through out the loop. So this is the decryption mechanism. This encryption is quite easy to understand. So when the password is decrypted we will jump to END OF STRING, here is that:

MOV   SI,01E6
XOR   AX,AX
MOV   SI,01E6     ; Correct password
MOV   DI,01DD     ; User password
MOV   CL,[SI]     ; Put first char of correct password in CL
MOV   BL,[DI]     ; Put first char of user password in BL
CMP   CL,BL       ; Compare them
JZ    FALSE PASSWORD
So changing the instruction CMP CL,BL into CMP CL,CL will do the trick for this program. It's always good to make the crack at the churn of the protection scheme. If you crack the JZ, it might be a mirror check somewhere and you'll have to find that one and change it. By cracking the CMP any kind of flag, etc. that could be set if the the check fails/succeeds will be set as if we entered the right password.

The BPR 183D:0 183D:6574 W took us to the correct place. We first landed in an interrupt routine and stepped from there to the program's execution place. Now you'll probably know that you can breakpoint on interrupts in Softice with the command:

BPINT interrupt number |al|ah|ax=value

If we would have used that breakpoint we would have avoided the BIOS routine and gone straight to the location the IRET took us. But we don't know the interrupt service number that TEST1 is using. Well, that is easily changed. Run TEST1 but this time use the LDR (DLDR if you are using Winice) command to load TEST1 and BPINT 21 before you press CTRL-D. Here is the list of INT 21 called from TEST1.COM:

  1. Display String
  2. Buffered Keyboard Input.
Put a breakpoint on the line after the INT 21 and switch to the program to enter a password. You'll see Softice puts you in the decryption routine. Less than 5 seconds to get to the churn of the protection scheme. How did I know that I was gonna breakpoint on INT 21? Well, since the protection takes place in DOS it's very likely it uses DOS interrupt which is service 21. This method is fast and quite reliable, it does however not work for protections that "hook" an interrupt.
Hooking an interrupt means that the program directs an interrupt to a special place in the code where, i.e., the protection scheme is hidden. Checking for cheat codes in games is usually done by this way. INT 16 is keyboard and I/O service and is called from programs everytime you press a key. A game might change the address of INT 16 to its own code where its checks to see if the key 'W' was pressed and if so increase player's energy. Then when the checks are done the game jumps to the original address of INT 16.

There are utilities that log interrupts during the execution of a program (the best utilities are the ones you write yourself though, plus that you'll gain knowledge of assembler language as well). This can be useful if you get stuck on a protection scheme and need a push in the right direction. I described both methods in this lesson because the first one (BPR seg:0 seg:end W) is the basic approach and will give you a complete overview of what's going on.

Some programmers don't use interrupts and using BPINT on such application won't get you anywhere. But a BPR with write on different memory location will get you somewhere. But remember always to try first the easiest approaches because protection schemes are usually not well written in commercial software. Plus that it is difficult to write a smart protection scheme. So with programs that asks for a password in DOS you could try any of the above techniques starting with the fastest one. Let's try the next try_me file.

TEST2.COM

Let's apply the same approach as we have done with the other try_me files. If you breakpoint with BPR W you'll get in the interrupt routine and if you just use BPINT 21 you'll land on Buffered Keyboard Input. Your input is at DX+02. BPMB the first char of the string you entered and press CTRL-D. This is what you should see:
 __
|
| XOR   AX,[SI]
| CMP   BYTE PTR [SI+01],00     ; Here is where you land
| JZ    014E
| CMP   BYTE PTR [SI+01],0D     ; Check if char is carriage return (0D)
| JZ    014E
| INC   SI
| JMP   013D                    ; Run until end of string is reached
|__

014E    CMP    WORD PTR CS:[0245],00     ; What's this???
        JNZ    014E                      ; A loop! for what???
        MOV    SI,0241
        MOV    DX,[SI]
        PUSH   AX
        MOV    AX,[SI+02]
        MOV    DS,AX
        MOV    AX,251C
        INT    21                        ; Set vector
If you let the program run, it will show "Wrong password..." and exit to DOS. So it only reads our input string one time. The XOR AX,[SI] looks suspicious at first look, it looks like a encryption instruction. It's the only thing in the first piece of code that manipulates our string. The next thing that my eyes see is the CMP WORD PTR CS:[0245],00. The CMP is checking the status at CS:[0245] until it's zero. What's going on at address CS:0245? and what changes the 01 into 00? Here is where Softice takes us:
DEC    WORD PTR CS:[0245]     ; Here's the toggle from 01/00
IRET
-----------
INT    1C                     ; So INT 1C is one that is hooked
CALL   8CFA
INT 1C is the user timer click vector. It runs constantly, just try a BPINT 1C and you'll see. Now we understand the strange CMP WORD PTR CS:[0245],00. We now make a guess that the XOR AX,[SI] instruction encrypts our input letters for a later comparison. Since the encrypted string is stored in AX as a number and not as a string we have to look for instructions that move AX value to a place in memory. The PUSH AX stores our encrypted string on the stack. Step over it and do a D SS:SP and you'll see that the number that was in AX is now on another address. BPM that address:

BPM SS:FFFA (address could be different in your computer)

Softice will pop here:

XOR   [BP+08],BX
JNZ   01B2           ; Evil jump
--------
If you scan up a little in the code you'll see this:
MOV   BX,[BP+04]
XOR   BX,B816
XOR   [BP+08],BX     ; BP+08 is our encrypted code
JNZ   01B2
Now we can easily crack this by changing the JNZ into a JZ. If you wanna know the correct password you'll have to DECRYPT it. I'll leave that up to you. Here are some hints if you wanna have a try:

  1. If you look at the last compare before the evil jump, you'll see what our encrypted string should be after the encryption loop.
  2. The hardest part is to figure out how you'll get that result from the encryption loop.
  3. Number of chars is very important. You could make a table and put your results in.
  4. The fastest way is to use some sort of mathematical function or equation to calculate the correct password. You could write a small program to calculate the correct password.

CONCLUSION

When I first tried these try_me files I cracked them flat on three seconds. Try the different techniques we have studied here. These methods are basic cracking techniques and by using them you should be able to crack most of the programs that use the password protection. To crack TEST2.COM the above steps are not necessary. You could just have single stepped a few instructions from the encryption routine and you would have landed at the final comparison.

What we did was to dig out some pieces that we didn't understand at first, that's called "studying". The difference between cracking a scheme and studying it is that when studying you put together all the pieces the protection holds to get a full view. When that is done you have learned something new (if the protection was new to you). You usually first try to crack it and if you fail you study it. The purpose of this lesson was to show you how to approach different schemes of the same BASE protection, in this case the password protections. There are three more files left which I'll leave up to you to study on your own.

I know that some people want these solutions published. The reason why I don't publish my solutions to these three files in this lesson is because I don't have the time and because I would like you to work on your own. I'll happily answer all questions that you may have (or get), If you go on and study the last three files. Please write down your solution and e-mail it to me and I'll publish it here. This site is dedicated to newbies and the best thing that could happen is that newbies teach each other on this site. Fravia's Page of Reverse Engineering is the best cracking site I've ever seen. This page is meant to be like a preparation for a beginner before he moves on to other sitez. I'll also welcome all comments about my work, e-mail me.


indian_trail@hotmail.com (10-Oct-97)
Color line
Back Go to the parent page: Software hacking.