Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Cracking Oxford Advanced Learner's Dictionary (CD-COPS 1.8)

Download as pdf or txt
Download as pdf or txt
You are on page 1of 10

Cracking Oxford Advanced Learners

Dictionary
(CD-COPS 1.8)
by

macilaci

Introduction
Theres always some inroduction. This time it is about a CD protection. In general
I dont like them. Most of these copy schemes are defeated in times when the burners are
burning RAW mode. One may think, that theres no effective way to protect their products.
Tools used
CDCops when not using Icedump disables softices keyboard and inside code is a link
of checksums, so using breakpoint on execution makes things difficult. Advices: bpm, bpr,
bpx on API+some bytes.
Essays: mclallos and Laptonics both concerning CDCops protection in earlier
vesions
Disassemblers: IDA, WDasm
Debuggers: WinICE, Icedump
Other tools: Wdump 95
Assembler: Masm
These and many other tools can be found on various sites. You can use search engine to find
them (www.google.com, www.altavista.com ).
The essay
I doubt You will find this CD on some warez sites, since it is a educational CD. I just
wanted to make a copy of this disc and I realised that copy protection is not working with my
copy. I begun searching the web: found some silly information, that this protection measures
the angle between first and last sector on the CD, does the encryption test and refuses or
accepts the CD. Some people claim that these CDs are copyable by certain CD-R brands like
Kodak Gold. I havent tried this possibility. So my goal was to make this application working.
The fig.1 shows the rejected copy. We will talk abou the machine code later.

Fig.1

Concerning the above mentioned essays I looked at the executables and my surprise
was that the qz_ executable on the CD was an visual basic application. This could mean, that
the program each time it runs, it decrypts itself with the given key. This key has nothing to do
with the entered product number. The CD product number or whatever call they it is just
a number for a given CD to pass the encryption test.
Eleven encrypted sections, who wants more?
Looking at the main loader executable within Wdasm I wondered how are times
changing. At the version 1.8 there are eleven references to CD-Cops Ord3 call. Each call has
behind himself one encrypted section of data.
With a bunch of bprs (see softices manual) I started looking at these bytes. This data
section is decrypted by the dll with rotating key, which is not depending on the data itself.
Each section has two checksums, first is the checksum of the data itself, second is the
checksum of the checksum. The checksums are coputed many times within the dll and the
main executable, so patching the executable will result in an ugly crash within INT31. The
execution simply jumps through some calls and operations to be made. Using the DPMI
services also makes life difficult on the emulator like VMWare or VirtualPC.
So patching the executable isnt good way, even when all to do is to patch few bytes
inside the main file as you will see below.
The Registry Story
As I tried to figure out more on this program I was experimenting with the program.
Just doing simple CTRL+D within the CD-ROM measuring resulted into a running program.
I wondered how could be this possible. Of course youll have to enter a key that starts the
encryption test. Simple delay between reading from CD-ROM and the main program tricked
to run the program. After that the application stores its information within registry in these
keys (in the HKLM too):
HKEY_CLASSES_ROOT\OXFORD___ALD002OU_2241000
@="AABBCCDD/AABBCCDD"
HKEY_CLASSES_ROOT\OXFORD___ALD002OU_2241000.CRC
@="06B8BE09

As it turns out, the crc key is not life important, so the application runs without it. The HKLM
keys are just in case of data lost or something like that. The most important is the first
mentioned key. I tried it to copy to another machine, but that simply refused the key as in
fig.1 the CD passed not the encryption test. So this key must be machine dependent.
Dumping and code understanding
First we have to look at the executable it is a 16-bit application. There arent many
sixteen bit dumpers for Windows 9x, so I personally tried the Wdump v2.10 (fig.2). Use of
this dumper is quite easy. It allocates memory space and associates this memory space to file.
Using Softices move (m) command it is possible to move code pieces to this memory space
and then save it to file. Beware: when starting your debuggnig session, always start Icedump
because of anti-Softice code disabling keyboard.

Fig.2

The first reference to CDCops is at 0001:0fb7:


cseg01:0FB7
cseg01:0FBC
cseg01:0FBD
cseg01:0FBE

call
CDCOPS_3
db 6Dh
db 24h
db
9 ;

