Patching CD checks

Tuesday, April 19 2005 @ 07:28 PM CEST

Contributed by: detten

Level : beginner

Basic CD check patching tutorial. (Target included)



Intro


In this tutorial, we are going to patch a CD check. These days, most CDs have a commercial protection. (eg cu-dilla) You can find an unwrapper for (almost) every protection. But when you unwrapped it, you still need to patch the proggy to run without the CD.

The example I'm going to patch, is a little proggy that checks if 'the matrix' CD is inserted. Get it here : attachment

TOOLS USED : WDASM, hex-editor or Ollydbg

Let's dig in :)



Where to start?


Disassemble the example program in W32Dasm.
If you search for a CD-protection, the best place to start is the API GetDriveType. So hit the import functions button, and look for it.

Now, what does this API do? Let's check in Win32.hlp :

UINT GetDriveType(

LPCTSTR lpRootPathName // address of root path
);

The return value is an unsigned INT, with following possible return values :

Value                Meaning


0                    The drive type cannot be determined.
1                    The root directory does not exist.
2 DRIVE_REMOVABLE    The drive can be removed from the drive.
3 DRIVE_FIXED        The disk cannot be removed from the drive.
4 DRIVE_REMOTE       The drive is a remote (network) drive.
5 DRIVE_CDROM        The drive is a CD-ROM drive.
6 DRIVE_RAMDISK      The drive is a RAM disk.        

So, the API checks what type of drive LPCTSTR points to. For a CD-ROM the return value will be 5.

Now we are ready to check the deadlisting :)



The code
* Possible StringData Ref from Data Obj ->"A:"

0040100C 6836304000 push 00403036 ;Push pointer with address of root path
* Reference To: KERNEL32.GetDriveTypeA, Ord:00F0h

00401011 E800010000           Call 00401116
00401016 83F805               cmp eax, 00000005 ; Check if drive is CD-ROM
00401019 7411                 je 0040102C       ; If it is, jump to 'check CD'
0040101B 803D363040005A       cmp byte ptr [00403036], 5A  ;check if drive = 'Z'
00401022 7440                 je 00401064  ; If it is, jump to 'CD not found'
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
  00401062(U), :004010B0(U), :004010E0(U)

00401024 FE0536304000         inc byte ptr [00403036] ; Increment first byte of
                                                      ; the string with root path
                                                      ; so it points to the next drive
0040102A EBE0                 jmp 0040100C            ; Jump back to check next drive

What happens in the above code? We start to check if 'a:' is a CD-ROM. If it is no CD-rom, we increment the first byte of the root path-string.
( A: becomes B: ) We check the drive again, and so on till we find a CD-ROM drive.


* Referenced by a (U)nconditional or (C)onditional Jump at Address: 00401019(C)

0040102C 6A00 push 00000000
0040102E 6A00 push 00000000
00401030 6A00 push 00000000
00401032 6A00 push 00000000
00401034 6A00 push 00000000
00401036 6A14 push 00000014 ; Push the max number of bytes to read
00401038 68AA314000 push 004031AA ; Push address that will receive Volumename
* Possible StringData Ref from Data Obj ->"A:"

0040103D 6836304000      push 00403036   ; Push pointer to root path
* Reference To: KERNEL32.GetVolumeInformationA, Ord:0162h


00401042 E8DB000000      Call 00401122   ; Get the Volume Name of the drive
00401047 51              push ecx
00401048 33C9            xor ecx, ecx    ; Make ecx zero
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 00401060(U)

0040104A 8A8100304000    mov al, byte ptr [ecx+00403000] ; Move byte of 'THE MATRIX' to eax
00401050 3881AA314000    cmp byte ptr [ecx+004031AA], al ; Cmp volumename with 'THE MATRIX'
00401056 750A            jne 00401062 ; If a byte is different, we jump to 'check next drive'
00401058 41              inc ecx      ; Add 1 to counter
00401059 83F90A          cmp ecx, 0000000A ; First 10 bytes equal?

0040105C 7202            jb 00401060  ; If not 10 bytes yet, go check next byte
0040105E EB1E            jmp 0040107E ; jump to 'Next CD check'


The above code is quite simple, the program fetches the VolumeName of the CD-ROM drive, and checks it with a string.
To know the contents of the string at address 00403000, press the 'data hex' button in Wdasm (you will see that it is 'THE MATRIX').
If the VolumeName is 'THE MATRIX' we jump to the next CD check, otherwise we jump to 'Check next drive'.

