How to generate RealPlayer Plus 4.0 installation keys
(the "dummy code check" trick)

by sPIRIT and HellRaiser

(12 February 1998, slightly edited by fravia+) student
Programmers' corner
Courtesy of fravia's page of reverse engineering

Well, I really wish my readers would use the formamus.htm model before sending me their essay! It's not just a matter of having less editing work to do myself, wich should already be a good reason to do it :-)
I'll repeat it once more: essays without model, like this one, will NOT be automatically cataloged/retrieved in the (future) +HCU database.
This said you have here a very intersting work, made in couple by sPIRIT and HellRaiser. As anyone knows we are not so much favourable to the publishing of key generators, why the hell should we allow all lamers of the planet to use stolen software? Yet in this case there is serious reversing work in play (the "dummy code check" trick, if better implemented by a good protector, could really fence some intermediate cracker attacks). Therefore it makes good reading for intermediate crackers and intermediate protectors alike.
Besides I think that the complete ASM code for the keygenerator that sPIRIT and HellRaiser have included at the bottom can be useful for any serious reverser: this kind of structure (mainloop-critter) could in fact be used in order to reverse other ripped schemes...
I like moreover the work approach that the Authors have chosen: HellRaiser explaining the cracking session without code and sPIRIT commenting for you the code after you having read Hellraiser's overall intro... as you will read, this results in a very sound essay. Enjoy!

How to generate RealPlayer Plus 4.0 installation keys
By sPIRIT - sOFTPROJECT 98, and HellRaiser

Tools needed:
SoftICE (Winsuxx 95 or NT version)
Any ASM compiler (TASM 3.2 or greater will do)


Hello World, this is HellRaiser writing!

I've been searching for some time for a code/keygen for RealAudio Player Plus,
but it looks like I couldn't find anything so far... so I decided to do it on
my own :)
Too bad I discovered AFTERWARDS that the record function does not convert
audio streams to RA files, but only allows to save streams from netradios on
the hard disk... bah, who cares, I did this and I'm going to tell you
everything. Thanks to sPIRIT^SPJ who helped me a bit, expecially teaching SiCE
commands, messing up the debug phase and writing this article.

As a newbie to NT cracking (I just installed SoftIce/NT yesterday and never had
experiences about NT cracking) I played around a lot... sorry if it doesn't sound
clear O:) There has to be an easier way to crack this target, changing a flag in one
memory location or such, but I thought a keygen was more userful and funnier.

Okay, at first I expanded the installation package to my HDD and hooked the
GetWindowTextA, which the installation program seems to use intensively. 
Even if it calls it when you press a key, don't think about it, it's just 
to write out the new code.
The first thing that comes to mind is that just after you press "NEXT>" it
reads the 3 codes, copies them to another memory area and then checks for some
kind of flag (or at least I thought so) which will probably go wrong
(CAC instead of CAB). Setting the register and/or the memory to CAB will not
show the warning message, but it will also NOT install the target correcly. 
Something better had to be done...

BPM to the new-written memory area containing the codes (funny, it writes them
in 3 different lines, with an offset of 10h between each beginning) and some
skipping. It had to read them, or else they were useless... and indeed, SiCE
soon told me that it was. Even more funnier, it was copying them to another
memory area. New BPM to this new area (why was it coping them? not for fun, 
of course :).
When SiCE popped up again, the installation program was checking for the
numbers to be digits. Some skip of these routines, then I came to some other
call, which I traced... what a real mess!!! A lookup table, a lot of memory
writes... ARGH... I'll tell you... this is probably the most messed up
ASCIIZ -> BIN code snippet I've ever seen... they have for sure spent some 
days rewriting this part in assembly ;))) After some like 1 hour trying to 
understand it, it just returned a number in EAX, and it was what I wrote 
sooner. 
BPC, and BPX to next call, since it was pushing the code...

The next call contained some interesting code like I've rarely seen
elsewhere... it had to be the encrypt/test routine. It just checks for a
number to be inserted (1000-00-1000, which I first thought was some magic
register-all code, but instead it was the 'fake magic number' he was putting 
in when the input code was wrong 8))), does some cripples with the codes 
(the routine reads the 10 digits... I did not spend almost any time at 
all understanding it, I didn't care at all). 
A couple of lines below was the interesting part. Some codes were written
in memory and especially in EAX there was some interesting code... the program
was putting 2 different values from 2 quite different tables with values
ranging from 0 to 8 with some checks for equality... this had to be the code
check. It took just some patience to copy it down in a text file and make it
callable via assembly :))) just a loop to check if the output from the routine
(with 3 lines from the function which originally got original result) was
0 or 1... at the 1st 1 (duh...) I found I tried the code... it worked, among
with all other codes it said to be good 8)

Now it's sPIRIT writing.

I know that this program is rather old, but it's always interesting to see how
a GOOD utility is protected, just for learning' sake.
Here are some code snippets from the PNSETUP.EXE diassembled listing.
Take a look at this code:

* Referenced by a  Jump at Address:00404736(C)

:004047AB 8D4C2410     lea ecx, dword ptr [esp+10] ;Here is our key
:004047AF E8BCD4FFFF   call 00401C70               ;Let's check it!!
:004047B4 85C0         test eax, eax               ;Is EAX=00000001 ?
:004047B6 7420         je 004047D8                 ;Too bad! It's a WRONG key
:004047B8 8D4C2410     lea ecx, dword ptr [esp+10]
:004047BC E89F430000   call 00408B60               ;Let's check it another time
:004047C1 85C0         test eax, eax
:004047C3 7513         jne 004047D8                ;Uhm... BAD key!
:004047C5 8D4C2410     lea ecx, dword ptr [esp+10]
:004047C9 E8D2000000   call 004048A0               ;Good key, you honest guy!
:004047CE 33C0         xor eax, eax
:004047D0 5E           pop esi
:004047D1 81C45C010000 add esp, 0000015C
:004047D7 C3           ret

* Referenced by a  Jump at Addresses:004047B6(C), :004047C3(C)

:004047D8 8D442434     lea eax, dword ptr [esp+34] ;Jumps here when a BAD key is
:004047DC 682C010000   push 0000012C               ;entered
:004047E1 50           push eax

* Possible Reference to String Resource ID=03271: "Warning: This is an
  invalid serial number! "

And the program goes on asking for a new key, or installing the lite version.
Now the question is, what does the "call 00401C70" do?

* Referenced by a  Jump at Address:00401C65(U)

:00401C70 E84B6E0000    call 00408AC0              ;ASCIIZ -> HEX conversion
:00401C75 50            push eax                   ;the code now is in EAX
:00401C76 E8656D0000    call 004089E0              ;Let's check it
:00401C7B 83C404        add esp, 00000004
:00401C7E C3            ret

Here is the code that checks the keycode, as HellRaiser explained:

* Referenced by a CALL at Addresses:004089E5   , :00408B71   

:00408BA0 83EC14        sub esp, 00000014
:00408BA3 53            push ebx
:00408BA4 56            push esi
:00408BA5 8B742420      mov esi, dword ptr [esp+20]
:00408BA9 57            push edi
:00408BAA 55            push ebp
:00408BAB 81FEE8CD9A3B  cmp esi, 3B9ACDE8          ;This checks for a dummy code
:00408BB1 750D          jne 00408BC0               ;(1000-00-1000)
:00408BB3 B801000000    mov eax, 00000001
:00408BB8 5D            pop ebp
:00408BB9 5F            pop edi
:00408BBA 5E            pop esi
:00408BBB 5B            pop ebx
:00408BBC 83C414        add esp, 00000014
:00408BBF C3            ret

The dummy code check was a bitch to understand. It sees that that key is
something similar to a *magic* one, as it also sets the flag in eax (00000001),
but the code that followed doesn't fill all the flags. In fact, a later call
checks for HEXcode = 3B9ACDE8, and reports a BAD KEY status.
Let's go on.

* Referenced by a  Jump at Address:00408BB1(C)

:00408BC0 85F6       test esi, esi     ;The key can't be all 0's
:00408BC2 750A       jne 00408BCE      ;The key isn't all 0's: real check
:00408BC4 33C0       xor eax, eax      ;or the fake "magic"
:00408BC6 5D         pop ebp
:00408BC7 5F         pop edi
:00408BC8 5E         pop esi
:00408BC9 5B         pop ebx
:00408BCA 83C414     add esp, 00000014
:00408BCD C3         ret

The "jne 00408BCE" brings to the REAL key check, it begins this way:

* Referenced by a  Jump at Address:00408BC2(C)

:00408BCE 33C9          xor ecx, ecx
:00408BD0 BF01000000    mov edi, 00000001
...
...
:00408C7E 89442420      mov dword ptr [esp+20], eax

EAX contains the flag, 00000000 if the key is not valid, 00000001 otherwise
(as we want!)

* Jump from Addresses:00408C08(C), :00408C68(C), :00408C6E(C), :00408C78(C)

:00408C82 8B442420     mov eax, dword ptr [esp+20]
:00408C86 5D           pop ebp
:00408C87 5F           pop edi
:00408C88 5E           pop esi
:00408C89 5B           pop ebx
:00408C8A 83C414       add esp, 00000014
:00408C8D C3           ret

Ok. This check is over, but later on, at :004047BC      call 00408B60, it
checks the code another time, to see if the first time we entered the fake magic
I mentioned above (in that case the flag in eax was set), and if it was done,
fails the installation.

