Name: Stars2k2s CrackMe #2 or TrashMan v2.0.
Type: Serial / checksum + patch / keygen.
Tools: W32Dasm, Filemon. SoftICE for serial sniffing, MASM 6.15 for writing the keygen and DLDR to debug the keygen.
Knowledge of assembler equivalent to step 7 in my assembler course at http://stars2k.free-host.com
I tried to follow a tutorial for TrashMan v2.0 & couldnt, or imho the tutorial was badly written and it didnt even remove half the protection there was. Well I didnt want to let it go, it would be a defeat and I didnt know how to crack it. As soon as I changed something in the application it wouldnt start. And I didnt have a clue what I could do. My thanks to CrackZ who helped me on this bit.
Lets take a look at this application. It has an about box, and a register box. When you have read the about box and close it you get a nag screen. A funny feature about it is that if you have started it several times it will nag you more by showing up even when you close the application, it will say how many times you have used it, and if you have used it a lot, you even get it at the beginning. There is a registering dialog box. It takes a name and a serial.
OK so how are we going to crack this one?
I have started using W32Dasm patch 2 by bratalarm now. Its the best patch right now. If you havent, get it! OK read in the file in W32Dasm. You will notice that you cant debug it there. Why? Because its a Win 3.x application in 16-bit. You cannot debug it in Olly either!.
Now check the imported functions and see whats there. As we type the serial in a dialog box USER.GETDLGITEMTEXT is a good choice. Click on it. Scroll down some and you will see the RegName and RegCode entry. Great what is happening there? First guess is retrieving the input. Upon further investigation, it seems it says wow success! We are at the place where it has verified the user. And we do see another API function called. Aha! It uses an ini file! Time to use FileMon!.
Lets look where it jumps to this success function.
:0001.4674 push ax
:0001.4675 push 0050
:0001.4677 call USER.GETDLGITEMTEXT <-- 1. we arrive here
:0001.467C lea ax, [bp-50]
:0001.467F push ss
:0001.4680 push ax
:0001.4681 call KERNEL.LSTRLEN
:0001.4686 or ax, ax
:0001.4688 jne 468D
:0001.468A jmp 473A <-- 4. another failure (same as if length was 0)
* Referenced by a (U)nconditional or (C)onditional Jump
:0001.468D lea ax, [bp+FF60]
:0001.4691 push ax
:0001.4692 lea ax, [bp-50]
:0001.4695 push ax
:0001.4696 call 39A2 <-- 5. the verification area
:0001.4699 add sp, 0004
:0001.469C or ax, ax
:0001.469E jne 46A3 <-- 2. and this is where it jumps to the success function
:0001.46A0 jmp 473A <-- 3. and obviously this is a IF/ELSE failed
Follow my comments 1 to 5. Lets go to verification address 39A2. (Use the call button in w32dasm). Now scroll down to where it ends. See what it compares.
:0001.39F3 push 0008
:0001.39F5 lea ax, [bp-54]
:0001.39F8 push ax
:0001.39F9 push word ptr [bp-02]
:0001.39FC push word ptr [bp-04]
:0001.39FF call 66C0
:0001.3A02 add sp, 0008
:0001.3A05 lea ax, [bp-54]
:0001.3A08 push ss
:0001.3A09 push ax <-- 3. it compares ss:ax with
:0001.3A0A push ds
:0001.3A0B push word ptr [bp+06] <-- 4. ds:[bp+06]
:0001.3A0E call USER.LSTRCMP <-- 2. and compare here
:0001.3A13 cmp ax, 0001
:0001.3A16 sbb ax, ax
:0001.3A18 neg ax
:0001.3A1A pop si
:0001.3A1B pop di
:0001.3A1D ret <-- 1. we return here
Now if you are following the course on my site you know that the parameter ds:[bp+06] is passed to the function. i.e. the password we typed, also ss:ax must be the serial. Just start SoftICE. Did you read everything necessary about SoftICE on step 6 of my course? Break at GETDLGITEMTTEXTA. Push F12 to get out to our code. Step through the code with F10. Jump in verification area with F8. Step through again and dump ss:ax before the compare. Voila, you will have the serial to whatever name you typed in the dialog box. Wasnt that easy?.
OK, but this is tutorial two. It should be tougher. Am I wasting your time? No, we are not finished yet. Lets try to patch it. The above was just to warm-up.
OK so we patch the jump to success area. Patch :
:0001.469E 7503 jne 46A3
:0001.469E EB03 jmp 46A3
You can now patch using W32dasm patch 2s own patcher. No need for Hiew. Restart application. Huh? Where it go? It won't show. Whats wrong?. NE checksum is it? NE is the file format for Win 16 applications. So what do we do?.
To calculate the checksum it probably needs to get the module name. So search for a file manipulation import. KERNEL.GETMODULEFILENAME is interesting. It returns the name of the running application. To be sure run Filemon. If it reads the file in big chunks it is probably a checksum routine. Open Filemon, filter to include only stars2k2 and exclude explorer (it reads just a lot of file info). Now start and you will see the file is read in 512 byte chunks all alike. We go for it. We get :
:0001.3B87 call KERNEL.GETMODULEFILENAME <-- 1. we
come in here
* Possible StringData Ref from Data Seg 002 ->"rb" <-- 2. rb stands for read / binary mode
:0001.3B8C push 057D
:0001.3B8F lea ax, [bp+FEF6]
:0001.3B93 push ax
:0001.3B94 call 4BD8 <-- 3. open file
:0001.3B97 add sp, 0004
:0001.3B9A mov si, ax
:0001.3B9C or si, si
:0001.3B9E je 3C02 <-- 4. did we open file successfully ?
:0001.3BA0 mov [bp-06], si
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0001.3BB5(C)
:0001.3BA3 push si <-- 6. loop begin
:0001.3BA4 call 4C38
:0001.3BA7 add sp, 0002
:0001.3BAB add [bp-04], ax
:0001.3BAE adc [bp-02], dx
:0001.3BB1 test byte ptr [si+06], 10
:0001.3BB5 je 3BA3 <-- 5. loop end
:0001.3BB7 push si
:0001.3BB8 call 4ACE
:0001.3BBB add sp, 0002
:0001.3BBE mov al, [014D] <-- 7. seems like some processing
:0001.3BC2 sub ah, ah
:0001.3BC4 mov cx, [bp-04]
:0001.3BC7 mov dx, [bp-02]
:0001.3BCA sub cx, ax
:0001.3BCC sbb dx, 0000
:0001.3BCF mov ax, word ptr [014C]
:0001.3BD2 sub ah, ah
:0001.3BD4 mov bx, dx
:0001.3BD7 sub cx, ax
:0001.3BD9 sbb bx, dx
:0001.3BDB mov ax, word ptr [014A]
:0001.3BDE mov dx, ax
:0001.3BE0 sub ah, ah
:0001.3BE2 mov dl, dh
:0001.3BE4 sub dh, dh
:0001.3BE6 sub cx, dx
:0001.3BE8 sbb bx, 0000
:0001.3BEB sub cx, ax
:0001.3BED sbb bx, 0000
:0001.3BF0 cmp cx, [014A] <-- 8. this must be the check
:0001.3BF4 jne 3C02
:0001.3BF6 cmp bx, [014C]
:0001.3BFA jne 3C02 <-- 9. disable this one too
:0001.3BFC xor ax, ax
:0001.3BFE pop si
Here are some file open modes used in C++ btw.
"rb" - Open for reading in binary mode.
"wb" - Create for writing in binary
mode. If the file exists it is overwritten.
"ab" - Append (write) in binary mode
at the end of the file. If the file doesn't exist, it is created.
"rb+", "wb+" - Open for reading/writing
in binary mode. The second, "wb+", overwrites the file
if it exists.
So what are the [014A] and [014C] ? memory addresses. Look at where DS is located and find that in the file. If you run a debugger, you can sniff what the calculated checksums in BX and CX are and then write that in your modified file. This is a simplistic way to make a checksum protection. Running Filemon you also see where the ini file is, and its name, Stars2k2.ini in the windows directory.
OK so we have disabled the checksum. Run the application again and this time it will start. Register and you will see its a success. Close and run the program again. Oops! you are not registered anymore. OK so something goes wrong when reading the file. We search for KERNEL.GETPRIVATEPROFILESTRING that reads strings from ini files. How did I know that? Well we had already encountered it when seeing the RegName and RegCode in the success section. Obviously it wrote it down to the ini file. We get :
* Possible StringData Ref from Data Seg 002 ->"RegName"
<-- 2. maybe the right place
:0001.0631 push 01DE
:0001.0634 lea ax, [bp-64]
:0001.0637 push ss
:0001.0638 push ax
:0001.0639 push ds
:0001.063A push 1196
:0001.063D push 0050
:0001.063F push ds
* Possible StringData Ref from Data Seg 002 ->"STARS2K2.INI"
:0001.0640 push 012E
:0001.0643 call KERNEL.GETPRIVATEPROFILESTRING <--1. we arrive here
* Possible Reference to String Resource ID=00507: "Hmmz_eh"
* Possible StringData Ref from Data Seg 002 ->"TrashMan.Hlp"
:0001.0648 push 01FB
:0001.064B call 3A1E
:0001.064E add sp, 0002
:0001.0651 push ds
:0001.0652 push ax
:0001.0653 push ds
* Possible StringData Ref from Data Seg 002 ->"RegCode"
<--- 3. looks good
:0001.0654 push 01E7
:0001.0657 push ds
:0001.0658 push 01E6
:0001.065B lea ax, [bp-64]
:0001.065E push ss
:0001.065F push ax
:0001.0660 push 0050
:0001.0662 push ds
* Possible StringData Ref from Data Seg 002 ->"STARS2K2.INI"
:0001.0663 push 012E
:0001.0666 call KERNEL.GETPRIVATEPROFILESTRING
:0001.066B push ds
:0001.066C push 1196
:0001.066F call KERNEL.LSTRLEN
:0001.0674 or ax, ax
:0001.0676 je 06AA <-- 4. length check here
:0001.0678 lea ax, [bp-64]
:0001.067B push ss
:0001.067C push ax
:0001.067D call KERNEL.LSTRLEN
:0001.0682 or ax, ax
:0001.0684 je 06AA <-- 5. and here
:0001.0686 lea ax, [bp-64]
:0001.0689 push ax
:0001.068A push 1196
:0001.068D call 39A2
:0001.0690 add sp, 0004
:0001.0693 or ax, ax
:0001.0695 je 06AA <-- 6. and there we go. Disable!
:0001.0697 mov word ptr , 0001
:0001.069D push si
OK, the patch is ready!.
Did you see the validation area earlier?. Look at that more closely. Its a little different than the first check isnt it?. Instead of a line of coding it has tons and tons of processing. We can get a formula for that? Why dont we just rip it? Hmm will we meet any difficulties? Well ... the import libraries! Now do we have 16 bit libraries?. I havent. The source code of the key generator can be found at Stars2k2's website.
Thats it! Have a nice day!.