
Free Information Xchange presents:

Quake 2 (v3.05&3.14) - CD crack tutorial by R!SC - 28/01/99

REQUIREMENTS:
Hex editor
W32Dasm 8.93 !
SoftIce95 for cracking v3.14
Quake 2 (200mb install)


-----Chapter 1 (v3.05)--------------------------------------------------------------------------

  Well, there's about 12 different patches available for Quake2, same amount of cracks, but no (1)
tutorial(s)! Just for all you 'Newbie Crackerz', i decided to tell you how crack it. Later
versions of this game, the CD protection has been removed :( wonder why? Anyway, i have run out
of new games to crack, and since everybody owns Quake 2, thought it would make a good tutorial.

  Do a medium install, remove the CD, run the game. You get a standard messagebox asking you to 
insert the CD. "You must have the Quake2 CD in the drive to play" !YOU must FiX this!

  First off, make a copy of quake2.exe, load this into W32Dasm. We begin by looking through the
string references for our message. Heh, its in there, right at the bottom (alphabetical order, or
something :) Take a little look...


:0042B22D 51                      push ecx                 <-- contains drive to check
:0042B22E FFD6                    call esi                 <-- GetDriveTypeA (lpRootPathName?)
:0042B230 83F805                  cmp eax, 00000005        <-- Was it a CD-ROM (05 means it was)
:0042B233 7421                    je 0042B256              <-- This conditional Jump Skips the Message...

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042B21E(C)
|
:0042B235 8A442404                mov al, byte ptr [esp+04] <-- pull drive letter off stack
:0042B239 FEC0                    inc al                    <-- Goto next drive
:0042B23B 3C7A                    cmp al, 7A                <-- Compare letter to 'Z'
:0042B23D 88442404                mov byte ptr [esp+04], al <-- Store it again
:0042B241 0F8E6AFFFFFF            jle 0042B1B1              <-- Run the CD check again if the drive is <=Z (jump if less or equal)

* Possible StringData Ref from Data Obj ->"You must have the Quake2 CD in "
                                        ->"the drive to play."
                                  |
:0042B247 6864474400              push 00444764            <-- RVA of Message (just ignore me)
:0042B24C 6A00                    push 00000000            <-- Null :)
:0042B24E E89D6CFEFF              call 00411EF0            <-- Otherwise, we never found the Quake2 CD
:0042B253 83C408                  add esp, 00000008        <-- Hmmn, this bit never happens, as the 
                                                             - previous call exits the program! what a waste of 3 bytes

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0042B1F1(C), :0042B233(C)
|
:0042B256 5E                      pop esi
:0042B257 83C444                  add esp, 00000044        <-- FiX up stack pointer
:0042B25A C3                      ret                      <-- retURN to caller, we've got a CD present


 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. But the exit routine of this CD check doesn't leave a return value to
be checked AND the code is pretty linear, with the message inside of it. It looks like you can
skip the message by changing this

:0042B233 7421                    je 0042B256

to a jump always

:0042B233 EB21                    jmp 0042B256

This works, but i wasn't happy, the CD check still gets taken, fails, but skips the error message
and continues with the game. I wanted to trace this code back to the original caller and kill the
check all together. Scroll the screen up (with pageup key?) looking for line referring to a CALL

About three pages up, you find this bit of code, referenced to by only ONE caller.


* Referenced by a CALL at Address: <-- 'CALL' = good, 'Jump' = not good
|:00427408   <-- Lets take a look here, double right click this number!
|
:0042B190 83EC44                  sub esp, 00000044        <-- Get some stack to play with
:0042B193 56                      push esi                 <-- Save esi
:0042B194 6A01                    push 00000001            <-- Error Mode flag

* Reference To: KERNEL32.SetErrorMode, Ord:0213h
                                  |
:0042B196 FF152803B000            Call dword ptr [00B00328] <-- Stop the OS from handling certain errors (like file io, gpf ...)

* Reference To: KERNEL32.GetDriveTypeA, Ord:00DFh
                                  |
