Submitted by emoticons in Piracy

What's needed to know

For this guide u gonna need 2 know a lil C or c++ and assembly. In the warming-up, there're gonna be a bit of the asm u'll need for this guide. Rn, u ain't gonna need 2 know how 2 work a debugger, but 4 those of u that do, it's p helpful. I'll make the asm into blocks and speak on some blocks. & some blocks, I'll give some Code that kinda's does the same

Why make a keygen instead of patch?

When u make keygen, u have 2 comprehend more the software or the parts where it Valid8s ur key. it's a neat way 2 get more comfort in reverse engineer or 2 learn asm. also It's def feels awesome when u get it

warm-up

Here're some warming-up. but feelin' ready to get in2 it? u can skip this.

what this code do?

mov ecx,dword ptr ss:[ebp-4]
mov esi,dword ptr ss:[ebp+10]
shl ecx,2
mov edi,edx
mov edx,ecx
shr ecx,2
xor eax,eax
rep stosd
mov ecx,edx
and ecx,3
rep stosb

it copies bytes in groups of 4, then it copies the last few -- first gets ecx, esi and edi. then edx ⬅️ ecx << 2, this's the total bytes to copy. floor divides ecx by 4. this's for rep stosd which's repeat the copying 4 bytes of esi in2 edi. then for rep stosb, all the other bytes which didn't yet get copy

what does this?

imul eax,eax,11E9
cdq
mov ecx,3E8
idiv ecx
push 8
pop ecx
cdq
idiv ecx
mov edi,eax
xor eax,eax
test edx,edx
setne al
add eax,edi

eax ⬅️ eax*11E9, and sign extend to edx:eax. divide by 3E8, eax ⬅️ int part, edx ⬅️ signed modulo. sign extend and divide by 8, edi ⬅️ int part, eax ⬅️ 0. but eax ⬅️ 1 when division wasn't an int. then add int part to eax

the disassembler

I'll be showing the blocks w comments after semi-colon and i gonna leave the part 2 finding the key sub-routine for another place. the key sub-routine of this soft ware's has two string argument, for the key and a name

ΛuΛ(key,name)

mov edx,dword ptr ss:[esp+4]
sub esp,20C
or ecx,FFFFFFFF
xor eax,eax
push ebx
push ebp
push esi
push edi
mov edi,edx
repne scasb
not ecx
dec ecx
cmp ecx,A
jae 42D62E; on lengthAs(key)>=A
pop edi
pop esi
pop ebp
mov ax,A
pop ebx
add esp,20C
ret

First, ecx ⬅️ -1, eax ⬅️ 0, edx ⬅️ key, and then edi ⬅️ edx. hmm. I wanna look in2 the jump, it's when ecx is above or equal. There's a repne scasb. What this'll do's try 2 get [edi]==eax. it'll keep subtract 1 from ecx and add 1 to edi. when [edi]==eax, it'll do that again but then stop. & When [edi]'s a string, ecx'll have subtracted by how many chars wasn't equal to eax. Bc eax ⬅️ 0, it'll subtract the string's length + 1 and bc the string's the key, ecx's gonna be the -2 - key's length. then it'll bitwise negation on ecx. that'll be 1 + key's length, and then dec it, which'll make ecx the key's length. When the key's length's 2 little, this's return 10. what i didn't yet say was that it must return 0

42D62E

mov ebp,dword ptr ss:[esp+224]
cmp byte ptr ss:[ebp],0
je 42D655
cmp byte ptr ds:[edx],0
je 42D655
push ebp
push edx
call 42D8A0; ➡️ ax
add esp,8
pop edi
pop esi
pop ebp
pop ebx
add esp,20C
ret

⬆️ Here's the place that'll jump when lengthAs(key) >= 10. it'll see that the name and key aren't empty, then it'll call 42D8A0 and return the ax from that call.

42D655; name or key was empty

test cl,1
je 42D669
pop edi
pop esi
pop ebp
mov ax,B
pop ebx
add esp,20C
ret

⬆️ when jumping to 42D655, it'll return 11.

k Now i looking in2 42D8A0, which's what gets the next call

42D8A0; Valid8(key,name)

