PC98 Trial CrackMe KeyFile Solution
by Kill3xx


No comments. This tute is absolutely dynamite! This is the first tute I publish which is not thought just for newbies... you'll need some more skills to understand it well, but hey, you will learn a lot too! Here you'll find some hints about using IDA for the disassembling, and you'll learn some precious information about PE files decrypting.
ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßÛ
²                               [x] ringZ3r0  Proudly Presents [x]                            ²
±                ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ                 ±
                 ³            PC98 Trial CrackMe KeyFile Solution           ³

                                              Kill3xx

                                         December, 28 1998


--==[  DISCLAIMER  ]==------------------------------------------------------------------------

ALL THE INFORMATION YOU FIND IN THIS FILE ARE FOR EDUCATIONAL PURPOSES ONLY. THE AUTHOR
DOESN'T ENCOURAGE THOSE WHO WANT TO USE IT FOR ILLEGAL PURPOSES AND IS NOT RESPONSIBLE
FOR WHAT _YOU_ DO WITH IT.

--==[  +Ma's DISCLAIMER  ]==------------------------------------------------------------------

ALL THE ERRORS YOU FIND IN THIS FILE ARE +MALATTIA'S (THAT IS, THE TRANSLATOR) FAULT ONLY. 
THE AUTHOR DOESN'T ENCOURAGE THOSE WHO WANT TO MAILBOMB HIM... ANYWAY HE'S NOT RESPONSIBLE
FOR WHAT _YOU_ DO TO HIM. :)

--==[  DIFFICULTY  ]==------------------------------------------------------------------------

*=Novice, **=Medium, ***=Expert, ****=Guru
target: **1/2

--==[  TOOLS USED  ]==------------------------------------------------------------------------

* PROCDUMP 1.2 (Grom/Stone UCF)          
* SoftIce 3.23 (Numega)
* IDA ver 3.80 (Ilflak / DataRescue)
* TASM 5.0     (Imprise)
* HIEW 6.0     

--==[ INTRODUCTION ]==------------------------------------------------------------------------

I've decided to write a tute about the CrackMe by The+Q and Plushm, as soon as the PC Trail
Session was ended of course ;), because I consider it a great example of how you can use IDA
to build a keymaker in an easier and faster way.

The Crackme is divided in 4 steps, ordered by difficulty:

 1) find the classic user/code combination
 2) eliminate the nagscreen at the start
 3) find the right serial number
 4) build a valid keyfile which lets us register, for instance, our name

But let's start now:

--==[  THE KEYFILE STEP  ]==------------------------------------------------------------------

The last step is the most interesting one... well at least for me (I'll quickly talk about 
the first three at the end of this tute).

Now, I know that some of you can't wait and are still ready to push F8...F12 keys, well stay
calm: a thing I suggest everybody to do before a cracking session is to sit down and start 
to think about the target you have to work on:

1) We have an advantage because we know that we have to deal with a keyfile, so we can
   avoid the usual workaround (FileMon/RegMon, etc.)
2) This crackme is presumably written in asm... so the code is almost exclusively dedicated
   to the protection... which really helps us in the routine identification phase... eheh
   no overbloated code like in delphi or vb :)
3) If it's written in asm as we think, we can expect to find some dirty tricks too ;)
   such as anti-Sice, self-modifying code and so on
4) Analyzing the PE Header structure with Procdump (or whatever PE Browser) we see that the
   exe file is encrypted/packed... and that the decrypter is in the last section Q_Plush.

After this short (but important) preview of our target, we can now start to use our beloved
sIce.
Since we're working on a keyfile, the breakpoints to try immediately are the ones on the
Winsuxx APIs which are used to open/read files, that is BPX on CreateFileA, ReadFile,
GetFileAttributesA, GetFileSize...
If you try to use these breakpoints, SoftIce doesn't break... uhm, what's happening here?
Nothing in particular, our friend The+Q used the obsolete _lopen, _lread, _lclose etc. to
make the work harder for us :) Let's delete all the old breaks with "BC *" and let's do
a "BPX _lopen" and a "BPX _lread"...