* Possible StringData Ref from Data Obj ->"A:"

0040107E 6836304000      push 00403036
00401083 68C2314000      push 004031C2        
* Reference To: KERNEL32.lstrcpyA, Ord:02DCh

00401088 E8A1000000      Call 0040112E  ; Copy root path in new string

* Possible StringData Ref from Data Obj ->"Matrix.DVDivX.*"

0040108D 684A304000      push 0040304A
00401092 68C2314000      push 004031C2
* Reference To: KERNEL32.lstrcatA, Ord:02D3h

00401097 E88C000000      Call 00401128 ; Paste 'MatrixDvDivx.*' behind the root path
0040109C 6860304000      push 00403060 ; Poiter to a WIN32_FIND_DATA structure

004010A1 68C2314000      push 004031C2 ; Pointer to the string just created
* Reference To: KERNEL32.FindFirstFileA, Ord:008Ch

004010A6 E865000000      Call 00401110
004010AB 83F8FF          cmp eax, FFFFFFFF ; Is the file there?
004010AE 7505            jne 004010B5      ; then jump to 'Next check'
004010B0 E96FFFFFFF      jmp 00401024      ; else jump to 'Check next drive'
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 004010AE(C)

:004010B5 A3BE314000     mov dword ptr [004031BE], eax ; Store File Handle
:004010BA FF35BE314000 push dword ptr [004031BE]
* Reference To: KERNEL32.FindClose, Ord:0088h

004010C0 E83B000000      Call 00401100 ; Close the searchHandle
004010C5 A180304000      mov eax, dword ptr [00403080] ; Move size member of WIN32_FIND_DAT to eax

004010CA 3B055A304000    cmp eax, dword ptr [0040305A] ; Check size with dword
004010D0 7504            jne 004010D6  ; If not equal, jump to 'Check next Drive'
004010D2 EB07            jmp 004010DB  ; Else, jump to good-guy message
004010D4 EB05            jmp 004010DB
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 004010D0(C)

004010D6 E949FFFFFF      jmp 00401024
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: 004010D2(U), :004010D4(U)

004010DB 6A00            push 00000000 ;Good guy Message
* Possible StringData Ref from Data Obj ->"Great!"

004010DD 6843304000      push 00403043
* Possible StringData Ref from Data Obj ->"CD Found"

004010E2 683A304000      push 0040303A
004010E7 6A00            push 00000000
* Reference To: USER32.MessageBoxA, Ord:01BBh

004010E9 E806000000     Call 004010F4
004010EE 50             push eax
* Reference To: KERNEL32.ExitProcess, Ord:0075h

004010EF E806000000     Call 004010FA ; Exit Program
In the above code, the following happens :

First the root path and a file string get pasted together. (eg. 'F:' + 'Matrix.DVDivX.*' = 'F:Matrix.DVDivX.*') Then the program checks if a file which matches the description is there. If not, we jump back to 'check next drive'. If it is there, the program checks its FileSize with the value in 0040305A. (If you press the dataHex button again, you can see that value : 2B6F2000h (mind the reversed order) = 728.702.976 bytes )
If the FileSize matches this value, the program assumes that the right CD is inserted. If not, we go to 'check next drive' again.



Patching the code


If you read all the above code, patching should be easy.

I assume that your computer has a CD-ROM drive. When the program finds your CD-ROM drive, it will check the VolumeLabel. To prevent from jumping back to 'check next drive' we nop that jump out :

00401056 750A   jne 00401062 -> nop nop


 750A -> 9090

Next, the program searches for the file, let's change the conditional
jump so it always jumps to the next check :
004010AE 7505 jne 004010B5 -> jmp 004010B5


 7505 -> EB05


The last thing we have to patch, is the size-check :

004010D0 7504 jne 004010D6 -> nop nop


 7504 -> 9090

Get all the FileOffsets of the above codelines in Wdasm (displayed at the bottom of the screen in hex), fire up your hexeditor
and change the appropriate bytes.

Run the program, now it thinks the CD is inserted ! Job done :)

The method to check the CD I used in my little matrix-proggy is very simular to how it's done in real programs. Of course, don't expected that the program you want to patch, will only check one file, or will have such clean code as my proggy :))
Good luck!

If you have questions, or remarks abou this tutorial, feel free to ask questions on the forum, or post a comment.

Detten
Detten(at)reversing(dot)be

Greetz to everybody that contributes to this site, and all crackers out there!

4 comments



http://www.reversing.be/article.php?story=20050419192823822