【吾爱2013CM大赛解答】-- MK的杀手锏 算法分析+PATCH一码复用(复杂)
本帖最后由 playboysen 于 2013-12-30 22:34 编辑本文针对以下CM:
http://www.52pojie.cn/thread-230220-1-1.html
主程序加了UPX壳、简单自校验什么的,直接下断GetFileSize即可轻松找到自校验
(或者根据异常提示也可以轻松定位自校验,这种方法仅限XP,Win7不显示错误信息)
XP上错误提示
自校验代码(以下截图均出自Win7X64,所以地址显示可能有异)
注册类型是机器码对应注册码,F9运行程序打开OD的Windows窗口看看控件ID
找到Edit框ID,在代码窗口直接搜索常量来定位相关处理代码
010216C7 .51 PUSH ECX ; /lParam
010216C8 .68 00010000 PUSH 0x100 ; |wParam = 0x100
010216CD .0FB7F3 MOVZX ESI, BX ; |
010216D0 .33C0 XOR EAX, EAX ; |
010216D2 .6A 0D PUSH 0xD ; |Message = WM_GETTEXT
010216D4 .895C24 34 MOV DWORD PTR SS:, EBX ; |
010216D8 .8BD8 MOV EBX, EAX ; |
010216DA .83C6 01 ADD ESI, 0x1 ; |
010216DD .68 EA030000 PUSH 0x3EA ; |ControlID = 3EA (1002.)
010216E2 .13C3 ADC EAX, EBX ; |
010216E4 .52 PUSH EDX ; |hWnd => 0010070E ('crack me2:MK的杀手锏 by:MK',class='#32770')
010216E5 .893D D8FA2301 MOV DWORD PTR DS:, EDI ; |
010216EB .894424 40 MOV DWORD PTR SS:, EAX ; |
010216EF .FF15 3CB10201 CALL NEAR DWORD PTR DS: ; \SendDlgItemMessageA
010216F5 .68 00010000 PUSH 0x100 ;0x3EA是注册码EDIT框
......
01021774 .8B4C24 14 MOV ECX, DWORD PTR SS:
01021778 >0FB6940C 6801>MOVZX EDX, BYTE PTR SS: ;regcode
01021780 .885424 11 MOV BYTE PTR SS:, DL
01021784 .0FB69434 7101>MOVZX EDX, BYTE PTR SS: ;regcode
0102178C .885424 13 MOV BYTE PTR SS:, DL
01021790 .0FB69404 7A01>MOVZX EDX, BYTE PTR SS: ;regcode
01021798 .885424 12 MOV BYTE PTR SS:, DL
0102179C .0FB6943C 8301>MOVZX EDX, BYTE PTR SS:
010217A4 .C6840C 680100>MOV BYTE PTR SS:, 0x0 ;以上四字节置零
010217AC .C68434 710100>MOV BYTE PTR SS:, 0x0
010217B4 .C68404 7A0100>MOV BYTE PTR SS:, 0x0
010217BC .885424 10 MOV BYTE PTR SS:, DL
010217C0 .C6843C 830100>MOV BYTE PTR SS:, 0x0
010217C8 .33C0 XOR EAX, EAX
010217CA .8D8C24 680200>LEA ECX, DWORD PTR SS:
010217D1 >8A9404 680100>MOV DL, BYTE PTR SS:
010217D8 .84D2 TEST DL, DL
010217DA .74 03 JE SHORT MKssj.010217DF
010217DC .8811 MOV BYTE PTR DS:, DL
010217DE .41 INC ECX
010217DF >40 INC EAX
010217E0 .83F8 24 CMP EAX, 0x24 ;连接字符串 去除0x00共32位
010217E3 .^ 7C EC JL SHORT MKssj.010217D1
010217E5 .8D8424 680200>LEA EAX, DWORD PTR SS:
010217EC .899C24 040100>MOV DWORD PTR SS:, EBX
010217F3 .899C24 000100>MOV DWORD PTR SS:, EBX
010217FA .C78424 080100>MOV DWORD PTR SS:, 0x67452301 ;这几组数字,标准的MD5函数
01021805 .C78424 0C0100>MOV DWORD PTR SS:, 0xEFCDAB89
01021810 .C78424 100100>MOV DWORD PTR SS:, 0x98BADCFE
0102181B .C78424 140100>MOV DWORD PTR SS:, 0x10325476
01021826 .8D48 01 LEA ECX, DWORD PTR DS:
01021829 .8DA424 000000>LEA ESP, DWORD PTR SS:
01021830 >8A10 MOV DL, BYTE PTR DS:
01021832 .40 INC EAX
01021833 .84D2 TEST DL, DL
01021835 .^ 75 F9 JNZ SHORT MKssj.01021830
01021837 .2BC1 SUB EAX, ECX
01021839 .8D8C24 680200>LEA ECX, DWORD PTR SS:
01021840 .51 PUSH ECX
01021841 .8DB424 040100>LEA ESI, DWORD PTR SS:
01021848 .E8 83120000 CALL MKssj.01022AD0
0102184D .8BC6 MOV EAX, ESI
0102184F .E8 8C130000 CALL MKssj.01022BE0
01021854 .8D9424 5C0100>LEA EDX, DWORD PTR SS:
0102185B .52 PUSH EDX
0102185C .B8 10000000 MOV EAX, 0x10
01021861 .8D7424 38 LEA ESI, DWORD PTR SS:
01021865 .895C24 3C MOV DWORD PTR SS:, EBX
01021869 .895C24 38 MOV DWORD PTR SS:, EBX
0102186D .C74424 40 012>MOV DWORD PTR SS:, 0x67452301 ;第二次MD5
01021875 .C74424 44 89A>MOV DWORD PTR SS:, 0xEFCDAB89
0102187D .C74424 48 FED>MOV DWORD PTR SS:, 0x98BADCFE
01021885 .C74424 4C 765>MOV DWORD PTR SS:, 0x10325476
0102188D .E8 3E120000 CALL MKssj.01022AD0
01021892 .8BC6 MOV EAX, ESI
01021894 .E8 47130000 CALL MKssj.01022BE0
01021899 .8D8424 900000>LEA EAX, DWORD PTR SS:
010218A0 .50 PUSH EAX
010218A1 .B8 10000000 MOV EAX, 0x10
010218A6 .8DB424 A40000>LEA ESI, DWORD PTR SS:
010218AD .899C24 A80000>MOV DWORD PTR SS:, EBX
010218B4 .899C24 A40000>MOV DWORD PTR SS:, EBX
010218BB .C78424 AC0000>MOV DWORD PTR SS:, 0x67452301
010218C6 .C78424 B00000>MOV DWORD PTR SS:, 0xEFCDAB89
010218D1 .C78424 B40000>MOV DWORD PTR SS:, 0x98BADCFE
010218DC .C78424 B80000>MOV DWORD PTR SS:, 0x10325476 ;第三次MD5
010218E7 .E8 E4110000 CALL MKssj.01022AD0
010218EC .8BC6 MOV EAX, ESI
010218EE .E8 ED120000 CALL MKssj.01022BE0
010218F3 .68 00010000 PUSH 0x100
010218F8 .8D8C24 780200>LEA ECX, DWORD PTR SS:
010218FF .53 PUSH EBX
01021900 .51 PUSH ECX
01021901 .E8 BA610000 CALL MKssj.01027AC0
01021906 .83C4 18 ADD ESP, 0x18
01021909 .33F6 XOR ESI, ESI
0102190B .8935 DCFA2301 MOV DWORD PTR DS:, ESI
01021911 >68 00010000 PUSH 0x100 ;一个大循环
01021916 .53 PUSH EBX
01021917 .68 D8F92301 PUSH MKssj.0123F9D8 ;ASCII "%X%x"
0102191C .E8 9F610000 CALL MKssj.01027AC0
01021921 .8A0D E0EC0201 MOV CL, BYTE PTR DS: ;CL = 0x4A ‘J’
01021927 .83C4 0C ADD ESP, 0xC
0102192A .80F9 6F CMP CL, 0x6F
0102192D .74 17 JE SHORT MKssj.01021946
0102192F .33C0 XOR EAX, EAX
01021931 >80F1 6F XOR CL, 0x6F ;解码出"%x%x"
01021934 .8888 D8F92301 MOV BYTE PTR DS:, CL
0102193A .8A88 E1EC0201 MOV CL, BYTE PTR DS:
01021940 .40 INC EAX
01021941 .80F9 6F CMP CL, 0x6F
01021944 .^ 75 EB JNZ SHORT MKssj.01021931
01021946 >0FB68434 F000>MOVZX EAX, BYTE PTR SS:
0102194E .8BD0 MOV EDX, EAX
01021950 .81E2 0F000080 AND EDX, 0x8000000F
01021956 .79 05 JNS SHORT MKssj.0102195D
01021958 .4A DEC EDX
01021959 .83CA F0 OR EDX, 0xFFFFFFF0
0102195C .42 INC EDX
0102195D >8B35 38B10201 MOV ESI, DWORD PTR DS: ;user32.wsprintfA
01021963 .52 PUSH EDX
01021964 .C1E8 04 SHR EAX, 0x4
01021967 .50 PUSH EAX ; /<%d>
01021968 .68 D8F92301 PUSH MKssj.0123F9D8 ; |Format = "%X%x"
0102196D .68 E4FA2301 PUSH MKssj.0123FAE4 ; |s = MKssj.0123FAE4
01021972 .FFD6 CALL NEAR ESI ; \wsprintfA
01021974 .68 00010000 PUSH 0x100
01021979 .53 PUSH EBX
0102197A .68 D8F92301 PUSH MKssj.0123F9D8 ;ASCII "%X%x"
0102197F .E8 3C610000 CALL MKssj.01027AC0
01021984 .8A0D E8EC0201 MOV CL, BYTE PTR DS:
0102198A .83C4 1C ADD ESP, 0x1C
0102198D .80F9 6F CMP CL, 0x6F
01021990 .74 23 JE SHORT MKssj.010219B5
01021992 .33C0 XOR EAX, EAX
01021994 .EB 0A JMP SHORT MKssj.010219A0
01021996 .8DA424 000000>LEA ESP, DWORD PTR SS:
0102199D .8D49 00 LEA ECX, DWORD PTR DS:
010219A0 >80F1 6F XOR CL, 0x6F ;解码出"%s%s"
010219A3 .8888 D8F92301 MOV BYTE PTR DS:, CL
010219A9 .8A88 E9EC0201 MOV CL, BYTE PTR DS:
010219AF .40 INC EAX
010219B0 .80F9 6F CMP CL, 0x6F
010219B3 .^ 75 EB JNZ SHORT MKssj.010219A0
010219B5 >68 E4FA2301 PUSH MKssj.0123FAE4
010219BA .8D8424 6C0200>LEA EAX, DWORD PTR SS:
010219C1 .50 PUSH EAX
010219C2 .8BC8 MOV ECX, EAX
010219C4 .68 D8F92301 PUSH MKssj.0123F9D8 ;ASCII "%X%x"
010219C9 .51 PUSH ECX
010219CA .FFD6 CALL NEAR ESI
010219CC .8B35 DCFA2301 MOV ESI, DWORD PTR DS:
010219D2 .46 INC ESI
010219D3 .83C4 10 ADD ESP, 0x10
010219D6 .83FE 10 CMP ESI, 0x10
010219D9 .8935 DCFA2301 MOV DWORD PTR DS:, ESI
010219DF .^ 0F8C 2CFFFFFF JL MKssj.01021911
010219E5 .8B5424 28 MOV EDX, DWORD PTR SS:
010219E9 .52 PUSH EDX ; /String2
010219EA .8D8424 6C0200>LEA EAX, DWORD PTR SS: ; |
010219F1 .50 PUSH EAX ; |String1
010219F2 .FF15 18B00201 CALL NEAR DWORD PTR DS: ; \lstrcmpA
010219F8 .85C0 TEST EAX, EAX
010219FA .0F85 5E010000 JNZ MKssj.01021B5E ;跳转失败
......
01021B34 .05 A0F92301 ADD EAX, MKssj.0123F9A0 ; |
01021B39 .FF15 3CB10201 CALL NEAR DWORD PTR DS: ; \SendDlgItemMessageA
01021B3F .8B0D ACF92301 MOV ECX, DWORD PTR DS:
01021B45 .6A 01 PUSH 0x1 ; /ShowState = SW_SHOWNORMAL
01021B47 .68 EC030000 PUSH 0x3EC ; |/ControlID = 3EC (1004.)
01021B4C .51 PUSH ECX ; ||hWnd => 0010070E ('crack me2:MK的杀手锏 by:MK',class='#32770')
01021B4D .FF15 40B10201 CALL NEAR DWORD PTR DS: ; |\GetDlgItem
01021B53 .50 PUSH EAX ; |hWnd
01021B54 .FF15 48B10201 CALL NEAR DWORD PTR DS: ; \ShowWindow
......
01021C25 .8BE5 MOV ESP, EBP
01021C27 .5D POP EBP
01021C28 .C2 1400 RETN 0x14
大概流程就是SendDlgItemMessageA来获取并简单处理注册码EDIT文本,然后三次MD5加密运算得出的值与程序内置码表对应位置的值作比较
由于MD5为不可逆运算(更何况还变形后做了多次MD5),所以即使可以轻松找到内置码表(在010219E9断点查看数据窗口即可)也很难还原出注册码,逆向出注册机更不用说了(当然拼人品暴力算号不在讨论范围),有心之人在这里试一试爆破吧(提示:简单修改跳转是行不通的后面还有一些校验)
(这种MD5查表注册法N年前好像在Quick Batch File Compiler时遇到过,注册验证逻辑基本一致,不过QBFC是注册码一次MD5值查表对照,幸好QBFC爆破容易(一字节搞定)当初也并未深究
没想到几年后在这里又遇到了其加强改进版,颇有种“他乡遇故知”的感觉,进而想到了那句词“物是人非事事休,欲语泪先流”忽然有些伤感……)
这里不得不学习下@qq54007 的一个“猥琐”伎俩
程序枚举进程模块查找可疑点,如被调试等不提示不退出而是偷偷修改解码关键Key致使无论输入注册码正确与否均提示失败
0040221C 50 ||push eax
0040221D 68 D8F96100 ||push a_a.0061F9D8 ; ASCII "ollydbg.ini"
00402222 FF15 30B04000 ||call dword ptr ds:[<&kernel32.lstrcmpi>] ; kernel32.lstrcmpiA
00402228 85C0 ||test eax,eax
0040222A 75 22 ||jnz short a_a.0040224E
0040222C B9 45651352 ||mov ecx,52136545 ; 这里开始搞破坏了!!!
00402231 310D B0EC4000 ||xor dword ptr ds:,ecx ; 等等是用于注册解码的关键Key
00402237 310D B8EC4000 ||xor dword ptr ds:,ecx ; KEY被改的面目全非
0040223D B8 74981685 ||mov eax,85169874
00402242 3105 B4EC4000 ||xor dword ptr ds:,eax ; “偷偷的进村,打枪的不要!”
00402248 3105 BCEC4000 ||xor dword ptr ds:,eax ; 太猥琐鸟儿o_0
0040224E 8D8C24 2C1000>||lea ecx,dword ptr ss:
00402255 51 ||push ecx
00402256 57 ||push edi
00402257 FF15 40B04000 ||call dword ptr ds:[<&kernel32.FindNextFileA>] ; kernel32.FindNextFileA
0040225D 85C0 ||test eax,eax
0040225F^ 0F85 1BFFFFFF |\jnz a_a.00402180
00402265 57 |push edi
00402266 FF15 34B04000 |call dword ptr ds:[<&kernel32.FindClose>] ; kernel32.FindClose
既然无法还原注册机,能否替换机器码来实现一码多用?那首先得知道机器码在哪里计算出来的
如何快速定位机器码计算部分呢?程序运算出机器码后会在主界面显示出来,下端点SetDlgItemTextA或者代码段搜索Edit框ID均可
01021DC4 > \8B7424 14 MOV ESI, DWORD PTR SS: ;Case 110 (WM_INITDIALOG) of switch 01021D14
01021DC8 .BF B0F92301 MOV EDI, MKssj.0123F9B0
01021DCD .8935 ACF92301 MOV DWORD PTR DS:, ESI
01021DD3 .E8 38F7FFFF CALL MKssj.01021510
01021DD8 .33FF XOR EDI, EDI
01021DDA .803D B0F92301>CMP BYTE PTR DS:, 0x0
01021DE1 .893D D8FA2301 MOV DWORD PTR DS:, EDI
01021DE7 .0F84 5C010000 JE MKssj.01021F49
01021DED .8B2D 4CB10201 MOV EBP, DWORD PTR DS: ;user32.GetDlgItemTextA
01021DF3 .8B1D 24B00201 MOV EBX, DWORD PTR DS: ;kernel32.lstrcatA
01021DF9 >68 00010000 PUSH 0x100 ;循环计算机器码
01021DFE .6A 00 PUSH 0x0
01021E00 .68 D8F92301 PUSH MKssj.0123F9D8 ;ASCII "%X%x"
01021E05 .C64424 20 00MOV BYTE PTR SS:, 0x0
01021E0A .C64424 21 00MOV BYTE PTR SS:, 0x0
01021E0F .C64424 22 00MOV BYTE PTR SS:, 0x0
01021E14 .E8 A75C0000 CALL MKssj.01027AC0
01021E19 .8A0D D8EC0201 MOV CL, BYTE PTR DS:
01021E1F .83C4 0C ADD ESP, 0xC
01021E22 .80F9 6F CMP CL, 0x6F
01021E25 .74 1E JE SHORT MKssj.01021E45
01021E27 .33C0 XOR EAX, EAX
01021E29 .8DA424 000000>LEA ESP, DWORD PTR SS:
01021E30 >80F1 6F XOR CL, 0x6F
01021E33 .8888 D8F92301 MOV BYTE PTR DS:, CL
01021E39 .8A88 D9EC0201 MOV CL, BYTE PTR DS:
01021E3F .40 INC EAX
01021E40 .80F9 6F CMP CL, 0x6F
01021E43 .^ 75 EB JNZ SHORT MKssj.01021E30 ;***begin***
01021E45 >0FB687 B0F923>MOVZX EAX, BYTE PTR DS: ;MD5(" WD-WMC1S4965023") 占16字节
01021E4C .8BC8 MOV ECX, EAX
01021E4E .81E1 0F000080 AND ECX, 0x8000000F ;机器码计算过程见python代码
01021E54 .79 05 JNS SHORT MKssj.01021E5B
01021E56 .49 DEC ECX
01021E57 .83C9 F0 OR ECX, 0xFFFFFFF0
01021E5A .41 INC ECX
01021E5B >8D1449 LEA EDX, DWORD PTR DS: ;EDX = 3*ECX
01021E5E .81E2 0F000080 AND EDX, 0x8000000F
01021E64 .79 05 JNS SHORT MKssj.01021E6B
01021E66 .4A DEC EDX
01021E67 .83CA F0 OR EDX, 0xFFFFFFF0
01021E6A .42 INC EDX
01021E6B >C1E8 04 SHR EAX, 0x4 ;md5_16
01021E6E .8D0440 LEA EAX, DWORD PTR DS: ;EAX = 3*EAX
01021E71 .25 0F000080 AND EAX, 0x8000000F
01021E76 .52 PUSH EDX
01021E77 .79 05 JNS SHORT MKssj.01021E7E
01021E79 .48 DEC EAX
01021E7A .83C8 F0 OR EAX, 0xFFFFFFF0
01021E7D .40 INC EAX
01021E7E >50 PUSH EAX ; /***end***
01021E7F .8D4C24 1C LEA ECX, DWORD PTR SS: ; |
01021E83 .68 D8F92301 PUSH MKssj.0123F9D8 ; |Format = "%X%x"
01021E88 .51 PUSH ECX ; |s
01021E89 .FF15 38B10201 CALL NEAR DWORD PTR DS: ; \wsprintfA
01021E8F .83C4 10 ADD ESP, 0x10
01021E92 .807C24 14 41CMP BYTE PTR SS:, 0x41
01021E97 .7C 05 JL SHORT MKssj.01021E9E
01021E99 .804424 14 0AADD BYTE PTR SS:, 0xA
01021E9E >807C24 15 41CMP BYTE PTR SS:, 0x41
01021EA3 .7C 05 JL SHORT MKssj.01021EAA
01021EA5 .804424 15 0AADD BYTE PTR SS:, 0xA
01021EAA >68 00010000 PUSH 0x100
01021EAF .68 98F82301 PUSH MKssj.0123F898 ;ASCII "951768P9-0883351n-P25n33O3-9mM281"
01021EB4 .68 E9030000 PUSH 0x3E9 ;0x3E9是机器码EDIT框
01021EB9 .56 PUSH ESI
01021EBA .FFD5 CALL NEAR EBP ;GetDlgItemTextA
01021EBC .8D5424 14 LEA EDX, DWORD PTR SS:
01021EC0 .52 PUSH EDX
01021EC1 .68 98F82301 PUSH MKssj.0123F898 ;ASCII "951768P9-0883351n-P25n33O3-9mM281"
01021EC6 .FFD3 CALL NEAR EBX ;lstrcatA
01021EC8 .68 98F82301 PUSH MKssj.0123F898 ; /Text = "951768P9-0883351n-P25n33O3-9mM281"
01021ECD .68 E9030000 PUSH 0x3E9 ; |ControlID = 3E9 (1001.)
01021ED2 .56 PUSH ESI ; |hWnd
01021ED3 .FF15 54B10201 CALL NEAR DWORD PTR DS: ; \SetDlgItemTextA
01021ED9 .8B3D D8FA2301 MOV EDI, DWORD PTR DS: ;0x3E9是机器码EDIT框的ControlID
01021EDF .8D47 01 LEA EAX, DWORD PTR DS:
01021EE2 .25 03000080 AND EAX, 0x80000003
01021EE7 .79 05 JNS SHORT MKssj.01021EEE
01021EE9 .48 DEC EAX
01021EEA .83C8 FC OR EAX, 0xFFFFFFFC
01021EED .40 INC EAX
01021EEE >75 45 JNZ SHORT MKssj.01021F35
01021EF0 .8D4F FF LEA ECX, DWORD PTR DS:
01021EF3 .83F9 0D CMP ECX, 0xD
01021EF6 .77 3D JA SHORT MKssj.01021F35
01021EF8 .68 00010000 PUSH 0x100
01021EFD .68 98F82301 PUSH MKssj.0123F898 ;ASCII "951768P9-0883351n-P25n33O3-9mM281"
01021F02 .68 E9030000 PUSH 0x3E9
01021F07 .56 PUSH ESI
01021F08 .C74424 28 2D0>MOV DWORD PTR SS:, 0x2D
01021F10 .FFD5 CALL NEAR EBP
01021F12 .8D5424 18 LEA EDX, DWORD PTR SS:
01021F16 .52 PUSH EDX
01021F17 .68 98F82301 PUSH MKssj.0123F898 ;ASCII "951768P9-0883351n-P25n33O3-9mM281"
01021F1C .FFD3 CALL NEAR EBX
01021F1E .68 98F82301 PUSH MKssj.0123F898 ; /Text = "951768P9-0883351n-P25n33O3-9mM281"
01021F23 .68 E9030000 PUSH 0x3E9 ; |ControlID = 3E9 (1001.)
01021F28 .56 PUSH ESI ; |hWnd
01021F29 .FF15 54B10201 CALL NEAR DWORD PTR DS: ; \SetDlgItemTextA
01021F2F .8B3D D8FA2301 MOV EDI, DWORD PTR DS:
01021F35 >47 INC EDI
01021F36 .80BF B0F92301>CMP BYTE PTR DS:, 0x0
01021F3D .893D D8FA2301 MOV DWORD PTR DS:, EDI
01021F43 .^ 0F85 B0FEFFFF JNZ MKssj.01021DF9
01021F49 >6A 01 PUSH 0x1
01021F4B .6A 20 PUSH 0x20
01021F4D .68 50160201 PUSH MKssj.01021650 ;回调函数地址
01021F52 .68 E8030000 PUSH 0x3E8
01021F57 .6A 64 PUSH 0x64
01021F59 .FF15 5CB10201 CALL NEAR DWORD PTR DS: ;winmm.timeSetEvent
01021F5F .B8 01000000 MOV EAX, 0x1
01021F64 .5F POP EDI
01021F65 .5E POP ESI
01021F66 .5D POP EBP
01021F67 .5B POP EBX
01021F68 .C2 1000 RETN 0x10
一个大循环逐位计算出机器码并写入Edit(具体计算过程见后面Python代码),最后timeSetEvent设置回调函数(就是上面那段注册码验证函数)
上面的计算都是基于字符串" WD-WMC1S4965023",看起来好像磁盘信息,不知道怎么计算出来的,下断点CreateFileA看看
01021345 > \53 PUSH EBX ; /<%d>
01021346 .8D8424 3C0400>LEA EAX, DWORD PTR SS: ; |
0102134D .68 D8F92301 PUSH MKssj.0123F9D8 ; |Format = "\\.\PhysicalDrive%d"
01021352 .50 PUSH EAX ; |s
01021353 .FF15 38B10201 CALL NEAR DWORD PTR DS: ; \wsprintfA
01021359 .83C4 0C ADD ESP, 0xC
0102135C .53 PUSH EBX ; /hTemplateFile
0102135D .53 PUSH EBX ; |Attributes
0102135E .6A 03 PUSH 0x3 ; |Mode = OPEN_EXISTING
01021360 .53 PUSH EBX ; |pSecurity
01021361 .6A 03 PUSH 0x3 ; |ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
01021363 .68 000000C0 PUSH 0xC0000000 ; |Access = GENERIC_READ|GENERIC_WRITE
01021368 .8D8C24 500400>LEA ECX, DWORD PTR SS: ; |
0102136F .51 PUSH ECX ; |FileName
01021370 .FF15 00B00201 CALL NEAR DWORD PTR DS: ; \CreateFileA
01021376 .8BF0 MOV ESI, EAX
01021378 .83FE FF CMP ESI, -0x1
0102137B .75 07 JNZ SHORT MKssj.01021384
......
010213C9 .53 PUSH EBX ; /pOverlapped
010213CA .8D4424 14 LEA EAX, DWORD PTR SS: ; |
010213CE .50 PUSH EAX ; |pBytesReturned
010213CF .68 13020000 PUSH 0x213 ; |OutBufferSize = 213 (531.)
010213D4 .8D8C24 440500>LEA ECX, DWORD PTR SS: ; |
010213DB .51 PUSH ECX ; |OutBuffer
010213DC .6A 23 PUSH 0x23 ; |InBufferSize = 23 (35.)
010213DE .8D9424 280400>LEA EDX, DWORD PTR SS: ; |
010213E5 .52 PUSH EDX ; |InBuffer
010213E6 .68 88C00700 PUSH 0x7C088 ; |IoControlCode = SMART_RCV_DRIVE_DATA
010213EB .56 PUSH ESI ; |hDevice
010213EC .889C24 380400>MOV BYTE PTR SS:, BL ; |
010213F3 .C68424 390400>MOV BYTE PTR SS:, 0x1 ; |
010213FB .889C24 3B0400>MOV BYTE PTR SS:, BL ; |
01021402 .889C24 3C0400>MOV BYTE PTR SS:, BL ; |
01021409 .C68424 3D0400>MOV BYTE PTR SS:, 0xA0 ; |
01021411 .C68424 3E0400>MOV BYTE PTR SS:, 0xEC ; |
01021419 .889C24 400400>MOV BYTE PTR SS:, BL ; |
01021420 .C78424 340400>MOV DWORD PTR SS:, 0x200; |
0102142B .FF15 0CB00201 CALL NEAR DWORD PTR DS: ; \DeviceIoControl
01021431 .85C0 TEST EAX, EAX
01021433 .74 32 JE SHORT MKssj.01021467
01021435 .33C0 XOR EAX, EAX
01021437 .EB 07 JMP SHORT MKssj.01021440
01021439 .8DA424 000000>LEA ESP, DWORD PTR SS:
01021440 >0FB78C44 4805>MOVZX ECX, WORD PTR SS:[ESP+EAX*2+0x54>;Copy DeviceIoControl的OutBuffer
01021448 .894C84 14 MOV DWORD PTR SS:, E>
0102144C .40 INC EAX
0102144D .3D 00010000 CMP EAX, 0x100
01021452 .^ 7C EC JL SHORT MKssj.01021440
01021454 .8BC5 MOV EAX, EBP
01021456 .8D4C24 14 LEA ECX, DWORD PTR SS: ;Copy后的地址:Addr
0102145A .895D 00 MOV DWORD PTR SS:, EBX
0102145D .E8 CEFBFFFF CALL MKssj.01021030 ;加工Addr数据
01021462 .BF 01000000 MOV EDI, 0x1
01021467 >56 PUSH ESI ; /hObject
01021468 .FF15 10B00201 CALL NEAR DWORD PTR DS: ; \CloseHandle
看API也基本明了,具体API参数如下
0020DC30 01021376/CALL 到 CreateFileA 来自 MKssj.01021370
0020DC34 0020E088|FileName = "\\.\PhysicalDrive0"
0020DC38 C0000000|Access = GENERIC_READ|GENERIC_WRITE
0020DC3C 00000003|ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
0020DC40 00000000|pSecurity = NULL
0020DC44 00000003|Mode = OPEN_EXISTING
0020DC48 00000000|Attributes = 0
0020DC4C 00000000\hTemplateFile = NULL
0061DF4C 00121431/CALL 到 DeviceIoControl 来自 MKssj.0012142B
0061DF50 00000094|hDevice = 00000094 (window)
0061DF54 0007C088|IoControlCode = SMART_RCV_DRIVE_DATA
0061DF58 0061E384|InBuffer = 0061E384
0061DF5C 00000023|InBufferSize = 23 (35.)
0061DF60 0061E4A8|OutBuffer = 0061E4A8
0061DF64 00000213|OutBufferSize = 213 (531.)
0061DF68 0061DF80|pBytesReturned = 0061DF80
0061DF6C 00000000\pOverlapped = NULL
0102145D处对DeviceIoControl得出信息进行了加工
(这里要注意:如果在虚拟机中DeviceIoControl读取失败则将取"\\.\Scsi0:"值来计算,届时不触及下面的处理函数)
00311030 8B51 28 MOV EDX, DWORD PTR DS: ; 以字节组合
00311033 C1EA 08 SHR EDX, 0x8
00311036 8810 MOV BYTE PTR DS:, DL
00311038 0FB651 28 MOVZX EDX, BYTE PTR DS:
0031103C 8850 01 MOV BYTE PTR DS:, DL
......
003110BF 8B51 4C MOV EDX, DWORD PTR DS:
003110C2 8A49 4C MOV CL, BYTE PTR DS:
003110C5 C1EA 08 SHR EDX, 0x8
003110C8 8850 12 MOV BYTE PTR DS:, DL
003110CB 8848 13 MOV BYTE PTR DS:, CL ; 生成磁盘号字符串
003110CE C640 14 00 MOV BYTE PTR DS:, 0x0 ; '\x00'截断字符串
003110D2 B9 13000000 MOV ECX, 0x13
003110D7 803C01 20 /CMP BYTE PTR DS:, 0x20 ; 从字符串的最后一位依次处理
003110DB 75 09 |JNZ SHORT MKssj.003110E6 ; 简单的说就是删除字符串后面的空格
003110DD C60401 00 |MOV BYTE PTR DS:, 0x0
003110E1 49 |DEC ECX
003110E2 85C9 |TEST ECX, ECX
003110E4^ 7F F1 \JG SHORT MKssj.003110D7
003110E6 C3 RETN
DeviceIoControl_OutBuffer处理函数大致如下
将DeviceIoControl得出并二次处理过的磁盘号字符串MD5运算一次,值再经过一些运算和处理最终生成了机器码,代码见下
import md5
from binascii import hexlify
from string import upper
#为方便运算,变量md5_16故意多设置了一位(首字节无意义可忽略)
md5_16 ='!' + md5.new(" WD-WMC1S4965023").digest()
tempID = {}
machineID = ''
for j in range(1,17):
tempID= chr(((ord(md5_16) >> 0x4)*3) & 0x8000000F)
if tempID<=0:
tempID = chr(ord(tempID) - 1)
tempID = chr(ord(tempID) | 0xFFFFFFF0)
tempID = chr(ord(tempID) + 1)
tempID = upper(hexlify(tempID))
if ord(tempID)>=0x41:
tempID = chr(ord(tempID) + 0xA)
tempID = chr(ord(md5_16) & 0x8000000F)
if tempID<=0:
tempID = chr(ord(tempID) - 1)
tempID = chr(ord(tempID) | 0xFFFFFFF0)
tempID = chr(ord(tempID) + 1)
tempID = chr((ord(tempID) * 3) & 0x8000000F)
if tempID<=0:
tempID = chr(ord(tempID) - 1)
tempID = chr(ord(tempID) | 0xFFFFFFF0)
tempID = chr(ord(tempID) + 1)
tempID = hexlify(tempID)
if ord(tempID)>=0x41:
tempID = chr(ord(tempID) + 0xA)
#最终显示出的机器码共32位,使用"-"均分四段
for j in range(1,33):
if j in (9,17,25):
machineID = machineID + '-'
machineID = machineID +tempID
print u'本机机器码是:%s' % machineID
在题目原帖第二页看到@xiaobai 和@qq54007 提供的一对机器码和注册码
http://www.52pojie.cn/thread-230220-2-1.html
机器码:Om503m10-L7K46494-2n44N8M0-1pO8L8M0
注册码:m5CYe7j3bnsW4NArE8kTA2Se6fbfNmNIKXV4
这下被我们抓住了小辫儿{:301_1000:}
经过前面分析DeviceIoControl_OutBuffer处理函数结尾处生成了磁盘号字符串,而在我的虚拟XP中该字符串为“00000000000000000001”,机器码恰好是Om503m10-L7K46494-2n44N8M0-1pO8L8M0
这就好办了,脱壳后SM主程序强制修改DeviceIoControl_OutBuffer处理函数来PATCH(即强制修改磁盘号字符串为“00000000000000000001”),PATCH前后对比如图
(PATCH的时机可以自己考虑,比如也可以尝试在机器码生成处(断点winmm.timeSetEvent直接定位)直接PATCH机器码)
PATCH前
PATCH后
为了防止DeviceIoControl读取失败导致程序不执行PATCH函数,再做个小修改
PATCH两处代码后无论在哪台电脑运行机器码都是固定的,成功注册——搞定!
查表注册固有缺点就是码表会增加程序体积、码表易受攻击、注册码数量有限(同码表值对应),且防窜改机制显得尤为重要!
本文采用机器码ID的PATCH攻击,而话音刚落@MistHill就放出了码表攻击思路(具体见28楼),@qq54007可以考虑查缺补漏再次加强
代码运算结果截图如下: 膜拜会C++的大牛 膜拜大神 找关键位置的方法学习了,感谢 你好你的分析写的很详细,脱壳其实很简单,他的自校验很失败,他其实本想用内存读取错误来代替ExitProcess,结果报错的时候直接出现错误地址了,定位一下改个跳转就行了(就是你图片的那个)。就能脱了。
他有个致命失误是用了api来比较,所以 比较容易定位关键
最后的比较位置是
004019D3 83C4 10 add esp,0x10
004019D6 83FE 10 cmp esi,0x10
004019D9 8935 DCFA6100 mov dword ptr ds:,esi
004019DF^ 0F8C 2CFFFFFF jl 123.00401911
004019E5 8B5424 28 mov edx,dword ptr ss:
004019E9 52 push edx
004019EA 8D8424 6C020000 lea eax,dword ptr ss:
004019F1 50 push eax
004019F2 FF15 18B04000 call dword ptr ds:[<&kernel32.lstrcmp>]; kernel32.lstrcmpA;对比了两个md5
004019F8 85C0 test eax,eax
004019FA 0F85 5E010000 jnz 123.00401B5E ;nop进入解码部分
00401A00 F3: prefix rep:
00401A01 0F7E05 B0EC4000 movd dword ptr ds:,mm0
00401A08 8A4424 11 mov al,byte ptr ss:
00401A0C 3C 61 cmp al,0x61
00401A0E 66:0FD6 ??? ; 未知命令
00401A11 05 C0EC4000 add eax,123.0040ECC0
00401A16 F3: prefix rep:
00401A17 0F7E05 B8EC4000 movd dword ptr ds:,mm0
00401A1E 66:0FD6 ??? ; 未知命令
00401A21 05 C8EC4000 add eax,123.0040ECC8
00401A26 0FBED0 movsx edx,al
00401A29 7E 05 jle X123.00401A30
00401A2B 83EA 57 sub edx,0x57
00401A2E EB 0C jmp X123.00401A3C
00401A30 3C 41 cmp al,0x41
看代码应该是加了花,我并没发现的你分析里有着一段,MD5校验前的数值应该是来解码的。
接下来就是解码的过程。而且是不断的解码。我估计是用来防止爆破的。
0102FB54 00000009
0102FB58 00000009
0102FB5C 00000009
应该是循环的次数
你把那个跳nop之后
004019FA 0F85 5E010000 jnz 123.00401B5E
会不断的去解码,结果就变成了乱码。
当然也许要是真码的话,是解码多次出来的。不清楚
结过凑了一个值123123再在那个位置修改标志,而不是nop就是说解码一次,尼玛就恭喜了。
这个cm要写注册机,完全分析很耗体力,果然挡住破解就是耗cracker体力的办法。
本帖最后由 playboysen 于 2013-12-29 18:59 编辑
L4Nce 发表于 2013-12-29 14:27 static/image/common/back.gif
你好你的分析写的很详细,脱壳其实很简单,他的自校验很失败,他其实本想用内存读取错误来代替ExitProcess, ...
L4Nce这一刀补的很及时!
关于脱壳,我想说的是64位系统真的不适合调试!!!坑死哥了!
本机Win7 X64脱壳N次IAT修复总是错误,手工修复也不行害得我每次都要带壳调试⊙﹏⊙b
关于爆破,直接NOP跳转肯定不行毕竟这是个解码程序
不过就算将解码次数修改为1次,想要凑出“恭喜”对应的明文注册码也不容易
考虑下出篇破文吧
L4Nce 发表于 2013-12-29 14:27 static/image/common/back.gif
你好你的分析写的很详细,脱壳其实很简单,他的自校验很失败,他其实本想用内存读取错误来代替ExitProcess, ...
完整的字符串因该是:“恭喜你注册成功”,字串加密这里只是简单的异或加密,所以很容易穷举破解(以后会考虑其他算法),如果再细心一点,可以发现加密的位置就在程序初始化的时候(考虑程序的可解性),程序之所以多次验证(4096次),是为了找到注册码中正确的解码因子,以及让验证代码数量大于破解者可以接受的单步跟踪次数。最阴险的暗桩在于检测调试器到后并不报警,而是悄悄的再一次加密关键代码,以至于即使注册码正确,依然无法正确解密。程序并没有加花,自校验也如你所说,在xp下直接暴漏关键位置(在win7下只显示程序停止)。如果程序关键加密部分分析的清楚,爆破起来还是挺容易的,并没有太多的暗桩。 playboysen 发表于 2013-12-29 17:53 static/image/common/back.gif
L4Nce这一刀补的很及时!
不过关于脱壳能否说的再详细点?
应该是你系统的问题吧,脱壳就正常脱即可,只有一处自校验而已。
关于你的4个方案,第3个事最靠谱的、最无敌的,不过得首先得有一套正确的机器码和注册码。
分析的很好,只有解码那里差了一点点。十分佩服。 本帖最后由 playboysen 于 2013-12-29 23:38 编辑
qq54007 发表于 2013-12-29 18:29 static/image/common/back.gif
应该是你系统的问题吧,脱壳就正常脱即可,只有一处自校验而已。
关于你的4个方案,第3个事最靠谱的、最 ...
Win7 X64害死人了{:301_995:}
以后调试还是要建个XP虚拟机才行
因为考虑算法不可逆性,也懒得耗费大量精力关注解码部分
你提到的那个“最阴险的暗桩”确实效果非凡,折腾了几次都不正确我都想干脆放弃了⊙﹏⊙b
playboysen 发表于 2013-12-29 18:51 static/image/common/back.gif
Win7 X64害死人了
以后调试还是要建个XP虚拟机才行
凡是调用CopyMemory函数,代码都被编译成这个模样,应该是因为这个函数是个内联函数?最有效率的执行方式就是这样?