Statistics

Members: 1927
News: 293
Web Links: 1
Visitors: 3931632

Who's Online

We have 1 guest online
Damn Vulnerable LinuxDamn Vulnerable Linux (DVL) is a Linux-based (modified Damn Small Linux) tool for IT-Security & IT-Anti- Security and Attack & Defense. [CLICK HERE FOR MORE INFOS! ]

Featured Conference Video

T16-Recon2006-Joe_Stewart-OllyBonE.gif OllyBone - Semi-Automatic Unpacking on IA-32. View the conference video here!
Keygen Coding Competition
User Rating: / 0
PoorBest 
Written by Ghiribizzo   


The competition was to write the smallest key generator for the simple serial scheme I wrote as a trainer for newbies.

 

 

 

I had a few reasons for starting this
competition:
· To give the newbies a chance to participate in a competition · To give old hands the chance to brush up on their assembly skills · To promote tight assembly coding
· To demonstrate the various different methods used to improve efficiency in coding

Well, I'm back from my short European jaunt and the competition is now closed. I have greatly enjoyed the entire competition, from the coding of the crackme and the chats with various crackers on IRC through to deciding the winner and writing this document.

Analysis of the Serial Scheme

The serial scheme was kept deliberately simple as it was written for newbies to train with. The scheme took a name of up to 16 bytes long and required a 16 byte serial number. There was a 256 byte lookup table that was indexed directly with the ASCII values of the name field. The name was padded to a length of 16 (if necessary) using values hardcoded into the scheme. The 256 byte lookup table was created using eight maximal 8 bit linear feedback shift registers (LFSRs) in parallel i.e. producing one output byte per 'clock'. The LFSRs were initialised to produce 'Ghir_OCU' as the first 8 output bytes. The table was precomputed and it was not expected that the cracker recognise the nature of the lookup table - although a post I made to the cracking forum about LFSRs might have tipped the more astute crackers!

The rules of the competition required that some standard interface text be included which strongly urged the use of service 9 interrupt 21h - though this would probably be used in any case - and discouraged blank screens and other unfriendly UIs from being used to save bytes. Also, the rules specified a range of input to be handled smaller than the possible 256 maximum. Due to the simple nature of the serial scheme, this meant that the lookup table could immediately be stripped down to the input range.

I envisioned that there would be 3 ‘fights’. One to reduce the original algorithm, second to reduce the packed table lookup algorithm and the last to reduce the LFSR algorithm. As it turned it, everyone seemed to go for the packed table option.

The Entrants

The following entrants have been included because they illustrate the different ideas and methods used to reach the common goal of reducing code size. I didn't realise that so many crackers would use the precomputed table method. Perhaps word got out during IRC chats and everybody started using them? In any case, this didn’t reduce the size cutting war as precomputation had its own routines that needed to be optimised.

Ghiribizzo Alpha (223 bytes)

This was not an entrant as it would hardly be fair for me to enter the competition knowing how the lookup table was generated! This keygen was basically converted from the crackme and improved 'on-the-fly' by generating the lookup table in code and tidying up routines where they were obviously inefficient. No great thought went into this and the code size was just to give myself an idea of what crackers would be aiming for. Aside from generating the lookup table, the only other unusual feature of this keygen was the use of the XLAT command instead of the standard indexing used in the crackme. I didn't stop to check whether this used less space or not, but included it as newbies may not be familiar with the XLAT instruction. As it happened, the XLAT instruction was used in Spyder’s keygen.

From the size I got from this keygen, I tried to guess a required key input range to put this size between thestraight table precomputation and the packed table precomputation.

One thing to note is how I ended the program. I was quite surprised by the fact that nobody else seemed to know that you could quit com programs with a ret instruction. Further size savings can be made by using Bb’s trick of keeping DH and also by tweaking the generator to fix some of the bitstreams produced to give us the bits we need and save later processing.

Cruehead Alpha (244 bytes)

I got this from Cruehead on IRC when I asked to see what he had managed so far. Although this version is unfinished it is still impressive. The keygen relies on precomputing the whole table and reducing the keygen to a single table lookup.

The coding is very simple - almost seems as if Cruehead was typing the steps going through his head straight onto the keyboard (perhaps he was?) the resulting code is consequently very easy to understand and follow.

Bb #10 (230 bytes)

