Well Little-John gave us two path to wander on in his essay on Winrar
95 I've chosen the long way,
studing the encryption mechanism in Winrar 95:
It's using base
indexed relative
adressing heavily inside tables, and shifting instructions as encryption
mechanism.
There is no echo of the correct serial number wich directs that the
protectionists have learned something. The whole protection scheme is
quite a mess, wich require alot of studing and experimenting
This is a good exercise in methodically mode of cracking procedure.
I read Little-John essay about Winrar[95]. It has two way of cracking paths, like all of these serial# protections, brute forcing them or crack the math behind the encryption. Little-John brute forced Winrar. I however became very curios on the encryption routines that Winrar use. In this essay I'll show you how Winrar's encryption works. Before we begin let me first tell you that there is no crack in this essay neither a serial number. This is because of three things
Winice 1.xx or higher :-)
W32Dasm 8.9 Regged
3 sixpacks of
Carlsberg beer, Swedes shouldn't drink anything stronger than beer while
cracking due to our alchol culture, unless you really sure you can
handle some vodka, without emptying the whole bottle :-)
Tea (200g)
This protection manipulates the username by adding the letters in the name in an algorithm. It's not complicated but I haven't seen this method before. The name manipulation is very straight and short btw. It is the serial number that is complicated. Winrar use tables very heavily and base indexed adressing, wich makes things alittle more fun than the usual serial protection schemes. It doesn't calculate a correct serial# that we can use for input in the register dialogbox. Wich is an indication that more complicated protections are to come. Lets start:
Name used: IndianI n d i a n
49 6E 64 69 61 6E
49 XOR 00 = 49 69 XOR 43 = 2A 6E XOR 49 = 27 61 XOR 2A = 4B 64 XOR 27 = 43 6E XOR 4B = 25
The result value is a key, lets call it namekey. Next is the final
manipulation with the chars:
From the start we have:
Ebp-10= beginning of our name, Ebx=0, Ebp-04=length of our name edx = ebp-10 :0041926E mov eax, ebx :00419270 mov edx, dword ptr [ebp-10] :00419273 mov esi, edx :00419275 lea edx, dword ptr [ebp+eax-0000008C] :0041927C cmp eax, dword ptr [ebp-04] :0041927F jge 00419290 :00419281 mov cl, byte ptr [edx] :00419283 add byte ptr [esi], cl :00419285 add eax, 00000005 :00419288 add edx, 00000005 :0041928B cmp eax, dword ptr [ebp-04] :0041928E jl 00419281 :00419290 inc ebx :00419291 inc [ebp-10] :00419294 cmp ebx, 00000005 :00419297 jl 0041926E
This routine has two loops, one inner loop and one outer loop. The inner loop adds the char pointed by edx to the char pointed by Esi, Esi is the same as Edx from the beginning of the loop. From the beginning Edx points to the first char. Then it adds 5 to edx wich will make edx point 5 steps away, wich would be the 5th char during the first time we go through the loop. Eax is added with 5 and checked to see if it is greater than our string length, wich is stored at [Ebp-04]. If it isn't we will go through the loop again. Only that this time Edx has been added with 5 and is now pointing to the 5th position in our name.
If Eax was greater than the length, control will be given to the outer loop. The outer loop will increase Ebx and ebp-10, and compare ebx with 5 and at last jump back to the top of the loop. By increasing ebp-10 we will get the 2nd char loaded in edx at the top. And the inner loop will be given control only this time with the 2nd char as base.
(Un)Lucky as we are our name "Indian" is only 6 chars wich means that the inner loop will only loop once each time it's given control. Anyway when those crazy loops are finished our name will be transformed into:
00 DC C8 D2 C2 6EThe 1:st number is shifted left with four and then added by the second, it is then Xored with the namekey. Then the pointer to our numbers is incremented with two and points to the third number wich is Shifted Left with 4. This is done in a loop through out the Regcode.
Ax points at first number:SHL ax,04 Add ax,[ax+1] xor ax,name key (25) cmp cx, length of code inc ax,2 loop
The result is 37 16 04. These are substracted with 3,4,5. It starts with half the length of our false code and subtracts it with 1, = (6/2) -1 = 2. It's only to get how many numbers there are to be subtracted. In this case 3 (0,1,2). It's always substracted with 3 4 5 6 etc, result = 34 12 FF
This number is the start base number in this scheme. It is used for collect the index from a table. That index is then a pointer in a second table, wich is where the final code comes from. So if get the correct indexes we will get a code that match our transformed name
This is done by three routines. The first one is preparing the base number and the second one use the base number to calculate the index number. The third routine use the index to locate the final numbers in the final code.
1st routine is at: 420465 we name it Charlie The encryptor 2nd routine is at: 420A63 we name it Tom the bookie 3rd routine is at: 420724 we name it Smith the organizerLets see what our index numbers should be. Here is the last step in the third routine:
Dec ebx | | Mov ecx, [ebx*4+450240] ebx is our index number Shr ecx, 08 Mov [edx+eax], clIf you look inside table#2 (450240) you'll see that the index number should be the same as what we want our final number to be. Due to the Dec EBX (wich comes earlier) it'll need to be added with 1.
Charlie 's only task is to create a new base number, wich he gladly gives to Tom. Who use it to get another number , that he gets from a table. Tom's number is passed to Smith, who looks in another table from where he takes the final number. Charlie and Tom are cousins and they have some things in common, some numbers from Tom are passed to Charlie. Lets concentrate on how their relationship works.
This is what Charlie does::00420469 mov esi, 0044C8D4 ; number at 44C8D4 comes from Tom :0042046E mov edi, 0044A4C8 :00420473 mov ebp, 00452E40 :00420478 mov ecx, 00000008 :0042047D sub ecx, dword ptr [0044C8D8] ;Comes from Tom :00420483 mov edx, dword ptr [esi] :00420485 xor ebx, ebx :00420487 mov bl, byte ptr [edx+0044A4D0] :0042048D shl ebx, 10 :00420490 mov eax, dword ptr [esi] :00420492 xor edx, edx :00420494 mov dl, byte ptr [eax+0044A4D1] :0042049A movzx edx, dx :0042049D shl edx, 08 :004204A0 or ebx, edx :004204A2 movzx eax, byte ptr [eax+0044A4D2] :004204A9 or ebx, eax :004204AB shr ebx, cl :004204AD and ebx, 0000FFFF ; make sure that base# is <= FFFF :004204B3 mov dword ptr [edi], ebxCode from 420487 to 4204A9 is basically loading ebx with base# and then shift rights it with Cl. Cl get it's number from (8-number from Tom).
:00420A63 push ebp :00420A64 mov ebp, esp :00420A66 push ebx :00420A67 push esi :00420A68 mov edx, dword ptr [ebp+0C]; put 05 in edx :00420A6B mov esi, dword ptr [ebp+08]; put base# in esi :00420A6E and esi, 0000FFF0 ; zero out the lowest :00420A74 xor ebx, ebx ; Digit :00420A76 mov eax, dword ptr [ebp+10] ;Put table pointer :00420A79 jmp 00420A80 ;in Eax :00420A7B inc edx :00420A7C inc ebx :00420A7D add eax, 00000004 :00420A80 mov ecx, dword ptr [eax] ; is table value is >= Base# :00420A82 cmp ecx, esi *important* :00420A84 jbe 00420A7B :00420A86 mov eax, dword ptr [0044C8D8] :00420A8B add eax, edx :00420A8D mov ecx, eax :00420A8F shr ecx, 03 :00420A92 add dword ptr [0044C8D4], ecx ; Passed to Charlie :00420A98 and eax, 00000007 :00420A9B mov dword ptr [0044C8D8], eax ; Passed to Charlie :00420AA0 mov ecx, 00000010 :00420AA5 sub ecx, edx :00420AA7 test ebx, ebx :00420AA9 je 00420AB4 :00420AAB mov eax, dword ptr [ebp+10] :00420AAE mov eax, dword ptr [ebx*4+eax-04] ; *important* :00420AB2 jmp 00420AB6 :00420AB4 xor eax, eax :00420AB6 push eax :00420AB7 mov eax, esi :00420AB9 pop ebx :00420ABA sub eax, ebx :00420ABC shr eax, cl *important* :00420ABE mov ecx, dword ptr [ebp+14] :00420AC1 eax, dword ptr [edx*4+ecx] *important* :00420AC4 pop esi :00420AC5 pop ebx :00420AC6 pop ebp :00420AC7 ret
00420A82 a compare is made to see if the table value is greater than our base#. If it isn't we increase edx and ebx and get a new table value. Edx and ebx is important later, when they are used to navigate in table#1. If ebx !=0 we will load eax with [ebx*4+eax-04]. This value is substracted with our base# and the result is Shr with Cl, where Cl is 16-Edx. At last we will add a value from table#1, add eax, dword ptr [edx*4+ecx].
There are two values that Tom passes to Charlie that are important. Both of them are zero from the start. Value:Charlie main thing is: Ebx contains base# Sub ecx, dword ptr [0044C8D8] Shr Ebx,Cl And Ebx, 0000FFFFAnd Toms main thing is:
EAX=pointer to the first value in table#1, 01 And base# with FFF0 02 Cmp value from table#1 with base# 03 If lower then: Inc ebx Inc Edx ADD EAX,04 (Mov pointer to next value) JMP 2 04 Talk to Charlie and pass him values, 05 Sub 10,EDX EDX is depending on the Cmp at 02. 06 CMP EBX,EBX 07 JZ 08 08 MOV EAX, [EBX*4+EAX-04] put table value in EAX. 09 Sub Eax, base# 10 Shr result with CL 11 Add result with [edx*4+440E88]Now it's time to look at table#1 wich begins at 440E68
----Table #1 the first entries--------- 440E68: 2000 C000 E000 F000 440E78: F200 F200 F7E0 FFFF 440E88: 0000 0000 0000 0000 // Here is where Ecx is 440E98: 0000 0000 0004 002C // when[edx*4+ecx] is 440EA8: 003C 004C 0050 0050 // added to the Sum. 440EB8: 007F 1000 2400 8000 440EC8: C000 FA00 FFFF FFFF 440ED8: FFFF 0000 0000 0000 440EE8: 0000 0000 0000 0002 440EF8: 0007 0035 0075 00E9 440F08: 0000 0000 0800 2400 440F18: EE00 FE80 FFFF FFFF 440F28: FFFF 0000 0000 0000 440F38: 0000 0000 0000 0000 440F48: 0002 0010 00DA 00FB 440F58: 0000 0000 FF00 FFFF 440F68: FFFF FFFF FFFF FFFF 440F78: 0000 0000 0000 0000 440F88: 0000 0000 0000 0000 440F98: 0000 00FF 0000 0000 440FA8: 0000 0000 0000 0000 440FB8: 0000 0000 0000 0000 440FC8: 0000 0000 0000 0000 440FD8: 0000 0000 0000 0000 440FE8: 0000 0000 0000 0000 440FF8: 0000 0000 0000 0000 441008: 0000 0000 0000 0000 441018: 0000 0000 0000 0000 --------------------------------------
We have quite alot of material. The encryption contains alot elements, wich we need to include when we decrypt it. Let's concentrate on Tom and see what he needs to bring us the satisfactory return codes. While we investigate Tom we use the stripped down "version" of Tom. We do not care for now about Charlie, we only focus on Tom and what base# that will bring us the correct codes. The most important variables are set at 02 and 03.
02 Cmp value from table#1 with base# 03 If lower then: Inc ebx Inc Edx ADD EAX,04 (Mov pointer to next value) JMP 2
Both edx and ebx are used for indexing tables. ebx is used at 08 and edx at 11. Edx is 5 when we enter Tom's residence, and ebx is zero. So edx and ebx are depending on how big our base# is. They are tested against:
2000, C000, E000, F000, F200, F200, F7E0, FFFFOur base# cannot be larger than FFFF due to Charlies And 0000FFFF. Even if it could it wouldn't be a good idea to set it larger than FFFF, since no value in the table is greater than FFFF. Next up is:
05 Sub 10,EDX, Move sum to Ecx 08 MOV EAX, [EBX*4+EAX-04] 09 Sub Eax, base# 10 Shr result with CL
Here ebx's decides what value to Shr with Cl. This is semifinal stage, Cl gets it's value from 10h-Edx. And finally we will add the value at [edx*4+440E88] to our result.
11 Add result with [edx*4+440E88]Y=Edx , X=(10h-edx) V=[EBX*4+EAX-04] C=[edx*4+440E88], seed=sum of (Shr Eax, CL) - C, Z=Ebx
We need to substract C from our seed cause C will be added right after the Shr instruction. The relationship between these variable and our base# is:
base# = seed+VTo calculate the seed we need to reverse the formula. A shift right instruction is the same as dividing with 2^operand. This makes our seed formula:
Original: Reversed: Eax / 2^CL+C Eax = (desired # - C) * 2^CL
Base# must be < FFFF since Charlie won't give us any greater values. This means that we can quickly dismiss any Y values that are > FFFF. The condition is that seed+V must be smaller than FFFF. Here's a table of Tom's variables:
Z Y X C V 0 5 B 0 0 1 6 A 04 2000 2 7 9 2C C000 3 8 8 3C E000 4 9 7 4C F000 5 A 6 50 F200 6 B 5 50 F200 7 C 4 7F F7E0
Let's try this formula with our first number wich is 01, now that we have a clear view of the variables. (01-0)*2^11 = 800h (2048) 800+0= 800h. So one possible base# is 800 Infact numbers from 800 --- 0FA0 will give 01. This is because number between will give 1.xxx and somewhere after 0FA0 it will turn over to 2.0. If you been paying attention, you might have discovered that only the lower bytes are taken. That means that we can get away with number such as, xx01. This becomes important when we will investigate how to make Charlie give us some useful numbers. Now let's move on to next numbers. We can first dismiss all number that will give a base# >=FFFF.
(01-0)*2^11+0 = 800 (DDh-7F)*2^4+F7e0 = FDC0 (C9-7F)*2^4+F7E0 = FC80 (D3-7F)*2^4+F7E0 = FD20 (C3-7F)*2^4+F7e0 = FC20
Above is some examples of base# that will make Tom to give us correct index numbers and wich will lead to a correct serial# in Smith. Anyway none of the above base# are useful because of Charlie. He will need ONE number, that must work as a trigger for all the other numbers. So the main thing is the first base#, it must make Charlie spit out other base# that are correct and that will make Tom give us the correct index values. To try to find THE base# by hand using a calculator is really not a good approach, and if you would choose this approach you would probably make +Orc cry.
Tom has one weakness, he dont handle zeros very well. In fact when ever he get a zero as index value he get's depressed. When Tom is down he does the following:
Tom is called from 15 different locations. As long as Tom does not return any zeros, he will be called from 420537. Right after that call is a test made to see if that the index number is not zero.:00420568 test ebx, ebxTest made here :0042056A jne 00420579 :0042056C cmp dword ptr [edi], 00000FFF ;Edi points to :00420572 jbe 00420579 ;base# from Charlie 00420574 mov ebx, 00000100 :00420579 dec ebx :0042057A cmp ebx, FFFFFFFF :0042057D jne 004206EA
This code check if index# was zero, and if base# is < 0FFF. If base# is > 0FFF we jump with ebx=FF to 4206EA.
:004206EA add dword ptr [ebp+00], ebx Ebp+00 = 0035 :004206ED mov eax, dword ptr [ebp+00] :004206F0 shr eax, 08 :004206F3 sub dword ptr [ebp+00], eax :004206F6 add dword ptr [00452E5C], 00000010 :004206FD cmp dword ptr [00452E5C], 000000FF :00420707 jbe 00420719
The first time this part of the code is executed it adds FF to ebp+00 = FF35 then substracts it with 35 = CA35h. The second time FF will be added to CA35h and so on. The otherway is more complicated. Here's a short explaination:
This happens only if index# is zero and base# < 0FFF. A combination of charlie and Tom is executed. It first creates a new base# like Charlie and use the same variables ( [0044C8D8] and [0044C8D4]). Then it creates the values for [0044C8D8] and [0044C8D4]. This is done exactly like Tom does. This bazaar is done twice, with some checks inside then Tom is called. Then some similiar code "is.class" tppabs="http://Fravia.org/is.class" executed. Basically what this is doing is creating a new base# over and over again. When it has finished Charlie is called with this new base# to deal with. Then Tom is called and everything turns back to normal, execpt that Tom has another offset in his table. The offset is 440EBC. I didn't find this very interesting since it's just a combination of our fellows Charlie & Tom. Besides we really don't need to bother with this. The only time it is important to get an index# = 0 is if your name encryption ends up in FF. The chances of that are very small, the condition is
2*'char'+[ char+5] = FF ie 2*49+6D=FF Indiam=FFI didn't include any code of the above here since it you already seen it with in Charlie and Tom. If you still wanna know everything about the manipulation when index#=0, just study the deadlisting of winrar.
There are many ways of cracking this encryption. You could for example make a math function of this, however that would include quite advanced mathematical functions. The formula we reversed in tom isn't very useful. A useful formula must include both Charlies and Toms manipulations, otherwise it would be useless. When building the formula, only include the important things. Since this encryption is based upon tables we could make a graph out of it and then seek the corressponding formula.
Another and much faster way (at least for me) is to translate these pieces of code into C. Building up the encryption in C and then reverse it to produce a serial number. This is very easily done, and you'll probably get some fun out of it. I think that the new serial number protection will look like this one and that's why it would be a good idea to make a more or less generic decryptor program. You'll get some hints from Tom, the numbers passed to Charlie tells us how many numbers our Real code will hold.
You could also change your name so the index numbers will be easier to collect. Once you find the relations between Charlie and Tom, you can narrow down alot of possible base#.
I think this encryption is quite strong and safe against normal crackers. This doesn't mean that the protection scheme of winrar is strong or good. It has alot of blunders in it. The most obvious one is that it is depending on a simple set AL (see little johns essay) to determine unregistered/registered. Another blunder is the messagebox, there is no need to inform the user that the correct registration number was entered. The third misstake is the Unregistered text in the title window. It very easy to locate the changes in a title window and then work ones way up until the comparisment is made.
One way to make things a little harder for a cracker (though not much) is to write one byte to an innocent file when a registration# has been entered. The byte could be A to symbolize registered or F to symbolize unregistred. The writing should be hided among hundreds of writings to the same file. For example, after the compare has been made between correct# and entered#, write alot of garbage code that writes to a file using base indexed relative adressing, and somewhere one of them is writing the actual registered/unregisered flag. That should be written using a whole string like:
mov [ebx+offset], FF33F20F | | This F is the correct Flag for registered.
Among alot of very similiary instructions. This would delay the crack for some time depending on who's cracking it. The file should also be written to on every startup. Disguised protections seems to have dissappeared since the programmers switched to windows enviroment. This is most likely to come back. Also a very few program for windows are written in assembler, wich explains why many windows protection are weak.
Well for too long the serial number protections have looked the same. I
think it's going to change, considering that alot of windows programmers
are advancing in their art. But as long as they rely on a simple compare
to determine good_guy/Bad_guy they'll be very weak. If Winrar[95] hided
and disguised the compare, it would have been harder to crack it. At
least I'd like to believe so. Dos offeres many tough protections, my
guess why windows doesn't is that not many windows programmer knows how
to "translate" dos protections and approaches to windows code. Plus
that's there is probably more people who knows how to program DOS in
assembly than there is people who write windows code in asm.
/Email me at Indian_Trail for
question etc (Saddle all the horses far on the indian trail. Til
it's time to change the key and jump to a different scale........A
boogie woogie on the run).