Free Information Xchange presents: Thief v1.14 - CD crack by R!SC - 10/01/99 REQUIREMENTS: Hex editor W32Dasm 8.93 ! Do a full install, remove the CD, run the game. You get a standard messagebox asking you to insert the CD. "Missing CD"-"Please insert the CD into the CD Drive" First off, make a copy of thief.exe, load this into W32Dasm. We begin by looking through the string references for one of our messages. Heh, I didnt find any ref's! So we take a different approach. Look through the imported functions for a reference to KERNEL32.GetDriveTypeA (commonly used in CD checks(as you remember!)). Double click on the reference several times, looking at the code every time you click it. GetDriveTypeA returns a value in eax between 0 & 6, we are only intrested in the code that checks eax for a '5'. There is only one reference that does this. * Referenced by a CALL at Addresses: |:0050D773 , :0050DA5E <-- Trace the call back by double right clicking these (hit F12 to return) | :0050D720 8B442404 mov eax, dword ptr [esp+04] :0050D724 50 push eax <-- Points to a null-terminated string that specifies - the root directory of the disk to return information about. * Reference To: KERNEL32.GetDriveTypeA, Ord:00DFh - this mean 'C:\',0 or whatever drive letter the caller might be on... | :0050D725 FF1528315B00 Call dword ptr [005B3128] :0050D72B 33C9 xor ecx, ecx <-- zero ecx (ecx=32bit reg, cx=16bit reg, cl=low 8bits of cx) :0050D72D 83F805 cmp eax, 00000005 <-- checking for a CD-ROM :0050D730 0F94C1 sete cl <-- sets the byte in cl to a '01' if eax=5 :0050D733 8BC1 mov eax, ecx <-- copies ecx into eax (what the last command did or didnt set) :0050D735 C3 ret <-- Return to caller Normally, in most cases I have seen, when there's a call to a CD check, there is normally a test and a conditional jump afterwards, the protection can be bypassed by either forcing the jump, or not taking the call at all. So I wanted to trace this code back to the original caller. Using WDasm 8.93! like I stated at the top, double right click the first caller '50D773'. This code is right beneath the call to GetDriveTypeA. * Referenced by a CALL at Addresses: |:0050D7FA , :0050D84D | :0050D740 8B442404 mov eax, dword ptr [esp+04] :0050D744 53 push ebx :0050D745 85C0 test eax, eax :0050D747 B341 mov bl, 41 <-- 'A' :0050D749 7444 je 0050D78F :0050D74B 8B4C240C mov ecx, dword ptr [esp+0C] :0050D74F 85C9 test ecx, ecx :0050D751 7410 je 0050D763 :0050D753 0FBE00 movsx eax, byte ptr [eax] :0050D756 50 push eax :0050D757 E8D4EF0800 call 0059C730 :0050D75C 8BD8 mov ebx, eax :0050D75E 83C404 add esp, 00000004 :0050D761 FEC3 inc bl <-- go onto the next drive letter... * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0050D751(C) | :0050D763 80FB5A cmp bl, 5A <-- 'Z' :0050D766 7F27 jg 0050D78F - simple routine to clear eax and ret * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0050D784(C) <-- referenced to by itself | * Possible StringData Ref from Data Obj ->"D:\" | :0050D768 6808BF6000 push 0060BF08 <-- pushes the drive letter were on :0050D76D 881D08BF6000 mov byte ptr [0060BF08], bl <-- stores the next letter to check :0050D773 E8A8FFFFFF call 0050D720 <-- call to GetDriveTypeA Subroutine that brought us here :0050D778 83C404 add esp, 00000004 - return a 01 in eax if weve got a CD-ROM :0050D77B 85C0 test eax, eax :0050D77D 7509 jne 0050D788 <-- if eax in not equal(to 0) jump! :0050D77F FEC3 inc bl <-- increase the letter :0050D781 80FB5A cmp bl, 5A <-- compare it with 'Z' :0050D784 7EE2 jle 0050D768 <-- loop until we found a CD-ROM :0050D786 5B pop ebx - or ran out of drive letters :0050D787 C3 ret * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0050D77D(C) | * Possible StringData Ref from Data Obj ->"D:\" | :0050D788 B808BF6000 mov eax, 0060BF08 <-- exit with the Drive letter of CD-Drive in eax :0050D78D 5B pop ebx :0050D78E C3 ret * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:0050D749(C), :0050D766(C) | :0050D78F 33C0 xor eax, eax :0050D791 5B pop ebx :0050D792 C3 ret This code starts with drive letter 'A' and calls our previous subroutine to determine if were looking at a CD-ROM, if not it increases the letter and checks again. Anyway trace this back to one of its callers. Double right click '50D7FA' * Referenced by a CALL at Addresses: |:0050D9CA , :0050D9E1 <-- the original callers to this routine (trace these back) | :0050D7A0 81EC04010000 sub esp, 00000104 :0050D7A6 8D442400 lea eax, dword ptr [esp] :0050D7AA 53 push ebx :0050D7AB 56 push esi :0050D7AC 57 push edi :0050D7AD 6804010000 push 00000104 :0050D7B2 50 push eax * Possible StringData Ref from Data Obj ->"cd_path" | :0050D7B3 68C0BF6000 push 0060BFC0 :0050D7B8 33DB xor ebx, ebx :0050D7BA E871B20300 call 00548A30 :0050D7BF 83C40C add esp, 0000000C :0050D7C2 84C0 test al, al :0050D7C4 7528 jne 0050D7EE <-- this jne :0050D7C6 BF18516700 mov edi, 00675118 :0050D7CB 83C9FF or ecx, FFFFFFFF :0050D7CE 33C0 xor eax, eax :0050D7D0 8D54240C lea edx, dword ptr [esp+0C] :0050D7D4 F2 repnz :0050D7D5 AE scasb :0050D7D6 F7D1 not ecx :0050D7D8 2BF9 sub edi, ecx :0050D7DA 8BC1 mov eax, ecx :0050D7DC 8BF7 mov esi, edi :0050D7DE 8BFA mov edi, edx :0050D7E0 C1E902 shr ecx, 02 :0050D7E3 F3 repz :0050D7E4 A5 movsd :0050D7E5 8BC8 mov ecx, eax :0050D7E7 83E103 and ecx, 00000003 :0050D7EA F3 repz :0050D7EB A4 movsb :0050D7EC EB05 jmp 0050D7F3 <-- this jmp * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0050D7C4(C) <-- ref by same routine, the jne up there | :0050D7EE BB01000000 mov ebx, 00000001 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0050D7EC(U) <-- ref by same routine, the jmp just up there. | :0050D7F3 8D4C240C lea ecx, dword ptr [esp+0C] :0050D7F7 6A00 push 00000000 :0050D7F9 51 push ecx :0050D7FA E841FFFFFF call 0050D740 <-- Call that we traced back :0050D7FF 83C408 add esp, 00000008 :0050D802 A314516700 mov dword ptr [00675114], eax <-- save varible in eax :0050D807 85C0 test eax, eax <-- check return value of previous call :0050D809 746B je 0050D876 <-- if it was zero we never found a CD-Drive Weve not get to the original caller to the CD-Check routine yet, so lets carry on tracing back. Double right click '50D9CA' at the top of this code. * Referenced by a CALL at Address: |:0050DAD5 <-- only one caller this time ;) | :0050D9C0 C7051451670000000000 mov dword ptr [00675114], 00000000 :0050D9CA E8D1FDFFFF call 0050D7A0 <-- Call that we just traced back :0050D9CF 85C0 test eax, eax <-- check return value :0050D9D1 7517 jne 0050D9EA Goddamn it. Still not there yet, trace this back. * Referenced by a CALL at Address: |:00414FCC <-- YIPPEE! only one caller and the code is located faraway near the start of the executable | :0050DAC0 6A00 push 00000000 :0050DAC2 6A00 push 00000000 * Possible StringData Ref from Data Obj ->"only_check_path" | :0050DAC4 68E0BF6000 push 0060BFE0 :0050DAC9 E862AF0300 call 00548A30 :0050DACE 83C40C add esp, 0000000C :0050DAD1 84C0 test al, al :0050DAD3 7507 jne 0050DADC :0050DAD5 E8E6FEFFFF call 0050D9C0 <-- call we traced back :0050DADA EB05 jmp 0050DAE1 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0050DAD3(C) | :0050DADC E82FFFFFFF call 0050DA10 <-- see 'ps' at the bottom of the file * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0050DADA(U) | :0050DAE1 85C0 test eax, eax :0050DAE3 7405 je 0050DAEA :0050DAE5 E986FBFFFF jmp 0050D670 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0050DAE3(C) | :0050DAEA 33C0 xor eax, eax :0050DAEC C3 ret OK, Trace this back to 414FCC, I think weve found what weve been looking for at last.. * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00414F9B(C), :00414FBF(C) | :00414FCC E8EF8A0F00 call 0050DAC0 <-- A CALL :00414FD1 85C0 test eax, eax <-- A TEST :00414FD3 750B jne 00414FE0 <-- A CONDITIONAL JUMP :00414FD5 50 push eax - jump if eax not equal zero :00414FD6 6A01 push 00000001 :00414FD8 E8337B1100 call 0052CB10 <-- This traces to a call to Terminate Process :00414FDD 83C408 add esp, 00000008 -- Which is very BAD * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00414FD3(C) | :00414FE0 E8EB020000 call 004152D0 :00414FE5 8D542440 lea edx, dword ptr [esp+40] :00414FE9 C744244003000000 mov [esp+40], 00000003 :00414FF1 52 push edx :00414FF2 6A01 push 00000001 OK, I(we) traced all those calls back to this one call at the start of the file, theres a call, a test and a conditional jump. I first decided to force the jne by changing it to a jmp. Saved my change to the code, and ran the game. Shit, it still asks for the CD, I clicked cancel, then the bloody thing loaded into the game! OK, the message box is somewhere deep inside of the CD-check routine. I decided to kill the call to the routine instead, so the CD is never checked, the message box will never be shown, and the game will work? Highlight the call to the check ':00414FCC E8EF8A0F00 call 0050DAC0' Write down the offset at the bottom of the screen, probably 0143CC, hexedit thief.exe goto 143CC and change the E8 to a B8. Save and run the game, it works fine now without needing the CD. Another tutorial comes to an end and another game has been FiX'ed! happy cracking love R!SC -- risc@notme.com PS. If you trace back the second caller to GetDriveTypeA (0050DA5E), scroll up a bit, this is referenced by a call at 50DADC, trace this back to routine, this one is referenced by 414FCC ;) three easy steps to get there, you should always follow all the routes possible and check them out, it pays off! for v1.14 - edit thief.exe at offset 143CC (hex) =========================================================== Search for: E8 EF 8A 0F 00 call 0050DAC0 Change to : B8 -- -- -- -- mov eax, 000F8AEF (which is not 0, so the jne is always taken) I borrowed these off someone else and included them here. (win32api.txt) GetDriveType Return Function codes: (Donated by: +-=Riddler=-+) Value Meaning 0 Drive Cannot Be determined 1 Root Dir Does not exist 2 DriveRemoveable 3 A Fixed Disk (HardDrive) 4 Remote Drive(Network) 5 Cd-Rom Drive <-- Game crackers only intrested if return code is 05! 6 RamDisk