Bb has written an excellent keygen. He has put some serious hard work into this including taking the time to calculate the dx offsets manually instead of just using the ‘offset’ feature that the compiler provides. It has been fun watching Bb’s keygen progress as the first one I received was version 5 which was 256 bytes long. The keygen presented here is version 10. There are other nice bits and bobs throughout this code. This makes it quite frustrating as in various places so much space is blatantly wasted. Just take a look at the last 6 lines of code! There shouldn’t even be 6 lines there! I’m sure Bb will learn a lot from seeing some of the other keygens here and I’m sure he will do very well should he enter the next competition.

Spyder (211 bytes)

Tidy, compact and elegantly coded. A little sparse in commenting (it seems like Spyder coerced IDA to write the keygen for him ;-p). The table lookup is an interesting piece of code.

VoidLord (247 bytes)

Another keygen using the idea of a packed precomputed table. VoidLord’s first keygen. Let’s hope we see more!

Honourable Mentions

Special mention given to Trykka who managed to deduce how the look-up table was created - but never sent in an entry!

The Winner

Well it looks like Spyder is the winner by quite a large margin. Incidentally, I have just made a quick check that the keygens work. You might be able to bump yourself up on the scale by picking holes in the other keygens :-)

Rankings

__Keygen______Size________Author______

    kgen.com    211         Spyder
kg.com      224         Ghiribizzo (alpha)
kg10.com    230         Bb
kg9.com     233         Bb
kg6.com     239         Bb
kgvoid.com  247         VoidLord
kgcrue.com  255         Cruehead (alpha)
kg5.com     256         Bb
kgt.com     529         Serial Scheme

Final Words

There have been some excellent ideas in the keygens. However, none of the keygens are as small as they could be. They all have some scope for improvement. By combining some of the ideas given in the above keygens, we could create a new smaller keygen. It will be interesting to see what the smallest possible keygen would look like.

I hope that everyone who has taken part in the competition, or who has followed it, has gained something from it. I hope that there will be more entries for the next competition!

The Source Codes

; Ghiribizzo’s Keygen ========================================================= .model tiny
.386
.code
.startup
; The first part of the code is the table generator ; Note that we can actually do some ‘precomputing’ by ; fixing some of the bits in the generator to produce ; the bits that we need. This will save some bytes ; in the serial section. I have not bothered to do this.

mov ax, 5547h
mov bx, 6869h
mov cx, 725fh
mov dx, 4f43h
mov di, offset PRD
mov si, offset PRD + 0ffh
LFSR:

stosb
;Save MSB
mov bp,ax
mov al,ah
and ax,0ffh
xchg ax,bp
;Tap
xor ah,bl
xor ah,ch
xor ah,al
;Shift
mov al,bh
mov bh,bl
mov bl,ch
mov ch,cl
mov cl,dh
mov dh,dl
;Store MSB
and dx,0ff00h
or dx,bp
cmp di,si
jle LFSR
;-----------------------------------------------------------------

mov ah,9
mov dx,offset startMsg
int 21h
mov ah,10
mov dx,offset NameInput
int 21h
;-----------------------------------------------------------------

mov si,offset NameBuffer
mov di,offset NameHash
mov bx,offset Table1
MakeSerial:

lodsb
xlat
and al,3fh
or al,30h
byteOK:

cmp al,39h
jle keepit
add al,7
keepit:

stosb
cmp di,offset stopbyte
jl MakeSerial
;-----------------------------------------------------------------

mov dx,offset NH2
printMsg:

mov ah,9
int 21h
exit:

ret

StartMsg    db      0dh,0ah,'OCU Keggen #1 ',0feh,' Ghiribizzo 1998 ',0dh,0ah
db      0dh,0ah,'Enter Name : $'
NameInput   db      17
NameRead    db      ?
NameBuffer  db      'mk3 "![]ns)%3x#0Z'
nh2         db      0dh,0ah,'Serial Number: '
NameHash    db      16 dup('y')
stopbyte    db      0dh,0ah,'$'

Table1:
PRD:
END

; Cruehead’s Keygen =========================================================== .model tiny
.386
.stack
.data