:0042B19C 8B352403B000            mov esi, dword ptr [00B00324]
:0042B1A2 C64424053A              mov [esp+05], 3A         <-- ':'
:0042B1A7 C644240600              mov [esp+06], 00         <-- 0 (remember, null terminated)
:0042B1AC C644240463              mov [esp+04], 63         <-- 'c'
                                                             - [esp + 4] = 63 3a00 = db 'c:',0
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042B241(C)
|
:0042B1B1 8D442404                lea eax, dword ptr [esp+04]
:0042B1B5 8D4C2408                lea ecx, dword ptr [esp+08]
:0042B1B9 50                      push eax
...

Ignore all the above crap, it'll only get ya confused, lets just take a look at the caller to
this routine. Goto code location 00427408...


* Possible StringData Ref from Data Obj ->"maxclients"
                                  |
:004273F2 6838DB4300              push 0043DB38
:004273F7 E804D1FEFF              call 00414500
:004273FC A1C4C14600              mov eax, dword ptr [0046C1C4] <-- some flag type thing (about 7 references to this)
:00427401 83C40C                  add esp, 0000000C               - which all do tests on it, with a conditional jump afterwards
:00427404 85C0                    test eax, eax                   - not to worry though, the game seems to work fine without us knowing
:00427406 7505                    jne 0042740D                    - what its for, but this jne is VERY nice
:00427408 E8833D0000              call 0042B190            <-- BadBoy CD check routine, that we traced back...

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004273B5(U), :004273C5(C), :004273E9(U), :00427406(C)
|
:0042740D E8EE600000              call 0042D500            <-- If we get here, we must have a CD or the game 
:00427412 8B0D64BB4600            mov ecx, dword ptr [0046BB64]   - would have exited eh?
:00427418 A308BC4600              mov dword ptr [0046BC08], eax


OK, we found the original caller, its all set up for a crack to skip the CD check, we just need
to change a byte here or there, which ones do i hear you cry, NO? Well i will tell you anyway!
three choices, lies in your hands...

change
004273FC A1C4C14600              mov eax, dword ptr [0046C1C4] 
to
004273FC B801000000              mov eax, 00000001         <-- eax is not equal, so the jne is taken!
         ^^^^^^^^^^
or change
:00427406 7505                    jne 0042740D
to
:00427406 EB05                    jmp 0042740D             <-- fuck eax, i'm jumping anyway!
          ^^^^ 
or change
:0042B190 83EC44                  sub esp, 00000044        <-- The first command in CD check
to
:0042B190 C3                      ret                      <-- retURN to caller, goes straight back without
          ^^                                                 - doing any tests, or displaying nasty messages.


 Another tutorial comes to an end and another game has been FiX'ed!
oi, what about v3.14?
 
  sorry, i forgot, well, here you go...

  
-----Chapter 2 (v3.14)----Cracked 'Live'--------------------------------------------------------

 Install the 3.14 patch. Chances are its the same CD check, just different offsets. Remember the
messagebox asking you to insert the CD. "You must have the Quake2 CD in the drive to play", well
were going to search the refs for that again.

  First off, make a copy of quake2.exe(3.14), load this into W32Dasm. We begin by looking through the
string references for our message. Heh, its in there, right at the bottom (alphabetical order, or
something :) Take a little look... DeJaVu


* Referenced by a CALL at Address:
|:0042B386                                                 <-- Bit different, but only ref'ed by one caller!
|
:0042F5F0 E82BFFFFFF              call 0042F520            <-- Do some stuff (see next snippet)
:0042F5F5 803800                  cmp byte ptr [eax], 00   <-- Check return value
:0042F5F8 750F                    jne 0042F609             <-- jump if not zero to good CD check

* Possible StringData Ref from Data Obj ->"You must have the Quake2 CD in "
                                        ->"the drive to play."
                                  |
