Free Information Xchange '98 presents: ProPinball: TimeShock! - CD crack by Static Vengeance Requirements: hex editor and full install ProPinball: Timeshock! is a great pinball game that can run in resolutions up to 1600x1200 at 16 bit color. Which is great becuase so can my video card and monitor. The game has a nice look, good sound and good play; however there seems to be that same old bug that pops up with most games. I'm talking about the need to have the CD in your drive before you can play the game. This is one bug that needs to be FiX'ed and as it turns out isn't all that hard. To begin, you will need to disassemble TimeShock!.exe and the program I use for this is W32Dasm from RUSoft. When W32Dasm has finished it's job, go up to the main menu bar and select "Refs" and then select "String Data References" from the drop down menu. From here, grab the slider bar on the refs box and scroll down to where it says: "Please insert "Timeshock!" CD ". Double click on this ref and you're in the middle of the CD check routine. TimeShock! uses the Windows Multi-Media (WINMM) DLL to check for the CD. The routine looks like this: * Referenced by a CALL at Address: |:00445963 | :0045A090 53 push ebx :0045A091 51 push ecx :0045A092 52 push edx :0045A093 56 push esi :0045A094 57 push edi :0045A095 55 push ebp :0045A096 83EC54 sub esp, 00000054 :0045A099 833DD0A64700FF cmp dword ptr [0047A6D0], FFFFFFFF :0045A0A0 7427 je 0045A0C9 :0045A0A2 A0D0A64700 mov al, byte ptr [0047A6D0] :0045A0A7 0441 add al, 41 * Possible StringData Ref from Data Obj ->"?:" | :0045A0A9 68E4A64700 push 0047A6E4 :0045A0AE A2E4A64700 mov byte ptr [0047A6E4], al * Reference To: KERNEL32.GetDriveTypeA, Ord:0003h <-- Commonly used in CD checks | :0045A0B3 2EFF1508824B00 Call dword ptr cs:[004B8208] :0045A0BA 83F805 cmp eax, 00000005 <-- Value for a CD-ROM drive :0045A0BD 740A je 0045A0C9 :0045A0BF C705D0A64700FFFFFFFF mov dword ptr [0047A6D0], FFFFFFFF * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:0045A0A0(C), :0045A0BD(C) | :0045A0C9 BEFFFFFFFF mov esi, FFFFFFFF :0045A0CE 3B35D0A64700 cmp esi, dword ptr [0047A6D0] :0045A0D4 754C jne 0045A122 :0045A0D6 31DB xor ebx, ebx * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0045A106(C) | :0045A0D8 88D8 mov al, bl :0045A0DA 0441 add al, 41 * Possible StringData Ref from Data Obj ->"?:" | :0045A0DC 68E4A64700 push 0047A6E4 :0045A0E1 A2E4A64700 mov byte ptr [0047A6E4], al * Reference To: KERNEL32.GetDriveTypeA, Ord:0003h | :0045A0E6 2EFF1508824B00 Call dword ptr cs:[004B8208] :0045A0ED 83F805 cmp eax, 00000005 :0045A0F0 7510 jne 0045A102 :0045A0F2 83FEFF cmp esi, FFFFFFFF :0045A0F5 7502 jne 0045A0F9 :0045A0F7 89DE mov esi, ebx * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0045A0F5(C) | :0045A0F9 E84AFFFFFF call 0045A048 :0045A0FE 85C0 test eax, eax :0045A100 7506 jne 0045A108 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0045A0F0(C) | :0045A102 43 inc ebx :0045A103 83FB1A cmp ebx, 0000001A :0045A106 7CD0 jl 0045A0D8 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0045A100(C) | :0045A108 83FB19 cmp ebx, 00000019 :0045A10B 7E0F jle 0045A11C * Possible StringData Ref from Data Obj ->"Please insert "Timeshock!" CD " <-- How I found this routine ->"drive and try again" <-- so quickly | :0045A10D 68F8F34600 push 0046F3F8 :0045A112 6A2B push 0000002B :0045A114 E8B37CFFFF call 00451DCC :0045A119 83C408 add esp, 00000008 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0045A10B(C) | :0045A11C 891DD0A64700 mov dword ptr [0047A6D0], ebx * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0045A0D4(C) | :0045A122 BB14000000 mov ebx, 00000014 :0045A127 8D442430 lea eax, dword ptr [esp+30] :0045A12B BE04020000 mov esi, 00000204 :0045A130 31D2 xor edx, edx * Possible StringData Ref from Data Obj ->"?:" | :0045A132 BFE4A64700 mov edi, 0047A6E4 :0045A137 E824170000 call 0045B860 :0045A13C 89742438 mov dword ptr [esp+38], esi :0045A140 897C243C mov dword ptr [esp+3C], edi :0045A144 BB02000000 mov ebx, 00000002 :0045A149 31F6 xor esi, esi * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:0045A19D(C), :0045A1A4(C), :0045A1B8(U) | :0045A14B 8D442430 lea eax, dword ptr [esp+30] :0045A14F 50 push eax :0045A150 6802320000 push 00003202 :0045A155 6803080000 push 00000803 :0045A15A 56 push esi * Reference To: WINMM.mciSendCommandA, Ord:0006h <-- The check is done through WINMM calls | :0045A15B 2EFF1510814B00 Call dword ptr cs:[004B8110] :0045A162 85C0 test eax, eax :0045A164 7454 je 0045A1BA :0045A166 8D442430 lea eax, dword ptr [esp+30] :0045A16A 50 push eax :0045A16B 6802330000 push 00003302 :0045A170 6803080000 push 00000803 :0045A175 56 push esi * Reference To: WINMM.mciSendCommandA, Ord:0006h | :0045A176 2EFF1510814B00 Call dword ptr cs:[004B8110] :0045A17D 85C0 test eax, eax :0045A17F 7439 je 0045A1BA :0045A181 53 push ebx * Possible StringData Ref from Data Obj ->"Pro Pinball - Timeshock!" | :0045A182 682CF44600 push 0046F42C * Possible StringData Ref from Data Obj ->"Unable to play CD tracks. This " ->"may be because another program " ->"such as CDPLAYER is already using " ->"the drive" | :0045A187 6848F44600 push 0046F448 :0045A18C 8B2D308F4900 mov ebp, dword ptr [00498F30] :0045A192 55 push ebp * Reference To: USER32.MessageBoxA, Ord:000Bh | :0045A193 2EFF15A4814B00 Call dword ptr cs:[004B81A4] :0045A19A 83F803 cmp eax, 00000003 :0045A19D 72AC jb 0045A14B :0045A19F 760C jbe 0045A1AD :0045A1A1 83F805 cmp eax, 00000005 :0045A1A4 75A5 jne 0045A14B :0045A1A6 31C0 xor eax, eax :0045A1A8 E927010000 jmp 0045A2D4 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0045A19F(C) | :0045A1AD 53 push ebx :0045A1AE 6A01 push 00000001 :0045A1B0 E8177CFFFF call 00451DCC :0045A1B5 83C408 add esp, 00000008 :0045A1B8 EB91 jmp 0045A14B * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:0045A164(C), :0045A17F(C) | :0045A1BA 8B442434 mov eax, dword ptr [esp+34] :0045A1BE 31DB xor ebx, ebx :0045A1C0 A3C0A64700 mov dword ptr [0047A6C0], eax * Reference To: WINMM.auxGetNumDevs, Ord:0002h | :0045A1C5 2EFF1500814B00 Call dword ptr cs:[004B8100] :0045A1CC 89C6 mov esi, eax :0045A1CE 85C0 test eax, eax :0045A1D0 7639 jbe 0045A20B * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0045A209(C) | :0045A1D2 6A30 push 00000030 :0045A1D4 8D442404 lea eax, dword ptr [esp+04] :0045A1D8 50 push eax :0045A1D9 53 push ebx * Reference To: WINMM.auxGetDevCapsA, Ord:0001h | :0045A1DA 2EFF15FC804B00 Call dword ptr cs:[004B80FC] :0045A1E1 85C0 test eax, eax :0045A1E3 7521 jne 0045A206 :0045A1E5 66837C242801 cmp word ptr [esp+28], 0001 :0045A1EB 7519 jne 0045A206 :0045A1ED F644242C01 test [esp+2C], 01 :0045A1F2 7412 je 0045A206 :0045A1F4 B801000000 mov eax, 00000001 :0045A1F9 891D4C604A00 mov dword ptr [004A604C], ebx :0045A1FF A3C4A64700 mov dword ptr [0047A6C4], eax :0045A204 EB05 jmp 0045A20B * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:0045A1E3(C), :0045A1EB(C), :0045A1F2(C) | :0045A206 43 inc ebx :0045A207 39F3 cmp ebx, esi :0045A209 72C7 jb 0045A1D2 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:0045A1D0(C), :0045A204(U) | :0045A20B 8D442444 lea eax, dword ptr [esp+44] :0045A20F 50 push eax :0045A210 6800040000 push 00000400 :0045A215 680D080000 push 0000080D :0045A21A 8B1DC0A64700 mov ebx, dword ptr [0047A6C0] :0045A220 BA0A000000 mov edx, 0000000A :0045A225 53 push ebx :0045A226 89542458 mov dword ptr [esp+58], edx * Reference To: WINMM.mciSendCommandA, Ord:0006h | :0045A22A 2EFF1510814B00 Call dword ptr cs:[004B8110] :0045A231 85C0 test eax, eax :0045A233 7414 je 0045A249 :0045A235 31F6 xor esi, esi :0045A237 31C0 xor eax, eax :0045A239 8935C0A64700 mov dword ptr [0047A6C0], esi :0045A23F 83C454 add esp, 00000054 :0045A242 5D pop ebp :0045A243 5F pop edi :0045A244 5E pop esi :0045A245 5A pop edx :0045A246 59 pop ecx :0045A247 5B pop ebx :0045A248 C3 ret That's the important section of the routine. Although you don't really need to understand each and every instruction, as long as you can see the general flow of the program you'll get by. There is just one call to the above CD check and that is made from 445963. -- Program Code -- :00445953 8B1598794700 mov edx, dword ptr [00477998] :00445959 A154794700 mov eax, dword ptr [00477954] :0044595E E851FAFFFF call 004453B4 :00445963 E828470100 call 0045A090 <-- Call the CD check routine :00445968 A350794700 mov dword ptr [00477950], eax :0044596D 85C0 test eax, eax :0044596F 7513 jne 00445984 :00445971 E8E6D20000 call 00452C5C :00445976 85C0 test eax, eax :00445978 7E0A jle 00445984 :0044597A B87F000000 mov eax, 0000007F :0044597F E8E0D20000 call 00452C64 -- Continuing Code -- However there is a newer patch on the net for v1.07 of Timeshock! The copy protection is almost the same but in a different location. But there has been a secondary CD check added that can be found by double clicking the "Insert ..." string twice. This newly added check looks like this: * Referenced by a CALL at Addresses: |:0040D5A1 , :0040F0C0 | :0045A0C0 E8B3FFFFFF call 0045A078 <-- Check for the CD again :0045A0C5 85C0 test eax, eax <-- Test the result of the check :0045A0C7 750F jne 0045A0D8 <-- Take this jump for a good check * Possible StringData Ref from Data Obj ->"Please insert "Timeshock!" CD " <-- "THE" string for a 2nd time ->"drive and try again" | :0045A0C9 688CF64600 push 0046F68C :0045A0CE 6A2B push 0000002B :0045A0D0 E8A77CFFFF call 00451D7C :0045A0D5 83C408 add esp, 00000008 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0045A0C7(C) | :0045A0D8 C3 ret <-- Just need to get here some way From here all you need to do is to prevent the call to the CD check routine and the game will run just fine without the CD present. The only flaw I have run into is at the end of the game the sound "Game Over" is truncated. Not sure what the reason for this is, but I would suspect that when the CD is present there is enough time between MCI (winmm) calls to allow you to hear the entire game over sound. So I'd rather live with that little problem then with the CD check. Anyways, as a good CD check returns a 00000000 in eax and esi I zero'ed them out and used a nop as a filler for the first CD call. While for version 1.07 I simply overwrote the CD check call with mov eax, 00000001 to force the jne to always be true. Simple enough, here are the actual edits: Edit Timeshock!.exe v1.05 off the CD ============================================ Search for: E8 28 47 01 00 at offset 281,955 Change to : 31 C0 31 F6 90 Edit Timeshock!.exe v1.07 patch off the net ============================================ Search for: E8 C4 47 01 00 at offset 281,875 Change to : 31 C0 31 F6 90 Search for: E8 B3 FF FF FF at offset 365,760 Change to : B8 01 00 00 00 With the edits you have a FiX'ed version of TimeShock! Pro Pinball! Static Vengeance