Da ja bald Diablo III herauskommt, da dachte ich mir: „Ach, spielst du doch einmal wieder den ersten Teil von Diablo!“. Schnell die CD aus dem Regal geholt, dass Spiel installiert und dann musste ich mit bedauern feststellen: Diablo 1 braucht die CD ständig im Laufwerk. Ich hasse es, wenn Spiele irgendeinen Kopierschutz haben, die meine Freiheit einschränken. In diesem Fall eine einfache CD Protection. Also habe ich mich einmal herangesetzt und wollte schauen, wie denn dieser Kopierschutz genau funktioniert. Als erstes habe ich mir alle Dateien von Diablo 1 angeschaut und stellte fest, dass das installierte Spiel gerade mal 5 MB groß war. „Hmm… da scheint wohl irgendetwas zu fehlen“, dachte ich mir. Nun noch schaue welcher Inhalt auf der CD ist und tatsächlich, auf der CD befindet sich eine Datei, welche über 500 MB groß ist. Diese habe ich dann erst einmal in mein Diablo Verzeichnis kopiert, auf der Hoffnung der Kopierschutz sei umgangen, was natürlich nicht der Fall war. Immer wenn keine CD eingelegt war, kam eine Fehlermeldung,welche aussagte dass die „diabdat.mpq“ nicht geladen werden konnte.
Die „diabdat.mpq“ scheinen wohl alle Spielinhalte zu sein, deswegen liest er auch ständig von der CD, wenn man ohne Crack spielt. Also habe ich mir nun die Diablo.exe vorgenommen und die Fehlermeldung gesucht. Dafür habe ich erst einmal alle referenzierten Strings durchsucht und speziell nach „mpq“ gesucht. So habe ich dann die folgende Stelle ausfindig machen können:
;CPU Disasm ;Address Hex dump Command Comments ;... 0041ACF0 |> /6A 01 /PUSH 1 ; /Arg3 = 1 0041ACF2 |. |68 E8030000 |PUSH 3E8 ; |Arg2 = 3E8 0041ACF7 |. |68 B4E74800 |PUSH OFFSET 0048E7B4 ; |Arg1 = ASCII "DiabloCD" 0041ACFC |. |BA A4E74800 |MOV EDX,OFFSET 0048E7A4 ; |ASCII "\diabdat.mpq" 0041AD01 |. |8BCE |MOV ECX,ESI ; | 0041AD03 |. |E8 6A000000 |CALL 0041AD72 ; \Diablo.0041AD72 0041AD08 |. |85C0 |TEST EAX,EAX 0041AD0A |. |A3 944B6300 |MOV DWORD PTR DS:[634B94],EAX 0041AD0F |. |75 1B |JNE SHORT 0041AD2C 0041AD11 |. |8D45 FC |LEA EAX,[LOCAL.1] 0041AD14 |. |50 |PUSH EAX 0041AD15 |. |E8 12EA0400 |CALL <JMP.&DiabloUI.UiCopyProtError> ; Jump to DiabloUI.UiCopyProtError 0041AD1A |. |837D FC 02 |CMP DWORD PTR SS:[LOCAL.1],2 0041AD1E |.^\75 D0 |JNE SHORT 0041ACF0 0041AD20 |. |B9 98E74800 |MOV ECX,OFFSET 0048E798 ; ASCII "diabdat.mpq" 0041AD25 |. |E8 726FFEFF |CALL 00401C9C ; [Diablo.00401C9C 0041AD2A |.^\EB C4 \JMP SHORT 0041ACF0 ;...
Die in den Kommentaren gekennzeichnete erste Funktion wird wohl versuchen die „diabdat.mpq“ von CD zu laden. Diese weißt im ersten Argument den String „DiabloCD“ auf. Diese Funktion sollte ich mir also einmal genauer anschauen. Ein Breakpoint auf die Funktion setzen und das Programm starten. Nach dem Klick auf den Play Button, passierte eine ganze Weile nichts, bis Schlussendlich Diablo startete und mir wieder die Fehlermeldung zeigte. „Wie kann das sein? Ich hab den Breakpoint doch an der richtigen Stelle gesetzt und er sollte genau dort anhalten!“. Also noch einmal das Programm zurücksetzen und manuell zu dem Punkt hinsteuern, wo Diablo gestartet wird. Ich bin jeden verdächtigen CALL Aufruf nachgegangen um so den Übeltäter zu finden. Ohne die CALLs zurückzuverfolgen wird man bei folgender Funktion merken, das hier Diablo gestartet wird.
;CPU Disasm ;Address Hex dump Command Comments ;... 0046BD70 |> \50 PUSH EAX ; /Arg4 0046BD71 |. 56 PUSH ESI ; |Arg3 0046BD72 |. 6A 00 PUSH 0 ; |Arg2 = 0 0046BD74 |. 6A 00 PUSH 0 ; |/ModuleName = NULL 0046BD76 |. FF15 BC914700 CALL DWORD PTR DS:[<&KERNEL32.GetModuleH ; |\KERNEL32.GetModuleHandleA 0046BD7C |. 50 PUSH EAX ; |Arg1 0046BD7D |. E8 C8CDF9FF CALL 00408B4A ; \Diablo.00408B4A ;... [/x86] Nach dieser Funktion wird wieder Diablo gestartet und ich sollte mir diese einmal genauer anschauen. In dieser Funktion sieht es folgendermaßen aus. [x86] ;CPU Disasm ;Address Hex dump Command Comments 00408B4A /$ 55 PUSH EBP ; Diablo.00408B4A(guessed Arg1,Arg2,Arg3,Arg4) 00408B4B |. 8BEC MOV EBP,ESP 00408B4D |. 81EC 0C010000 SUB ESP,10C 00408B53 |. 53 PUSH EBX 00408B54 |. 56 PUSH ESI 00408B55 |. 8B75 08 MOV ESI,DWORD PTR SS:[ARG.1] 00408B58 |. 8BCE MOV ECX,ESI 00408B5A |. E8 95020000 CALL 00408DF4 ; Diablo wird gestartet 00408B5F |. 8935 EC565200 MOV DWORD PTR DS:[5256EC],ESI 00408B65 |. E8 1B9D0400 CALL 00452885 ; [Diablo.00452885 00408B6A |. 85C0 TEST EAX,EAX ;... [/x86] In dem Kommentar hab ich wieder eine CALL-Funktion markiert, welche das Spiel startet. Diese sollte man also wieder zurückverfolgen und schauen was sie macht. Folgendes habe ich dann in dieser Funktion gefunden. [x86] ;CPU Disasm ;Address Hex dump Command Comments ;... 00408E2A |. FF15 70914700 CALL DWORD PTR DS:[<&KERNEL32.GetModuleF ; \KERNEL32.GetModuleFileNameA 00408E30 |. 8D85 78FEFFFF LEA EAX,[LOCAL.98] 00408E36 |. 50 PUSH EAX ; /<%s> = "D:\Spiele\Diablo\Diablo.exe" // hier ist gerade mein Pointer 00408E37 |. 8D85 64FDFFFF LEA EAX,[LOCAL.167] ; | 00408E3D |. 68 48444800 PUSH OFFSET 00484448 ; |Format = "Reload-%s" 00408E42 |. 50 PUSH EAX ; |Buf => OFFSET LOCAL.167 00408E43 |. FF15 E8934700 CALL DWORD PTR DS:[<&USER32.wsprintfA>] ; \USER32.wsprintfA 00408E49 |. 33DB XOR EBX,EBX ;...
Das hört sich ja gar nicht gut an: „Reload-D:\Spiele\Diablo\Diablo.exe“. Will er etwa das Spiel neustarten? Schauen wir doch mal weiter.
;CPU Disasm ;Address Hex dump Command Comments ;... 00408F19 |. 50 PUSH EAX 00408F1A |. FF15 80914700 CALL DWORD PTR DS:[<&KERNEL32.CreateProcessA>] 00408F20 |. 6A FF PUSH -1 ; /Timeout = WAIT_FOREVER 00408F22 |. FF75 E4 PUSH DWORD PTR SS:[LOCAL.7] ; |hProcess => [LOCAL.7] 00408F25 |. FF15 68934700 CALL DWORD PTR DS:[<&USER32.WaitForInputIdle>] ; \USER32.WaitForInputIdle 00408F2B |. FF75 E8 PUSH DWORD PTR SS:[LOCAL.6] ;...
OK. Die folgende Stelle zeigt, dass ein neuer Prozess gestartet wird und dieser Prozess auf ewig auf den anderen wartet. Sprich Diablo wird noch einmal in einem neuen Prozess gestartet. Dadurch kann dieser nicht durch den Debugger zurückverfolgt werden und erst wenn das Spiel beendet wird, greift der Debugger wieder ein.
Da er Diablo einfach als neuen Prozess startet, dies in der nächsten Instanz aber so erkennt, dass das Spiel nicht noch eine neue Instanz startet, kann man auch einfach diese Funktion per NOP überschreiben und so unschädlich machen. So kann man dann im selben Prozess weiter debuggen.
;CPU Disasm ;Address Hex dump Command Comments ;... 00408B58 |. 8BCE MOV ECX,ESI 00408B5A |. E8 95020000 CALL 00408DF4 ; Diablo wird gestartet // diese Funktion per NOP überschreiben 00408B5F |. 8935 EC565200 MOV DWORD PTR DS:[5256EC],ESI 00408B65 |. E8 1B9D0400 CALL 00452885 ; [Diablo.00452885 00408B6A |. 85C0 TEST EAX,EAX ;...
Nach dem ich die Funktion per NOP überschrieben habe, kommt noch weiterer Code der das Fenster aufbaut, das Blizzard-Logo-Intro initialisiert und endlich zu der Stelle kommt, wo die „diabdat.mpq“ geladen werden soll.
;CPU Disasm ;Address Hex dump Command Comments 0041ACF0 |> /6A 01 /PUSH 1 ; /Arg3 = 1 0041ACF2 |. |68 E8030000 |PUSH 3E8 ; |Arg2 = 3E8 0041ACF7 |. |68 B4E74800 |PUSH OFFSET 0048E7B4 ; |Arg1 = ASCII "DiabloCD" 0041ACFC |. |BA A4E74800 |MOV EDX,OFFSET 0048E7A4 ; |ASCII "\diabdat.mpq" 0041AD01 |. |8BCE |MOV ECX,ESI ; | 0041AD03 |. |E8 6A000000 |CALL 0041AD72 ; \Diablo.0041AD72 // unsere Ladefunktion 0041AD08 |. |85C0 |TEST EAX,EAX 0041AD0A |. |A3 944B6300 |MOV DWORD PTR DS:[634B94],EAX 0041AD0F |. |75 1B |JNE SHORT 0041AD2C ; //Die Überprüfung ob die CD vorhanden ist/war, wenn ja springe zu 0041AD2C 0041AD11 |. |8D45 FC |LEA EAX,[LOCAL.1] 0041AD14 |. |50 |PUSH EAX 0041AD15 |. |E8 12EA0400 |CALL <JMP.&DiabloUI.UiCopyProtError> ; Jump to DiabloUI.UiCopyProtError 0041AD1A |. |837D FC 02 |CMP DWORD PTR SS:[LOCAL.1],2 0041AD1E |.^\75 D0 |JNE SHORT 0041ACF0 0041AD20 |. |B9 98E74800 |MOV ECX,OFFSET 0048E798 ; ASCII "diabdat.mpq" 0041AD25 |. |E8 726FFEFF |CALL 00401C9C ; [Diablo.00401C9C 0041AD2A |.^\EB C4 \JMP SHORT 0041ACF0 ; //Hier wird das Fehlermeldungsfenster erzeugt 0041AD2C |> 6A 01 PUSH 1 ; /Arg1 = 1 //also CD drin, hier her springen 0041AD2E |. 8D55 F8 LEA EDX,[LOCAL.2] ; | 0041AD31 |. B9 84E74800 MOV ECX,OFFSET 0048E784 ; |ASCII "ui_art\title.pcx" 0041AD36 |. E8 CB7F0400 CALL 00462D06 ; \Diablo.00462D06 0041AD3B |. 85C0 TEST EAX,EAX 0041AD3D |. 75 0A JNE SHORT 0041AD49 ; wenn die diabdat.mpq korrekt geladen wurde überspringen, ansonsten Fehlermeldung 0041AD3F |. B9 60E74800 MOV ECX,OFFSET 0048E760 ; ASCII "Main program archive: diabdat.mpq" 0041AD44 |. E8 536FFEFF CALL 00401C9C ; [Diablo.00401C9C 0041AD49 |> 8B4D F8 MOV ECX,DWORD PTR SS:[LOCAL.2] 0041AD4C |. E8 2C7F0400 CALL 00462C7D ; [Diablo.00462C7D 0041AD51 |. BA 50E74800 MOV EDX,OFFSET 0048E750 ; ASCII "\patch_rt.mpq" 0041AD56 |. 8BCF MOV ECX,EDI 0041AD58 |. 6A 00 PUSH 0 ; /Arg3 = 0 0041AD5A |. 68 D0070000 PUSH 7D0 ; |Arg2 = 7D0 0041AD5F |. 68 40E74800 PUSH OFFSET 0048E740 ; |Arg1 = ASCII "DiabloInstall" 0041AD64 |. E8 09000000 CALL 0041AD72 ; \Diablo.0041AD72 //Oha selbe Ladefunktion wie bei Diabdat.mpq
Also bei 0041AD0F sollte man am besten einen JMP einfügen, damit man nicht mehr aufgefordert wird die CD einzulegen, jedoch ist unsere Ladefunktion so eingestellt das sie die „diabdat.mpq“ immer noch von CD laden will. Da sich die CD nicht mehr im Laufwerk befindet, wird die Funktion die Datei nie finden. Um das zu umgehen sollte ich wohl irgendetwas an der Funktion ändern. Die Lösung befindet sich weiter unten am Ende des Codes. Die „patch_rt.mpq“ befindet sich in dem lokalen Diablo Verzeichnis und diese wird immer korrekt geladen. Also schreiben wir die Ladefunktion so um, dass sie das Selbe macht, wie bei der „patch_rt.mpq“. Also passen wir alle Argumente so an, das die Funktion wie die, von der „patch_rt.mpq“ aussieht:
;CPU Disasm ;Address Hex dump Command Comments 0041ACF0 |> /6A 00 PUSH 0 ; /Arg3 = 0 0041ACF2 |. |68 D0070000 PUSH 7D0 ; |Arg2 = 7D0 0041ACF7 |. |68 40E74800 PUSH OFFSET 0048E740 ; |Arg1 = ASCII "DiabloInstall" 0041ACFC |. |BA A4E74800 MOV EDX,OFFSET 0048E7A4 ; |ASCII "\diabdat.mpq" 0041AD01 |. |8BCE MOV ECX,ESI ; | 0041AD03 |. |E8 6A000000 CALL 0041AD72 ; \Diablo.0041AD72 0041AD08 |. |85C0 TEST EAX,EAX 0041AD0A |. |A3 944B6300 MOV DWORD PTR DS:[634B94],EAX 0041AD0F |. |EB 1B JMP SHORT 0041AD2C ; nun wird immer ein JMP ausgeführt. UiCopyProt(ect)Error wird umgangen :D 0041AD11 |. |8D45 FC LEA EAX,[EBP-4] 0041AD14 |. |50 PUSH EAX 0041AD15 |. |E8 12EA0400 CALL <JMP.&DiabloUI.UiCopyProtError> ; Jump to DiabloUI.UiCopyProtError 0041AD1A |. |837D FC 02 CMP DWORD PTR SS:[EBP-4],2 0041AD1E |.^\75 D0 JNE SHORT 0041ACF0 0041AD20 |. |B9 98E74800 MOV ECX,OFFSET 0048E798 ; ASCII "diabdat.mpq" 0041AD25 |. |E8 726FFEFF CALL 00401C9C ; [Diablo.00401C9C 0041AD2A |.^\EB C4 JMP SHORT 0041ACF0 0041AD2C |> 6A 01 PUSH 1 ; /Arg1 = 1 0041AD2E |. 8D55 F8 LEA EDX,[EBP-8] ; | 0041AD31 |. B9 84E74800 MOV ECX,OFFSET 0048E784 ; |ASCII "ui_art\title.pcx" 0041AD36 |. E8 CB7F0400 CALL 00462D06 ; \Diablo.00462D06 0041AD3B |. 85C0 TEST EAX,EAX 0041AD3D |. 75 0A JNE SHORT 0041AD49 0041AD3F |. B9 60E74800 MOV ECX,OFFSET 0048E760 ; ASCII "Main program archive: diabdat.mpq" 0041AD44 |. E8 536FFEFF CALL 00401C9C ; [Diablo.00401C9C 0041AD49 |> 8B4D F8 MOV ECX,DWORD PTR SS:[EBP-8] 0041AD4C |. E8 2C7F0400 CALL 00462C7D ; [Diablo.00462C7D 0041AD51 |. BA 50E74800 MOV EDX,OFFSET 0048E750 ; ASCII "\patch_rt.mpq" 0041AD56 |. 8BCF MOV ECX,EDI 0041AD58 |. 6A 00 PUSH 0 ; /Arg3 = 0 0041AD5A |. 68 D0070000 PUSH 7D0 ; |Arg2 = 7D0 0041AD5F |. 68 40E74800 PUSH OFFSET 0048E740 ; |Arg1 = ASCII "DiabloInstall" 0041AD64 |. E8 09000000 CALL 0041AD72 ; \Diablo.0041AD72
Somit wird nun die „diabdat.mpq“ genauso wie die „patch_rt.mpq“ geladen und man kann das Spiel nun ohne CD spielen. Nun kann ich endlich das Spiel das erste Mal ohne CD starten und konnte nun mit einem Krieger in den Kampf ziehen. Doch was passierte nun, überall sind bunte Pixel und das Spiel sieht dadurch total doof aus.
Doch das liegt am Spiel und nicht am Crack. Unter Windows Vista/Windows 7 wird durch den Explorer irgendein Grafik-Glitch verursacht. Ich tippe einmal das es etwas mit der Aero-Oberfläche zu tun hat. Dieser Grafik-Bug gilt für die meisten Spiele die DirectX unter der Version 9 benutzen. Dafür musste dann noch ein kleines Batch-Skript her:
taskkill /IM explorer.exe /F cd "D:\Spiele\Diablo\" start diablo.exe pause start explorer.exe
Nun kann man das Spiel in seiner vollen Pracht genießen. Mal schauen, welches Programm als nächstes dran glauben muss ;)! Ich hoffe es hat euch gefallen. Bis zum nächsten Mal.
Greetz TheVamp
Leider ist es unter Win7 durchaus so, das Diablo sein eigenes „Installationslaufwerk“ wiederfindet.
Deamontools ist zwar ne schöne Sache, muss aber nicht sein, solche Geschütze aufzufahren ;)
Die Methode mit der Batch-Datei ist bei älteren Spielen unter Win7 durchaus vertretbar.
P.S. Kannst du mir die .exe vererben ;)
Du machst dir das vieleicht kompliziert:
Zum Kopierschutz: CD in ein ISO konvertieren und dies dann über ein entsprechendes Programm (z.B. Deamon Tools) bereit stellen
Zum Grafikproblem: In den Eigenschaften der EXE den Karteikartenreiter „Kompatibilität“ auswählen und Haken bei 256 Farben und 640×480 setzen. ggf. noch den Kompatibilitätsmodus auf win XP stellen.