Evolution of the Cool Edit Protection Scheme Part ii


Filename:            COOL.EXE
Type:                Segmented executable
Module description:  Cool Edit 1.51
Module name:         COOL151
Filesize:	     413,472 bytes

There is not much difference between this and version [1.50] except
that our METHOD 1 from last time will still work but a silly window
suggesting that you have enetered invalid information pops up.

	les     bx, [Address of ID]
	cmp     byte ptr es:[bx], 00
	jne     A6B7  			// jmp if ID string != NULL
	mov     es, [Address of Data Segment]
	cmp     byte ptr es:[di], 00    // jmp if PASSWORD String == NULL
	je      A6B7push    0006
	....
	....
	push    dx
	push    ax
	push    0010
	call    USER.MESSAGEBOX		// Sends out stupid warning
					// but then continues as normal 

This should demonstrate how STUPID or TRUSTING programmers can be when
attempting to protect their programs.  

METHOD 5 from the last tutorial will not work here however the codes that you 
obtain from this method using [v150] still work for this version.
The actual comparing of the ANSWERME and PASSWORD strings still occurs but 
if you enter it it calls up the same stupid message mentioned before :

	cmp     byte ptr es:[di+01], 4E //'N'
	jne    	Fick off
	cmp     byte ptr es:[di], 41	// 'A'
	jne     Fick off..
	cmp     byte ptr es:[di+03], 57 // 'W'
	jne     Fick off
	cmp     byte ptr es:[di+02], 53 // 'S'
	jne     Fick off
	cmp     byte ptr es:[di+04], 45 // 'E'
	jne     Fick off
	cmp     byte ptr es:[di+05], 52 // 'R'
	jne     Fick off
	mov     al, byte ptr es:[di+06]
	cbw    
	cmp     ax, [bp-04]		// 'M'
	jne     Fick off
	cmp     byte ptr es:[di+07], 45 // 'E'
	jne     Fick off
	....
	....
	push    dx
	push    ax
	push    0010
	call    USER.MESSAGEBOX  // duh

Why the code is still here has probably got something to do with 'greedy'
programmers not bothering to check their code properly and only seeing
little $$$$$$ signs infront of their eyes.

Although this version still registers with the old code it has codes that
are specfic to this version if you use GREP you can see them in their 
orignal version.

d this pre-registered version from a BBS or elsewhere, please contact me.^@PLEAS
E_DON'T_PIRATE_SOFTWARE!!! (at least not Cool Edit, eh?)^@HOJDIVAD^@^@LOVELOVE^@
^@PEACEONE^@^@ARTHLOVE^@^@c:\saveme.txt^@Upgrade Form^@^@%s\compress.xfm^@%s\del

As you can see we now have PEACEONE('Lite' ver) and ARTHLOVE('Full' ver). You
may now register using METHOD 1 from that last lesson with these new codes.

Now for a bit of reverse engineering.
=====================================

Get a pen and piece of paper and fire up Cool edit and the debugger.
Go through the usal motions .. BPX getdlgitemtext [ctrl-D] [F11] ... to get into 
the protection scheme.  Scroll up a little until you get this :

  3.AC50  FF760E                   push    word ptr [bp+0E]
  3.AC53  68F403                   push    03F4
  3.AC56  8D46CC                   lea     ax, [bp-34] *** This is the important 
							bit-> the address where
							you ID string is to be 
							stored ****************	
  3.AC59  16                       push    ss
  3.AC5A  50                       push    ax
  3.AC5B  6A2F                     push    002F
  3.AC5D  9A70AC0000               call    USER.GETDLGITEMTEXT
  3.AC62  FF760E                   push    word ptr [bp+0E]
  3.AC65  68F303                   push    03F3
  3.AC68  8D46F0                   lea     ax, [bp-10] *** PASSWORD STRING ***
  3.AC6B  16                       push    ss
  3.AC6C  50                       push    ax
  3.AC6D  6A0E                     push    000E
  3.AC6F  9A10A30000               call    USER.GETDLGITEMTEXT
  3.AC74  8D46F0                   lea     ax, [bp-10]
  3.AC77  16                       push    ss
  3.AC78  50                       push    ax
  3.AC79  8D46CC                   lea     ax, [bp-34]
  3.AC7C  16                       push    ss
  3.AC7D  50                       push    ax
  3.AC7E  9A10A6FDA2               call    3:A610

Ok note this down on your cracker's note pad and trace through the call at
3.AC7E and you will arrive at the main protection routine. Trace through it 
noting down what the bp-xx are (REMEMBER: bp-xx's are local variables bp+xx's are 
function parameters). Some of them will appear to have no string, these are just 
variables such as int's or long's :

