首页
社区
课程
招聘
加密与解密随书光盘习题中的f0dder的Crackme #2学习笔记
2006-5-20 20:11 6404

加密与解密随书光盘习题中的f0dder的Crackme #2学习笔记

2006-5-20 20:11
6404
【软件名称】"加密与解密"随书光盘习题中的"f0dder的Crackme #2"
【应用平台】Win2000
【软件大小】46KB
【软件限制】Name/Keyfile
【破解声明】该文是在学习了Cronos,Lightning,C-Mendoza给出的Solution之后,经过我漫长的学习过程,终于理解之后写出的破文,希望给我这样的菜鸟一定的帮助.
【破解工具】OllyDbg v1.10
【软件简介】作者给出的介绍:Welcome to my SECOND crackme. Wooohooo, big thing.This project was started on 2000/01/03 (third january 2000for those fools who use stupid date formats), around 19:00,while having a severe case of influenza (that started sometimelast millenium (kinda, depending on whether you feel zero orone based), around 1999/12/25). Finished 2000/01/24 21:44.I'll try not to help too much, but I just can't help it,helping you out a little might be a little too much help,but help can be such a help when dealing with...well...Anyway, this shouldn't prove too difficult for all youcracking masters out there. This time, you have to makea keygen that produces a keyfile. Simple as that, simpleas eating cherry pie while driving without hands on aonewheeled bike. Perhaps easier, perhaps tougher, dependingon your skillz in various areas of life. If you can make itproduce the victory text without keyfiling it, just by doingan oldfashioned patch, I'll be happy to see it.(Is f0dder on acid or is that influenza really bad? Who cantell? Can f0dder tell? Will he tell you? Great unexplained mystery!).Perhaps this file is used by the crackme? Have you triedrenaming it? Deleting it? Changing it's contents? Do you DARE?Perhaps there's a builtin superstealthed flashbios erasingtripwire piece of code? Perhaps there isn't? Perhaps this pieceof software is not penguin friendly? Perhaps it's more of a sh33p
thing?IOW: HAVE FUN, dammit, and send me your keyfiles and keygens!(Ohyeah...in a real life situation, the keyfiles would havebeen different, more error tolerant, and easier to get to stupidcustomers via email. Nuff said, that might've been too much help).The pass for the source archive? Ah yes. If you reverse engineer thecrackme, getting an understanding of the code, you will KNOW thepassword. It's the first line of the text you see when you see thelight...which you will. (Umm, not the MessageBox text, but the textfrom the...well...secret place :-). Otherwise, you could always trybruteforcing a rather long password...whatever path you choose, Iwish you luck. (Bruteforcing the archive password will probablytake longer time than bruteforcing the protection).Updated 2000/02/12 (12. Feb 2000):  At last I found the correct linker switch (/OPT:NoWin98), reduced  executable file size from 60k to 47k. Should have just run the  linker from the cmdline rather than browsing around MSDN, it would  have saved me about 5 hours or so (but then I wouldn't have found  all those *other* intersting articles ;-)  Also, to be fair, I believe a little clarification on this crackme  is needed. To keyfile it, you WILL need bruteforcing. And perhaps  more bruteforcing than is feasible on most machines. There's no RSA  or other puke (you'll see how easy it is, though probably
timeconsuming).  I still recommend you to give it a try, even if you don't feel like  bruteforcing, for I have been told that this crackme is mighty
interesting.Updated 2000/03/08 (8. March 2000):  I've included a hint inside the executable now, that should help  you write a bruteforcer (ie, make it possible to "bruteforce" in  one second). You should notice this help when you get "far enough".  It might be too easy now, actually. But, then, you can always try  finishing it without accepting the word of help.signed f0dder, f0dder@yahoo.com
========================================================================================
【分析过程】
        在这里,我首先给出我分析这个程序的时间过程,2005年11月26日,我从"加密与解密"随书光盘的习题中,找到了这个练习,当时也作了十多个CrackMe了,心想应该没有太大问题,毕竟它只是一个CrackMe啊,而且,万一不行,还有Solution可以参考啊.结果,我非常失望,苦战一天一夜之后,放弃,结合"Cronos,Lightning,C-Mendoza"给出的Solution对照反汇编的程序看,我都没有理解该程序是怎么加密的.当时就想是不是本人在这方面天赋太差.应该放弃学习加密与解密.而且由于当时比较忙,就放弃了.
        时间转眼到了2006年4月,我在看雪论坛上注册,并问了个弱智的问题,"初学者怎样才能提高水平",大虾们告诉我,多练习,从CrackMe开始,我对此又开始了学习,从"加密与解密"随书光盘的习题开始,有空就做一个练习.当我2006年5月14日,我将习题的Serial部分的最后一个练习"Harlequin的Crackme #1"学习完了,感觉收获很大.2006年5月17日,我又开始分析f0dder的这个CrackMe,遗憾的是,经过一个不眠夜,我又失败了.不明白程序到底是怎么回事.
        2006年5月18日晚6点.我又一次"不务正业"(项目要求很紧,置领导要求于不顾),我又开始从头跟这个CrackMe的代码,这次,我心静下来,仔细分析,将作者的介绍和Cronos,Lightning,C-Mendoza给出的Solution从头看了一遍.在Cronos的Solution中发现了他引用作者的话
"Why do I use Self Modifying Code? Because it's fun to use
SMC, especially in the totally useless way I'm doing it.
......"
我这次才开始注意到原来作者使用了Self Modifying.下面给出我学习大虾们的Solution然后的分析过程. 为了尊重他人的劳动成果,我将我主要学习的Lightning的Solution先贴出来.
Fodder #2 crackme

This crackme was fun to crack. It does take alittle skill but there isn't anything directly
in the way to getting to what you want other than the fact that there is encrypted code. But
at the end I prove that the encrypted code can be made to say and do anything. I give it a
3 or 4 in difficulty.

This crackme's objective is to write a keygen that outputs a keyfile for the crackme. If we
run the crackme, we notice that it has a textbox to type in and clicking on the image will
produce a message.

First, I want to know what file it checks for for the keyfile before I got tracing thru a
ton of code. This way I know what i'm looking for. If we use FileMon we find out that it is
looking for Skeleton.key as a file and wants to read 256 bytes (create the file to find out
how many bytes it wants).

Open up the crackme in your debugger, I use W32Dasm so all references will be it's numbers.
Go directly to the code where the app starts, 00401493. We can check the header in W32Dasm
to see that it doesn't appear to have any modifications to the EXE which is nice. Header
modifications just make debugging a short pain until you undo them. 3 calls then a ret to
end the application. GetModuleHandleA, DialogBoxParamA, then ExitProcess. Only
DialogBoxParamA is interesting as it controls the main running. The 2nd param to it is a
push of 004013C0. This is where WndProc must be so lets go take a look.

At 004013C0 is a value moved into eax (the message value from windows), 4 checks with jumps
then a return. From top to bottom, it is checking WM_INITDIALOG, WM_SHOWWINDOW, WM_COMMAND,
and WM_CLOSE. If you don't know the values, look up the WM_* commands in Winuser.h (MSVC has
the header but it can also be looked up on the MSDN). WM_CLOSE shouldn't be doing much, lets
just verify that. Jump over to 00401488. A call to ExitProcess and a ret. That is fine. No
tricks on the close of the app then. WM_COMMAND hits anytime you do anything with the text
box or picture so we will go to it in a second. WM_INITDIALOG, let's see what stuff it is
doing at 004013EB.

004013EB doesn't appear to be doing much. A call to GetTickCount, load a picture based on
the tick count, send a dialog message then a call to 0040104C. At the very beginning of
0040104C is a mov to edi with a reference to an encrypted string. Looking a few lines down
though we see that 1 is subtracted from every letter in the string. This reveals
"skeleton.key". We must be nearing the code to read the file then. If you follow the call
you find right after the decryption you run into CreateFileA. A check to see if the file
opened then another call, this time to ReadFile of course. 0x100 bytes are read to 004030C0.
After that call is 1 more call. This is to CloseHandle which closes the file. The jmp after
the call is to return from WM_INITDIALOG.

The next WM_ command to hit should be WM_SHOWWINDOW. You can look but there isn't much
happening at 0040143A. This means that the real checks are at 00401464. The WM_COMMAND
function. A check to see if you clicked the picture or not. If you didn't then it exits,
otherwise it jumps to 00401151.

Now we are getting into some interesting code. Within a few assembly lines is a check to see
if the key file was read in or not. You should be passing over this jump. If not then go
create a skeleton.key file with a size of 256 bytes. The first call starts checking bytes
in the file. Jumping to 0040132E results in a message we don't want. After reading thru the
code, it should be obvious that it is reading 2 bytes at a time. these bytes are hex values
that are converted and moved into memory then the next pair is done. This means that the
whole key file is a bunch of hex values. Let's fill the skeleton.key file with all As (not
as hex but as text) and see where we go then.

