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 ...