sub esp,138
push ebx
push ebp
push esi
mov esi,dword ptr ss:[esp+148]; key
mov ebx,dword ptr ds:[<&sscanf>]
lea edx,dword ptr ss:[esp+C]
mov al,byte ptr ds:[esi+4]; key+4
mov cl,byte ptr ds:[esi+5]; key+5
push edi
mov byte ptr ss:[esp+24],al
push edx
lea eax,dword ptr ss:[esp+28]
push 4B6B88; "%02x"
push eax
mov dword ptr ss:[esp+24],0
mov byte ptr ss:[esp+31],cl
mov byte ptr ss:[esp+32],0
call ebx; sscanf
mov eax,dword ptr ss:[esp+1C]; eax ⬅️ "%02x"(key[4,5])
mov ebp,FF
sub ebp,eax; ebp ⬅️ FF-"%02x"(key[4,5])
mov edi,esi; edi ⬅️ key
or ecx,FFFFFFFF
xor eax,eax
add esp,C
repne scasb
not ecx
dec ecx
cmp cx,bp
mov dword ptr ss:[esp+10],ecx
je 42D915; on lengthAs(key)==(FF-"%02x"(key[4,5]))
pop edi
pop esi
pop ebp
mov ax,14
pop ebx
add esp,138
ret

⬆️ the first part of Valid8 looks at the key's length. it gets key[4] and key[5] and places them in2 sscanf which'll make a number from 0 to 255 bc It's %02x. this's meaning the key[4] and key[5]'s in hex. it'll do again the repne scasb, then it'll take 255 - what it had from sscanf. When the key's length doesn't equal, it'll return 20. it's kinda like this in C

char q[2]={key[4],key[5]};short p;sscanf(q,0x4B6B88,&p);
if(255-p!=strlen(key))return 20;

42D915

mov eax,ebp
and eax,FFFF
mov dword ptr ss:[esp+20],eax; [esp+20] ⬅️ lengthAs(key)
mov cl,byte ptr ds:[eax+esi-2]; cl ⬅️ key[lengthAs(key)-2]
mov byte ptr ss:[esp+34],cl
mov dl,byte ptr ds:[eax+esi-1]; dl ⬅️ key[lengthAs(key)-1]
mov al,byte ptr ds:[esi+2]; al ⬅️ key[2]
mov cl,byte ptr ds:[esi+3]; cl ⬅️ key[3]
mov byte ptr ss:[esp+35],dl
lea edx,dword ptr ss:[esp+16]
mov byte ptr ss:[esp+36],al
push edx
lea eax,dword ptr ss:[esp+38]
push 4B5814; "%04x"
push eax; key[lengthAs(key)-2,lengthAs(key)-1,2,3]
mov byte ptr ss:[esp+43],cl
mov byte ptr ss:[esp+44],0
call ebx
add esp,C
xor ecx,ecx
xor edi,edi
mov dword ptr ss:[esp+10],ecx; [esp+10] ⬅️ "%04x"(key[lengthAs(key)-2,lengthAs(key)-1,2,3])
test bp,bp
jbe 42DAD2; on lengthAs(key) <= 0

⬆️ this part's kinda the same, but it's with 4 chars of the key, the last 2 and the third and 4th. This goes on the stack but it doesn't yet get compared w anyplace.

42D968

mov edx,ecx
and edx,FFFF
mov al,byte ptr ds:[edx+esi]; al ⬅️ key[edx]
cmp al,2D
jne 42D981; key[edx]!='-'
inc ecx; counter for past chars
mov dword ptr ss:[esp+10],ecx
jmp 42DAC9; continue on key[edx]=='-'

⬆️ this's the start of a loop, ecx'll be the counter 2 index in2 the key. edi's some-other counter. When the char from key's a -, it'll add 1 to ecx and loop again at the start

42D981

cmp cx,2
jb 42D98D
cmp cx,5
jbe 42D998; continue on cx >= 2 && cx <= 5

⬆️ when ecx's at the 3rd to sixth char in the key, it'll add 1 to ecx and loop again at the start. recollect that the last 2 and third and 4th was 4 someplace on the stack, & that the key[4] and key[5] was 2 compare w length. The third and 4th was key[2] and key[3].