So lets do a break on the 0001:0fb7. First do a break on the windows procedure at 0001:0e30.
Tip: Use winices log capabilities to locate the segment 01 of the executable within memory
space.
Example (from winice history log):
WINICE: Load16 Sel=555F Seg=0001 Mod=WINASM
- here we go
WINICE: Load16 Sel=53E7 Seg=0002 Mod=WINASM
WINICE: StartDLL CSIP=4DEF:0B6F Mod=CDCOPS
Break due to BPMB #555F:00000FDC X DR3
- Ive already set this breakpoint
MSR LastBranchFromIp=00000A62
MSR LastBranchToIp=00000A6A
:?1192-fb7
-how long is our section?
'% 
:? 13c8-fb7
00000411 0000001041 ""
-better get more
:m 555f:0fb7 l 411 030:82f0e000
-move it to Wdumps memory
space
:d 0030:82f0e000
-let me see if it is there

Bold marked string is what I wrote in winice. So we have a new file with 411 bytes inside.
Start up hexeditor and paste that code into appropriate space within the executable. When
done, start up IDA and look around.

So this way Ive got four new sections inside my executable. Next we will explore the registry
values. Doing a bpx on shell!regqueryvalue function will show us how many times it reads
registry. I explored the parameters and values and searched for our favourite registry key. At
the second time when the breakpoint occurs, you will get to this routine:
cseg01:131E
cseg01:1321
cseg01:1322
cseg01:1325
cseg01:1326
cseg01:1329
cseg01:132D
cseg01:132F
cseg01:1333
cseg01:1335
cseg01:1338
cseg01:133A
cseg01:133C
cseg01:133F
cseg01:1341
cseg01:1342
cseg01:1343
cseg01:1345
cseg01:1347
cseg01:1348
cseg01:134A
cseg01:134C
cseg01:134E
cseg01:1350
cseg01:1353
cseg01:1356
cseg01:1357
cseg01:1359
.
.
.
cseg01:136D
cseg01:1371
cseg01:1373
cseg01:1375
cseg01:1376
cseg01:1378
cseg01:137A
cseg01:137D
cseg01:137F
cseg01:1381
cseg01:1384
cseg01:1386
cseg01:1389
cseg01:138B
cseg01:138D
cseg01:1391
cseg01:1395
cseg01:1399
cseg01:139D
cseg01:139F
cseg01:13A1
cseg01:13A7
machine code low byte

push
push
push
push
push
call
or
jnz
mov
mov
mov
mov
mov
inc
cld
lodsb
cmp
jz
stosb
or
jnz
or
jz
mov
mov
lodsb
cmp
jb

shl
or
loop
lodsb
or
jnz
mov
xor
xor
shr
rcr
shr
rcl
loop
sub
xor
add
xor
cmp
jnz
mov
mov

26Dh
ds
3E5Eh
ds
2B8h
dword ptr ds:0EA6h ;shell!regqueryvalue
ax, dx
loc_0_13C8
ah, 0FFh
si, 3E5Eh
di, ds
es, di
di, 3E4Ah
ah
;load the string
al, 2Fh ; /
;look for slash
loc_0_1338
;store it to new location
al, al
loc_0_1342
ah, ah
loc_0_13C0
si, 3E4Ah
cx, 8
al, 61h ; a
loc_0_135D

edx, 4
;get the hex string to edx
dl, al
loc_0_1356
;got it all?
al, al
loc_0_13C8
cx, 10h
;we will do it ten times
ax, ax
bx, bx
;zero ax and bx
edx, 1
;shift right edx
bx, 1
;rotate through carry flag
edx, 1
;shift right edx
ax, 1
;rotate through carry flag
loc_0_1381 ;next man
ax, ds:3DFFh ;sutract with key1
ax, ds:2B6h
;xor with key2
bx, ds:3DFFh
bx, ds:2B6h
ax, bx
; is that code valid?
loc_0_13C8
;if no then jump
word ptr ds:3E13h, 3 ; yes, it is...
ds:187Fh, al ;store the computed

cseg01:13AA
machine code high byte
cseg01:13AE
cseg01:13B0

mov

ds:1881h, ah ;store the computed

xor
mov

al, al
;keep execution
ds:1883h, al

