Free Information Xchange '98 presents: Hexen II - CD crack by Static Vengeance Requirements: hex editor and full install There are many versions of Hexen II out there, but I'll show you what I learned from the version off the CD and how I applied it to the lastest version of 1.11 for both the normal version and the OpenGL version. To begin you'll need to get W32Dasm up and running and dissassmble h2.exe, which is the Hexen II program file. Once W32Dasm has finished disassmebling H2, go up to the menu bar and select "Refs" and then select "String Data References" from the drop down menu. Then when the String Data Ref box pops up, grab the sliderbar and scroll all the way to the end. Then find the string: "You need to have the Hexen 2 CD " and double click on it. This will put you in the middle of a section of code that deals with the CD check. And it should in general look something like this for h2.exe v1.03 off the CD: -- Program Code -- :0043F84A C3 ret <-- End of a routine :0043F84B 0500000000 add eax, 00000000 <-- No direct reference or call :0043F850 81EC140A0000 sub esp, 00000A14 :0043F856 8D842414020000 lea eax, dword ptr [esp+00000214] :0043F85D 53 push ebx :0043F85E 56 push esi :0043F85F 57 push edi :0043F860 55 push ebp :0043F861 50 push eax :0043F862 6800080000 push 00000800 * Reference To: KERNEL32.GetLogicalDriveStringsA, Ord:00F7h | :0043F867 FF15E8C45700 Call dword ptr [0057C4E8] :0043F86D 8DAC2424020000 lea ebp, dword ptr [esp+00000224] :0043F874 8DBC2424020000 lea edi, dword ptr [esp+00000224] :0043F87B B9FFFFFFFF mov ecx, FFFFFFFF :0043F880 2BC0 sub eax, eax :0043F882 F2 repnz :0043F883 AE scasb :0043F884 F7D1 not ecx :0043F886 49 dec ecx :0043F887 7476 je 0043F8FF * Reference To: KERNEL32.GetVolumeInformationA, Ord:014Eh | :0043F889 8B35E4C45700 mov esi, dword ptr [0057C4E4] * Reference To: KERNEL32.GetDriveTypeA, Ord:00DEh <-- Common in CD checks | :0043F88F 8B1DE0C45700 mov ebx, dword ptr [0057C4E0] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043F8FD(C) | :0043F895 55 push ebp :0043F896 FFD3 call ebx :0043F898 83F805 cmp eax, 00000005 <-- Returned value from GetDriveTypeA :0043F89B 7543 jne 0043F8E0 <-- If not 05 (CD-Rom) then jump to fail :0043F89D 8D842420010000 lea eax, dword ptr [esp+00000120] :0043F8A4 6804010000 push 00000104 :0043F8A9 8D4C241C lea ecx, dword ptr [esp+1C] :0043F8AD 50 push eax :0043F8AE 8D54241C lea edx, dword ptr [esp+1C] :0043F8B2 51 push ecx :0043F8B3 8D44241C lea eax, dword ptr [esp+1C] :0043F8B7 52 push edx :0043F8B8 8D4C242C lea ecx, dword ptr [esp+2C] :0043F8BC 50 push eax :0043F8BD 6804010000 push 00000104 :0043F8C2 51 push ecx :0043F8C3 55 push ebp :0043F8C4 FFD6 call esi :0043F8C6 85C0 test eax, eax :0043F8C8 7416 je 0043F8E0 :0043F8CA 8D44241C lea eax, dword ptr [esp+1C] * Possible StringData Ref from Data Obj ->"Hexen II" | :0043F8CE 6834DC4800 push 0048DC34 :0043F8D3 50 push eax :0043F8D4 E807620300 call 00475AE0 :0043F8D9 83C408 add esp, 00000008 :0043F8DC 85C0 test eax, eax :0043F8DE 7433 je 0043F913 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:0043F89B(C), :0043F8C8(C) | :0043F8E0 8BFD mov edi, ebp :0043F8E2 B9FFFFFFFF mov ecx, FFFFFFFF :0043F8E7 2BC0 sub eax, eax :0043F8E9 F2 repnz :0043F8EA AE scasb :0043F8EB F7D1 not ecx :0043F8ED 03E9 add ebp, ecx :0043F8EF B9FFFFFFFF mov ecx, FFFFFFFF :0043F8F4 8BFD mov edi, ebp :0043F8F6 2BC0 sub eax, eax :0043F8F8 F2 repnz :0043F8F9 AE scasb :0043F8FA F7D1 not ecx :0043F8FC 49 dec ecx :0043F8FD 7596 jne 0043F895 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043F887(C) | * Possible StringData Ref from Data Obj ->"You need to have the Hexen 2 CD " <-- The text Ref that got us here ->"in order to play!" | :0043F8FF C7058010480040DC4800 mov dword ptr [00481080], 0048DC40 :0043F909 C70530E0470000000000 mov dword ptr [0047E030], 00000000 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0043F8DE(C) | :0043F913 5D pop ebp :0043F914 5F pop edi :0043F915 5E pop esi :0043F916 5B pop ebx :0043F917 81C4140A0000 add esp, 00000A14 :0043F91D C3 ret From here it's easy to see that the important thing is to exit this routine at 43F913, which is past the "You need.." text. To do this I decided to change the conditional jump after the cmp eax, 00000005 to a non conditional jump (jmp) to 43F913. So you'll end up changing the 75 43 to EB 76. The farthest you can jump with a 8 bit relative displacement is 7F bytes. The eighth bit is used to determine which direction you are jumping. High bit set is jumping backwards and high bit clear is jumping forward. So you can see with this edit we are close the maximum jump allowed with the two byte jump instruction (EB xx). To continue, with this edit allows you to play Hexen II without the CD in the CD-Rom drive. The same patch works for glh2.exe, although in a different place. Next up is v1.11 of Hexen II. This version's CD check routine is very close to the above listed routine, however it has a few basic changes. -- Program Code -- :0040477A C3 ret <-- End of a routine :0040477B 0500000000 add eax, 00000000 <-- Again, no direct reference or call :00404780 81EC140A0000 sub esp, 00000A14 :00404786 8D842414020000 lea eax, dword ptr [esp+00000214] :0040478D 53 push ebx :0040478E 56 push esi :0040478F 57 push edi :00404790 55 push ebp :00404791 50 push eax :00404792 6800080000 push 00000800 * Reference To: KERNEL32.GetLogicalDriveStringsA, Ord:00F7h | :00404797 FF15AC25EB00 Call dword ptr [00EB25AC] :0040479D 8DAC2424020000 lea ebp, dword ptr [esp+00000224] :004047A4 8DBC2424020000 lea edi, dword ptr [esp+00000224] :004047AB B9FFFFFFFF mov ecx, FFFFFFFF :004047B0 2BC0 sub eax, eax :004047B2 F2 repnz :004047B3 AE scasb :004047B4 F7D1 not ecx :004047B6 49 dec ecx :004047B7 0F8490000000 je 0040484D <-- This time we'll use this conditional <-- jump with it's 32 bit relative offset * Reference To: KERNEL32.GetVolumeInformationA, Ord:014Eh | :004047BD 8B35A825EB00 mov esi, dword ptr [00EB25A8] * Reference To: KERNEL32.GetDriveTypeA, Ord:00DEh <-- Common call in CD checks | :004047C3 8B1DA425EB00 mov ebx, dword ptr [00EB25A4] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00404847(C) | :004047C9 55 push ebp :004047CA FFD3 call ebx :004047CC 83F805 cmp eax, 00000005 <-- Again, 05 is the value for a CD-ROM :004047CF 7559 jne 0040482A :004047D1 8D842420010000 lea eax, dword ptr [esp+00000120] :004047D8 6804010000 push 00000104 :004047DD 8D4C241C lea ecx, dword ptr [esp+1C] :004047E1 50 push eax :004047E2 8D54241C lea edx, dword ptr [esp+1C] :004047E6 51 push ecx :004047E7 8D44241C lea eax, dword ptr [esp+1C] :004047EB 52 push edx :004047EC 8D4C242C lea ecx, dword ptr [esp+2C] :004047F0 50 push eax :004047F1 6804010000 push 00000104 :004047F6 51 push ecx :004047F7 55 push ebp :004047F8 FFD6 call esi :004047FA 85C0 test eax, eax :004047FC 742C je 0040482A :004047FE 8D44241C lea eax, dword ptr [esp+1C] * Possible StringData Ref from Data Obj ->"Hexen II" | :00404802 684CE94500 push 0045E94C :00404807 50 push eax :00404808 E8F30E0500 call 00455700 :0040480D 83C408 add esp, 00000008 :00404810 85C0 test eax, eax :00404812 744D je 00404861 :00404814 8D44241C lea eax, dword ptr [esp+1C] * Possible StringData Ref from Data Obj ->"Hexen_II" | :00404818 6858E94500 push 0045E958 :0040481D 50 push eax :0040481E E8DD0E0500 call 00455700 :00404823 83C408 add esp, 00000008 :00404826 85C0 test eax, eax :00404828 7437 je 00404861 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004047CF(C), :004047FC(C) | :0040482A 8BFD mov edi, ebp :0040482C B9FFFFFFFF mov ecx, FFFFFFFF :00404831 2BC0 sub eax, eax :00404833 F2 repnz :00404834 AE scasb :00404835 F7D1 not ecx :00404837 03E9 add ebp, ecx :00404839 B9FFFFFFFF mov ecx, FFFFFFFF :0040483E 8BFD mov edi, ebp :00404840 2BC0 sub eax, eax :00404842 F2 repnz :00404843 AE scasb :00404844 F7D1 not ecx :00404846 49 dec ecx :00404847 0F857CFFFFFF jne 004047C9 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004047B7(C) | * Possible StringData Ref from Data Obj ->"You need to have the Hexen 2 CD " <--The text Ref we're looking for ->"in order to play!" | :0040484D C705C8FC460064E94500 mov dword ptr [0046FCC8], 0045E964 :00404857 C705C8D1460000000000 mov dword ptr [0046D1C8], 00000000 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: <-- Need to get here |:00404812(C), :00404828(C) | :00404861 5D pop ebp :00404862 5F pop edi :00404863 5E pop esi :00404864 5B pop ebx :00404865 81C4140A0000 add esp, 00000A14 :0040486B C3 ret Ok, you can see that the conditional jump we used for v1.03 is not close enough to the section of code where we need to end up. So instead we'll be using the long conditional jump. This is easier and get's us right to the exting section of code at 40484D. The one thing you need to know, however, is that a conditional jump is 6 bytes long. The opcode (operation code or instruction) is a two byte opcode 0F 84 with a 32bit (4 bytes) relative offset. While a non conditional jump is 5 bytes, 1 byte for the opcode E9 and a 32bit relative offset. So you'll need to add 1 NOP instruction for a filler. Other then that all you need to do is make sure you have the right offset to get to the end of the CD check. Now we come to a bundled version of Hexen II called Hexen II: Continent of Blackmarsh. Using the same methods as outlined above you'll find the CD check routine. However, this time there is a direct reference made by a call. So cracking it will be much easier. That routine looks like this: * Referenced by a CALL at Address: |:004388EE <-- Write this address down | :00404760 81EC140A0000 sub esp, 00000A14 :00404766 8D842414020000 lea eax, dword ptr [esp+00000214] :0040476D 53 push ebx :0040476E 56 push esi :0040476F 57 push edi :00404770 55 push ebp :00404771 50 push eax :00404772 6800080000 push 00000800 * Reference To: KERNEL32.GetLogicalDriveStringsA, Ord:00F7h | :00404777 FF15AC15E900 Call dword ptr [00E915AC] :0040477D 8D9C2424020000 lea ebx, dword ptr [esp+00000224] :00404784 B9FFFFFFFF mov ecx, FFFFFFFF :00404789 8BFB mov edi, ebx :0040478B 2BC0 sub eax, eax :0040478D F2 repnz :0040478E AE scasb :0040478F F7D1 not ecx :00404791 49 dec ecx :00404792 7476 je 0040480A * Reference To: KERNEL32.GetDriveTypeA, Ord:00DEh <-- Commonly used in CD checks | :00404794 8B35A815E900 mov esi, dword ptr [00E915A8] * Reference To: KERNEL32.GetVolumeInformationA, Ord:014Eh | :0040479A 8B2DA415E900 mov ebp, dword ptr [00E915A4] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00404808(C) | :004047A0 53 push ebx :004047A1 FFD6 call esi :004047A3 83F805 cmp eax, 00000005 <-- 05 returned from getdrivetype :004047A6 7543 jne 004047EB <-- is the code for a CD-Rom drive :004047A8 8D842420010000 lea eax, dword ptr [esp+00000120] :004047AF 6804010000 push 00000104 :004047B4 8D4C241C lea ecx, dword ptr [esp+1C] :004047B8 50 push eax :004047B9 8D54241C lea edx, dword ptr [esp+1C] :004047BD 51 push ecx :004047BE 8D44241C lea eax, dword ptr [esp+1C] :004047C2 52 push edx :004047C3 8D4C242C lea ecx, dword ptr [esp+2C] :004047C7 50 push eax :004047C8 6804010000 push 00000104 :004047CD 51 push ecx :004047CE 53 push ebx :004047CF FFD5 call ebp :004047D1 85C0 test eax, eax :004047D3 7416 je 004047EB :004047D5 8D44241C lea eax, dword ptr [esp+1C] * Possible StringData Ref from Data Obj ->"m3D_2" <-- Volume name of bundled CD | :004047D9 684CD94500 push 0045D94C :004047DE 50 push eax :004047DF E8FCFF0400 call 004547E0 :004047E4 83C408 add esp, 00000008 :004047E7 85C0 test eax, eax :004047E9 7433 je 0040481E * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004047A6(C), :004047D3(C) | :004047EB 8BFB mov edi, ebx :004047ED B9FFFFFFFF mov ecx, FFFFFFFF :004047F2 2BC0 sub eax, eax :004047F4 F2 repnz :004047F5 AE scasb :004047F6 F7D1 not ecx :004047F8 03D9 add ebx, ecx :004047FA B9FFFFFFFF mov ecx, FFFFFFFF :004047FF 8BFB mov edi, ebx :00404801 2BC0 sub eax, eax :00404803 F2 repnz :00404804 AE scasb :00404805 F7D1 not ecx :00404807 49 dec ecx :00404808 7596 jne 004047A0 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00404792(C) | * Possible StringData Ref from Data Obj ->"You need to have the Hexen 2 CD " <-- String we needed to find ->"in order to play!" | :0040480A C705F8E5460054D94500 mov dword ptr [0046E5F8], 0045D954 :00404814 C70548C0460000000000 mov dword ptr [0046C048], 00000000 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004047E9(C) | :0040481E 5D pop ebp :0040481F 5F pop edi :00404820 5E pop esi :00404821 5B pop ebx :00404822 81C4140A0000 add esp, 00000A14 :00404828 C3 ret Ok, from here you need to go to the address of the caller (*Referenced by a CALL at Address: 004388EE ) and check out that code. You should see something like this (with surounding code): :004388AE 8B44240E mov eax, dword ptr [esp+0E] :004388B2 50 push eax :004388B3 E89887FCFF call 00401050 :004388B8 83C404 add esp, 00000004 :004388BB 25FFFF0000 and eax, 0000FFFF :004388C0 3D45E80000 cmp eax, 0000E845 :004388C5 740F je 004388D6 :004388C7 3D52D60000 cmp eax, 0000D652 :004388CC 7408 je 004388D6 :004388CE 33C0 xor eax, eax :004388D0 EB09 jmp 004388DB * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:0043885A(C), :00438863(C) | :004388D2 33C0 xor eax, eax :004388D4 EB05 jmp 004388DB * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004388C5(C), :004388CC(C) | :004388D6 A148C04600 mov eax, dword ptr [0046C048] * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004388D0(U), :004388D4(U) | :004388DB A348C04600 mov dword ptr [0046C048], eax :004388E0 85C0 test eax, eax :004388E2 750A jne 004388EE * Possible StringData Ref from Data Obj ->"You need to re-install Hexen 2 " ->"on this computer!" | :004388E4 C705F8E54600A0C14600 mov dword ptr [0046E5F8], 0046C1A0 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004388E2(C) | :004388EE E86DBEFCFF call 00404760 <-- Call to CD check :004388F3 5F pop edi :004388F4 5E pop esi :004388F5 5B pop ebx :004388F6 81C4BC010000 add esp, 000001BC :004388FC C3 ret There you have the call to the CD check with no other flag checks or anything. To crack this version of Hexen II you simply need to stop the call to the CD check from being made. This bast way to do this is to change the call to 5 NOP's. Or you could just change the E8 to a B8, that would change the instruction from call 00404760 to mov eax, FFFCBE6D. Either way will work, but I just like to nop it out. There is one other slight problem or quick fix that you'll need to set up. In the Win95 registry Hexen II sets a special registry key. So you'll have to add the following to your registry, open [HKEY_LOCAL_MACHINE\System\CurrentControlSet\control\ComputerName\ComputerName] add the key "RAID" and set it to the value of "Santa needs a new sled!" To crack Hexen II make the edits by version and type of Hexen you're using: Hexen II v1.03 off the CD: Edit h2.exe at offset 257,179 ============================= Search for: 74 43 8D 84 Change to : EB 76 -- -- Edit glh2.exe at offset 15,254 ============================== Search for: 74 43 8D 84 Change to : EB 76 -- -- Hexen II v1.11 upgrade patch off the net: Edit h2.exe at offset 263,986 ============================= Search for: 0F 84 90 00 00 00 Change to : 90 E9 A4 00 00 00 Edit glh2.exe at offset 15,287 ============================== Search for: 0F 84 90 00 00 00 Change to : 90 E9 A4 00 00 00 For the bundled version of Hexen II: Continent of Blackmarsh v1.08 Edit glh2.exe at offset 228,590 =============================== Search for: E8 6D BE FC FF Change to : 90 90 90 90 90 For V1.11 edit pvrh2.exe at offset 232,078 ============================= Search for: E8 ED B0 FC FF Change to : 90 90 90 90 90 Now for this to work you MUST run Hexen II by running the H2 EXE and NOT the "splash.exe" Splash.exe will still require you to have CD in the your CD-ROM drive. Other then that, I ran the OpenGL version with the 3Dfx mini-gl port and it works fine and looks great. There you have it, three different versions of Hexen II have been FiX'ed! Static VengeanceFFFF call 004240D0