42D98D

mov ebx,dword ptr ss:[esp+20]; ebx ⬅️ lengthAs(key)-3
add ebx,FFFFFFFD
cmp edx,ebx
jle 42D9A2; on past chars <= lengthAs(key)-3

⬆️ that'll go2 42D9A2 when ecx isn't indexing 2 the places in the key that were sscanf

42D998

inc ecx
mov dword ptr ss:[esp+10],ecx
jmp 42DAC9; continue when past chars > lengthAs(key)-3

⬆️ when ecx's at the last 2, it'll do the same. it'll loop again

Here's kinda the idea the same

int q=0;
for(;q<strlen(key);)
{
     char p=key[q];
     if(p=='-')
     {++q;
              continue;
     }
     if (q>=2 && q<=5){++q;
              continue;}
     if(q>strlen(key)-3) {++q;
              continue;
     }

Loop's gonna've more tho.

42D9A2

mov edx,dword ptr ds:[<&__mb_cur_max>]
cmp dword ptr ds:[edx],1
jle 42D9C4; on 1 <= 1
and eax,FF
push 2
push eax
call dword ptr ds:[<&_isctype>]
mov ecx,dword ptr ss:[esp+18]
add esp,8
jmp 42D9D7

⬆️ this go2 42D9C4

42D9C4

mov edx,dword ptr ds:[<&_pctype>]
and eax,FF
mov edx,dword ptr ds:[edx]
mov al,byte ptr ds:[edx+eax*2]
and eax,2; eax ⬅️ 0

⬆️ this go2 42D9D7 w eax ⬅️ 0

42D9D7

test eax,eax
jne 42DAFC; when eax ain't 0, ax ⬅️ 15 and ret
mov eax,dword ptr ds:[<&__mb_cur_max>]
cmp dword ptr ds:[eax],1
jle 42DA07; on 1 <= 1
and ecx,FFFF
xor edx,edx
push 2
mov dl,byte ptr ds:[ecx+esi+1]
push edx
call dword ptr ds:[<&_isctype>]
mov ecx,dword ptr ss:[esp+18]
add esp,8
jmp 42DA21

⬆️ this go2 42DA07

42DA07

mov eax,ecx
xor edx,edx
and eax,FFFF
mov dl,byte ptr ds:[eax+esi+1]; dl ⬅️ key[eax+1]
mov eax,dword ptr ds:[<&_pctype>]
mov eax,dword ptr ds:[eax]
mov al,byte ptr ds:[eax+edx*2]
and eax,2; eax ⬅️ 0

⬆️ this go2 42DA21 w eax ⬅️ 0

42DA21

test eax,eax
jne 42DAFC; when eax ain't 0, ax ⬅️ 15 and ret
and ecx,FFFF
lea edx,dword ptr ss:[esp+1C]
push edx
lea eax,dword ptr ss:[esp+28]
mov cx,word ptr ds:[ecx+esi]; cx ⬅️ key[ecx,ecx+1]
push 4B6B88; "%02x"
push eax
mov word ptr ss:[esp+30],cx
call dword ptr ds:[<&sscanf>]
mov edx,dword ptr ss:[esp+28]; edx ⬅️ "%02x"(key[ecx,ecx+1])
add esp,C
test dx,dx
je 42DAED; on "%02x"(key[ecx,ecx+1])==0
mov eax,FF
mov ecx,dword ptr ss:[esp+10]; ecx ⬅️ counter of past chars
sub eax,edx; eax ⬅️ FF-"%02x"(key[ecx,ecx+1])
mov edx,dword ptr ss:[esp+18]
add edx,eax
mov dword ptr ss:[esp+1C],eax; [esp+1C] ⬅️ FF-"%02x"(key[ecx,ecx+1])
mov dword ptr ss:[esp+18],edx; [esp+18] ⬅️ sum of all (FF-"%02x"(key[ecx,ecx+1]))
mov dl,al
shr dl,4; dl ⬅️ (FF-"%02x"(key[ecx,ecx+1])) >>> 4
shl al,4; al ⬅️ (FF-"%02x"(key[ecx,ecx+1])) << 4
or dl,al; dl ⬅️ swapped low and high half-word of FF-"%02x"(key[ecx,ecx+1])
mov eax,edi; eax ⬅️ bytes in [esp+44] buffer
and eax,FFFF
add ecx,2
inc edi; counter of bytes in buffer
mov dword ptr ss:[esp+10],ecx
mov byte ptr ss:[esp+eax+44],dl; next byte in [esp+44] buffer ⬅️ dl
mov eax,edi
and eax,FFFF
mov edx,eax
and edx,80000003; edx ⬅️ edi&FFFF&80000003
jns 42DAA9; on sign flag not set
dec edx
or edx,FFFFFFFC
inc edx

⬆️ this's a long 1 but dw it ain't 2 much. Here, ecx was the index in2 the key, but then cx ⬅️ key[ecx,ecx+1]. it'll put this in2 sscanf and the word'll go in2 dx. it mustn't be 0 or it'll return 21. this means this part of the key can't be like a string that's 00. Then ecx'll be the key's index again. this's bc the loop's start does [esp+10] ⬅️ ecx. but instead of adding 1 to the index, it'll add 2. From dx which's the sscanf of the word from the key, eax's gonna be 255 - dx, and that'll go in2 [esp+1C]. in Same moment, edx ⬅️ [esp+18] + eax. but then this'll go back in2 [esp+18]. Now al's got 255 - the word, but it'll be copy in2 dl. then it'll swap the low and high half-word and that goes back in2 dl which then's placed in2 the next byte of the [esp+44] buffer. & w all that done, edi mustn't have a set sign flag bc of the FFFF bitmask. hence go2 42DAA9 next

42DAA9

jne 42DAC9; on edi&3 != 0 -- don't jump every 4 bytes in [esp+44] buffer
mov bl,byte ptr ss:[esp+eax+41]
mov dl,byte ptr ss:[esp+eax+40]
lea eax,dword ptr ss:[esp+eax+44]
mov byte ptr ds:[eax-4],bl
mov bl,byte ptr ds:[eax-1]
mov byte ptr ds:[eax-3],bl
mov bl,byte ptr ds:[eax-2]
mov byte ptr ds:[eax-1],bl
mov byte ptr ds:[eax-2],dl

⬆️ it's swaps like [esp+44+eax-4],[esp+44+eax-3],[esp+44+eax-1],[esp+44+eax-2]=[esp+44+eax-3],[esp+44+eax-1],[esp+44+eax-2],[esp+44+eax-4] after every 4 bytes in2 the buffer. but first eax ⬅️ edi, which's the counter 4 the bytes. it means that it'll do this swap w the last 4 bytes that got added

42DAC9

cmp cx,bp
jb 42D968; on past chars < lengthAs(key)

⬆️ when there's more to the key, this'll keep the loop goin

42DAD2

mov ax,word ptr ss:[esp+18]; ax ⬅️ sum of all (FF-"%02x"(key[ecx,ecx+1]))
cmp ax,word ptr ss:[esp+16]
je 42DB0B; on ax == "%04x"(key[lengthAs(key)-2,lengthAs(key)-1,2,3])
pop edi
pop esi
pop ebp
mov ax,17
pop ebx
add esp,138
ret

⬆️ comparing the sum to the double-word from sscanf from the very start. does it equals? then it'll go2 42DB0B. but when it doesn't, this'll return 23.

42DAED

pop edi
pop esi
pop ebp
mov ax,16
pop ebx
add esp,138
ret

⬆️ Returns 22. Musn't come here.

42DAFC

pop edi
pop esi
pop ebp
mov ax,15
pop ebx
add esp,138
ret

⬆️ Returns 21. Musn't come here.

42DB0B

and edi,FFFF
lea ecx,dword ptr ss:[esp+44]
push ecx
mov byte ptr ss:[esp+edi+48],0; last byte in buffer ⬅️ 0
call 4665A0; Place0(buffer)
lea edi,dword ptr ss:[esp+48]; edi ⬅️ &buffer
or ecx,FFFFFFFF
xor eax,eax
add esp,4
repne scasb
not ecx
dec ecx
test cx,cx
mov dword ptr ss:[esp+10],ecx
jne 42DB49; on lengthAs(buffer) != 0
pop edi
pop esi
pop ebp
mov ax,18
pop ebx
add esp,138
ret

⬆️ Comes here when the loop sum equal the double-word in the key.

42DB49

lea edx,dword ptr ss:[esp+44]
push edx
call dword ptr ds:[<&_strrev>]; strrev(buffer)
mov edi,dword ptr ss:[esp+154]; edi ⬅️ &name
add esp,4
lea esi,dword ptr ss:[esp+44]; esi ⬅️ &buffer
mov eax,edi

42DB64

mov dl,byte ptr ds:[eax]
mov bl,byte ptr ds:[esi]
mov cl,dl
cmp dl,bl
jne 42DB8C; on dl[0]!=bl[0]
test cl,cl
je 42DB88; on *name==0
mov dl,byte ptr ds:[eax+1]
mov bl,byte ptr ds:[esi+1]
mov cl,dl
cmp dl,bl
jne 42DB8C; on dl[1]!=bl[1]
add eax,2
add esi,2
test cl,cl
jne 42DB64; on *name!=0

⬆️ another loop, compare name and reverse-buffer. It'll end when *name==0 or that some char in name doesn't equal buffer. When *name==0, it'll go2 next block.

42DB88

xor eax,eax
jmp 42DB91

⬆️ to 42DB91 with eax ⬅️ 0

42DB8C

sbb eax,eax
sbb eax,FFFFFFFF

⬆️ to 42DB91 with eax ⬅️ -1

42DB91

test eax,eax
je 42DBF6; on all *name==*buffer
lea eax,dword ptr ss:[esp+44]
lea ecx,dword ptr ss:[esp+44]
push eax; buffer
push ecx; buffer
call dword ptr ds:[<&CharToOemA>]
lea esi,dword ptr ss:[esp+44]; esi ⬅️ &buffer
mov eax,edi; eax ⬅️ &name

42DBAB

mov dl,byte ptr ds:[eax]
mov bl,byte ptr ds:[esi]
mov cl,dl
cmp dl,bl
jne 42DBD3; on dl[0]!=bl[0]
test cl,cl
je 42DBCF; on *name==0
mov dl,byte ptr ds:[eax+1]
mov bl,byte ptr ds:[esi+1]
mov cl,dl
cmp dl,bl
jne 42DBD3; on dl[1]!=bl[1]
add eax,2
add esi,2
test cl,cl
jne 42DBAB; on *name!=0

⬆️ same kinda loop like 42DB64, but reverse-buffer goes thru CharToOemA

42DBCF

xor eax,eax
jmp 42DBD8

⬆️ to 42DBD8 with eax ⬅️ 0

42DBD3

sbb eax,eax
sbb eax,FFFFFFFF

⬆️ to 42DBD8 with eax ⬅️ -1

42DBD8

test eax,eax
je 42DBEE
mov byte ptr ds:[edi],0
pop edi
pop esi
pop ebp
mov ax,19
pop ebx
add esp,138
ret

42DBEE

push edi
push edi
call dword ptr ds:[<&OemToCharA>]

42DBF6

pop edi
pop esi
pop ebp
xor ax,ax
pop ebx
add esp,138
ret

⬆️ Returns 0 !!! :O we must come here

also I've here what Place0 does, but i'm not gonna saying 2 much abt it bc it's not rly part of the Valid8ing of the key. basically it puts 0 at buffer's end.

4655A0; Place0(buffer)

mov eax,dword ptr ss:[esp+4]; eax ⬅️ &buffer
push ebx
xor dl,dl
mov ecx,eax; ecx ⬅️ &buffer
cmp byte ptr ds:[eax],dl
je 4665B5; on buffer[0]==0

4665AD

mov bl,byte ptr ds:[ecx+1]
inc ecx
cmp bl,dl
jne 4665AD; on not last byte

4665B5

dec ecx; ecx ⬅️ last byte in buffer
cmp ecx,eax
jb 4665C4; on ecx < buffer's start

4665BA

cmp byte ptr ds:[ecx],20
jne 4665C4; on [ecx]!=20
dec ecx
cmp ecx,eax
jae 4665BA; on ecx >= buffer's start

4665C4

mov byte ptr ds:[ecx+1],dl; [ecx+1] ⬅️ 0
pop ebx
ret

Making the KeyGen!

i'm gonna make this in pseudo-code bc then i can get the ideas out and don't gotta get uneasy abt it being a lil off rn

ok what can i say abt Valid8? ik that key[4,5] must be 255 - lengthAs(key). ik that key[lengthAs(key)-2,lengthAs(key)-1,2,3] must be the sum of all FF - the words from the key that don't go in2 sscanf but the low and high half-words are swapped. these words can't be 00. & bc When the buffer's next byte's added on every word from the key, ik the key's length must be 6+lengthAs(name)+lengthAs(name). this's bc it's in pairs and the last 2 and key index 2,3,4,5 isn't gonna be one of the words. and the lengthAs(buffer) must equals lengthAs(name). this also means that the lengthAs(name)>1 bc the key's length must be >=10. Now imma start w the lengthAs(key) bc it's someplace 2 start

procedure keygen(name) is
 key ← string of lengthAs(name)+6+lengthAs(name) chars
 key[4,5] ←255-lengthAs(key)

also When name's compared w buffer, the buffer's reverse and every 4 chars gets a swap. hence

 i ←0
 name ← reverse-name
 While i < lengthAs(name) do
  q ← name[i+3]
  name[i+3] ← name[i+1]
  name[i+1] ← name[i]
  name[i] ← name[i+2]
  name[i+2] ←q
  i ← i + 4
 Next

& again every char in the name gets a word that's 255 - that char, and it's has low and high half-words swapped

 For i←0 To lengthAs(name) do
  name[i] ← 255-name[i]
  name[i] ← swap low and high half-words name[i]
 Next

Last the sum of all 255 - every word can do it in same loop

 sum←0
 For i←0 To lengthAs(name) do
  name[i] ← 255-name[i]
  name[i] ← swap low and high half-words name[i]
  sum←sum+ 255-name[i]
 Next
 key[lengthAs(key)-2,lengthAs(key)-1,2,3]←sum

& Then here's all 2gether :D

procedure keygen(name) is
 key ← string of lengthAs(name)+6+lengthAs(name) chars
 i ←0
 name ← reverse-name
 While i < lengthAs(name) do
  q ← name[i+3]
  name[i+3] ← name[i+1]
  name[i+1] ← name[i]
  name[i] ← name[i+2]
  name[i+2] ←q
  i ← i + 4
 Next
 sum←0
 For i←0 To lengthAs(name) do
  name[i] ← 255-name[i]
  name[i] ← swap low and high half-words name[i]
  sum←sum+ 255-name[i]
 Next
 key[lengthAs(key)-2,lengthAs(key)-1,2,3]←sum
 key[4,5] ←255-lengthAs(key)

wrapping-up!

Here're some more question 4 u. You can skip if ure wanting.

what do this code do?

or ecx,FFFFFFFF
xor eax,eax
mov edi,edx
repne scasb
not ecx
dec ecx

ecx ⬅️ number of chars before [edi]==eax. when eax's 0, this's string's length

what's the calling convention?

push 3DD8
push 8
call dword ptr ds:[<&GetProcessHeap>]
push eax
call dword ptr ds:[<&RtlAllocateHeap>]
mov ebx,eax
test ebx,ebx
jne 366E39EA
push dword ptr ds:[ebx+1484]
lea eax,dword ptr ds:[ebx+3AB4]
push esi
push 1
push edi
push eax
push edi
call 366E51AE
test eax,eax
je 366E3BC5

stdcall

and how about this calling convention?

push rbp
push rsi
sub rsp,58
xor ebp,ebp
mov rsi,rcx
mov dword ptr ss:[rsp+70],ebp
lock bts qword ptr ds:[rcx],0
jb 7FFB85DD90C0
add rsp,58
pop rsi
pop rbp
ret

fastcall

15

Comments

You must log in or register to comment.

There's nothing here…