The next call to 00401225 gets the value we put into the textbox. It is then compared to see
if the length is 16. If it isn't then it is copied over and over so it is (IE "abc" becomes
"abcabcabcabcabca"). After that the string is copied then we do alittle xor routine on it.
The routine is simple. Go thru 0x70 bytes (112 bytes), xor 1 byte with the byte after it
then store the result and move on. What is interesting is that the results stored is
directly after the string that is read from the textbox. This means that it starts xor'ing
the results to create more results.

5 calls total. Sure are alot of calls. The next call to goto is 00401291. Before we start
being confused, EDX is set to the converted skeleton.key file in memory. ESI is the values
generated in the above call and EDI is where to write the results to. It does do all this
backwards but there is no reason for it. "But wait, the above function only did 0x70 bytes
and this is going thru 0x80". Well, the string from the text box (after being expanded) is
another 0x10 bytes that it includes as you have to remember that the string from the text
box and the results follow each other.

The next call to check is at 004012BA. We take 0x0BADC0DE, xor al against the first byte
rol eax by 1 then do this for 0x80 more bytes. The result has to equal 0xFCC5A375 to pass.
The checks are almost done. The next check is at 004012DA.

There is a check to see if the byte at 004010CB equals C3. If it does then it exits. If not
then we go thru a loop. This loop decrypts 004010B0 with the data at 004032C0 (the key file
xor'd against the generated values). It goes thru the data backwards. It does reuse some of
the values due to the and and starting at 0x91. After it is decrypted, the jmp after all
the calls hits. This jump results in calling the decrypted code (note, the calls exit out
thru a message if something fails elsewhere so it doesn't call bad code). So. How can we
figure out the correct values? You can't without brute forcing the rar file. This is due to
the fact that the encrypted code could be just about anything. My goal, instead, is to write
a keygen that will create keyfiles that have "valid" code to run when it is decrypted. This
is possible because each byte does not rely on another byte and uses a simple xor. This
means we can force the encrypted code to come out as anything we want.

Attached is a keygen that will generate keyfiles. These keyfiles will result in a
messagebox being displayed with the text "Registered to <name here>". What people don't
think about is the fact that the encrypted code (due to the way it is encrypted) can be
forced to do anything (within a certain boundry of space). I could, for instance, cause it
to load up another file and process that file if I was clever in how I called the functions
that already exist in the exe. What is interesting to note is that any name or value entered
that is shorter than 16 characters can be duplicated over (until it is 16 characters long)
and still have it work. This means that "abc" and "abcabcabcabcabca" are the same as far as
the app is concerned.

Lightning

下面将我的学习和分析过程写出来:

该程序并没有加壳,因此程序代码非常清楚:
程序是一个对话框程序,其主框架程序如下:
00401493 >/$  68 00000000   PUSH 0                                   ; /pModule = NULL
00401498  |.  FF15 14204000 CALL DWORD PTR DS:[<&KERNEL32.GetModuleH>; \GetModuleHandleA
0040149E  |.  A3 B8304000   MOV DWORD PTR DS:[4030B8],EAX
004014A3  |.  31DB          XOR EBX,EBX
004014A5  |.  31C0          XOR EAX,EAX
004014A7  |.  50            PUSH EAX                                 ; /lParam => NULL
004014A8  |.  68 C0134000   PUSH crackme2.004013C0                   ; |DlgProc = crackme2.004013C0
004014AD  |.  53            PUSH EBX                                 ; |hOwner => NULL
004014AE  |.  68 65000000   PUSH 65                                  ; |pTemplate = 65
004014B3  |.  50            PUSH EAX                                 ; |hInst => NULL
004014B4  |.  FF15 2C204000 CALL DWORD PTR DS:[<&USER32.DialogBoxPar>; \DialogBoxParamA
004014BA  |.  50            PUSH EAX                                 ; /ExitCode
004014BB  \.  FF15 04204000 CALL DWORD PTR DS:[<&KERNEL32.ExitProces>; \ExitProcess
004014C1   .  C3            RETN
下面看看对话框的DlgProc-004013C0
004013C0   .  8B4424 08     MOV EAX,DWORD PTR SS:[ESP+8]
004013C4   .  3D 10010000   CMP EAX,110                              ;  WM_INITDIALOG
004013C9   .  74 20         JE SHORT crackme2.004013EB
004013CB   .  3D 18000000   CMP EAX,18                               ;  WM_SHOWWINDOW
004013D0   .  74 68         JE SHORT crackme2.0040143A
004013D2   .  3D 11010000   CMP EAX,111                              ;  WM_COMMAND
004013D7   .  0F84 87000000 JE crackme2.00401464
004013DD   .  3D 10000000   CMP EAX,10                               ;  WM_CLOSE
004013E2   .  0F84 A0000000 JE crackme2.00401488
004013E8   .  31C0          XOR EAX,EAX
004013EA   .  C3            RETN
在该函数中处理4个消息,WM_INITDIALOG,WM_SHOWWINDOW,WM_COMMAND和WM_CLOSE.
而对话框初始化的时候,会进行下面的操作:
004013EB   > \8B4424 04     MOV EAX,DWORD PTR SS:[ESP+4]
004013EF   .  A3 BC304000   MOV DWORD PTR DS:[4030BC],EAX
004013F4   .  FF15 08204000 CALL DWORD PTR DS:[<&KERNEL32.GetTic>; [GetTickCount
004013FA   .  C1E8 02       SHR EAX,2
004013FD   .  25 07000000   AND EAX,7
00401402   .  05 C8000000   ADD EAX,0C8
00401407   .  50            PUSH EAX                             ; /RsrcName
00401408   .  FF35 B8304000 PUSH DWORD PTR DS:[4030B8]           ; |hInst = 00400000
0040140E   .  FF15 38204000 CALL DWORD PTR DS:[<&USER32.LoadBitm>; \LoadBitmapA
00401414   .  50            PUSH EAX                             ; /lParam
00401415   .  68 00000000   PUSH 0                               ; |wParam = 0
0040141A   .  68 72010000   PUSH 172                             ; |Message = STM_SETIMAGE
0040141F   .  68 E9030000   PUSH 3E9                             ; |ControlID = 3E9 (1001.)
00401424   .  FF35 BC304000 PUSH DWORD PTR DS:[4030BC]           ; |hWnd = 000E0378 ('f0dder's crackme #2',class='#32770')
0040142A   .  FF15 34204000 CALL DWORD PTR DS:[<&USER32.SendDlgI>; \SendDlgItemMessageA
00401430   .  E8 17FCFFFF   CALL crackme2.0040104C
程序将根据程序启动的当前时间(The GetTickCount function retrieves the number of milliseconds that have elapsed since the system was started. It is limited to the resolution of the system timer. ),来选择一副图片,然后设置图片.
然后CALL crackme2.0040104C,在该函数中将读取KeyFile的内容,代码分析如下:
0040104C   $  BF A7304000   MOV EDI,crackme2.004030A7            ;  ASCII "skeletoo/lfz"
00401051   .  B1 03         MOV CL,3
00401053   >  FE0F          DEC BYTE PTR DS:[EDI]
00401055   .  FE4F 01       DEC BYTE PTR DS:[EDI+1]
00401058   .  FE4F 02       DEC BYTE PTR DS:[EDI+2]
0040105B   .  FE4F 03       DEC BYTE PTR DS:[EDI+3]
0040105E   .  83C7 04       ADD EDI,4
00401061   .  FEC9          DEC CL
00401063   .^ 75 EE         JNZ SHORT crackme2.00401053
00401065   .  8D47 F4       LEA EAX,DWORD PTR DS:[EDI-C]
00401068   .  E8 F3020000   CALL crackme2.00401360               ; 打开"skeleton.key"文件,判断文件是否存在
0040106D   .  09C0          OR EAX,EAX
0040106F   .  74 27         JE SHORT crackme2.00401098
00401071   .  A3 A3304000   MOV DWORD PTR DS:[4030A3],EAX
00401076   .  BB C0304000   MOV EBX,crackme2.004030C0
0040107B   .  B9 00010000   MOV ECX,100
00401080   .  E8 12030000   CALL crackme2.00401397               ;读取256个字节的内容,读取内容存放在004030C0处
00401085   .  3D 00010000   CMP EAX,100
0040108A   .  75 0C         JNZ SHORT crackme2.00401098          ;判断是否读取了256个字节的内容
0040108C   .  A1 A3304000   MOV EAX,DWORD PTR DS:[4030A3]
00401091   .  E8 F9020000   CALL crackme2.0040138F               ;关闭文件句柄
00401096   .  EB 07         JMP SHORT crackme2.0040109F
00401098   >  C605 A2304000>MOV BYTE PTR DS:[4030A2],0           ;对话框初始化程序读取文件是否成功标志,0为失败,1为成功
0040109F   > \C3            RETN

通过简单分析,知道必须有一个skeleton.key文件,而且该文件至少为256个字节.
建立skeleton.key文件.并利用WinHex,填充256个字节的"1".重新运行程序,输入用户名"laowang",然后在观察对话框程序处理WM_COMMAND消息的代码:
00401464   > \8B4424 10     MOV EAX,DWORD PTR SS:[ESP+10]
00401468   .  85C0          TEST EAX,EAX
0040146A   .  74 23         JE SHORT crackme2.0040148F
0040146C   .  8B4424 0C     MOV EAX,DWORD PTR SS:[ESP+C]
00401470   .  66:3D E903    CMP AX,3E9
00401474   .  74 02         JE SHORT crackme2.00401478
00401476   .  EB 17         JMP SHORT crackme2.0040148F
00401478   >  C1E8 10       SHR EAX,10
0040147B   .  66:3D 0000    CMP AX,0
0040147F   .  75 0E         JNZ SHORT crackme2.0040148F
00401481   .^ E9 CBFCFFFF   JMP crackme2.00401151
00401486   .  EB 07         JMP SHORT crackme2.0040148F
00401488   >  50            PUSH EAX                                  ; /ExitCode
00401489   .  FF15 04204000 CALL DWORD PTR DS:[<&KERNEL32.ExitProcess>; \ExitProcess
0040148F   >  31C0          XOR EAX,EAX
00401491   .  40            INC EAX
00401492   .  C3            RETN
在0040147F处下断点,因为前面的代码都走向了0040148F处,而该处显然不是程序处理注册的地方。
然后点击图片。程序如你所愿的断了下来。
单步跟踪,程序来到下面的地方:
00401151   ?  50            PUSH EAX
00401152   .  B8 00104000   MOV EAX,crackme2.00401000                 ;  ASCII "You cannot seem to focus on the little devil as he runs merrily around you."
00401157   .  B8 A2304000   MOV EAX,crackme2.004030A2
0040115C   .  8038 01       CMP BYTE PTR DS:[EAX],1                   ;  对话框初始化阶段读取KeyFile是否成功标志
0040115F   .  0F85 BD010000 JNZ crackme2.00401322
00401165   .  58            POP EAX
00401166   .  E8 1A000000   CALL crackme2.00401185
0040116B   .  E8 B5000000   CALL crackme2.00401225
00401170   .  E8 1C010000   CALL crackme2.00401291
00401175   .  E8 40010000   CALL crackme2.004012BA
0040117A   .  E8 5B010000   CALL crackme2.004012DA
0040117F   .^ E9 2CFFFFFF   JMP crackme2.004010B0
00401184   ?  C3            RETN

程序首先检查对话框初始化阶段读取KeyFile是否成功,如果失败则弹出错误对话框,否则,进行5个函数的调用,然后JMP到004010B0。
下面具体分析每一个函数。
(1)00401185函数如下:
00401185   $  BE C0304000   MOV ESI,crackme2.004030C0                 ; 读取文件的内容,长度为256个字节
0040118A   .  BF C0314000   MOV EDI,crackme2.004031C0
0040118F   .  31C9          XOR ECX,ECX
00401191   .  B1 80         MOV CL,80
00401193   >  8A06          MOV AL,BYTE PTR DS:[ESI]
00401195   .  3C 30         CMP AL,30                                 ;  Switch (cases 30..46)
00401197   .  0F82 91010000 JB crackme2.0040132E
0040119D   .  3C 39         CMP AL,39
0040119F   .  0F87 04000000 JA crackme2.004011A9
004011A5   .  2C 30         SUB AL,30
004011A7   .  EB 12         JMP SHORT crackme2.004011BB
004011A9   >  3C 41         CMP AL,41
004011AB   .  0F82 7D010000 JB crackme2.0040132E
004011B1   .  3C 46         CMP AL,46
004011B3   .  0F87 75010000 JA crackme2.0040132E
004011B9   .  2C 37         SUB AL,37                                 ;  Cases 41 ('A'),42 ('B'),43 ('C'),44 ('D'),45 ('E'),46 ('F') of switch 00401195
004011BB   >  88C4          MOV AH,AL                                 ;  Cases 30 ('0'),31 ('1'),32 ('2'),33 ('3'),34 ('4'),35 ('5'),36 ('6'),37 ('7'),38 ('8'),39 ('9') of switch 00401195
004011BD   .  C0E4 04       SHL AH,4
004011C0   .  8A46 01       MOV AL,BYTE PTR DS:[ESI+1]
004011C3   .  3C 30         CMP AL,30                                 ;  Switch (cases 30..46)
004011C5   .  0F82 63010000 JB crackme2.0040132E
004011CB   .  3C 39         CMP AL,39
004011CD   .  0F87 04000000 JA crackme2.004011D7
004011D3   .  2C 30         SUB AL,30
004011D5   .  EB 12         JMP SHORT crackme2.004011E9
004011D7   >  3C 41         CMP AL,41
004011D9   .  0F82 4F010000 JB crackme2.0040132E
004011DF   .  3C 46         CMP AL,46
004011E1   .  0F87 47010000 JA crackme2.0040132E
004011E7   .  2C 37         SUB AL,37                                 ;  Cases 41 ('A'),42 ('B'),43 ('C'),44 ('D'),45 ('E'),46 ('F') of switch 004011C3
004011E9   >  08C4          OR AH,AL                                  ;  Cases 30 ('0'),31 ('1'),32 ('2'),33 ('3'),34 ('4'),35 ('5'),36 ('6'),37 ('7'),38 ('8'),39 ('9') of switch 004011C3
004011EB   .  8827          MOV BYTE PTR DS:[EDI],AH
004011ED   .  46            INC ESI
004011EE   .  46            INC ESI
004011EF   .  47            INC EDI
004011F0   .  FEC9          DEC CL
004011F2   .^ 75 9F         JNZ SHORT crackme2.00401193
程序首先判断文件中每个字符是否都属于{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}这个字符集。如果文件中有字符不属于这个字符集。则JMP到0040132E处弹出错误对话框。
上面程序,将文件的256个字节两两组合为128个字节,存储在004031C0处开始的128个字节长的地址中,例如文件内容为“5634”,则组合成0x56和0x34 这两个字节。上面程序如果用C语言描述如下:primFile为文件内容,fileContent为新组合生成的128个字节的变量。
for(i=0;i<0x80;i++)
{
        temp[0]=primFile[i*2];
        if(temp[0]<=0x39)
                temp[0]-=0x30;
        else
                temp[0]-=0x37;
               
        temp[1]=primFile[i*2+1];
        if(temp[1]<=0x39)
                temp[1]-=0x30;
        else
                temp[1]-=0x37;       
        __asm
        {
                mov AH,temp[0]
                SHL AH,4
                mov AL,temp[1]
                OR  AH,AL
                movzx eax,AH
                mov temp1,eax   
        }
        fileContent[i]=temp1;
}
(2)00401225处函数如下:
00401225   $  68 11000000   PUSH 11                                   ; /Count = 11 (17.)
0040122A   .  68 40334000   PUSH crackme2.00403340                    ; |Buffer = crackme2.00403340
0040122F   .  68 E8030000   PUSH 3E8                                  ; |ControlID = 3E8 (1000.)
00401234   .  FF35 BC304000 PUSH DWORD PTR DS:[4030BC]                ; |hWnd = 001A033C ('f0dder's crackme #2',class='#32770')
0040123A   .  FF15 24204000 CALL DWORD PTR DS:[<&USER32.GetDlgItemTex>; \GetDlgItemTextA
00401240   .  3D 10000000   CMP EAX,10
00401245   .  0F87 E3000000 JA crackme2.0040132E
0040124B   .  74 1C         JE SHORT crackme2.00401269
0040124D   .  BE 40334000   MOV ESI,crackme2.00403340
00401252   .  BF 40334000   MOV EDI,crackme2.00403340
00401257   .  01C7          ADD EDI,EAX
00401259   .  2D 10000000   SUB EAX,10
0040125E   .  F7D8          NEG EAX
00401260   >  8A16          MOV DL,BYTE PTR DS:[ESI]
00401262   .  8817          MOV BYTE PTR DS:[EDI],DL
00401264   .  46            INC ESI
00401265   .  47            INC EDI
00401266   .  48            DEC EAX
00401267   .^ 75 F7         JNZ SHORT crackme2.00401260
00401269   >  BE 40334000   MOV ESI,crackme2.00403340
0040126E   .  BF 40324000   MOV EDI,crackme2.00403240
00401273   .  B9 04000000   MOV ECX,4
00401278   .  F3:A5         REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:>
0040127A   .  B9 70000000   MOV ECX,70
0040127F   .  BE 40324000   MOV ESI,crackme2.00403240
00401284   >  8A06          MOV AL,BYTE PTR DS:[ESI]
00401286   .  3246 01       XOR AL,BYTE PTR DS:[ESI+1]
00401289   .  8807          MOV BYTE PTR DS:[EDI],AL
0040128B   .  46            INC ESI
0040128C   .  47            INC EDI
0040128D   .  49            DEC ECX
0040128E   .^ 75 F4         JNZ SHORT crackme2.00401284
00401290   .  C3            RETN

程序获取你输入的用户名称,如果长度大于0x10,则弹出错误对话框,否则将其补充到16个字节长的用户名称,存放在403340地址处。然后进行0x70次运算,将结果保存在0x403250开始的地址处,而0x403240地址存放的为补充到16个字节长的用户名称,将从0x403240地址处开始的0x80个字节长的内容记作MiddleBuff[0x80]。运算采用C语言描述如下:
        //get the length of the name
        NameLen = strlen(RegName);

        //blank the middle buffer out
        memset(MiddleBuff, 0, 0x80);

        //copy the name until it is 16 chars long
        memcpy(MiddleBuff, &RegName, NameLen);
        if(NameLen < 16)
        {
                Counter = 16 / NameLen;
                Counter++;
                while(Counter)
                {
                        memcpy(&MiddleBuff[NameLen * Counter], RegName, NameLen);
                        Counter--;
                }
        }

        //generate one of the buffers used based on the name/key passed in
        for(Counter=0; Counter < 0x70; Counter++)
                MiddleBuff[Counter+0x10] = MiddleBuff[Counter] ^ MiddleBuff[Counter + 1];

(3)401291处的函数如下所示:
00401291  /$  BE 40324000   MOV ESI,crackme2.00403240
00401296  |.  BA C0314000   MOV EDX,crackme2.004031C0
0040129B  |.  BF C0324000   MOV EDI,crackme2.004032C0
004012A0  |.  B9 80000000   MOV ECX,80
004012A5  |>  8A440E FF     MOV AL,BYTE PTR DS:[ESI+ECX-1]                   ;  根据用户名生成的0x80个长度的字节,记为 MiddleBuff
004012A9  |.  32440A FF     XOR AL,BYTE PTR DS:[EDX+ECX-1]                   ;  根据文件内容生成的0x80长度的字节,记为fileContent
004012AD  |.  88440F FF     MOV BYTE PTR DS:[EDI+ECX-1],AL
004012B1  |.  49            DEC ECX
004012B2  |.^ 75 F1         JNZ SHORT crackme2.004012A5
004012B4  |.  B8 F5114000   MOV EAX,crackme2.004011F5                        ;  ASCII "Uif@ipvtf3pg5vtifs3@hfu2juFbu$Qspkfdu[Hvufocfsh"
004012B9  \.  C3            RETN

程序将前面(1)和(2)函数生成的结果进行异或运算,用C语言描述如下:
for(Counter=0x7F; Counter >= 0; Counter--)
                KeyFile[Counter] = MiddleBuff[Counter] ^ fileContent[Counter];
(4)4012BA处的函数如下:
004012BA   $  BE C0324000   MOV ESI,crackme2.004032C0
004012BF   .  B9 80000000   MOV ECX,80
004012C4   .  A1 9A304000   MOV EAX,DWORD PTR DS:[40309A]                    ;  设置EAX初始值
004012C9   >  3206          XOR AL,BYTE PTR DS:[ESI]
004012CB   .  D1C0          ROL EAX,1
004012CD   .  46            INC ESI
004012CE   .  49            DEC ECX
004012CF   .^ 75 F8         JNZ SHORT crackme2.004012C9
004012D1   .  3B05 9E304000 CMP EAX,DWORD PTR DS:[40309E]                    ;  关键判断,判断EAX与0xFCC5A375是否相等
004012D7   .  75 61         JNZ SHORT crackme2.0040133A
004012D9   .  C3            RETN

程序比较简单,将(3)运算的结果进行运算,然后判断EAX与0xFCC5A375是否相等,如果不相等则认为注册错误。运算过程用C语言描述如下:
        MagicValue = 0x0BADC0DE;
        __asm
        {
                mov ecx, 0x80
                mov eax,0x0BADC0DE
                lea ebx, KeyFile
        GenMagic:
                xor al, [ebx]
                rol eax, 1
                inc ebx
                dec ecx
                jnz GenMagic
                mov MagicValue,eax

        }

最开始,我将程序分析到此,心中非常高兴,就没有往下分析,认为只要能够在004012D1进行判断时,能够判断正确就行了.而根据算法,我首先设置文件内容都为'1',然后,最后程序修改文件中的后32个字节,就行了(因为在上面运算中将EAX每次运算后都进行了rol运算,这样无论KeyFile前面0x60内容是什么,都可以修改KeyFile的后32个字节,使得运算结果与参考值相等).为了给我这样的菜鸟一定的帮助,我将当时写的程序放上来.

int main()
{
        char KeyFile[0x80];
        char primFile[0x100];
        char MiddleBuff[0x80];
        int fileContent[0x80];
        char temp[3];
        char        RegName[50];
        long        NameLen;
        long        Counter=0;
        long        MagicValue;
        char        HexConvert;
        HANDLE        FileHandle;
        unsigned long temp1;
        int     i;
        for(i=0;i<0x100;i++)
                primFile[i]=0x31;
        for(i=0;i<0x80;i++)
        {
                temp[0]=primFile[i*2];
                if(temp[0]<=0x39)
                        temp[0]-=0x30;
                else
                        temp[0]-=0x37;
                //ah=temp[0];
                temp[1]=primFile[i*2+1];
                if(temp[1]<=0x39)
                        temp[1]-=0x30;
                else
                        temp[1]-=0x37;       
                __asm
                {
                        mov AH,temp[0]
                        SHL AH,4
                        mov AL,temp[1]
                        OR  AH,AL
                        movzx eax,AH
                        mov temp1,eax   
                }
                fileContent[i]=temp1;
        }

        while(1)
        {
                printf("KeyGen for F0dder's #2 Crackme");
                printf("\n\nRegistered Name: ");
                scanf("%[^\n\0]s", RegName);
                if(strlen(RegName) > 16)
                {
                        printf("\nName must be shorter than 16 characters");
                        continue;
                }

                if(strlen(RegName) == 0)
                {
                        printf("\n\nNo name entered. Exiting.");
                        printf("\nCtrl+C to close");
                        while(1)
                                Counter++;
                }

                break;
        }

        //get the length of the name
        NameLen = strlen(RegName);

        //blank the middle buffer out
        memset(MiddleBuff, 0, 0x80);

        //copy the name until it is 16 chars long
        memcpy(MiddleBuff, &RegName, NameLen);
        if(NameLen < 16)
        {
                Counter = 16 / NameLen;
                Counter++;
                while(Counter)
                {
                        memcpy(&MiddleBuff[NameLen * Counter], RegName, NameLen);
                        Counter--;
                }
        }

        //generate one of the buffers used based on the name/key passed in
        for(Counter=0; Counter < 0x70; Counter++)
                MiddleBuff[Counter+0x10] = MiddleBuff[Counter] ^ MiddleBuff[Counter + 1];

        for(Counter=0x7F; Counter >= 0; Counter--)
                KeyFile[Counter] = MiddleBuff[Counter] ^ fileContent[Counter];

        MagicValue = 0x0BADC0DE;
        __asm
        {
                mov ecx, 0x60
                mov eax,0x0BADC0DE
                lea ebx, KeyFile
        GenMagic:
                xor al, [ebx]
                rol eax, 1
                inc ebx
                dec ecx
                jnz GenMagic

                lea ebx, [KeyFile + 60h]
                mov edi, 0xFCC5A375
                mov ecx, 0x20

        FixMagic:
                mov edx, edi
                mov dh, al
                and dh, 1
                and dl, 1
                cmp dh, dl
                xor dh,dl
                mov [ebx],dh
                inc ebx
                dec ecx
                rol eax, 1
                rol edi, 1
                jne FixMagic
        }
               
        //the last step should be to take the KeyFile buffer and the MiddleBuff and xor em together
        for(Counter=0; Counter < 0x80; Counter++)
                KeyFile[Counter] = KeyFile[Counter] ^ MiddleBuff[Counter];

        //now the final step. convert each char to hex to put out to the file.
        MagicValue = 0;
        for(Counter=0; Counter < 0x80; Counter++, MagicValue++)
        {
                HexConvert = (KeyFile[Counter] >> 4) & 0x0F;
                if(HexConvert > 0x09)
                        MiddleBuff[MagicValue] = HexConvert + 0x37;
                else
                        MiddleBuff[MagicValue] = HexConvert + 0x30;

                MagicValue++;
                HexConvert = KeyFile[Counter] & 0xF;
                if(HexConvert > 0x09)
                        MiddleBuff[MagicValue] = HexConvert + 0x37;
                else
                        MiddleBuff[MagicValue] = HexConvert + 0x30;

        }
        FileHandle = CreateFile("skeleton.key", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
        if((long)FileHandle == 0xFFFFFFFF)
                printf("Error opening skeleton.key file");
        else
        {
                WriteFile(FileHandle, MiddleBuff, 0x100, (unsigned long*)&Counter, 0);
                CloseHandle(FileHandle);
                printf("Keyfile generated.");
        }

        printf("\n\nCtrl+C to close");
        while(1)
                Counter++;

        return 0;

}

然而这样生成的KeyFile,虽然能够在004012D1判断正确,然而程序运行后报运行时错误.

下面只能继续分析了.

(5) 4012DA处函数如下:
004012DA  /$  803D CB104000>CMP BYTE PTR DS:[4010CB],0C3
004012E1  |.  74 21         JE SHORT crackme2.00401304
004012E3  |.  BE C0324000   MOV ESI,crackme2.004032C0
004012E8  |.  BF B0104000   MOV EDI,crackme2.004010B0
004012ED  |.  B9 91000000   MOV ECX,91
004012F2  |>  89CA          /MOV EDX,ECX
004012F4  |.  81E2 7F000000 |AND EDX,7F
004012FA  |.  8A0416        |MOV AL,BYTE PTR DS:[ESI+EDX]
004012FD  |.  30440F FF     |XOR BYTE PTR DS:[EDI+ECX-1],AL
00401301  |.  49            |DEC ECX
00401302  |.^ 75 EE         \JNZ SHORT crackme2.004012F2
00401304  \>  C3            RETN

原来这里就是作者提到的"Why do I use Self Modifying Code? Because it's fun to use
SMC, especially in the totally useless way I'm doing it.
......"
我惊呼一声My God!
程序将004010B0处0x91个字节与前面(3)生成的KeyFile进行异或运算,而由于KeyFile只有0x80个字节长,因此实际上只有0x80个字节有意义.

下面我们先看一下程序执行完该函数后干点什么.
0040117F   .^\E9 2CFFFFFF   JMP crackme2.004010B0
00401184   ?  C3            RETN
原来修改完004010B0处0x91个字节内容后,程序就跳到004010B0处执行你修改好的内容.终于明白奥妙所在,精神为之振奋.再回头看Lightning给出的Solution中的程序,我才明白了他定义的EncryptedBuff[0x80]变量的意义.
char EncryptedBuff[0x80] =
{
    (char)0x0D, (char)0x20, (char)0x73, (char)0x61,
    (char)0x74, (char)0x48, (char)0xA8, (char)0x7F,
    (char)0x37, (char)0x6E, (char)0x53, (char)0xC5,
    (char)0x71, (char)0x2E, (char)0x64, (char)0xDF,
    (char)0x53, (char)0xD3, (char)0x42, (char)0x60,
    (char)0x73, (char)0x90, (char)0x78, (char)0x45,
    (char)0x00, (char)0x2D, (char)0x6F, (char)0xAE,
    (char)0x3C, (char)0x01, (char)0x01, (char)0x53,
    (char)0x5B, (char)0x64, (char)0x64, (char)0x5B,
    (char)0x48, (char)0x04, (char)0x05, (char)0x02,
    (char)0x48, (char)0x1C, (char)0x1C, (char)0x00,
    (char)0x10, (char)0x02, (char)0x0E, (char)0x08,
    (char)0x0E, (char)0x45, (char)0x1C, (char)0x4E,
    (char)0x74, (char)0x7B, (char)0x48, (char)0x26,
    (char)0x00, (char)0x02, (char)0x0F, (char)0x0E,
    (char)0x00, (char)0x16, (char)0x45, (char)0x01,
    (char)0x18, (char)0x4F, (char)0x06, (char)0x45,
    (char)0x1A, (char)0x0C, (char)0x04, (char)0x4E,
    (char)0x5B, (char)0x49, (char)0x15, (char)0x48,
    (char)0x43, (char)0x0E, (char)0x4E, (char)0x17,
    (char)0x10, (char)0x00, (char)0x1E, (char)0x0C,
    (char)0x1C, (char)0x47, (char)0x48, (char)0x09,
    (char)0x15, (char)0x04, (char)0x46, (char)0x42,
    (char)0x06, (char)0x08, (char)0x1B, (char)0x7D,
    (char)0x3E, (char)0x1C, (char)0x16, (char)0x0C,
    (char)0x7E, (char)0x66, (char)0x09, (char)0x18,
    (char)0x09, (char)0x12, (char)0x48, (char)0x4F,
    (char)0x12, (char)0x48, (char)0x04, (char)0x57,
    (char)0x01, (char)0x5C, (char)0x41, (char)0x47,
    (char)0x3C, (char)0x1B, (char)0x5E, (char)0x45,
    (char)0x18, (char)0x11, (char)0x49, (char)0x00,
    (char)0x1E, (char)0x09, (char)0x07, (char)0x32
};
重新运行程序,察看004010B0处的代码:
004010B0   > /0D 20736174              OR EAX,74617320
004010B5   . |48                       DEC EAX
004010B6   . |A8 7F                    TEST AL,7F
004010B8   . |37                       AAA
004010B9   . |6E                       OUTS DX,BYTE PTR ES:[EDI]                        ;  I/O 命令
004010BA   . |53                       PUSH EBX
004010BB   . |C571 2E                  LDS ESI,FWORD PTR DS:[ECX+2E]                    ;  修正的段位寄存器
004010BE   . |64:DF53 D3               FIST WORD PTR FS:[EBX-2D]
004010C2   . |42                       INC EDX
004010C3   . |60                       PUSHAD
004010C4   .^|73 90                    JNB SHORT crackme2.00401056
004010C6   . |78 45                    JS SHORT crackme2.0040110D
004010C8     |00                       DB 00
004010C9     |2D                       DB 2D                                            ;  CHAR '-'
004010CA     |6F                       DB 6F                                            ;  CHAR 'o'
004010CB     |AE                       SCAS BYTE PTR ES:[EDI]
004010CC     |3C                       DB 3C                                            ;  CHAR '<'
004010CD     |01                       DB 01
004010CE     |01                       DB 01
004010CF     |53                       DB 53                                            ;  CHAR 'S'
004010D0     |5B                       DB 5B                                            ;  CHAR '['
004010D1     |64                       DB 64                                            ;  CHAR 'd'
004010D2     |64                       DB 64                                            ;  CHAR 'd'
004010D3     |5B                       DB 5B                                            ;  CHAR '['
004010D4     |48                       DB 48                                            ;  CHAR 'H'
004010D5     |04                       DB 04
004010D6     |05                       DB 05
004010D7     |02                       DB 02
004010D8     |48                       DB 48                                            ;  CHAR 'H'
004010D9     |1C                       DB 1C
004010DA     |1C                       DB 1C
004010DB     |00                       DB 00
004010DC     |10                       DB 10
004010DD     |02                       DB 02
004010DE     |0E                       DB 0E
004010DF     |08                       DB 08
004010E0     |0E                       DB 0E
004010E1     |45                       DB 45                                            ;  CHAR 'E'
004010E2     |1C                       DB 1C
004010E3   . |4E 74 7B 48 26 00        ASCII "Nt{H&",0
004010E9     |02                       DB 02
004010EA     |0F                       DB 0F
004010EB     |0E                       DB 0E
004010EC     |00                       DB 00
004010ED     |16                       DB 16
004010EE     |45                       DB 45                                            ;  CHAR 'E'
004010EF     |01                       DB 01
004010F0     |18                       DB 18
004010F1     |4F                       DB 4F                                            ;  CHAR 'O'
004010F2     |06                       DB 06
004010F3     |45                       DB 45                                            ;  CHAR 'E'
004010F4     |1A                       DB 1A
004010F5     |0C                       DB 0C
004010F6     |04                       DB 04
004010F7     |4E                       DB 4E                                            ;  CHAR 'N'
004010F8     |5B                       DB 5B                                            ;  CHAR '['
004010F9     |49                       DB 49                                            ;  CHAR 'I'
004010FA     |15                       DB 15
004010FB     |48                       DB 48                                            ;  CHAR 'H'
004010FC     |43                       DB 43                                            ;  CHAR 'C'
004010FD     |0E                       DB 0E
004010FE     |4E                       DB 4E                                            ;  CHAR 'N'
004010FF     |17                       DB 17
00401100     |10                       DB 10
00401101     |00                       DB 00
00401102     |1E                       DB 1E
00401103     |0C                       DB 0C
00401104     |1C                       DB 1C
00401105     |47                       DB 47                                            ;  CHAR 'G'
00401106     |48                       DB 48                                            ;  CHAR 'H'
00401107     |09                       DB 09
00401108     |15                       DB 15
00401109     |04                       DB 04
0040110A     |46                       DB 46                                            ;  CHAR 'F'
0040110B     |42                       DB 42                                            ;  CHAR 'B'
0040110C     |06                       DB 06
0040110D   > |081B                     OR BYTE PTR DS:[EBX],BL
0040110F   . |7D 3E                    JGE SHORT crackme2.0040114F
00401111   . |1C 16                    SBB AL,16
00401113   . |0C 7E                    OR AL,7E
00401115   . |66:0918                  OR WORD PTR DS:[EAX],BX
00401118   . |0912                     OR DWORD PTR DS:[EDX],EDX
0040111A   . |48                       DEC EAX
0040111B   . |4F                       DEC EDI
0040111C   . |1248 04                  ADC CL,BYTE PTR DS:[EAX+4]
0040111F   . |57                       PUSH EDI
00401120   . |015C41 47                ADD DWORD PTR DS:[ECX+EAX*2+47],EBX
00401124   . |3C 1B                    CMP AL,1B
00401126   . |5E                       POP ESI
00401127   . |45                       INC EBP
00401128   . |1811                     SBB BYTE PTR DS:[ECX],DL
0040112A   . |49                       DEC ECX
0040112B   . |001E                     ADD BYTE PTR DS:[ESI],BL
0040112D   . |0907                     OR DWORD PTR DS:[EDI],EAX
0040112F   . |3209                     XOR CL,BYTE PTR DS:[ECX]

因为这部分代码是将来需要根据文件内容进行自修改的代码,将其取出来记为EncryptedBuff[0x80].

Ligntening在他的Solution中说了"But at the end I prove that the encrypted code can be made to say and do anything."
也就是说,至于这部分内容解密后的内容,可以是你想让它干什么,你就将其解密为什么代码,为了简单,我决定学习Ligntening的做法,在注册正确后弹出一个标题为"Cracked By Laowang-)",内容为"Registered to username"的对话框,而这样就需要知道一点点一点点一点点逆向的知识,
我是菜鸟,不知道我的说法准确与否,请大虾指点.
由于004012DA  /$  803D CB104000>CMP BYTE PTR DS:[4010CB],0C3如果4010CB为0xC3,则解密完成一次之后,就不需要每次都解密了,因此,我们决定004010CB为     |C3                   RETN
通过简单试验,并参考Ligntening的Solution,可以确定要向实现上述功能的004010B0前面的代码需要解密为:
004010B0   > /E8 00000000          CALL crackme2.004010B5
004010B5   . |6A 00                PUSH 0
004010B7   ? |68 CC104000          PUSH crackme2.004010CC                  ;  ASCII "Cracked by Laowang-)"
004010BC   ? |68 E1104000          PUSH crackme2.004010E1                  ;  ASCII "Registered to laowanghai"
004010C1   ? |E9 4E020000          JMP crackme2.00401314
004010C6   . |90                   NOP
004010C7   ? |90                   NOP
004010C8     |90                   DB 90
004010C9     |90                   DB 90
004010CA     |90                   DB 90
004010CB     |C3                   RETN                                    ; 注意此处为0xC3
004010CC     |43                   DB 43                                   ;  CHAR 'C'
004010CD     |72                   DB 72                                   ;  CHAR 'r'
004010CE     |61                   DB 61                                   ;  CHAR 'a'
004010CF     |63                   DB 63                                   ;  CHAR 'c'
004010D0     |6B                   DB 6B                                   ;  CHAR 'k'
004010D1     |65                   DB 65                                   ;  CHAR 'e'
004010D2     |64                   DB 64                                   ;  CHAR 'd'
004010D3     |20                   DB 20                                   ;  CHAR ' '
004010D4     |62                   DB 62                                   ;  CHAR 'b'
004010D5     |79                   DB 79                                   ;  CHAR 'y'
004010D6     |20                   DB 20                                   ;  CHAR ' '
004010D7     |4C                   DB 4C                                   ;  CHAR 'L'
004010D8     |61                   DB 61                                   ;  CHAR 'a'
004010D9     |6F                   DB 6F                                   ;  CHAR 'o'
004010DA     |77                   DB 77                                   ;  CHAR 'w'
004010DB     |61                   DB 61                                   ;  CHAR 'a'
004010DC     |6E                   DB 6E                                   ;  CHAR 'n'
004010DD     |67                   DB 67                                   ;  CHAR 'g'
004010DE     |2D                   DB 2D                                   ;  CHAR '-'
004010DF     |29                   DB 29                                   ;  CHAR ')'
004010E0     |00                   DB 00
004010E1     |52                   DB 52                                   ;  CHAR 'R'
004010E2     |65                   DB 65                                   ;  CHAR 'e'
004010E3   . |67 69 73 74 65 72    ASCII "gister"
004010E9     |65                   DB 65                                   ;  CHAR 'e'
004010EA     |64                   DB 64                                   ;  CHAR 'd'
004010EB     |20                   DB 20                                   ;  CHAR ' '
004010EC     |74                   DB 74                                   ;  CHAR 't'
004010ED     |6F                   DB 6F                                   ;  CHAR 'o'
004010EE     |20                   DB 20                                   ;  CHAR ' '
004010EF     |6C                   DB 6C                                   ;  CHAR 'l'
004010F0     |61                   DB 61                                   ;  CHAR 'a'
004010F1     |6F                   DB 6F                                   ;  CHAR 'o'
004010F2     |77                   DB 77                                   ;  CHAR 'w'
004010F3     |61                   DB 61                                   ;  CHAR 'a'
004010F4     |6E                   DB 6E                                   ;  CHAR 'n'
004010F5     |67                   DB 67                                   ;  CHAR 'g'
004010F6     |68                   DB 68                                   ;  CHAR 'h'
004010F7     |61                   DB 61                                   ;  CHAR 'a'
004010F8     |69                   DB 69                                   ;  CHAR 'i'
004010F9     |00                   DB 00
004010FA     |00                   DB 00
......

这样就完成了KeyFile[0x80] 的定义
char KeyFile[0x80] =
{
        (char)0x00,
    (char)0xE8, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x6A, (char)0x00, (char)0x68,
    (char)0xCC, (char)0x10, (char)0x40, (char)0x00,
    (char)0x68, (char)0xE1, (char)0x10, (char)0x40,
    (char)0x00, (char)0xE9, (char)0x4E, (char)0x02,
    (char)0x00, (char)0x00, (char)0x90, (char)0x90,
    (char)0x90, (char)0x90, (char)0x90, (char)0xC3,
    (char)0x43, (char)0x72, (char)0x61, (char)0x63,
    (char)0x6B, (char)0x65, (char)0x64, (char)0x20,
    (char)0x62, (char)0x79, (char)0x20, (char)0x4C,
    (char)0x61, (char)0x6F, (char)0x77, (char)0x61,
    (char)0x6E, (char)0x67, (char)0x2D, (char)0x29,
    (char)0x00, (char)0x52, (char)0x65, (char)0x67,
    (char)0x69, (char)0x73, (char)0x74, (char)0x65,
    (char)0x72, (char)0x65, (char)0x64, (char)0x20,
    (char)0x74, (char)0x6F, (char)0x20, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00//, (char)0x00
};

最后给出我修改的Ligntening的注册程序,希望菜鸟能够发现我做的修改,我想仔细跟踪一遍后,像我这样的菜鸟才能真正明白该注册程序的原理。
#include <windows.h>
#include <stdio.h>

//the buffer as it sits in the crackme. This needs to be transformed
//to what we want
char EncryptedBuff[0x80] =
{
    (char)0x0D, (char)0x20, (char)0x73, (char)0x61,
    (char)0x74, (char)0x48, (char)0xA8, (char)0x7F,
    (char)0x37, (char)0x6E, (char)0x53, (char)0xC5,
    (char)0x71, (char)0x2E, (char)0x64, (char)0xDF,
    (char)0x53, (char)0xD3, (char)0x42, (char)0x60,
    (char)0x73, (char)0x90, (char)0x78, (char)0x45,
    (char)0x00, (char)0x2D, (char)0x6F, (char)0xAE,
    (char)0x3C, (char)0x01, (char)0x01, (char)0x53,
    (char)0x5B, (char)0x64, (char)0x64, (char)0x5B,
    (char)0x48, (char)0x04, (char)0x05, (char)0x02,
    (char)0x48, (char)0x1C, (char)0x1C, (char)0x00,
    (char)0x10, (char)0x02, (char)0x0E, (char)0x08,
    (char)0x0E, (char)0x45, (char)0x1C, (char)0x4E,
    (char)0x74, (char)0x7B, (char)0x48, (char)0x26,
    (char)0x00, (char)0x02, (char)0x0F, (char)0x0E,
    (char)0x00, (char)0x16, (char)0x45, (char)0x01,
    (char)0x18, (char)0x4F, (char)0x06, (char)0x45,
    (char)0x1A, (char)0x0C, (char)0x04, (char)0x4E,
    (char)0x5B, (char)0x49, (char)0x15, (char)0x48,
    (char)0x43, (char)0x0E, (char)0x4E, (char)0x17,
    (char)0x10, (char)0x00, (char)0x1E, (char)0x0C,
    (char)0x1C, (char)0x47, (char)0x48, (char)0x09,
    (char)0x15, (char)0x04, (char)0x46, (char)0x42,
    (char)0x06, (char)0x08, (char)0x1B, (char)0x7D,
    (char)0x3E, (char)0x1C, (char)0x16, (char)0x0C,
    (char)0x7E, (char)0x66, (char)0x09, (char)0x18,
    (char)0x09, (char)0x12, (char)0x48, (char)0x4F,
    (char)0x12, (char)0x48, (char)0x04, (char)0x57,
    (char)0x01, (char)0x5C, (char)0x41, (char)0x47,
    (char)0x3C, (char)0x1B, (char)0x5E, (char)0x45,
    (char)0x18, (char)0x11, (char)0x49, (char)0x00,
    (char)0x1E, (char)0x09, (char)0x07, (char)0x32
};

//the data that will be written out to the keyfile
char KeyFile[0x80] =
{
        (char)0x00,
    (char)0xE8, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x6A, (char)0x00, (char)0x68,
    (char)0xCC, (char)0x10, (char)0x40, (char)0x00,
    (char)0x68, (char)0xE1, (char)0x10, (char)0x40,
    (char)0x00, (char)0xE9, (char)0x4E, (char)0x02,
    (char)0x00, (char)0x00, (char)0x90, (char)0x90,
    (char)0x90, (char)0x90, (char)0x90, (char)0xC3,
    (char)0x43, (char)0x72, (char)0x61, (char)0x63,
    (char)0x6B, (char)0x65, (char)0x64, (char)0x20,
    (char)0x62, (char)0x79, (char)0x20, (char)0x4C,//L
    (char)0x61, (char)0x6F, (char)0x77, (char)0x61,
    (char)0x6E, (char)0x67, (char)0x2D, (char)0x29,
    (char)0x00, (char)0x52, (char)0x65, (char)0x67,
    (char)0x69, (char)0x73, (char)0x74, (char)0x65,
    (char)0x72, (char)0x65, (char)0x64, (char)0x20,
    (char)0x74, (char)0x6F, (char)0x20, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00//, (char)0x00
};
/*
char KeyFile[0x81] =
{
        (char)0x00,
    (char)0x68, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x68, (char)0xCC, (char)0x10,
    (char)0x40, (char)0x00, (char)0x68, (char)0xE5,
    (char)0x10, (char)0x40, (char)0x00, (char)0xFF,
    (char)0x35, (char)0xBC, (char)0x30, (char)0x40,
    (char)0x00, (char)0xFF, (char)0x15, (char)0x20,
    (char)0x20, (char)0x40, (char)0x00, (char)0xC3,
    (char)0x59, (char)0x6F, (char)0x75, (char)0x20,
    (char)0x77, (char)0x69, (char)0x6E, (char)0x2C,
    (char)0x20, (char)0x6D, (char)0x69, (char)0x67,
    (char)0x68, (char)0x74, (char)0x79, (char)0x20,
    (char)0x63, (char)0x72, (char)0x61, (char)0x63,
    (char)0x6B, (char)0x65, (char)0x72, (char)0x21,
    (char)0x00, (char)0x57, (char)0x68, (char)0x6F,
    (char)0x20, (char)0x65, (char)0x6E, (char)0x74,
    (char)0x65, (char)0x72, (char)0x65, (char)0x74,
    (char)0x68, (char)0x65, (char)0x72, (char)0x65,
    (char)0x69, (char)0x6E, (char)0x2C, (char)0x20,
    (char)0x61, (char)0x20, (char)0x63, (char)0x6F,
    (char)0x6E, (char)0x71, (char)0x75, (char)0x65,
    (char)0x72, (char)0x65, (char)0x72, (char)0x20,
    (char)0x68, (char)0x61, (char)0x74, (char)0x68,
    (char)0x20, (char)0x62, (char)0x69, (char)0x00,
    (char)0x3B, (char)0x3B, (char)0x57, (char)0x68,
    (char)0x6F, (char)0x3b, (char)0x73, (char)0x6C,
    (char)0x61, (char)0x79, (char)0x65, (char)0x74,
    (char)0x68, (char)0x20, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00,
    (char)0x00, (char)0x00, (char)0x00, (char)0x00
};
*/

char MiddleBuff[0x100];

int main()
{
        char        RegName[50];
        long        NameLen;
        long        Counter=0;
        long        MagicValue;
        char        HexConvert;
        HANDLE        FileHandle;

        while(1)
        {
                printf("KeyGen for F0dder's #2 Crackme");
                printf("\n\nRegistered Name: ");
                scanf("%[^\n\0]s", RegName);
                if(strlen(RegName) > 16)
                {
                        printf("\nName must be shorter than 16 characters");
                        continue;
                }

                if(strlen(RegName) == 0)
                {
                        printf("\n\nNo name entered. Exiting.");
                        printf("\nCtrl+C to close");
                        while(1)
                                Counter++;
                }

                break;
        }

        //get the length of the name
        NameLen = strlen(RegName);

        //blank the middle buffer out
        memset(MiddleBuff, 0, 0x80);

        //copy the name into what will be the buffer for the keyfile
        memcpy(&KeyFile[0x40], RegName, NameLen);

        //copy the name until it is 16 chars long
        memcpy(MiddleBuff, &RegName, NameLen);
        if(NameLen < 16)
        {
                Counter = 16 / NameLen;
                Counter++;
                while(Counter)
                {
                        memcpy(&MiddleBuff[NameLen * Counter], RegName, NameLen);
                        Counter--;
                }
        }

        //generate one of the buffers used based on the name/key passed in
        for(Counter=0; Counter < 0x70; Counter++)
                MiddleBuff[Counter+0x10] = MiddleBuff[Counter] ^ MiddleBuff[Counter + 1];

        //now, take the KeyFile buff and manipulate it so we get what we want
        //for executable code
        for(Counter=0x7F; Counter >= 0; Counter--)
                KeyFile[Counter] = KeyFile[Counter] ^ EncryptedBuff[Counter-1];

        //you may note that I didn't do any changes to the last 32 bytes. The last
        //32 bytes are "garbage" bytes as far as I am conserned as they are used to
        //cause the buffer to pass
        MagicValue = 0x0BADC0DE;
        _asm
        {
                mov ecx, 0x60
                mov eax, 0x0BADC0DE
                lea ebx, KeyFile

        GenMagic:
                xor al, [ebx]
                rol eax, 1

                inc ebx
                dec ecx
                jnz GenMagic

                //time to manipulate the last 32 bytes so the MagicValue becomes what we want
                //I could sit down and figure out exactly how many bytes I need and how to manipulate
                //them to get what I want but this is just easier
               
                lea ebx, [KeyFile + 60h]
                mov edi, 0xFCC5A375
                mov ecx, 0x20

        FixMagic:
                mov edx, edi
                mov dh, al
                and dh, 1
                and dl, 1
                xor dh,dl
                mov [ebx],dh
                inc ebx
                dec ecx
                rol eax, 1
                rol edi, 1
                jne FixMagic
        }

        //the last step should be to take the KeyFile buffer and the MiddleBuff and xor em together
        for(Counter=0; Counter < 0x80; Counter++)
                KeyFile[Counter] = KeyFile[Counter] ^ MiddleBuff[Counter];

        //now the final step. convert each char to hex to put out to the file.
        MagicValue = 0;
        for(Counter=0; Counter < 0x80; Counter++, MagicValue++)
        {
                HexConvert = (KeyFile[Counter] >> 4) & 0x0F;
                if(HexConvert > 0x09)
                        MiddleBuff[MagicValue] = HexConvert + 0x37;
                else
                        MiddleBuff[MagicValue] = HexConvert + 0x30;

                MagicValue++;
                HexConvert = KeyFile[Counter] & 0xF;
                if(HexConvert > 0x09)
                        MiddleBuff[MagicValue] = HexConvert + 0x37;
                else
                        MiddleBuff[MagicValue] = HexConvert + 0x30;

        }

        FileHandle = CreateFile("skeleton.key", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
        if((long)FileHandle == 0xFFFFFFFF)
                printf("Error opening skeleton.key file");
        else
        {
                WriteFile(FileHandle, MiddleBuff, 0x100, &Counter, 0);
                CloseHandle(FileHandle);
                printf("Keyfile generated.");
        }

        printf("\n\nCtrl+C to close");
        while(1)
                Counter++;

        return 0;

}

【写在最后】这个CrackMe,我跟的好辛苦,我将过程记录下来希望对象我这样的菜鸟有帮助,希望和菜鸟多讨论,向大虾请教。
我的E-mail:chubing6143@sina.com

===================================================================,============================================                                                   2006-05-19

[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

上传的附件:
收藏
点赞7
打赏
分享
最新回复 (1)
雪    币: 241
活跃值: (35)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jjdg 2006-5-21 01:47
2
0
精神可嘉!
佩服佩服
游客
登录 | 注册 方可回帖
返回