//Note for the newbies//----------------------------------------------------------------------
Since, willing or not, you'll have to work a lot of times on windogz targets, it's a good
thing if you get SDK help with all the API prototypes and study at least the most important
ones... it's very important for you to know what parameters are passed to an API when it's
invoked.
//------------------------------------------------------------------------------------------// 

Et voila', sIce breaks in the middle of the routine which reads the keyfile: we can see that
the prog uses _lread to read 0xA4 bytes from a file whose name is "crackme.key" and then it
closes it... if the program is not able to read 0xA4 bytes, the function returns an error
with the value eax=-1... good, so we put a BPX to the address where the function begins, then
we delete BPX _lopen and so on, we exit sIce... we create a nice 164 bytes long crackme.key 
file full of 0xFF... we enter that function again, we create a 

BPR buffer_address buffer_address+164

(or just BPM if we're using sIceNT)... P RET (F12) and we give a look at what the program
does:

Uhmm... it reads the last dword and does something with it (ROL 6)... it calls a function
which builds a table of 8 elements (byte) and then it enters a loop with ecx=14 which
calls something which seems a decrypt function...
Argh... even at a first glance this function seems quite long and complex... well, even
if you didn't want to we have to change our live sIce approach in a more meditative one...
DEADLISTING WITH IDA. :)

But now there's a problem: how can we disassemble the exe file if it is encrypted? No
problem: since it decrypts itself when it's executed and, fortunately, the import table
isn't altered, we can dump it (using Icedump/The_Owl, Softdump/Quaine, Adump/UCF just to
name some of them), save the decrypted sections and overwrite them in the original exe
(remember that this is just a crypter, so the sections have the same sizes even after 
the memory mapping), and finally remove the Q-Plush section and adjust the pe_header
(the entry point, section table, imagesize etc.)... Ok, ok, this seems hard, but after 
all it's just a Cut&Paste job :)

Well, since we are lazy we can use ProcDump. ;)

I suggest you to use these options:

"recompute obj size"        = OFF 
"use actual import infos"   = ON

Choose Standard method... unpack... press ok... save the decrypted exe... use ProcDump
pe-editor... kill (what a nice term :)) the decrypter section which is now unuseful
(well you can study it if you like... ;). If you did these operations in the right way
now you can happily disassemble with IDA :)

Now forget the rest of the code and give a look at the instructions from 00402471:


loc_0_402471:                           ; CODE XREF: sub_0_40100C
                call    fnReadKeyFile   ; open/reads in mem the KeyFile - 00402551
                cmp     eax, 0FFFFFFFFh
                jz      loc_0_402530
                mov     cl, 6
                mov     eax, ds:CRC     ; uses the last dword of the keyfile = CRC
                rol     eax, cl         ; rol 6
                mov     ds:Seed, eax    ; like first seed
                
                call    fnBuildMagicTab ; Prepare the Magic Table 0..8


This is what we already saw in sIce...  now you'll notice the names I've given in IDA:
this deserves some words.