EXAMPLES
========

  3.A62B  8D4686                   lea     ax, [bp-7A]  'Please don't pirate'
  3.A62E  16                       push    ss
  3.A62F  50                       push    ax
  3.A630  1E                       push    ds
  3.A631  687831                   push    3178
  3.A634  9A43A60000               call    KERNEL.LSTRCPY
  3.A639  8D46EE                   lea     ax, [bp-12] 'HOJDIVAD' (1)
  3.A63C  16                       push    ss
  3.A63D  50                       push    ax
  3.A63E  1E                       push    ds
  3.A63F  68B631                   push    31B6
  3.A642  9A51A60000               call    KERNEL.LSTRCPY
  3.A647  8D46E4                   lea     ax, [bp-1C] 'HOJDIVAD' (2)
  3.A64A  16                       push    ss
  3.A64B  50                       push    ax
  3.A64C  1E                       push    ds
  3.A64D  68B631                   push    31B6
  3.A650  9A5FA60000               call    KERNEL.LSTRCPY
  3.A655  8D46DA                   lea     ax, [bp-26]	'LOVELOVE'
  3.A658  16                       push    ss
  3.A659  50                       push    ax
  3.A65A  1E                       push    ds
  3.A65B  68C031                   push    31C0
  3.A65E  9A6DA60000               call    KERNEL.LSTRCPY

-----------------------------------------------------------------

OK, after you get all your address's unassemble Cooledit (using WCB)
and then locate the relevant bit and get rid off all the rest of the
junk and load it into a nice fast text editor (use a DOS one).

Now using the replace function in your editor replace all, for instance,
the bp-12 's with HOJDIVAD_STRING_1  and
    bp-26 's with LOVELOVE_STRING
Your code will not start to make sense. Scan through the code looking out 
for section of code like :

  3.A893  2842E4                   sub     byte ptr [bp+si-1C], al
  3.A896  2842DA                   sub     byte ptr [bp+si-26], al
  3.A899  2842D0                   sub     byte ptr [bp+si-30], al
  3.A89C  2842C6                   sub     byte ptr [bp+si-3A], al
  3.A89F  46                       inc     si
 
and replace them with 

bp+si-1c  =>  HOJDIVAD_STRING_2 + SI ... etc

All done, not quite.

Before you start to convert this code back into 'C' lets look at a few bits 
of code that might through you off if you are new to this.

  3.A828 >8A8346FF                 mov     al, byte ptr [ID UPPERCASE + DI]
  3.A82C  2AE4                     sub     ah, ah
  3.A82E  2BD2                     sub     dx, dx
  3.A830  0146FC                   add     [bp-04], ax
  3.A833  1156FE                   adc     [bp-02], dx
  3.A836  8A42E4                   mov     al, byte ptr [HOJDIVAD_2 + SI]

Moving a letter from the uppercase copy of the Identy string in to 
AL, AH is then subtracted from itself, producing 00, this method of
clearing a register sucks and is quite slow ... No I stand corrected, I have
just check and as it turns out sub's are just as fast as xor 's which was
the method I was going to suggest

		SUB and XOR for register, register

		808x 286 386 486
Clock Ticks
		3    2    2   1