:0042F5FA 683CD24400              push 0044D23C            <-- RVA of message
:0042F5FF 6A00                    push 00000000            <-- Null :-P
:0042F601 E89A41FEFF              call 004137A0            <-- Show MB & Exit
:0042F606 83C408                  add esp, 00000008        <-- This still doesn't happen!

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042F5F8(C)
|
:0042F609 C3                      ret                      <-- retURN to caller, good CD check


 Well, it changed a bit eh? They moved all the CD check into 2 subroutines, as you will see now.
The part above calls a little chunk of code, that checks a flag, and then decides wether to do
the CD check we know and love from the previous versions?


* Referenced by a CALL at Addresses:
|:0042F5F0   , :0042FC81                                   <-- Oh my GOD, 2 references, only one of them is our normal CD check!
|
:0042F520 A1DC224700              mov eax, dword ptr [004722DC]  <-- move dword from memory into eax
:0042F525 83EC44                  sub esp, 00000044
:0042F528 85C0                    test eax, eax            <-- is eax zero?
:0042F52A 56                      push esi
:0042F52B 740A                    je 0042F537              <-- yes? then jump to CD check...
:0042F52D B8E0224700              mov eax, 004722E0        <-- otherwise, make sure eax !=0 (not equal to zero?)
:0042F532 5E                      pop esi
:0042F533 83C444                  add esp, 00000044
:0042F536 C3                      ret                      <-- and go back to caller..


 Damn! Thought it was going to be just as easy, but the check is called twice! Just if one CD
check fails, we get a nasty message box, and the game quits. If the other one fails, who knows
what could happen hey? Lets have a look at the other caller, 0042FC81..


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042FC5D(C)
|
:0042FC6B 8B4C2438                mov ecx, dword ptr [esp+38]
:0042FC6F 8B442430                mov eax, dword ptr [esp+30]
:0042FC73 51                      push ecx
:0042FC74 A354324700              mov dword ptr [00473254], eax
:0042FC79 E852FFFFFF              call 0042FBD0
:0042FC7E 83C404                  add esp, 00000004
:0042FC81 E89AF8FFFF              call 0042F520            <-- Its this nasty little command
:0042FC86 8B3D60324700            mov edi, dword ptr [00473260]  <--??
:0042FC8C 89442434                mov dword ptr [esp+34], eax    <-- save return value in memory 
:0042FC90 85C0                    test eax, eax                  <-- check return value, before, for a good CD check,
:0042FC92 747B                    je 0042FD0F                      - it should not be equal (zero)
:0042FC94 83FF7D                  cmp edi, 0000007D        <-- no idea?
:0042FC97 7D76                    jge 0042FD0F             <-- i just don't know wtf's going off!
:0042FC99 33DB                    xor ebx, ebx               - ?
:0042FC9B 85FF                    test edi, edi              - ?
:0042FC9D 7E46                    jle 0042FCE5


 Anyway, i got no idea whats going off at this second call to the CD check, so i'm going to patch
the first one, and see what happens..

* Referenced by a CALL at Address:
|:0042B386                                                 <-- We go here, and kill the call!
|
:0042F5F0 E82BFFFFFF              call 0042F520
:0042F5F5 803800                  cmp byte ptr [eax], 00
:0042F5F8 750F                    jne 0042F609

* Possible StringData Ref from Data Obj ->"You must have the Quake2 CD in "
                                        ->"the drive to play."
                                        

Ok, double right click 0042B386, we end up here...


* Possible StringData Ref from Data Obj ->"maxclients"
                                  |
:0042B370 68C05C4400              push 00445CC0
:0042B375 E836B3FEFF              call 004166B0
:0042B37A A164B54700              mov eax, dword ptr [0047B564]
:0042B37F 83C40C                  add esp, 0000000C
:0042B382 85C0                    test eax, eax
:0042B384 7505                    jne 0042B38B             <-- The jump to patch, easiest target
                                                             - File Offset 2A784
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042B367(U)
|
:0042B386 E865420000              call 0042F5F0

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0042B2B4(U), :0042B2C7(C), :0042B2EF(U), :0042B34F(C), :0042B365(C)
|:0042B384(C)
|
:0042B38B E810690000              call 00431CA0
:0042B390 8B15103F4700            mov edx, dword ptr [00473F10]
:0042B396 A3A83F4700              mov dword ptr [00473FA8], eax


 Pretty much the same as v3.05, just gonna make my patch, and see what happens. (p.s. i change the
7505 to EB05, jump always)