This subroutine computes the machinecode key (my was 4CCC) from the registry value. The
compare key is necessary to be sure that the key was computed using a given algorithm
(rotate through carry). Computed machine code is then stored for latter use by comparation
routine. With bpm on the above addresses we will get to the second jump where the machine
code is compared to the real machine code:
4D07:2550
4D07:2553
4D07:2557
4D07:255B
4D07:255F
4D07:2561

A07F18
8A268118
3B062A3E
0F849400
B104
E9D700

MOV
MOV
CMP
JZ
MOV
JMP

AL,[187F]
AH,[1881]
AX,[3E2A]
25F3
CL,04
263B

;get low byte


;get high byte
;compare to real code
;if good then jump
;bad guy

In the program are now two jumps deciding whether the program is running on the good
machine or not. When the first is not set and the second is set, the program continues running
no matter what registry values are inside windows. I was trying to modify the jumps doing
a bunch of bprs over the encrypted code, but a lot of checksums and security code gave me
a better idea of defeating this protection scheme. Looking after the 3e2a memory area lead
me to this routine:
cseg01:355C
cseg01:355D
cseg01:355E
cseg01:3561
cseg01:3564
cseg01:3564
cseg01:3564
cseg01:3564

push
cld
mov
mov
int

cseg01:3566
cseg01:356A
cseg01:356C
cseg01:356F
cseg01:3572
cseg01:3575
cseg01:3578
cseg01:3579
cseg01:357A
cseg01:357C
cseg01:357E
cseg01:3580
cseg01:3582
cseg01:3586
cseg01:358A
cseg01:358B

jb
mov
mov
mov
mov
mov
inc
inc
xor
shr
add
loop
and
mov
pop
retn

es
ax, 2
bx, 0FFFFh
31h ; DPMI Services
ax=func xxxxh
; SEGMENT TO DESCRIPTOR
; BX = real mode segment
; Return: CF set on error
; CF clear if successful, AX = selector
corresponding to real mode segment (64K
limit)
loc_0_4240
es, ax
; read bios date
cx, 5
si, 5
dx, 5873h
;set some initial value
ax, es:[si]
;get the date string
si
si
dx, ax
dx, 1
dx, ax
loc_0_3575
;compute machinecode
dx, 0FEFEh
word_429_3E2A, dx ;store at 3e2a
es

Taking look at 356a on the es:[si] address told me that the machine code was computed from
bios date. So modifying the bios date would solve the problem...

BIOS Flash or what?


So the above things gave me the idea to compute the machine code and store it in the
registry. Below I will provide the source code for this utility (Compiled with masm32 as
a win32 command line utility):
; #########################################################################
.386
.model flat, stdcall
option casemap :none

; case sensitive

; #########################################################################
include \masm32\include\windows.inc
include
include
include
include

\masm32\include\user32.inc
\masm32\include\kernel32.inc
\masm32\include\masm32.inc
\masm32\include\advapi32.inc

includelib
includelib
includelib
includelib

\masm32\lib\advapi32.lib
\masm32\lib\user32.lib
\masm32\lib\kernel32.lib
\masm32\lib\masm32.lib

; -----------; Local macros used from masm32 examples


; -----------print MACRO Quoted_Text:VARARG
LOCAL Txt
.data
Txt db Quoted_Text,0
.code
invoke StdOut,ADDR Txt
ENDM
input MACRO Quoted_Prompt_Text:VARARG
LOCAL Txt
LOCAL Buffer
.data
Txt db Quoted_Prompt_Text,0
Buffer db 128 dup(?)
.code
invoke StdOut,ADDR Txt
invoke StdIn,ADDR Buffer,LENGTHOF Buffer
mov eax, offset Buffer
ENDM
cls MACRO
invoke ClearScreen
ENDM
Main

PROTO

; #########################################################################
.data
key1
dw 0BBB5h
key2
dw 0C7DAh
Buffer2 db 128 dup(0)
Regkey
db "OXFORD___ALD002OU_2241000\",0
Fixed
db " Your computer is now ok ",0
; #########################################################################
.code
start:
invoke Main
invoke ExitProcess,0

; #########################################################################
sub_0_1412 proc near
;part of converting routine from edx to ascII string
and
al, 0Fh
add
al, 90h
daa
adc
al, 40h
daa
stosb
retn
sub_0_1412 endp
sub_0_140A proc near
push
shr
call
pop
and
add
daa
adc
daa
stosb
retn
sub_0_140A endp
sub_0_1403 proc near
xchg
call
xchg
push
shr
call
pop
and
add
daa
adc
daa
stosb
retn
sub_0_1403 endp

