"CD-Cops v1.76"

Published by Tsehp
Program Url: http://www.linkdata.com
Program Type: CD Protection


It is another ready made CD protection. Actually I spent a lot of time on this protection. Unpacking it, was very hard for me.It has a lot of debugger checks exceptions etc. However, 2 weeks ago I have started to think with fresh mind and here it is complete code for unpacking.


There is a superb essay about CD-Cops by mcLallo it helped me so much to understand the basics of this protection. I suggest you to read it first before looking this tutorial. Most of the info he said is still true at least in v1.75. We will try to unpack Vitamin Lise which is protected by CD-Cops v1.76.In its Kur/Dao folder it has three important files which are CD-Cops file.

il.HEL renamed 16 bit NE file
il.W_X renamed 32bit PE file 
il.QZ_ renamed 32bit PE file

They execute il.HEL file with CreateProcess, then il.HEL file do main CD checks.il.HEL do all protection when everything is OK i.e. serial, CD layout, no debugger, .. it creates a registry entry according to result of timeGetTime and it runs .il.QZ_ with command parameter which is based on result of GetTickCount. For detailed information about this please look at mcLallo's essay. il.QZ_ file then push il.W_X and use CreateProcess api.After it comes below nice routine.

loc_40EEEB:				; CODE XREF: sub_40EEBC+53j
					; sub_40EEBC+57j ...
		push	0FFFFFFFFh	; dwMilliseconds
		lea	eax, [esp+64h+dwProcessId]
		push	eax		; lpDebugEvent
		call	WaitForDebugEvent
		neg	eax
		sbb	eax, eax
		neg	eax
		cmp	ds:byte_411774,	0
		jz	short loc_40EF11
		test	al, al
		jnz	short loc_40EF11
		call	sub_40ED4C
		jmp	short loc_40EEEB
; 컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴

loc_40EF11:				; CODE XREF: sub_40EEBC+48j
					; sub_40EEBC+4Cj
		test	al, al
		jz	short loc_40EEEB
		mov	eax, esp  ;
		call	sub_40EDA0 ;decryption proc
		cmp	[ESP+60h+dwProcessId], 5
		jnz	short loc_40EF3D
		dec	ds:dword_4105C4
		cmp	ds:dword_4105C4, 0
		jz	short loc_40EF4F
		mov	edx, [ESP+4]
		cmp	edx, ds:dword_41177C
		jz	short loc_40EF4F

loc_40EF3D:				; CODE XREF: sub_40EEBC+64j
		push	eax		; dwContinueStatus
		mov	eax, [ESP+0Ch]
		push	eax		; dwThreadId
		mov	eax, [ESP+0Ch]
		push	eax		; dwProcessId
		call	ContinueDebugEvent ;When this function runs, program runs
		jmp	short loc_40EEEB

In order to dump this, you can bpx on WaitFordebugEvent and you can dump it before calling ContinueDebugEvent api.You will have virgin executable. I thought that maybe api hooking can be done but as I said earlier il.HEL is 16 bit NE file and all api hook stuff I know works on PE files.I then thought that maybe I can write some program which do same job like il.HEL and run il.QZ_ but unfortunately commandline parameter is based on some constant which is different for every CD-Cops protected exe and it is crypted in exe. Only solution I can think of is to put Global Hook which cant be unloaded until you restart like SafeHard example in EliCZ's ApiHook library.You can hook ContinueDebugEvent api.You will have processid,threadid.You can suspendthread and readprocess memory to some allocated memory and write it back to disk.

So, I started to trace what sub_40EDA0 function do. I have traced it, and it always crashed. I am not familiar with antidebugging and exception stuff so I don't know what was happened. So how this program decrypt .W_X file It must use some file apis or some process apis like ReadProcessMemory, WriteProcessMemory. I have but bpx on ReadProcessMemory.It stopped when I look to the buffer I saw that it reads all sections and check their names.Then it read first section which was .text to memory and compared is name to .text and CODE. After this comparison.It read all .text section to virtually allocated memory.

I started to trace very closely because we are very close to unpacking rutine.After a while I saw this