brb

 Heh, oh shit! The game works, single player, multiplayer deathmatch, but not co-op. Same message
box, click OK then it totally exits the game.. Hmmn...I take another look here, at the second
caller to the CD check.


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042FC5D(C)
|
:0042FC6B 8B4C2438                mov ecx, dword ptr [esp+38]
:0042FC6F 8B442430                mov eax, dword ptr [esp+30]
:0042FC73 51                      push ecx
:0042FC74 A354324700              mov dword ptr [00473254], eax
:0042FC79 E852FFFFFF              call 0042FBD0
:0042FC7E 83C404                  add esp, 00000004
:0042FC81 E89AF8FFFF              call 0042F520            <-- Second call to CD check
:0042FC86 8B3D60324700            mov edi, dword ptr [00473260]  <--??
:0042FC8C 89442434                mov dword ptr [esp+34], eax    <-- save return value in memory 
:0042FC90 85C0                    test eax, eax                  <-- check return value, before, for a good CD check,
:0042FC92 747B                    je 0042FD0F                    <-- file offset 2F092
:0042FC94 83FF7D                  cmp edi, 0000007D
:0042FC97 7D76                    jge 0042FD0F
:0042FC99 33DB                    xor ebx, ebx
:0042FC9B 85FF                    test edi, edi
:0042FC9D 7E46                    jle 0042FCE5


 Well, tamper with a few things here, y'no, and still nothing happens! Time to hit the alcohol,
and for the Zen? to take over control.

(few minutes later(well, about 30mins actually))

...and we kill this routine by putting a 'ret' at the start, then we can play the co-op game
without the CD online aswell. (zen cracking is cool!)


* Referenced by a CALL at Addresses:
|:004021B7   , :00402C52   , :00402C75   , :004034B0   , :004034F0   
|:004041E8   , :0040430E   , :004044BA   , :00404AFA   , :004093B7   
|:00409BA7   , :0040AAFD   , :0040B046   , :0040B298   , :0040B407   
|:0040B55C   , :0040B570   , :0040B584   , :0040CFFE   , :0040D0F3   
|:0040D2E2   , :0040D63A   , :0040D65D   , :0040EC53   , :0040F229   
|:0040F813   , :00410006   , :00410E7C   , :00410E9D   , :00410EB4   
|:00410F99   , :00410FC2   , :00410FD9   , :0041105E   , :00411087   
|:0041109E   , :00411148   , :0041116C   , :004111E8   , :00411211   
|:00411228   , :004112D4   , :00411326   , :0041136C   , :0041138D   
|:004113A4   , :0041146F   , :00411488   , :0041149F   , :00411500   
|:0041151D   , :00411571   , :004115C0   , :004115DD   , :0041164F   
|:0041166C   , :004116CF   , :0041177F   , :004118D1   , :00411944   
|:00411AB5   , :00411ADD   , :00411B48   , :00411B78   , :00411C00   
|:004132A5   , :00413365   , :004133B9   , :00413C8F   , :00413CD3   
|:00413CEA   , :00414594   , :004145A9   , :00414704   , :004148A7   
|:00414970   , :00414AE2   , :00416D08   , :00416E4C   , :00416E60   
|:00416F74   , :00416FB8   , :0041995A   , :0041A369   , :0041A40F   
|:0041D2E9   , :0041D350   , :0042028D   , :004203A9   , :0042048B   
|:004206B3   , :00420A3A   , :00424EE2   , :00424EF6   , :00424F16   
|:00424FAA   , :00425133   , :00426736   , :00429F0E   , :0042A557   
|:0042A69C   , :0042A6C1   , :0042A74C   , :0042AC08   , :0042AC26   
|:0042ACD0   , :0042D3C3   , :0042D54A   , :0042D585   , :0042D5C0   
|:0042DB45   , :0042DBC9   , :0042F0C2   , :0042F601   , :0042FA76   
|:0042FAA8   , :0042FEC9   , :00430747   , :0043085E   , :004308A4   
|:004309F1   <-- Okay then, whatever LOL :)
|
:004137A0 A12C444500              mov eax, dword ptr [0045442C] <-- file ofset 12ba0
:004137A5 85C0                    test eax, eax                   - simply replace the A1 with a c3
:004137A7 7412                    je 004137BB                     - thus, whenever there is an error,
:004137A9 6840544500              push 00455440                   - ya game will just crash, instead of telling you
                                                                  - not very clever, but you can play with no CD!