;part of converting routine from edx to ascII string


ax
al, 4
sub_0_1412
ax
al, 0Fh
al, 90h
al, 40h

;part of converting routine from edx to ascII string


al, ah
sub_0_140A
al, ah
ax
al, 4
sub_0_1412
ax
al, 0Fh
al, 90h
al, 40h

SetRegString proc HKEY: dword, lpszKeyName: dword, lpszValueName: dword, lpszString: dword
;set the registry
local Disp: dword
local pKey: dword
local dwSize: dword
invoke RegCreateKeyEx, 80000000h,
lpszKeyName, NULL, NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, NULL,
addr pKey, addr Disp
.if eax == ERROR_SUCCESS
invoke lstrlen, lpszString
mov dwSize, eax
invoke RegSetValueEx, pKey, lpszValueName,
NULL, REG_SZ,
lpszString, dwSize
push eax
invoke RegCloseKey, pKey
pop eax
.endif
ret
SetRegString endp
Main proc
LOCAL InputBuffer[128]:BYTE
; -----------cls
print "CDCops 1.8 Oxford Advanced Learners Dictionary",13,10,13,10
input "Enter Machine Code > "
push edi
push esi

push edx
push eax
push ebx
mov esi, eax
mov edi, offset Buffer2
mov ecx, 4
loc_0_1356:
lodsb
cmp al, 61h ; a
jb loc_0_135D
sub al, 20h ;
loc_0_135D:
sub al, 30h ; 0
jb loc_0_13C8
cmp al, 9
jbe loc_0_136D
sub al, 7
jb loc_0_13C8
cmp al, 0Fh
ja loc_0_13C8
loc_0_136D:
shl edx, 4
or dl, al
loop loc_0_1356
lodsb

;covert machine code to hex number=edx

;up to this point

;key computing:
mov ecx, 10h
;10 times loop
xor eax, eax
xor ebx, ebx
loc_0_1381:
mov ax, dx
xor ax, key1
; 2b6= B5 BB
add ax, key2
; 3dff= DA C7
mov bx, dx
xor bx, key1
sub bx, key2
xor edx, edx
mov cx, 10h
loc_0_13E8:
shr ax, 1
rcl edx, 1
;rotate through carry flag
shl bx, 1
rcl edx, 1
;rotate through carry flag
loop loc_0_13E8
;the next man please
cld
;the result is now in edx
mov al, 2Fh ; /
mov edi, offset Buffer2 ;print it out to the buffer with the slash above
stosb
mov ax, dx
shr edx, 10h
xchg ax, dx
call sub_0_1403
xchg ax, dx
xchg al, ah
call sub_0_140A
xchg al, ah
push ax
shr al, 4
call sub_0_1412
pop ax
and al, 0Fh
add al, 90h ; ''
daa
adc al, 40h ; '@'
daa
stosb
pop ebx
pop eax
pop edx
pop esi
pop edi
mov eax, offset Buffer2
invoke SetRegString, 80000000h,offset Regkey,NULL,eax ;also fix the registry
automatically
mov eax, offset Buffer2

invoke StdOut, eax


invoke StdOut, offset Fixed
; ---------------; using procedures
; ----------------

; return address in eax

loc_0_13C8:
invoke StdIn,ADDR InputBuffer,LENGTHOF InputBuffer
ret
Main endp
; #########################################################################
end start

The main program code is not big is quite simple was cut from the executable at
location 13CA. Program generates this code each time it runs and stores it in the registry.
How to use the console program gives itself. Just look at the source.
When you first time manage to run the original program with manipulating the above
mentioned jumps, the program sets the registry key and on the current machine will live
forever... Ive tried to fix the jumps, but almost got crazy when the dll was checking the
checksums of checksums and so on.
Conclusion
No one is perfect, neither the assembly protection used by linkdata security company.
Simple use of gettickcount within the CD-ROM encryption test shows the vulnerability of this
protection scheme. The BIOS date reading routine shows the need for 16-bit code within 32bit environment.
Exercise: Write an utility that computes the registry key from the bios date (eg. 07/11/01) and
stores it. When you manage to make it 16-bit code you can simply access the BIOS date by
the above code on page 6.

10

You might also like