Anyway enough of me trying to be clever (I could of swore  SUB's were slower ...mm)
The point I was going to make had nothing to do with SUB or XOR's but with 
double word arthmetic which what the above is.  To add two words together you are
going to need a 32bit register(long) to store the result, as winodw 3.1 is
only a 16 bit os you have to use shitty slow code instead of using the extended part 
of the registers.

Heres how it works :

First the byte al is extended to a double word DX AX
now we are gonig to add it to the long value [bp-02][bp-04]

	add ax to [bp-04] (if greater than FFFFH the carry flag if set)
	add dx to [bp-02] (if the carry flag is set add 1 to the result)
	ADC = add with carry

another way of doing the same thing is

	mov al,[charactor]
	cbw	// convert byte to word al => ax
	cwd	// convert word to double word ax => dx:ax pair
	add [bp-04],ax
	adc [bp-02],dx

Translated into C this would look like this

	char ch;
	long L;
	char UPPERCASE[]="YOURNAME"
	int offset;

	L = whatever value;
	ch = ID UPPERCASE[offset];
	L += ch;

to see exactly what you complier produces run this program with the 
-3 option(asm output) and have a look for yourself.

This next section of code is worth a mention, for those of you that don't
know much assembly.

  3.A933  8A42C6                   mov     al, byte ptr [ARTHLOVE+ SI]
  3.A936  2AE4                     sub     ah, ah
  3.A938  F6F1                     div     cl
  3.A93A  80C441                   add     ah, 41
  3.A93D  8862C6                   mov     byte ptr [ARTHLOVE+ SI], ah

here we are dividing a byte into a word

	before division AX = dividend, CL = divisor
	after division  AL = Quotient, AH = remainder

Thus, as we only keep the remainder this would look like this in C

	int A;
	char ch = (whatevers is in cl);

	A = (unsigned char)ARTHLOVE[offset];
	A %= ch;
	A += 0x41;
	ARTHLOVE[offset] = (unsigned char)A;

Sorry to go on about assembly but if you are only starting out little things 
like this can have you baffled (as most of us can remember, no matter how
long ago it was)

So if you go through all that you should end up with

#include 
#include 

void main(void)
{
	unsigned char A, B, A1;
	unsigned long C=0x0c;
	register int counter1, counter2=0;
	int temp;
	char HOJDIVAD[] =   "HOJDIVAD";
	char HOJDIVAD2[]=   "HOJDIVAD";
	char LOVELOVE[] =   "LOVELOVE";
	char PEACEONE[] =   "PEACEONE";
	char ARTHLOVE[] =   "ARTHLOVE";
	char ID[20];
	char UPPERID[20];
	int  Length1, Length2;

	printf("\n Enter Your Name Please => ");
	gets(ID);
	Length1 = strlen(ID);
	for(counter1=0;counter17)counter2=0;
	}


	for(counter1=0;counter17)counter2=0;
	}

	temp = counter2;

	for(counter1=0;counter17)counter2=0;
	}

	C = 0x0c;
	counter2 = 0;
	for(counter1=0;counter1<(Length2);counter1++)
	{
		C+= UPPERID[counter1];
		A = HOJDIVAD2[counter2];
		B = A;
		A += A;
		A += B;
		A <<= 2;
		A += B;
		A += 0x11;
		HOJDIVAD2[counter2] = A;

		A = LOVELOVE[counter2];
		A *= 0x11;
		A += 0x17;
		LOVELOVE[counter2] = A;
		
		A = ARTHLOVE[counter2];
		A *= 0x25;
		A += 0x11;
		ARTHLOVE[counter2] = A;

		A = PEACEONE[counter2];
		A *= 0x1F;
		A += 0x1D;
		PEACEONE[counter2] = A;
		counter2++;
		if(counter2 > 7) counter2 = 0;
	}

	for(counter1=0;counter1<(Length2);counter1++)
	{
		A = (char)C;
		HOJDIVAD2[counter2] -= A;
		LOVELOVE[counter2]	-= A;
		ARTHLOVE[counter2]	-= A;
		PEACEONE[counter2]	-= A;
		counter2++;
		if(counter2>7) counter2 = 0;
	}


	for(counter1=0;counter1<(Length2);counter1++)
	{
		A = UPPERID[counter1];
		HOJDIVAD2[counter2] ^= A;
		LOVELOVE[counter2]  ^= A;
		ARTHLOVE[counter2]  ^= A;
		PEACEONE[counter2]  ^= A;
		counter2++;
		if(counter2>7)counter2 = 0;
	}

	for(counter1=0;counter1<8;counter1++)
	{
		A = HOJDIVAD[counter1];
		B = 0x1A;
		A1 = (A%B);
		A1 += 0x41;
		HOJDIVAD[counter1] = A1;

		A = LOVELOVE[counter1];
		A1 = (A%B);
		A1 += 0x41;
		LOVELOVE[counter1] = A1;

		A = HOJDIVAD2[counter1];
		A1 = (A%B);
		A1 += 0x41;
		HOJDIVAD2[counter1] = A1;

		A = ARTHLOVE[counter1];
		A1 = (A%B);
		A1 += 0x41;
		ARTHLOVE[counter1] = A1;

		A = PEACEONE[counter1];
		A1 = (A%B);
		A1 += 0x41;
		PEACEONE[counter1] = A1;

	}
	printf(	"\n    Code for 'New' version  [1.50]=> %s"
			"\n             'Full' version [1.50]=> %s"
			"\n             'Lite' version [1.50]=> %s"
			"\n             'Full' version [1.51]=> %s"
			"\n             'Lite' version [1.51]=> %s"
			"\n Typing in 'ANSWERME' in the Password box is now disabled :<"
			,HOJDIVAD, HOJDIVAD2, LOVELOVE, ARTHLOVE, PEACEONE);

}

Ok so the basic Algorithm for the encryption is

	Subtract 0x41 from each Compare string.
	Using ID string modify HOJDIVAD and Add each value of ID to our
		aforementioned  long value
	Subtract the long value from each element of HOJDIVAD
	XOR each element of HOJDIVAD with each element od ID
	MULTIPLY Each element of each string by a constant
	Add a constant value to each element of each string
	Our long value has UPPERID added to it
	Subtract the least significant byte of our long value fron each element 
		of each string
	XOR each element of the compare strings with each element of UPPERID
	Take the remainder of each element of the compare strings div by 
		a constant and add another constant
	And there you have it 

Thats it for this lesson make sure you Mail me if you spot any mistakes or need some help, have comments ...