Free Information Xchange '98 presents: Sega Rally - CD crack by Static Vengeace Requirements: hex editor and full install Here we go again, another tutorial based on a Sega game. Sega Rally Championship racing is alright but needs a Direct3D (or a 3Dfx) patch for more realism and better frame rates. That would be nice but, I can live with the recently released MMX enhanced version 2.1 (from the www.sega.com) of the game. However, there is one thing I cannot live with and that's the CD check that occurs when I run the game. So of course I set out to FiX this minor problem. Then I thought, as long as I know how to crack it, I would show you how to do it too. To begin, like the other tutorials on CD checks cracking I have done, I will be using W32Dasm (URSoft) to disassemble and trace through this one. So if you would like to follow along you will need to use W32Dasm and disasseble rally.exe. Although the code listing is from the newer version 2.1, simular steps will work for the version that's on the CD. In fact, I have also included the needed edit for both versions at the end of this article. Using my favorite method: Go up to the menu bar and select "Refs" and then select "String data references" from the drop down menu. From there, grab the slider bar and scroll down checking for strings like "Insert..." or "Please insert.." and eventually you'll run across "Please insert Sega Rally CD." So let's double click on that string (this will put you in the middle of the routine that checks for the CD) and see what we have: * Referenced by a CALL at Addresses: |:004D894C , :004D8C21 <-- Where the calls came from | :004F7CA0 81EC04020000 sub esp, 00000204 :004F7CA6 53 push ebx :004F7CA7 56 push esi :004F7CA8 57 push edi :004F7CA9 55 push ebp * Reference To: KERNEL32.lstrcatA, Ord:0292h | :004F7CAA 8B2D60541001 mov ebp, dword ptr [01105460] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004F7DD4(C) | :004F7CB0 33DB xor ebx, ebx <-- Initialize number of times tries allowed * Reference To: KERNEL32.GetLogicalDrives, Ord:00FAh <-- Doesn't this line stand out? | :004F7CB2 FF155C541001 Call dword ptr [0110545C] :004F7CB8 89442410 mov dword ptr [esp+10], eax * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004F7D97(C) | :004F7CBC B801000000 mov eax, 00000001 :004F7CC1 8ACB mov cl, bl :004F7CC3 D3E0 shl eax, cl :004F7CC5 85442410 test dword ptr [esp+10], eax :004F7CC9 0F84C4000000 je 004F7D93 :004F7CCF 8D4341 lea eax, dword ptr [ebx+41] :004F7CD2 8D8C2414010000 lea ecx, dword ptr [esp+00000114] :004F7CD9 50 push eax * Possible StringData Ref from Data Obj ->"%c:\" <-- Another common ref string in CD checks | :004F7CDA 6808916500 push 00659108 :004F7CDF 51 push ecx * Reference To: USER32.wsprintfA, Ord:026Dh | :004F7CE0 FF157C551001 Call dword ptr [0110557C] :004F7CE6 8D8C2420010000 lea ecx, dword ptr [esp+00000120] :004F7CED 83C40C add esp, 0000000C :004F7CF0 51 push ecx * Reference To: KERNEL32.GetDriveTypeA, Ord:00DFh <-- Common text string to search for | :004F7CF1 FF1564541001 Call dword ptr [01105464] :004F7CF7 83F805 cmp eax, 00000005 <-- Value for CD-ROM drives :004F7CFA 0F8593000000 jne 004F7D93 :004F7D00 8DBC2414010000 lea edi, dword ptr [esp+00000114] :004F7D07 B9FFFFFFFF mov ecx, FFFFFFFF :004F7D0C 2BC0 sub eax, eax * Possible StringData Ref from Data Obj ->"srally\rally.exe" <-- The file we check for | :004F7D0E 6810916500 push 00659110 :004F7D13 F2 repnz :004F7D14 AE scasb :004F7D15 F7D1 not ecx :004F7D17 2BF9 sub edi, ecx :004F7D19 8BC1 mov eax, ecx :004F7D1B C1E902 shr ecx, 02 :004F7D1E 8BF7 mov esi, edi :004F7D20 8D7C2418 lea edi, dword ptr [esp+18] :004F7D24 F3 repz :004F7D25 A5 movsd :004F7D26 8BC8 mov ecx, eax :004F7D28 83E103 and ecx, 00000003 :004F7D2B F3 repz :004F7D2C A4 movsb :004F7D2D 8D4C2418 lea ecx, dword ptr [esp+18] :004F7D31 51 push ecx :004F7D32 FFD5 call ebp :004F7D34 8D442414 lea eax, dword ptr [esp+14] :004F7D38 6A00 push 00000000 :004F7D3A 50 push eax * Reference To: KERNEL32._lopen, Ord:028Eh | :004F7D3B FF1568541001 Call dword ptr [01105468] :004F7D41 83F8FF cmp eax, FFFFFFFF :004F7D44 744D je 004F7D93 :004F7D46 50 push eax * Reference To: KERNEL32._lclose, Ord:028Bh | :004F7D47 FF157C541001 Call dword ptr [0110547C] :004F7D4D 8DBC2414010000 lea edi, dword ptr [esp+00000114] :004F7D54 B9FFFFFFFF mov ecx, FFFFFFFF :004F7D59 2BC0 sub eax, eax * Possible StringData Ref from Data Obj ->"srally\lake.tex" <-- Check for this one also | :004F7D5B 6824916500 push 00659124 :004F7D60 F2 repnz :004F7D61 AE scasb :004F7D62 F7D1 not ecx :004F7D64 2BF9 sub edi, ecx :004F7D66 8BC1 mov eax, ecx :004F7D68 C1E902 shr ecx, 02 :004F7D6B 8BF7 mov esi, edi :004F7D6D 8D7C2418 lea edi, dword ptr [esp+18] :004F7D71 F3 repz :004F7D72 A5 movsd :004F7D73 8BC8 mov ecx, eax :004F7D75 83E103 and ecx, 00000003 :004F7D78 F3 repz :004F7D79 A4 movsb :004F7D7A 8D4C2418 lea ecx, dword ptr [esp+18] :004F7D7E 51 push ecx :004F7D7F FFD5 call ebp :004F7D81 8D442414 lea eax, dword ptr [esp+14] :004F7D85 6A00 push 00000000 :004F7D87 50 push eax * Reference To: KERNEL32._lopen, Ord:028Eh | :004F7D88 FF1568541001 Call dword ptr [01105468] :004F7D8E 83F8FF cmp eax, FFFFFFFF :004F7D91 750C jne 004F7D9F * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004F7CC9(C), :004F7CFA(C), :004F7D44(C) | :004F7D93 43 inc ebx <-- Increase the number of times we tried so far :004F7D94 83FB20 cmp ebx, 00000020 <-- We'll try 32 times to check for the file :004F7D97 0F8C1FFFFFFF jl 004F7CBC <-- Loop back up and try again :004F7D9D EB07 jmp 004F7DA6 <-- Tried all 32 times and STILL no CD * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004F7D91(C) | :004F7D9F 50 push eax * Reference To: KERNEL32._lclose, Ord:028Bh | :004F7DA0 FF157C541001 Call dword ptr [0110547C] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004F7D9D(U) | :004F7DA6 B8FFFFFFFF mov eax, FFFFFFFF <-- Load eax for a failed CD check :004F7DAB 83FB20 cmp ebx, 00000020 <-- Did we use all 32 tries :004F7DAE 7402 je 004F7DB2 <-- If yes, then leave FFFFFFFF in eax :004F7DB0 8BC3 mov eax, ebx <-- Any other value in eax = passed CD check * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004F7DAE(C) | :004F7DB2 A39C646500 mov dword ptr [0065649C], eax :004F7DB7 83F8FF cmp eax, FFFFFFFF <-- Was the check good or bad? :004F7DBA 752B jne 004F7DE7 <-- Take this jump for CD check=passed :004F7DBC 6A35 push 00000035 * Possible StringData Ref from Data Obj ->"SEGA RALLY CHAMPIONSHIP for PC" | :004F7DBE A170646500 mov eax, dword ptr [00656470] :004F7DC3 50 push eax * Possible StringData Ref from Data Obj ->"Please insert Sega Rally CD." <-- The string we don't want | <-- to ever see.. :004F7DC4 6834916500 push 00659134 :004F7DC9 6A00 push 00000000 * Reference To: USER32.MessageBoxA, Ord:019Bh | :004F7DCB FF15EC551001 Call dword ptr [011055EC] :004F7DD1 83F802 cmp eax, 00000002 <-- 02 means "we" pressed cancel :004F7DD4 0F85D6FEFFFF jne 004F7CB0 <-- This jump loops back up to recheck :004F7DDA 33C0 xor eax, eax <-- Set eax to zero for failed CD check :004F7DDC 5D pop ebp :004F7DDD 5F pop edi :004F7DDE 5E pop esi :004F7DDF 5B pop ebx :004F7DE0 81C404020000 add esp, 00000204 :004F7DE6 C3 ret * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004F7DBA(C) | :004F7DE7 B801000000 mov eax, 00000001 <-- Load eax with 01 for a passed CD check :004F7DEC 5D pop ebp :004F7DED 5F pop edi :004F7DEE 5E pop esi :004F7DEF 5B pop ebx :004F7DF0 81C404020000 add esp, 00000204 :004F7DF6 C3 ret That seems straight forward when you take into acount the couple of comments I made as to what's going on. So lets check the code that calls the above routine. First comes the code from 4D894C: :004D8943 A1C0646500 mov eax, dword ptr [006564C0] :004D8948 A804 test al, 04 :004D894A 7515 jne 004D8961 :004D894C E84FF30100 call 004F7CA0 <-- Call the CD check routine :004D8951 85C0 test eax, eax <-- Check to see what came back in eax :004D8953 750C jne 004D8961 <-- Take this jump for good CD check :004D8955 33C0 xor eax, eax <-- Zero out eax :004D8957 5D pop ebp :004D8958 5F pop edi :004D8959 5E pop esi :004D895A 5B pop ebx :004D895B 83C450 add esp, 00000050 :004D895E C21000 ret 0010 <-- Exit back to Win95 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004D894A(C), :004D8953(C) | :004D8961 33ED xor ebp, ebp <-- Continues on with the game :004D8963 E8584BFCFF call 0049D4C0 :004D8968 E8534BFCFF call 0049D4C0 :004D896D E8DEEE0100 call 004F7850 :004D8972 E8F9F50100 call 004F7F70 :004D8977 55 push ebp * Reference To: USER32.GetSystemMetrics, Ord:012Ch | :004D8978 8B1D3C561001 mov ebx, dword ptr [0110563C] :004D897E FFD3 call ebx :004D8980 6A01 push 00000001 :004D8982 A344416600 mov dword ptr [00664144], eax :004D8987 FFD3 call ebx -- more program code -- Alright, from here we can kill the call to the CD check routine by NOP'ing it out at 4D894C and changing the conditional jump to an unconditional jump so the game will always continue. But look at the conditional jump at 4D894A! It takes us right to the contiune section and jumps over the CD check in the process. Great!, we will make use of it and change that conditional jump to an unconditional jump also. So let's go on to the other section of code that calls the "check for original CD" routine at 4D8C21 with some surrounding code: :004D8C18 A1C0646500 mov eax, dword ptr [006564C0] :004D8C1D A804 test al, 04 :004D8C1F 7515 jne 004D8C36 <-- Another conditional jump past it all :004D8C21 E87AF00100 call 004F7CA0 <-- Call the "insert CD" routine :004D8C26 85C0 test eax, eax <-- Check to see what came back in eax :004D8C28 750C jne 004D8C36 <-- Take this jump for good CD check :004D8C2A 33C0 xor eax, eax <-- Zero out eax :004D8C2C 5D pop ebp :004D8C2D 5F pop edi :004D8C2E 5E pop esi :004D8C2F 5B pop ebx :004D8C30 83C450 add esp, 00000050 :004D8C33 C21000 ret 0010 <-- Exit back to Win95 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004D8C1F(C), :004D8C28(C) | :004D8C36 C744241000000000 mov [esp+10], 00000000 <-- Continue with the game :004D8C3E 56 push esi :004D8C3F 68007E4F00 push 004F7E00 * Reference To: USER32.EnumWindows, Ord:00C8h | :004D8C44 FF1528561001 Call dword ptr [01105628] :004D8C4A 8B442410 mov eax, dword ptr [esp+10] -- more program code -- Nearly the same as the first section we examined and well make the same type of edit for this one also. The program checks for CD once per gaming session, then sets a flag value at 6564C0 that tells rally the CD is present and already has been checked for. Then as you play the game rally checks 6564C0 and if set correctly skips checking for the CD again. Which is useful to us in removing the check from the game. Instead of relying on the conditional jump to skip over the CD check, we'll change it to always jump over the CD check call. Then NOP'ing out the call to the CD checking routine and forcing the following conditional jump is overkill, but that's ok I added it in anyways. Now all you need to do is make the patch to the rally.exe file by version: For the version off the CD, edit rally.exe ============================================================= Search for: 75 15 E8 81 62 FF FF 85 C0 75 0C (offset 235,304) Change to : EB -- 90 90 90 90 90 -- -- EB -- Search for: 75 15 E8 B1 5F FF FF 85 C0 75 0C (offset 236,024) Change to : EB -- 90 90 90 90 90 -- -- EB -- For the MMX Enhanced version 2.1, edit rally.exe ============================================================= Search for: 75 15 E8 4F F3 01 00 85 C0 75 0C (offset 884,042) Change to : EB -- 90 90 90 90 90 -- -- EB -- Search for: 75 15 E8 7A F0 01 00 85 C0 75 0C (offset 884,767) Change to : EB -- 90 90 90 90 90 -- -- EB -- Once again I helped guide you through another example of a CD check and showed you a way to defeat it. The end result is a copy of Rally Championship on your hard drive that you can run without having to dig out the original game CD first. Well, there you have it, Sega Rally is now FiX'ed Static Vengeance