StartMsg    db      0dh,0ah,'OCU Keggen #1 ',0feh,' Cruehead 1998 ',0dh,0ah
db      0dh,0ah,'Enter Name : $'
SerialMsg   db      0dh,0ah,'Serial Number: '
NameVar     db      011h,0h,06Bh,06bh,033h,020h,022h,021h,05bh,05dh,06eh
db      073h,029h,025h,033h,078h,023h,030h,'$'
Table       db      037h,035h,034h,031h,036h,032h,046h,044h,046h,044h,044h
db      031h,035h,035h,038h,035h,036h,046h,032h,045h,036h,030h
db      031h,039h,033h,034h,030h,046h,031h,042h,044h,030h,043h
db      036h,043h,035h,039h,045h,039h,033h,036h,043h,037h,035h
db      036h,044h,045h,036h,032h,044h,031h,037h,039h,030h,031h
db      042h,046h,043h,034h,032h,031h,035h,037h,034h,044h,032h
db      032h,032h,030h,043h,034h,030h,044h,044h,033h,039h,044h
db      043h,038h,036h,031h,038h,041h,037h,034h,046h,045h,041h
db      036h,044h,043h,041h,041h,039h,043h,037h

.code
.startup

mov ah,09h
lea dx,StartMsg
int 21h
mov ah,0ah
lea dx,NameVar
int 21h
OnceAgain:

mov bl,NameVar[di+4]
cmp bl,0dh
jne noprob
mov bl,02bh
noprob:

mov al,table[bx-020h]
mov NameVar[di+2],al
inc di
cmp di,0Eh
jne OnceAgain
mov word ptr NameVar[16],00a0dh
mov ah,09h
lea dx,SerialMsg
int 21h
.exit
end

; Bb’s Keygen ================================================================= ; KG10 - Ghiribizzo KeyGen
; written by bb 12Sep98 1:30AM
; next revision 13Sep98 5:00PM
; yet more changes - 26Sep98 - late late night ; eat 3 more bytes 28Sep98
;
; comments where the evils lay
;
; I just knew that I HAD to make this thing 256 bytes of less. Beware: This ; is NOT an example of good coding practice! I almost wish I could do a ; "bytes saved" comparison for all the little hacks. ;
; I've gotten this to assemble under TASM. It MUST assemble as a 16-bit COM file, ; and even then I can't guarantee that the offsets will remain stable between ; various assemblers. Let me restate that: I CAN guarantee that this won't ; work for you when you try and assemble it yourself. :) ;
P8086
MODEL TINY
DATASEG

OffsetStartMsg      EQU     52h
OffsetMySerial      EQU     7fh
OffsetSerialMsg     EQU     91h
OffsetMyName        EQU     0a3h

StartMsg db 0dh,0ah,'OCU Keggen #1 ',0feh,' ----- bb ----- 1998',0dh,0ah ; There's no reason not to re-use this section of the StartMsg, since it fits ; perfectly though code had to be added to affix a linefeed MySerial db 0dh,0ah,'Enter Name : $' SerialMsg db 0dh,0ah,'Serial Number: $' ; previous change to MyName not needed anymore

MyName      db  11h, 0h, 6Dh, 6Bh, 33h, 20h, 22h, 21h, 5Bh, 5Dh, 6Eh, 73h, 29h,
db  25h, 33h, 78h, 23h, 30h, 5Ah

; Not only does the full table not need to be used, but since it's basically a ; substitution cypher we can fit everything into these 96 or so bytes ; Also, the trailing commented-out 37h saves us one byte. It's the substitution for 7Fh, ; but since 7Fh is a DELETE when using 0a/int21h, it never gets accepted by KGT.COM or by ; this keygen. Therefore, it's useless and unneeded. ; NewTable db '754162FDFDD155856F2E6019340F1BD0C6C59E936C756DE62D17901BFC421574 ; D2220C40DD39DC8618A74FEA6DCAA9C';, 37h ; and I missed the fact that it also only uses characters 0-9 and A-F ; which can be expressed in 4 bits, cutting the 96 byte table in half

NewTable    db      75h, 41h, 62h, 0FDh, 0FDh, 0D1h, 55h, 85h
db      6Fh, 2Eh, 60h, 19h, 34h, 0Fh, 1Bh, 0D0h
db      0C6h, 0C5h, 9Eh, 93h, 6Ch, 75h, 6Dh, 0E6h
db      2Dh, 17h, 90h, 1Bh, 0FCh, 42h, 15h, 74h
db      0D2h, 22h, 0Ch, 40h, 0DDh, 39h, 0DCh, 86h
db      18h, 0A7h, 4Fh, 0EAh, 6Dh, 0CAh, 0A9h, 0C7h

CODESEG
STARTUPCODE
; A note here: We're at