Ok, the real key check was ripped, along with the two tables it used to check
if the entered code was valid, located at 0040B5A8 and 0040B6C8. Simply
take a look at the source code of the keygen to see how they are used.
----------------- CUT HERE ------------------------------------------------------------------- ; RealPlayer Plus 4.0 random installation key generator ; By sPIRIT - sOFTPROJECT 98, and HellRaiser .model small .486p .stack 100h .data table1 label dword dd 1,2,3,4 dd 5,6,7,8 dd 1,0,1,2 dd 3,4,5,6 dd 7,8,1,0 dd 1,2,3,4 dd 5,6,7,8 dd 1,0,1,2 dd 3,4,5,6 dd 7,8,1,0 dd 1,2,3,4 dd 5,6,7,8 dd 1,0,1,2 dd 3,4,5,6 dd 7,8,1,0 dd 1,2,3,4 dd 5,6,7,8 dd 1,0,1,2 table2 label dword dd 2,4,6,8 dd 0,1,3,5 dd 3,1,2,4 dd 6,8,0,1 dd 3,5,3,1 dd 2,4,6,8 dd 0,1,3,5 dd 3,1,2,4 dd 6,8,0,1 dd 3,5,3,1 dd 2,4,6,8 dd 0,1,3,5 dd 3,1,2,4 dd 6,8,0,1 dd 3,5,3,1 dd 2,4,6,8 dd 0,1,3,5 dd 3,1,2,4 vstack dd 40 dup (?) header db "RealPlayer Plus v4.0 installation random key generator",13,10 db "By sPIRIT - sOFTPROJECT 98, and HellRaiser - 9th february, 1998",10,10,13,"$" byebye db 13,10,10,"sOFTPROJECT 98",10,13 db "Mail us at: sPIRIT <IMMAGIKA@usa.net> and HellRaiser <L.raiser@deathsdoor.com>",10,13,"$" textbuffer db 13,"__________ <SPACE> for another code, any other key to quit$" .code .startup mov ax,@data mov ds,ax mov dx,offset header mov ah,9h int 21h in al, 40h shl ax, 8 in al, 40h shl eax,8 in al, 40h shl eax,8 in al, 40h mov esi,eax ;Random startup point for quite different codes in few time mainloop: push esi call critter pop esi or eax, eax jng short l_notfound mov di, offset textbuffer + 10 mov cx, 10 mov eax, esi mov ebp, 10 itoa: xor dx, dx div ebp add dl, 30h mov [di], dl dec di dec cx jnz short itoa mov ah, 9 mov dx, offset textbuffer int 21h xor ax, ax int 16h cmp al, ' ' je short l_notfound mov dx,offset byebye mov ah,9h int 21h mov ax,4c00h int 21h l_notfound: in al, 40h shl ax, 8 in al, 40h shl eax,8 in al, 40h shl eax,8 in al, 40h mov esi,eax ;Random startup point for quite different codes in few time jnz short mainloop ;original code test routine from RealAudio Player Plus 4.0 setup (PNSETUP.EXE) ;Requires (ASCIIZ -> HEX) code in ESI ;Returns EAX = 1 if code is valid, 0 else critter proc xor ecx,ecx mov edi,1 mov eax,-1 lea ebx, [vstack+14h] mov [vstack+20h], ecx mov [vstack+10h], ecx mov [vstack+1ch], ecx mov [vstack+14h], eax mov [vstack+18h], eax lbf2: mov ebp, 0ah mov eax, esi sub edx, edx div ebp cmp dl,06 jnz lc14 lea eax,[vstack+1ch] cmp edx,eax jae lc82 movsx eax,cl mov [ebx],eax add ebx,04 jmp lc2f lc14: movsx eax,dl add [vstack+1ch],eax cmp dl,6 jle lc22 dec dl lc22: movsx eax,dl imul eax,edi lea edi,[edi*8+edi] add [vstack+10h],eax lc2f: mov ebp,0ah mov eax,esi sub edx,edx inc cl div ebp mov esi,eax cmp cl,0ah jl lbf2 mov eax,[vstack+1ch] shl eax,2 mov edx,[eax+table1] mov eax,[eax+table2] cmp eax,edx mov ecx,eax jg lc5e mov ecx,edx lc5e: cmp eax,edx jl lc64 mov eax,edx lc64: cmp eax,[vstack+14h] jnz lc82 cmp ecx,[vstack+18h] jnz lc82 cmp dword ptr [vstack+10h], 121eabfh ; another fake magic??? ja lc82 mov eax,[vstack+10h] mov [vstack+20h],eax lc82: mov eax,[vstack+20h] cmp eax,1 sbb eax,eax inc eax ret critter endp END ----------------- CUT HERE -------------------------------------------------------------------
Keep up the good work
sPIRIT - sOFTPROJECT 98
(c) 1998 sPIRIT and HellRaiser All rights reversed
You are deep inside fravia's page of reverse engineering, choose your way out:

advanced
Back to Programmer's corner

redhomepage redlinks redanonymity +ORC redstudents' essays redacademy database
redtools redcocktails redjavascripts wars redantismut CGI-scripts redsearch_forms redmail_fravia+
redIs reverse engineering legal?