"Nomina Sunt Consequentia Rerum" , said S. Augustin; but we can also "reverse" this
phrase and consider the objects around us in relation with the names we ourselves 
give them (no, no, I haven't smoked anything strange ;)

Learning to comment in IDA is fundamental and this is what can make the difference.
Starting from those few things you understand from the program give name to the
locations, to the procedures, to the loops, to the variables in the stack and so on.
You'll see that the more names/comments you'll add to the source, the easier the
program will become.

// Example //--------------------------------------------------------------------------------

00402481                 mov     eax, ds:40245Dh

press CTRL+R keys and then CTRL+R... choose full 32 offset... now you'll have

00402481                 mov     eax, ds:dword_0_40245D

click on dword_0_40245D to go to that offset... then press the "N" key and give the
name "CRC"... now go back to 402481 (pressing ESC) and you'll have:

mov     eax, ds:CRC 

from now on, when you'll press CTRL+R on ds:40245Dh IDA will write CRC itself... and
it will then add an xref for that address. For the call, you just have to click on
CODE XREF:sub_0_40100C, press "N", give the name and, from now on, you'll always see
call fnReadKeyFile instead of a more cryptic call sub_0_402551. Also: to add comments
use the ":" key for single comments and ";" for the ones you want to be repeated
(try, for instance, to add one at the start of the fnReadKeyFile code, writing something
like "read keyfile , return eax=0 -> ok")

These are just some of the commands that IDA lets you use to comment the code... you
can create structures, arrays, rename the parameters/variables created with a
stackframe esp+ebp, etc. IDA is really interactive, you can make it do everything you
like... this is the main difference with wdasm: a disassembler, no matter how good it
is, cannot understand what YOU can understand using your brain :))

Ok, the subliminal ad is ended... ehm buy IDA if you've got the $ ofcoz! ;)

//------------------------------------------------------------------------------------------//

Now let's return to the disasm: we're interested in the call fnBuildMagicTab: 

fnBuildMagicTab proc near                   ; CODE XREF: DATA:0040248D
                mov     edi, offset Magic1  ; offset MagicTable 
                mov     al, 55h             ; beginning with seed "U"
                mov     ecx, 8              
loc_0_4025D8:                               ; CODE XREF: fnBuildMagicTab+12
                inc     al
                xor     eax, 12h            ; builds the Table with 8 Magics
                stosb
                loop    loc_0_4025D8
                retn
fnBuildMagicTab endp

Here there isn't much to say... well just that the table is always the same,
indipendently from the keyfile... good!

                mov     ecx, 14h        ; decrypt loop = 8 byte x 20 = 160 bytes
                mov     esi, offset KeyFileBuffer

loc_0_40249C:                           ; CODE XREF: DATA:004024A4
                call    fnDecrypt       ; decrypts 2 dwords x loop
                add     esi, 8          ; bf_index = bf_index + 8
                loop    loc_0_40249C

this is also straight... let's give a look to fnDecrypt now...

               mov     edi, [esi]      ; input : dword1 = buffer(bf_index)
               mov     ebx, [esi+4]    ;         dword2 = buffer(bf_index+4)
               push    esi             
               ; <<--- pattern 
               mov     cl, ds:Magic1   
               mov     eax, edi
               rol     eax, cl
               call    fnAdjustXorMask ; X = funct(dword1 ROL MagicX)
               xor     ebx, eax        ; dword2 = dword2 xor X
               mov     cl, ds:Magic2
               mov     eax, ebx
               ror     eax, cl
               call    fnAdjustXorMask ; Y = funct(dword2 ROR MagicY)
               xor     edi, eax        ; dword1 = dword1 xor Y
               ;  pattern --->>
               mov     cl, ds:Magic3
               mov     eax, edi
               rol     eax, cl
               call    fnAdjustXorMask ; like above but with MagicX = Magic3
               xor     ebx, eax
               mov     cl, ds:Magic4
               mov     eax, ebx
               ror     eax, cl
               call    fnAdjustXorMask ; like above but with MagicY = Magic4
               xor     edi, eax
               ... the same pattern is repeated 10 times...
               pop     esi
               mov     [esi], edi      ; output : buffer(bf_index)   = dword1
               mov     [esi+4], ebx    ;          buffer(bf_index+4) = dword2

The interesting part is that pattern that is repeated with the only change of the
Magic used... in practice, 2 dwords are decrypted every time, with a XOR sequence
where the xor_mask for dword1 is function of dword2 at every step. We don't mind
all those ROL, ROR anymore since they do not change from dword1 to dword2... the
only problems could be caused by fnAdjustXorMask:


fnAdjustXorMask proc near               ; CODE XREF: fnDecrypt+14
                push    ecx
                mov     ecx, ds:Seed
                shr     ecx, 18h
                ror     eax, cl
                xor     eax, ds:Seed    ; mask = (mask ror (seed shr 18)) xor seed
                call    fnAdjustSeed    ; adjust seed
                pop     ecx
                retn
fnAdjustXorMask endp

fnAdjustSeed    proc near               ; CODE XREF: fnAdjustXorMask+12
                push    eax
                mov     eax, 51656854h
                cdq
                imul    ds:Seed
                add     eax, 6D6D482Bh
                sub     ds:Seed, eax    ; seed = seed - (High(seed * const1) + const2)
                pop     eax
                retn
fnAdjustSeed    endp

here, as you can see, the mask is adjusted at every step using the Seed value which
is changed at every step too... You good guys, The+Q & Plushm :))
Now the algorithm used is quite clear, even if someone could think that it's not
easy to reverse... naaaa, not for us ;)
Now we just have to get a crypter and then our keymaker is just made by reversing
every step, starting from the final dwords (the text to encrypt) and trying to
do all the steps in inverted order...
So, first, let's invert all the "patterns" starting from the last one, so the Magic
will be used with reverse order and we're ok with the ROL, ROR too... now we have
to reverse the relation between dword1 and dword2:

_pattern proc
 X = funct(dword1 ROL MagicX)
 dwordB = dword2 xor X
 Y = funct(dwordB ROR MagicY)
 dwordA = dword1 xor Y
_pattern endp 

 whose reverse is:
 
_inv_pattern proc
 dword1 = dwordA xor funct(dwordB ROR MagicY) 
 dword2 = dwordB xor funct(dword1 ROL MagicX)
_inv_pattern endp  

So... you'll say "that's ok until here"... but what should we do with that funct?
Should we reverse that one too? And then, what can we do with fnAdjustXorMask, which
uses a different seed on every step?
Ah, how many questions! :)

If you give a detailed look to the disasm, you'll notice that the Seed is initialized
before the loop, using CRC ROL 6, and then it changes using just fnAdjustSeed, in a
way which is indipendent from dword1 and dword2, but related to the preceding steps...
Ok, it's done :)
We can just code a funct which, on every cycle of the external loop of the crypter,
builds a table with all the values of Seed for that iteration (12 patterns = 24 entries
in that table) and then use it instead of fnAdjustSeed...
To get the crc you just have to apply the same function used by the programmers on the
buffer (159bytes+null) you want to encrypt, and then save the value as the last dword
of the buffer itself... et voila', you have your keyfile! :)

If you've followed me until now (I've lost myself, to tell you the truth :)) now you
can write the keymaker... you can use whatever language you want (c/c++/asm/pascal)...
I suggest you to write it in w32asm, so you can use another function of IDA which is
VEEERY, VEEEERY useful ;)

If you've commented well the listing in IDA you can export the disasm of the different
functions (you just have to select the block and choose from the menu 
File->Produce output file->Produce ASM file) in a .asm file which you can directly use
from within TASM once you add the used variables in the data segment... it's just a
3 mins work :)... a little bit of cut&paste to reverse the fnDecrypt and fnAdjustXorMask
algos in the way you saw before (other 4 mins)... a little bit of coding to add the
procedure which builds the seed table (3/4 mins)... shake the ingredients all together
and in 10/12 minutes you'll have a ready keymaker... but don't take it as an example
of win32asm coding! ;))


--==[THE OTHER THREE STEPS]==-----------------------------------------------------------------

Ok... the keyfile step is done... now, as I promised you before, I'll tell you something
(ehehe... you don't want me to tell you everything, do you? ;)) about the other three
steps: user/code, nag screen and serial number:

a) the user/code is the easiest step: you can reach the algorithm with the classic
   BPX GetDlgItemTextA... here, your name is manipulated in a loop which uses
   ROR 8 + ADD (402111), while the code uses ROL 8 + ADD (4020F9)... then there is
   the check: Result_Code = Result_Name ROR 8 (40211E)
   I won't spend more time on this protection, because the solution is really easy...