017F:0040EC1F  B810064100          MOV       EAX,00410610 
017F:0040EC24  8B0D0C064100        MOV       ECX,[0041060C]                     
017F:0040EC2A  8A18                MOV       BL,[EAX]                           
017F:0040EC2C  80F35A              XOR       BL,5A                              
017F:0040EC2F  885C11FF            MOV       [EDX+ECX-01],BL                    
017F:0040EC33  42                  INC       EDX                                
017F:0040EC34  40                  INC       EAX                                
017F:0040EC35  83FA4A              CMP       EDX,4A                             
017F:0040EC38  75EA                JNZ       0040EC24                           
017F:0040EC3A  53                  PUSH      EBX                                
017F:0040EC3B  56                  PUSH      ESI                                
017F:0040EC3C  57                  PUSH      EDI                                
017F:0040EC3D  8B75FC              MOV       ESI,[EBP-04]                       
017F:0040EC40  0335E0184100        ADD       ESI,[004118E0] ;allocated mem  
017F:0040EC46  8B15D8054100        MOV       EDX,[004105D8] ;0 ?                    
017F:0040EC4C  8B3D081A4100        MOV       EDI,[00411A08] ;imgbase                    
017F:0040EC52  033DF4174100        ADD       EDI,[004117F4] ;virt offset of code       
017F:0040EC58  033DE0184100        ADD       EDI,[004118E0] ;0 ?                    
017F:0040EC5E  8B0DE4184100        MOV       ECX,[004118E4] ;code size                    
017F:0040EC64  E32F                JECXZ     0040EC95                           
017F:0040EC66  8B05E8194100        MOV       EAX,[004119E8] ;some constant
017F:0040EC6C  83050C06410002      ADD       DWORD PTR [0041060C],02            
017F:0040EC73  55                  PUSH      EBP                          
017F:0040EC74  BD04000000          MOV       EBP,00000004                 
017F:0040EC79  FF150C064100        CALL      [0041060C]    ;Here is code unpacking rutine
..When you trace in to this you will see below 

  OR        EDX,EDX
  JZ        LOC_00B78AA6
  CMP       EDI,[EDX]
  JBE       LOC_00B78AA4
  ADD       EDX,04
  JMP       LOC_00B78A9B
  MOV       EBX,[EDX]
  CMP       EDI,EBX
  JZ        LOC_00B78ABC
  ADD       [ESI],AL
  ROR       BYTE PTR [ESI],1
  XOR       EAX,EDI
  ROL       EAX,1
  JB        LOC_00B78AB6
  SUB       AH,AL
  INC       ESI
  INC       EDI
  LOOP      LOC_00B78AA6
  JMP       LOC_00B78AD8
  ROR       AL,1
  JAE       LOC_00B78AC6
  SUB       AL,98h
  XOR       AL,01Ch
  JMP       LOC_00B78ACC
  XOR       AH,0C3h
  SUB       AH,0AFh
  ADD       ESI,EBP
  ADD       EDI,EBP
  ADD       EDX,EBP
  MOV       EBX,[EDX]
  SUB       ECX,EBP

Everything is clear but a constant at 4119E8. I have run target again and put bpm on this memory location.At first break I saw this

		mov	ds:dword_4119E8, eax
		mov	eax, ds:dword_4119E8 ;MZ header of QZ_ file
		add	eax, 80h ;+80h
		mov	ds:dword_411A58, eax
		mov	edx, ds:dword_411A58 ;This are points to famous product string SEBIT_XXXX
		mov	eax, [edx]			;take word
		xor	eax, 72A96FB3h     ;
		ror	eax, 7
		add	eax, [edx+4]
		neg	eax
		sub	eax, [edx+2]
		mov	ds:dword_4119EC, eax

I thought where this constant comes from but it was false alarm.I think it is some kind of checksum.After that point it reads .CRC key from registry and I guess it compares this two hash. At second brake I saw this

       mov     ds:dword_4119E8, eax

eax was the string which was put in the registry by il.HEL At third break I saw this

		call    sub_40D984
		and	eax, 0FFFF00FFh 
		mov	ds:dword_411A64, eax ;another mystery
		mov	eax, ds:dword_411A60 ;this is the result of WinMM.timeGetTime
		movzx	edx, word ptr ds:dword_4119E8 ;string at .CRCreg key
		sub	eax, edx
		shr	eax, 10h
		movzx	edx, word ptr ds:dword_4119E8+2
		xor	eax, edx
		movzx	eax, ax  ; this was always same for me.
		xor	eax, ds:dword_411A64
		mov	ds:dword_4119E8, eax 

