【文章标题】: Slayer_Crackme2分析
【文章作者】: aal
【作者主页】: www.pediy.com
【软件名称】: Slayer_Crackme2
【下载地址】: http://crackmes.de/users/savage/slayers_crackme_2/download
【保护方式】: “Some big nums :)”
【编写语言】: Assembler
【使用工具】: OD,CALC,Radasm
--------------------------------------------------------------------------------
【详细过程】
很不错的一个crackme!peid:“什么都没找到 *”。输入表中只有LoadLibraryA和GetProcAddress,所以应该是加壳了。
载入OD慢慢来,最后到达一个似乎是OEP的地方:
00521101 -FFE0 JMP EAX ; Crackm_1.0041DBA0
不会脱壳,所以走到这里全凭耐心.;)累 其间会有警告说"Bad File format"之类的,不管.
到了这里,高兴了半天才发现,这里只不过是到了原程序处理之后得到的一个upx压缩过的exe得入口处.不过upx也很简单了,还慢慢来,
又到这里:
0041DD2B ^E9 D032FEFF JMP Crackm_1.00401000
这次应该行了吧.
dump:
起始地址0400000 大小 20000
入口点地址 修正为01000
重建输入表方式1
将脱好的文件随意起个名字,我的文件名为why.exe.好,开始正式分析.
00401000 >/$ E8 F8000000 CALL why.004010FD ; loadlibrary("crap0.dll")...得到what_the函数得地址
00401005 |. 6A 00 PUSH 0 ; /hTemplateFile = NULL
00401007 |. 68 80000000 PUSH 80 ; |Attributes = NORMAL
0040100C |. 6A 03 PUSH 3 ; |Mode = OPEN_EXISTING
0040100E |. 6A 00 PUSH 0 ; |pSecurity = NULL
00401010 |. 6A 01 PUSH 1 ; |ShareMode = FILE_SHARE_READ
00401012 |. 68 00000080 PUSH 80000000 ; |Access = GENERIC_READ
00401017 |. 68 1F354000 PUSH why.0040351F ; |FileName = "s2License.key"
0040101C |. E8 51030000 CALL <JMP.&kernel32.CreateFileA> ; \CreateFileA
00401021 |. 83F8 FF CMP EAX,-1
00401024 |. 75 0C JNZ SHORT why.00401032
00401026 |. C605 C5424000 >MOV BYTE PTR [4042C5],0
0040102D |. E9 9D000000 JMP why.004010CF
00401032 |> A3 B4404000 MOV DWORD PTR [4040B4],EAX
00401037 |. 6A 00 PUSH 0 ; /pFileSizeHigh = NULL
00401039 |. FF35 B4404000 PUSH DWORD PTR [4040B4] ; |hFile = NULL
0040103F |. E8 3A030000 CALL <JMP.&kernel32.GetFileSize> ; \GetFileSize
00401044 |. A3 BC404000 MOV DWORD PTR [4040BC],EAX
00401049 |. 6A 00 PUSH 0 ; /pOverlapped = NULL
0040104B |. 68 2D354000 PUSH why.0040352D ; |pBytesRead = why.0040352D
00401050 |. FF35 BC404000 PUSH DWORD PTR [4040BC] ; |BytesToRead = 0
00401056 |. 68 C0404000 PUSH why.004040C0 ; |Buffer = why.004040C0
0040105B |. FF35 B4404000 PUSH DWORD PTR [4040B4] ; |hFile = NULL
00401061 |. E8 30030000 CALL <JMP.&kernel32.ReadFile> ; \ReadFile
00401066 |. FF35 B4404000 PUSH DWORD PTR [4040B4] ; /hObject = NULL
0040106C |. E8 FB020000 CALL <JMP.&kernel32.CloseHandle> ; \CloseHandle
00401071 |. 68 AC384000 PUSH why.004038AC
00401076 |. 68 C0404000 PUSH why.004040C0 ; 文件
0040107B |. 6A 10 PUSH 10
0040107D |. 68 13304000 PUSH why.00403013 ; ASCII "8396A3806D9022BA3F636BFB4A310DEF15FF9A092289A2AC0312EBE7CD5B28438275EAEF3F74225201EA9DF584148A8BFF8382023AD508BBED7118CDA863B97E10B7938FEC257F13596D7B43E506674E1AC37EC60E62FB94FF4198417EE43B9ABF4870AC7F245AD905E55CFD856DB112A9C04A94E1670"...
00401082 |. 68 14324000 PUSH why.00403214 ; ASCII "6484DB2B4D06B0F0936190682A79A48A41C4BA574611DBB908B4188F3F58E63E03D7ADFACAFF1D048640421FFDA06F6198734E0A04051EA1E834DFDCA24863B7406FEB8222C355FA62F2F6199C4732FD0006DCBDD0D99E1368962407CC35EEAE3C76CAD38A602C21E2961E5DEFE22644FA7550C3C017A"...
00401087 |. FF15 B0404000 CALL DWORD PTR [4040B0] ; 调用what_the函数
0040108D E8 90000000 CALL <why.big2bytes> ; 将得到得大数保存到4040C0
00401092 E8 CE000000 CALL why.00401165 ; 分析处理的结果
00401097 |. C605 C5424000 >MOV BYTE PTR [4042C5],1
0040109E E8 F3000000 CALL <why.sumofbytes> ; key的第一部分的"和",保存到4042C1
004010A3 |. 68 4B354000 PUSH why.0040354B
004010A8 E8 6F020000 CALL why.0040131C ; key的第二部分转化为数字
004010AD |. 3B05 C1424000 CMP EAX,DWORD PTR [4042C1]
004010B3 |. 74 1A JE SHORT why.004010CF
004010B5 |. 6A 40 PUSH 40 ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
004010B7 |. 68 15344000 PUSH why.00403415 ; |Title = ".caption"
004010BC |. 68 5B384000 PUSH why.0040385B ; |Text = "You have potential, go ahead :DD"
004010C1 |. 6A 00 PUSH 0 ; |hOwner = NULL
004010C3 |. E8 FE020000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
004010C8 |. C605 C5424000 >MOV BYTE PTR [4042C5],0
004010CF |> 6A 00 PUSH 0 ; /pModule = NULL
004010D1 |. E8 AE020000 CALL <JMP.&kernel32.GetModuleHandleA> ; \GetModuleHandleA
004010D6 |. A3 A8384000 MOV DWORD PTR [4038A8],EAX
004010DB |. 6A 00 PUSH 0 ; /lParam = NULL
004010DD |. 68 CE114000 PUSH why.004011CE ; |DlgProc = why.004011CE
004010E2 |. 6A 00 PUSH 0 ; |hOwner = NULL
004010E4 |. 6A 64 PUSH 64 ; |pTemplate = 64
004010E6 |. FF35 A8384000 PUSH DWORD PTR [4038A8] ; |hInst = NULL
004010EC |. E8 B7020000 CALL <JMP.&USER32.DialogBoxParamA> ; \DialogBoxParamA
004010F1 |. 6A 00 PUSH 0 ; /ExitCode = 0
004010F3 \. E8 80020000 CALL <JMP.&kernel32.ExitProcess> ; \ExitProcess
004010F8 E8 DB E8
004010F9 DB DB DB
004010FA 02 DB 02
004010FB 00 DB 00
004010FC 00 DB 00
004010FD /$ 68 00304000 PUSH why.00403000 ; /FileName = "crap0.dll"
00401102 |. E8 89020000 CALL <JMP.&kernel32.LoadLibraryA> ; \LoadLibraryA
00401107 |. A3 AC404000 MOV DWORD PTR [4040AC],EAX
0040110C |. 68 0A304000 PUSH why.0040300A ; /ProcNameOrOrdinal = "what_the"
00401111 |. FF35 AC404000 PUSH DWORD PTR [4040AC] ; |hModule = NULL
00401117 |. E8 6E020000 CALL <JMP.&kernel32.GetProcAddress> ; \GetProcAddress
0040111C |. A3 B0404000 MOV DWORD PTR [4040B0],EAX
00401121 \. C3 RETN
OD给我们标识得很清晰明了了.
继续分析窗口处理过程可以看到主要有两个验证的地方.
第一个:
004011FE . 803D C5424000 >CMP BYTE PTR [4042C5],0
00401205 . 75 11 JNZ SHORT why.00401218
看前面的处理部分中可以看到,key的第一部分的"和"等于key的第二部分就可以满足这个条件:
00401097 |. C605 C5424000 >MOV BYTE PTR [4042C5],1
...
004010AD |. 3B05 C1424000 CMP EAX,DWORD PTR [4042C1]
004010B3 |. 74 1A JE SHORT why.004010CF
...
004010C8 |. C605 C5424000 >MOV BYTE PTR [4042C5],0
004010CF |> 6A 00 PUSH 0 ; /pModule = NULL
...
第二个:
0040123B . E8 DC000000 CALL why.0040131C ; key的第三部分转化为数字
00401240 . A3 C6424000 MOV DWORD PTR [4042C6],EAX
00401245 . 0BC0 OR EAX,EAX
00401247 . 75 12 JNZ SHORT why.0040125B ; 不为0就不使important按钮disabled
.......
004012AD . A1 C6424000 MOV EAX,DWORD PTR [4042C6]
004012B2 . 33D2 XOR EDX,EDX
004012B4 . 68 C8124000 PUSH why.004012C8
004012B9 . 64:FF32 PUSH DWORD PTR FS:[EDX]
004012BC . 64:8922 MOV DWORD PTR FS:[EDX],ESP
004012BF . 35 82198219 XOR EAX,19821982
004012C4 . FFD0 CALL EAX
四处看看发现eax=004012E1就可以,所以第三部分可以为固定值:19C20B63
004012E1 6A 40 PUSH 40
往回说,其实key分做了三部分是通过分析crap0.dll中的what_the函数和下面这个函数得知的:
00401092 E8 CE000000 CALL why.00401165 ; 分析处理的结果
先看what_the函数,crap0.dll经过了和主程序一样的处理,要脱壳的话同理即可.
...
10001189 52 PUSH EDX
1000118A 50 PUSH EAX
1000118B E8 60470000 CALL <cinstr>
10001190 8B4C24 3C MOV ECX,DWORD PTR [ESP+3C]
10001194 8B5424 20 MOV EDX,DWORD PTR [ESP+20]
10001198 51 PUSH ECX ; arg.2
10001199 52 PUSH EDX
1000119A E8 51470000 CALL <cinstr>
1000119F 8B4424 4C MOV EAX,DWORD PTR [ESP+4C]
100011A3 8B4C24 24 MOV ECX,DWORD PTR [ESP+24]
100011A7 50 PUSH EAX ; 文件中读取的内容
100011A8 51 PUSH ECX
100011A9 E8 42470000 CALL <cinstr>
100011AE 8D5424 30 LEA EDX,DWORD PTR [ESP+30]
100011B2 8D4424 34 LEA EAX,DWORD PTR [ESP+34]
100011B6 52 PUSH EDX ; 大数("8396...")
100011B7 8D4C24 30 LEA ECX,DWORD PTR [ESP+30]
100011BB 50 PUSH EAX ; 大数("6484...")
100011BC 8D5424 58 LEA EDX,DWORD PTR [ESP+58]
100011C0 51 PUSH ECX ; big(file)
100011C1 52 PUSH EDX ; arg.3=10h
100011C2 E8 39FEFFFF CALL 10001000
100011C7 8B4C24 38 MOV ECX,DWORD PTR [ESP+38]
100011CB 8B10 MOV EDX,DWORD PTR [EAX]
100011CD 51 PUSH ECX
100011CE 52 PUSH EDX
100011CF E8 0C100000 CALL 100021E0
...
其中10001000处的函数:
......
10001013 56 PUSH ESI ; 结果
10001014 8B08 MOV ECX,DWORD PTR [EAX]
10001016 8B02 MOV EAX,DWORD PTR [EDX]
10001018 51 PUSH ECX ; 大数("8396...")
10001019 8B4C24 1C MOV ECX,DWORD PTR [ESP+1C]
1000101D 50 PUSH EAX ; 大数("6484...")
1000101E 8B11 MOV EDX,DWORD PTR [ECX]
10001020 52 PUSH EDX ; 大数(file)=k
10001021 E8 BA380000 CALL <powmod>
10001026 6A 00 PUSH 0
10001028 E8 63070000 CALL <mirvar>
1000102D 8B7C24 24 MOV EDI,DWORD PTR [ESP+24]
......
另b1=大数("8396..."),b2=大数("6484..."),所以这里就是计算
key=k^b2 mod b1(k是由文件内容转化成的大数,^代表幂运算)
再看why.00401165:
00401165 /$ 56 PUSH ESI
00401166 |. 57 PUSH EDI
00401167 |. BE C0404000 MOV ESI,why.004040C0
0040116C |. BF 31354000 MOV EDI,why.00403531
00401171 |> AC /LODS BYTE PTR [ESI]
00401172 |. 3C 2C |CMP AL,2C ; ", " 逗号
00401174 |. 74 03 |JE SHORT why.00401179
00401176 |. AA |STOS BYTE PTR ES:[EDI]
00401177 |.^EB F8 \JMP SHORT why.00401171 ; key.part1
00401179 |> BF 4B354000 MOV EDI,why.0040354B
0040117E |> AC /LODS BYTE PTR [ESI]
0040117F |. 3C 2C |CMP AL,2C
00401181 |. 74 03 |JE SHORT why.00401186
00401183 |. AA |STOS BYTE PTR ES:[EDI]
00401184 |.^EB F8 \JMP SHORT why.0040117E
00401186 |> BF 54354000 MOV EDI,why.00403554 ; key.part2
0040118B |> AC /LODS BYTE PTR [ESI]
0040118C |. 3C 24 |CMP AL,24 ; $
0040118E |. 74 03 |JE SHORT why.00401193
00401190 |. AA |STOS BYTE PTR ES:[EDI]
00401191 |.^EB F8 \JMP SHORT why.0040118B ; key.part3
00401193 |> 5F POP EDI
00401194 |. 5E POP ESI
00401195 \. C3 RETN
所以key的格式应该是这样
keypart1,keypart2,keypart3$
00401218 > 68 31354000 PUSH why.00403531 ; /String2 = "";keypart1
0040121D . 68 97384000 PUSH why.00403897 ; |String1 = "Registered to : "
00401222 . E8 75010000 CALL <JMP.&kernel32.lstrcat> ; \lstrcat
00401227 . 68 97384000 PUSH why.00403897 ; /Text = "Registered to : "
0040122C . 6A 68 PUSH 68 ; |ControlID = 68 (104.)
0040122E . FF75 08 PUSH DWORD PTR [EBP+8] ; |hWnd
00401231 . E8 9C010000 CALL <JMP.&USER32.SetDlgItemTextA> ; \SetDlgItemTextA
所以如果对应"aalloverred",就有key:
aalloverred,29EBF1DD,19C20B63$
其中29EBF1DD=sum("aalloverred")
因为这是处理过后的字节转化成数字的结果
0040108D E8 90000000 CALL <why.big2bytes> ; 将得到得大数保存到4040C0
所以将对应的二进制值作为大数处理:
61 61 6C 6C 6F 76 65 72 72 65 64 2C 32 39 45 42 46 31 44 44 2C 31 39 43 32 30 42 36 33 24
所以key=61616C6C6F7665727265642C32394542463144442C313943323042363324
又因为有key=k^b2 mod b1所以k=key^D mod b1
但是b1有2048位之大,所以直接计算D估计是不可行的.不懂算法,但是看过bLaCk-eye的RSA攻击的文章,
用他的RAT(RSA ATTACKING TOOLKITT v.0.1f)试了试,^_^,还真行,使用wiener attack 瞬间就得到了D=10001.
到这里,注册机就简单了,见附件!
--------------------------------------------------------------------------------
【经验总结】
感觉数学学得太差,分析这种cm没有数论的基础太难了,这次这么顺利主要是运气好吧.期望看到大侠们更多的关于算法的文章.
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课