* Possible StringData Ref from Data Obj ->"recursive error after: %s"
                                  |
:004137AE 6860834400              push 00448360
:004137B3 E8A8BC0100              call 0042F460
:004137B8 83C408                  add esp, 00000008
... several other error messages then byebye quake2


Okay, i tell you how to get to this bit. My computer & softice & Quake2 don't mix well, when in
Q2, if i hit ctrl-d, it freezes my game, softice works, but i cant see my screen, i put a bpx
on GetDriveTypeA when mucking about with v3.05, the game froze when loading, just left with a
black screen, i typed bc*;x;, and it carried on loading, good stuff man! It means softice doesn't
work properly, but it doesn't crash your computer, like some other volitive games do. Well, the
game sortof exits to windows to display the message box, then totally quits. 

Crack the first CD check by changing the byte at file offset 2A784 to a EB. OK, enter softice,
type in 'bpx messageboxa'. (break point on execution of user32.messageboxa) (as u should know),
if you don't understand, get some tutorials on softice...

Run Quake, select multiplayer, gametype co-op, begin game. softice Pops up inside of user32.dll
or something, hit F11 to return to the caller. Hi message box, click OK, BINGO, back in quake2.exe 
eip = 42F4A1. Lets have a look here in Wdasm.


* Referenced by a CALL at Addresses:
|:004137B3   , :00413881   , :00414B0B   , :00423A05   , :0042F63B   
|:0042F64F   , :0042F664   , :0042F69D   , :0042F73A   , :0042F76B   
|:0042F77E   <-- Oh momma, which one called it this time?
|
:0042F460 81EC00040000            sub esp, 00000400        <-- We tamper with this bit, to find out the caller
:0042F466 E855B3FDFF              call 0040A7C0
:0042F46B E8E0B3FDFF              call 0040A850
:0042F470 8B8C2404040000          mov ecx, dword ptr [esp+00000404]
:0042F477 8D842408040000          lea eax, dword ptr [esp+00000408]
:0042F47E 50                      push eax
:0042F47F 8D542404                lea edx, dword ptr [esp+04]
:0042F483 51                      push ecx
:0042F484 52                      push edx
:0042F485 E866370000              call 00432BF0
:0042F48A 83C40C                  add esp, 0000000C
:0042F48D 8D442400                lea eax, dword ptr [esp] <-- pull the CD message from the stack
:0042F491 6A00                    push 00000000

* Possible StringData Ref from Data Obj ->"Error"
                                  |
:0042F493 6804D24400              push 0044D204            <-- The title of our message box
:0042F498 50                      push eax                 <-- naughty boys, hide the message in eax???
:0042F499 6A00                    push 00000000              - so theres no string data ref to it???

* Reference To: USER32.MessageBoxA, Ord:0195h
                                  |