b) the serial is quite easy too, but here FPU instructions are used (I suggest you to
   read Chapter 14 of ArtOfAssembly, which is completely dedicated to FPU). Anyway,
   the break from sIce is the same old BPX GEtDlgItemTextA; for what concerns the
   calculation, this passes through a loop which controls the serial layout, which
   is like xxxxxxxx-yyyyyyyy (where x and y are numbers), and a conversion function
   ascii_to_lomg (402d17)... finally, the conditions are 4, based on the same
   equation:

   2^[trunc(c*X)]*2^[(c*X)-trunc(c*X)] mod Y = j  
  
   where c = 1*log2(k) ; 

   and, respectively, k=[13,18,7,12] ; j=[2,7,18,23]

   The only important note here is the implementation of an easy antisIce check,
   createfileA to detect sIce w9x/NT vxd (402d93) which, if present, makes the
   values wrong using a fake k in the last check.

c) For what concerns the nag remover step... well, the rules are not to patch the
   dumped executable... so, the possible solutions are more than one, but I'll tell
   you just three of them, which are the more common:

   1) apihook : the import table isn't altered, so the hook is possible, but it isn't 
      the best method because it requires the presence of a loader... let's go on :)

   2) patching from inside (C) xOANON.
      This solution is quite easy to implement, mainly because we don't need to use 
      WriteProcessMemory because: 

      a) we're in the same addressing space
      b) the code section is writeable yet (it has to be, because the crypter 
         modifies it without using WriteProcessMemory).

      If you step in the crypter, you'll notice that it ends with the classic
      jmp entrypoint (=eax)... now, a little correction to this jump and we can
      modify it in jmp patch_thunk: that is, we can give the control to a thunk,
      which is created in the final part of the Q_Plush section (the place where
      The+Q&Plushm logo is stored will be fine :) which will act like a mem patcher:
        
      xor edx,edx   
      mov [eax+nag_box_ra],edx       
      restore the regs and the stack like in the original
      jmp eax

      with eax = entrypoint (kindly calculated by the program itself) and
      nag_box_ra = the offset, from the entry point, of the "Call NagBox"... so
      that we can modify it in E800000000

3) reverse of the encryption algorithm: that is, calculate the correct byte sequence
   to insert in the exe to make the call decrypted as E800000000 (here you should use
   a BPM address_call_nagbox to make your task easier). The encryption isn't so hard...
   well, to tell the truth that's just an easy xor with a mask which is calculated at
   every step:

   loc_40604D:                             ; CODE XREF: Q_Plush:00406070
                 push    ecx
                 lodsd
                 xor     eax, [ebp+402848h]
                 mov     ecx, [ebp+402848h]
                 shr     ecx, 18h
                 rol     eax, cl
  
  but be careful, because the next section (.DATA) has been encrypted with an initial
  seed which is calculated on the crc of the data which are in clear in the preceding
  section (.CODE). This problem is quite easy too, you just have to write down the
  right value for the seed and then hardcode it in the code instead of:

  004060B2                 mov     word ptr [ebp+402848h], 0FFFFh
  004060BB                 mov     word ptr [ebp+40284Ah], 0FFFFh

  and avoid the calculating routine going directly to address 00406135.

--==[CONCLUSION]==--------------------------------------------------------------------------

That's all folks... I wanted to show you that IDA is a really great tool, especially with 
complex targets like the keyfile in this CrackMe, and also how you can use it to make
your keymaker/keygen coding easier. I don't know if I've been clear enough... well, at
least I've tried to ;)


(c) Kill3xx 1998.
WARNING: this tutorial is published for EDUCATIONAL PURPOSES only! Nobody except you is responsible for what you do with the things you read here. Also, if you intend to use shareware programs for a period longer than the allowed one remember that you have to BUY them!