If you read mcLallo's essay, created registry string is based on timegettime function and product checksum which is the addition of all chars.So actually a number at dword_4119E8 is equal to dword_411A64 xor product checksum.We know how to calculate checksum let's find where dword_411A64 comes from. If you trace into sub_40D984 function you will see this

		push	eax		; lpFindFileData
		mov	eax, edi
		call	sub_403974
		push	eax		; lpFileName points to il.W_X file
		call	FindFirstFileA
		mov	esi, eax
		mov	[ebx+14h], esi
		cmp	esi, 0FFFFFFFFh
		jz	short loc_406BC4 ;file not exist
		mov	eax, ebx
		call    sub_406B20 ;trace into this function

loc_406B40:				; CODE XREF: sub_406B20+6j
					; sub_406B20+17j
		mov	eax, [ebx+18h]
		and	eax, [ebx+10h]
		jnz	short loc_406B28
		push	ESP		; lpLocalFileTime
		lea	eax, [ebx+2Ch]
		push	eax		; lpFileTime
		call	FileTimeToLocalFileTime
		push	ebx		; lpFatTime
		lea	eax, [ebx+2]
		push	eax		; lpFatDate
		lea	eax, [ESP+10h+FileTime]
		push	eax		; lpFileTime
		call	FileTimeToDosDateTime 

So it searchs w_x file and use date of files as encryption keys.At this point we know how to unpack code section let's go to .data section

Again but bpx on ReadProcessMemory and stop when Data section is read to virtually allocated memory.After very short period of time you will see this.

017F:0040EACC  8B55FC              MOV       EDX,[EBP-04] ;data offset
017F:0040EACF  8B4DF8              MOV       ECX,[EBP-08] ;data size
017F:0040EAD2  D1E9                SHR       ECX,1
017F:0040EAD4  E320                JECXZ     0040EAF6
017F:0040EAD6  0FB705461A4100      MOVZX     EAX,WORD PTR [00411A46] ;data key
017F:0040EADD  2802                SUB       [EDX],AL
017F:0040EADF  C0C404              ROL       AH,04
017F:0040EAE2  006201              ADD       [EDX+01],AH
017F:0040EAE5  C0C404              ROL       AH,04
017F:0040EAE8  663102              XOR       [EDX],AX
017F:0040EAEB  66D1C0              ROL       AX,1
017F:0040EAEE  7302                JAE       0040EAF2
017F:0040EAF0  00C4                ADD       AH,AL
017F:0040EAF2  42                  INC       EDX 
017F:0040EAF3  42                  INC       EDX 
017F:0040EAF4  E2E7                LOOP      0040EADD

Where this data key comes ? This time it comes from the exe it self.It is the NumberOfLinenumbers of data section.

OK we know how to decrypt code and data section what come next ? yes finding OEP.Fortunately IAT is not crypted.We again put bpx on ReadProcessMemory and trace it. We are looking something which changes our OEP when OEP is read to memory put bpm on this memory location.You will see something like below

		mov	edx, ds:dword_4117A0 ;memlocation MZ+20h
		not	edx ;not
		mov	eax, ds:dword_41179C
		cmp	edx, eax
		jz	short loc_40E872
		mov	eax, ds:dword_411A4C
		mov	ds:dword_411A50, eax
		jmp	short loc_40E882
; 컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴

loc_40E872:				; CODE XREF: CODE:0040E864j
		xor	eax, 5A5A5A5Ah ;xor it we have OEP now!
		add	eax, ds:dword_411778 ;add to imagebase.


Ok at this point we know all information to write our unpacker.I have written one and you can download from here. It has source in it. Beware that my coding style is very bad.If you find any error or bugs please let me know.Also if you find other versions unpack and OEP rutines please notify me. Thanks

Greetings:First of all, renaTgaD,MbR, and my group mates,CYDONIA,Iczelion, +Tsehp, +Splaj, Kayaker, and all great guys on RCE board, my old friends at PNC and PGC, Daemon,The Unknown One and all unpacker authors who are kind to release sources.