:0042F49B FF15E4214400            Call dword ptr [004421E4]     <-- show the message box
:0042F4A1 A1D8224700              mov eax, dword ptr [004722D8] <-- we end up here after pressing F11
:0042F4A6 85C0                    test eax, eax
:0042F4A8 7407                    je 0042F4B1
:0042F4AA 50                      push eax

 
Ok, no problem, referenced to by 11 callers, which one called it? you can probably have a look at
the stack pointer to find out, but i did it my way, remember this line?

:0042F460 81EC00040000            sub esp, 00000400        <-- file offset 2e860

Well, we put a 'int 3' here, by changing the 81 to a CC. Save the file. Back into softice, type
in 'bpint 03', run Quake, select multiplayer, gametype co-op, begin game. softice Pops up on the
int 03, eip=42F460. Type in 'a eip', type in 'ret' then press escape. the new instruction at
42f460 should be ret(URN to caller). Press F8 to trace the new instruction. BINGO, we have our
caller. 
 

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00413862(C)
|
:00413877 6840544500              push 00455440

* Possible StringData Ref from Data Obj ->"%s"
                                  |
:0041387C 68005F4400              push 00445F00
:00413881 E8DABB0100              call 0042F460            <-- So this is the call to the show message&exit routine
:00413886 83C408                  add esp, 00000008        <-- After hitting F8, we end up here
:00413889 C3                      ret

Heh, DeJaVu again, oh no, i just realised that to display the first CD message, it calls 4137A0,
the part ref'ed to by about a million callers. Anyway, from where we are, 413886, scroll up to
find the caller to this routine, as you see, you reach 4137A0, this is the start of the routine.
to kill the 'You must have...' message, i put a ret at the start, and the co-op game ran with no
problems...

So, forget about killing the CD check, just kill all the error messages by loading your copy of
quake2.exe,(the untampered one, with no cc's, no eb's etc) & changing the A1 at 12BA0 to a C3.
Oh yeah, and kill all your breakpoints in softice before running the game.


NoNoNo. Put a 'CC' at offset 12BA0, run quake again. Multiplayer, start network server, co-op,
begin. BOOM, back into softice, but i cant see a thing! OK, i can still type commands though, so
i type in 'a eip';'ret';F8; this would replace the 'int 03', at the start of the MB routine with
a 'ret', then hitting F8, we execute the 'ret', getting to the command after the caller...

 We then type in 'd eip', so the current location is displayed in the data window (which we cant
see, but all will be revealed), then hit F5 to exit s-ice. Okay, the game crashes, so what, back
into s-ice, look at your data window, we can see it now, and the memory address is 0042F606,
GODDAMNIT, thats the address right after the only ref to 'You must have the ...', but Wdasm told
us it only had one caller, the lying piece of crap.


* Referenced by a CALL at Address:
|:0042B386                                                 <-- only ref'ed by one caller(its a lie!)
|
:0042F5F0 E82BFFFFFF              call 0042F520            <-- check CD
:0042F5F5 803800                  cmp byte ptr [eax], 00   <-- Check return value
:0042F5F8 750F                    jne 0042F609             <-- We force this fukkah now (offset 2E9F8)
                                                             - what we should have done in the first place :(
* Possible StringData Ref from Data Obj ->"You must have the Quake2 CD in "
                                        ->"the drive to play."
                                  |
:0042F5FA 683CD24400              push 0044D23C            <-- RVA of message
:0042F5FF 6A00                    push 00000000            <-- Null :-P
:0042F601 E89A41FEFF              call 004137A0            <-- Show MB & Exit
:0042F606 83C408                  add esp, 00000008        <-- This still doesn't happen!

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042F5F8(C)
|
:0042F609 C3                      ret                      <-- retURN to caller, good CD check ALWAYS!


Heh, so ignore everything thats been said before, just understand that a lot of cracking is trial
& error, goto offset 2E9F8 and change the '75' to a 'EB'...

YES,YES,YES!!!!!

 aNOTHER tUTORIAL cOMES tO aN eND aND aNOTHER gAME hAS bEEN fIX'eD!

wELL, hOPE yOU lEARNT sOMETHING aT lEAST...

 R!SC -- risc@notme.com



