首页
社区
课程
招聘
[原创] CTF2017 第11题 ToBeBetter CrackMe 详细破解过程
2017-6-23 10:35 4459

[原创] CTF2017 第11题 ToBeBetter CrackMe 详细破解过程

2017-6-23 10:35
4459

序言:

   本题使用了父子进程守护、壳、SMC、反调试、DES算法、多个坑等设计,难度不低,可惜多解,现仅提交140组答案,
   破解此题需要攻击者具备解壳或动态调试能力、跟踪父子进程、熟谙DES算法细节和直觉(否则栽在坑里了)。

一、侦壳、PE格式,试运行了解作品(套路),未知壳,作品有窗口还有错误提示(容易找到合适的断点位置)

二、解壳:

// 破解本题无需真的脱掉壳,只需要找到OEP,利用OD脚本进行自动断在OEP并Patch代码即可。
// OD载入,停在这里:

0044742E >  E8 ECFBFFFF     CALL 11-ToBeB.0044701F
00447433    C3              RETN

// 跟入CALL 44701F,省略一系列解码过程,破解此题不需要关心这些,只需要找到OEP

0044739E    8B85 E0FEFFFF   MOV EAX,DWORD PTR SS:[EBP-0x120]
004473A4    8BE5            MOV ESP,EBP
004473A6    5D              POP EBP
004473A7    59              POP ECX
004473A8    FFE0            JMP EAX                                  ; 飞向光明顶,此处设断
004473AA    83BD ECFEFFFF 0>CMP DWORD PTR SS:[EBP-0x114],0x0

// 来到OEP

00418356    E8 574C0000     CALL 11-ToBeB.0041CFB2                   ; OEP
0041835B  ^ E9 7FFEFFFF     JMP 11-ToBeB.004181DF
00418360    8B5424 0C       MOV EDX,DWORD PTR SS:[ESP+0xC]
00418364    8B4C24 04       MOV ECX,DWORD PTR SS:[ESP+0x4]

// 可以写个OD脚本,自动断OEP

BPHWS 418356,"x"
RUN
BPHWC 418356


三、分析子进程的坑

// OD加载运行后,只出来一个带窗口进程,与正常运行的双进程不同,说明这是个坑
// 是坑也得分析不是?bp GetDlgItemTextW

0012F4F8   004017A9  /CALL 到 GetDlgItemTextW 来自 11-ToBeB.004017A3
0012F4FC   001701AC  |hWnd = 001701AC ('CrackMe',class='#32770')
0012F500   000003E9  |ControlID = 3E9 (1001.)
0012F504   0012F510  |Buffer = 0012F510
0012F508   000001FE  \Count = 1FE (510.)

// 返回程序

004017A3    FF15 90A14200   CALL DWORD PTR DS:[0x42A190]                            ; 获得输入
004017A9    C745 08 0000000>MOV DWORD PTR SS:[EBP+0x8],0x0
004017B0    33D2            XOR EDX,EDX
004017B2    B8 108B1E45     MOV EAX,0x451E8B10
004017B7    8B0D 087A4300   MOV ECX,DWORD PTR DS:[0x437A08]
004017BD    F7F1            DIV ECX                                                 ; 父进程对次处NOP
004017BF    8945 08         MOV DWORD PTR SS:[EBP+0x8],EAX
004017C2    FF75 08         PUSH DWORD PTR SS:[EBP+0x8]
004017C5    8D85 00FAFFFF   LEA EAX,DWORD PTR SS:[EBP-0x600]
004017CB    50              PUSH EAX
004017CC    E8 6F020000     CALL 11-ToBeB.00401A40                                  ; 算法流程(带坑)
004017D1    83C4 08         ADD ESP,0x8
004017D4    C785 00FCFFFF D>MOV DWORD PTR SS:[EBP-0x400],0x793A63D0
004017DE    85C0            TEST EAX,EAX
004017E0    8D85 04FCFFFF   LEA EAX,DWORD PTR SS:[EBP-0x3FC]
004017E6    68 FA010000     PUSH 0x1FA
004017EB    6A 00           PUSH 0x0
004017ED    50              PUSH EAX
004017EE    74 58           JE X11-ToBeB.00401848                                   ; 不能跳
004017F0    E8 6B6B0100     CALL 11-ToBeB.00418360
004017F5    68 F6010000     PUSH 0x1F6
004017FA    8D85 08FEFFFF   LEA EAX,DWORD PTR SS:[EBP-0x1F8]
00401800    C785 00FEFFFF E>MOV DWORD PTR SS:[EBP-0x200],0x518C6CE8
0040180A    6A 00           PUSH 0x0
0040180C    50              PUSH EAX
0040180D    C785 04FEFFFF 1>MOV DWORD PTR SS:[EBP-0x1FC],0x529F6210
00401817    E8 446B0100     CALL 11-ToBeB.00418360
0040181C    83C4 18         ADD ESP,0x18
0040181F    8D85 00FCFFFF   LEA EAX,DWORD PTR SS:[EBP-0x400]
00401825    6A 00           PUSH 0x0
00401827    50              PUSH EAX
00401828    8D85 00FEFFFF   LEA EAX,DWORD PTR SS:[EBP-0x200]
0040182E    50              PUSH EAX
0040182F    56              PUSH ESI
00401830    FF15 8CA14200   CALL DWORD PTR DS:[0x42A18C]                            ; 注册成功
00401836    6A 02           PUSH 0x2
00401838    56              PUSH ESI
00401839    FF15 88A14200   CALL DWORD PTR DS:[0x42A188]                            ; user32.EndDialog
0040183F    33C0            XOR EAX,EAX
00401841    5E              POP ESI
00401842    8BE5            MOV ESP,EBP
00401844    5D              POP EBP
00401845    C2 1000         RETN 0x10
00401848    E8 136B0100     CALL 11-ToBeB.00418360
0040184D    68 F6010000     PUSH 0x1F6
00401852    8D85 08FEFFFF   LEA EAX,DWORD PTR SS:[EBP-0x1F8]
00401858    C785 00FEFFFF E>MOV DWORD PTR SS:[EBP-0x200],0x518C6CE8
00401862    6A 00           PUSH 0x0
00401864    50              PUSH EAX
00401865    C785 04FEFFFF 3>MOV DWORD PTR SS:[EBP-0x1FC],0x8D255931
0040186F    E8 EC6A0100     CALL 11-ToBeB.00418360
00401874    83C4 18         ADD ESP,0x18
00401877    8D85 00FCFFFF   LEA EAX,DWORD PTR SS:[EBP-0x400]
0040187D    6A 00           PUSH 0x0
0040187F    50              PUSH EAX
00401880    8D85 00FEFFFF   LEA EAX,DWORD PTR SS:[EBP-0x200]
00401886    50              PUSH EAX
00401887    56              PUSH ESI
00401888    FF15 8CA14200   CALL DWORD PTR DS:[0x42A18C]                            ; 注册失败
0040188E    33C0            XOR EAX,EAX
00401890    5E              POP ESI
00401891    8BE5            MOV ESP,EBP
00401893    5D              POP EBP
00401894    C2 1000         RETN 0x10
00401897    66:83F8 02      CMP AX,0x2
0040189B    75 0B           JNZ X11-ToBeB.004018A8
0040189D    6A 02           PUSH 0x2
0040189F    FF75 08         PUSH DWORD PTR SS:[EBP+0x8]
004018A2    FF15 88A14200   CALL DWORD PTR DS:[0x42A188]                            ; user32.EndDialog
004018A8    33C0            XOR EAX,EAX
004018AA    8BE5            MOV ESP,EBP
004018AC    5D              POP EBP
004018AD    C2 1000         RETN 0x10

// CALL 401A40 为子进程算法,跟进

00401A40    55              PUSH EBP
00401A41    8BEC            MOV EBP,ESP
00401A43    83EC 0C         SUB ESP,0xC                                             ; 父进程对此处进行流程修改
00401A46    53              PUSH EBX
00401A47    56              PUSH ESI
00401A48    57              PUSH EDI
00401A49    8B7D 08         MOV EDI,DWORD PTR SS:[EBP+0x8]
00401A4C    33D2            XOR EDX,EDX
00401A4E    33F6            XOR ESI,ESI
00401A50    8955 F8         MOV DWORD PTR SS:[EBP-0x8],EDX
00401A53    8975 FC         MOV DWORD PTR SS:[EBP-0x4],ESI
00401A56    33C9            XOR ECX,ECX
00401A58    8D5F 02         LEA EBX,DWORD PTR DS:[EDI+0x2]
00401A5B    EB 03           JMP X11-ToBeB.00401A60
00401A5D    8D49 00         LEA ECX,DWORD PTR DS:[ECX]
00401A60    66:8B07         MOV AX,WORD PTR DS:[EDI]
00401A63    83C7 02         ADD EDI,0x2
00401A66    66:85C0         TEST AX,AX
00401A69  ^ 75 F5           JNZ X11-ToBeB.00401A60
00401A6B    2BFB            SUB EDI,EBX
00401A6D    D1FF            SAR EDI,1
00401A6F    83FF 14         CMP EDI,0x14                                            ; 虚假的 < 20位
00401A72    7C 09           JL X11-ToBeB.00401A7D
00401A74    5F              POP EDI
00401A75    5E              POP ESI
00401A76    33C0            XOR EAX,EAX
00401A78    5B              POP EBX
00401A79    8BE5            MOV ESP,EBP
00401A7B    5D              POP EBP
00401A7C    C3              RETN

// 异或累加 == 0x127514D 就是个很直白的坑

00401AE2    8D42 61         LEA EAX,DWORD PTR DS:[EDX+0x61]
00401AE5    8B55 F8         MOV EDX,DWORD PTR SS:[EBP-0x8]
00401AE8    98              CWDE
00401AE9    33F0            XOR ESI,EAX                                             ; 异或
00401AEB    03D6            ADD EDX,ESI                                             ; 累加
00401AED    8B75 FC         MOV ESI,DWORD PTR SS:[EBP-0x4]
00401AF0    8955 F8         MOV DWORD PTR SS:[EBP-0x8],EDX
00401AF3    3BDF            CMP EBX,EDI
00401AF5  ^ 7C 99           JL X11-ToBeB.00401A90
00401AF7    33C0            XOR EAX,EAX
00401AF9    81FA 4D512701   CMP EDX,0x127514D                                       ; 异或累加值 == 0x127514D
00401AFF    5F              POP EDI                                                 ; 测试了几组长度离答案差太多,肯定是坑

// 此时想到父进程必然有小动作
// 找到加载窗口的位置(父进程不显示窗口,所以在相关位置必有选择)

00401691  |.  8BEC          MOV EBP,ESP
00401693  |.  83EC 70       SUB ESP,0x70
00401696  |.  803D 509B4300>CMP BYTE PTR DS:[0x439B50],0x0
0040169D  |.  0F84 88000000 JE dumped_.0040172B
004016A3  |.  E8 08FFFFFF   CALL dumped_.004015B0
004016A8  |.  803D 519B4300>CMP BYTE PTR DS:[0x439B51],0x0
004016AF  |.  74 1C         JE Xdumped_.004016CD                                      ;  父进程子进程选择分支
004016B1  |.  6A 00         PUSH 0x0                                                  ; /lParam = NULL
004016B3  |.  68 40174000   PUSH dumped_.00401740                                     ; |DlgProc = dumped_.00401740
004016B8  |.  6A 00         PUSH 0x0                                                  ; |hOwner = NULL
004016BA  |.  6A 67         PUSH 0x67                                                 ; |pTemplate = 0x67
004016BC  |.  FF75 08       PUSH DWORD PTR SS:[EBP+0x8]                               ; |hInst
004016BF  |.  FF15 94A14200 CALL DWORD PTR DS:[<&user32.DialogBoxParamW>]             ; \DialogBoxParamW
004016C5  |.  33C0          XOR EAX,EAX
004016C7  |.  8BE5          MOV ESP,EBP
004016C9  |.  5D            POP EBP
004016CA  |.  C2 1000       RETN 0x10
004016CD  |>  8D4D 90       LEA ECX,DWORD PTR SS:[EBP-0x70]
004016D0  |.  E8 0BF10000   CALL dumped_.004107E0
004016D5  |.  E8 C6FEFFFF   CALL dumped_.004015A0
004016DA  |.  8D4D EC       LEA ECX,DWORD PTR SS:[EBP-0x14]

//置父子进程标志位的地方

0040145D    A2 519B4300     MOV BYTE PTR DS:[0x439B51],AL


四、分析父进程小动作

// 正常运行之后,OD附加父进程,对CODE段下访问断点,断到

04115B0    56              PUSH ESI
004115B1    8BF1            MOV ESI,ECX
004115B3    8B46 20         MOV EAX,DWORD PTR DS:[ESI+0x20]
004115B6    85C0            TEST EAX,EAX
004115B8    74 07           JE Xdumped_.004115C1
004115BA    50              PUSH EAX
004115BB    FF15 7CA04200   CALL DWORD PTR DS:[<&kernel32.SetEvent>]                  ; kernel32.SetEvent
004115C1    8B46 24         MOV EAX,DWORD PTR DS:[ESI+0x24]
004115C4    85C0            TEST EAX,EAX
004115C6    74 07           JE Xdumped_.004115CF
004115C8    50              PUSH EAX
004115C9    FF15 78A04200   CALL DWORD PTR DS:[<&kernel32.DebugActiveProcessStop>]    ; kernel32.DebugActiveProcessStop
004115CF    8B46 1C         MOV EAX,DWORD PTR DS:[ESI+0x1C]
004115D2    83F8 FF         CMP EAX,-0x1
004115D5    74 3E           JE Xdumped_.00411615
004115D7    57              PUSH EDI
004115D8    8B3D 80A04200   MOV EDI,DWORD PTR DS:[<&kernel32.WaitForSingleObject>]    ; kernel32.WaitForSingleObject
004115DE    6A 00           PUSH 0x0
004115E0    50              PUSH EAX
004115E1    FFD7            CALL EDI
004115E3    3D 02010000     CMP EAX,0x102
004115E8    75 1A           JNZ Xdumped_.00411604
004115EA    53              PUSH EBX
004115EB    8B1D 88A04200   MOV EBX,DWORD PTR DS:[<&kernel32.Sleep>]                  ; kernel32.Sleep
004115F1    6A 64           /PUSH 0x64
004115F3    FFD3            |CALL EBX
004115F5    6A 00           |PUSH 0x0
004115F7    FF76 1C         |PUSH DWORD PTR DS:[ESI+0x1C]
004115FA    FFD7            |CALL EDI
004115FC    3D 02010000     |CMP EAX,0x102
00411601  ^ 74 EE           \JE Xdumped_.004115F1
00411603    5B              POP EBX
00411604    FF76 1C         PUSH DWORD PTR DS:[ESI+0x1C]
00411607    FF15 2CA04200   CALL DWORD PTR DS:[<&kernel32.CloseHandle>]               ; kernel32.CloseHandle
0041160D    C746 1C FFFFFFF>MOV DWORD PTR DS:[ESI+0x1C],-0x1
00411614    5F              POP EDI
00411615    8B46 20         MOV EAX,DWORD PTR DS:[ESI+0x20]
00411618    85C0            TEST EAX,EAX
0041161A    74 0E           JE Xdumped_.0041162A
0041161C    50              PUSH EAX
0041161D    FF15 2CA04200   CALL DWORD PTR DS:[<&kernel32.CloseHandle>]               ; kernel32.CloseHandle
00411623    C746 20 0000000>MOV DWORD PTR DS:[ESI+0x20],0x0
0041162A    C746 54 0000000>MOV DWORD PTR DS:[ESI+0x54],0x0
00411631    5E              POP ESI
00411632    C3              RETN

// 父进程再设断点

00410AA2    8B1D 74A04200   MOV EBX,DWORD PTR DS:[0x42A074]          ; kernel32.WaitForDebugEvent
00410AA8    6A FF           PUSH -0x1
00410AAA    8D45 8C         LEA EAX,DWORD PTR SS:[EBP-0x74]
00410AAD    50              PUSH EAX
00410AAE    FFD3            CALL EBX
00410AB0    85C0            TEST EAX,EAX                             ; 断点这里
00410AB2    74 3C           JE X11-ToBeB.00410AF0
00410AB4    8D45 8C         LEA EAX,DWORD PTR SS:[EBP-0x74]
00410AB7    50              PUSH EAX
00410AB8    8B47 18         MOV EAX,DWORD PTR DS:[EDI+0x18]
00410ABB    57              PUSH EDI
00410ABC    6A 00           PUSH 0x0
00410ABE    FFD0            CALL EAX
00410AC0    8D45 8C         LEA EAX,DWORD PTR SS:[EBP-0x74]
00410AC3    50              PUSH EAX
00410AC4    57              PUSH EDI
00410AC5    FF75 8C         PUSH DWORD PTR SS:[EBP-0x74]
00410AC8    E8 93010000     CALL 11-ToBeB.00410C60
00410ACD    8945 EC         MOV DWORD PTR SS:[EBP-0x14],EAX
00410AD0    8D45 8C         LEA EAX,DWORD PTR SS:[EBP-0x74]
00410AD3    50              PUSH EAX
00410AD4    8B47 18         MOV EAX,DWORD PTR DS:[EDI+0x18]
00410AD7    57              PUSH EDI
00410AD8    6A 01           PUSH 0x1
00410ADA    FFD0            CALL EAX                                 ; 修改子进程2处
00410ADC    83C4 24         ADD ESP,0x24
00410ADF    FF75 EC         PUSH DWORD PTR SS:[EBP-0x14]
00410AE2    FF75 94         PUSH DWORD PTR SS:[EBP-0x6C]
00410AE5    FF75 90         PUSH DWORD PTR SS:[EBP-0x70]
00410AE8    FF15 70A04200   CALL DWORD PTR DS:[0x42A070]             ; kernel32.ContinueDebugEvent

// 410ADA CALL EAX 有小动作,跟进

00402000    50              PUSH EAX
00402001    6A 02           PUSH 0x2
00402003    8D45 08         LEA EAX,DWORD PTR SS:[EBP+0x8]
00402006    66:C745 08 9090 MOV WORD PTR SS:[EBP+0x8],0x9090
0040200C    50              PUSH EAX
0040200D    FF76 18         PUSH DWORD PTR DS:[ESI+0x18]
00402010    52              PUSH EDX
00402011    FFD3            CALL EBX                                 ; WriteProcessMemory

// 堆栈

015BFCD4   00000154
015BFCD8   004017BD  11-ToBeB.004017BD
015BFCDC   015BFE5C
015BFCE0   00000002
015BFCE4   015BFE44
015BFCE8   76EFD000  kernel32.GetLastError

// 将子进程 4017BD 位置用 9090 NOP掉

// 改前
004017BD    F7F1            DIV ECX

// 改后
004017BD    90              NOP                                                     ; 父进程对次处NOP
004017BE    90              NOP

// 还有小动作

00402080    50              PUSH EAX
00402081    6A 05           PUSH 0x5
00402083    8D45 F0         LEA EAX,DWORD PTR SS:[EBP-0x10]
00402086    50              PUSH EAX
00402087    8B02            MOV EAX,DWORD PTR DS:[EDX]
00402089    8B00            MOV EAX,DWORD PTR DS:[EAX]
0040208B    05 37373737     ADD EAX,0x37373737
00402090    35 4EE5B3C8     XOR EAX,0xC8B3E54E
00402095    83C0 03         ADD EAX,0x3
00402098    50              PUSH EAX
00402099    57              PUSH EDI
0040209A    FFD3            CALL EBX                                 ; WriteProcessMemory

// 堆栈

015BFCD8   00401A43  11-ToBeB.00401A43
015BFCDC   015BFE44
015BFCE0   00000005
015BFCE4   015BFE5C
015BFCE8   76EFD000  kernel32.GetLastError
015BFCEC   0012FECC

// [15BFE44] = E9 BB 08 00 00
// 将子进程 401A43 位置用 E9 BB 08 00 00 修改

// 改前:
00401A43    83EC 0C         SUB ESP,0xC
00401A46    53              PUSH EBX
00401A47    56              PUSH ESI

// 改后:
00401A43   /E9 BB080000     JMP 11-ToBeB.00402303

// 至此,已经分析清楚父进程对子进程的两处代码修改,后续调试我们就脱离父进程
// OD脚本模拟父进程自动patch代码

BPHWS 418356,"x"
RUN
BPHWC 418356
MOV [4017BD],#9090#
MOV [401A43],#E9BB080000#


五、分析第一段算法

// 重新再来(代码已经patch)

004017A3    FF15 90A14200   CALL DWORD PTR DS:[0x42A190]                            ; 获得输入
004017A9    C745 08 0000000>MOV DWORD PTR SS:[EBP+0x8],0x0
004017B0    33D2            XOR EDX,EDX
004017B2    B8 108B1E45     MOV EAX,0x451E8B10
004017B7    8B0D 087A4300   MOV ECX,DWORD PTR DS:[0x437A08]
004017BD    90              NOP                                                     ; 父进程对次处NOP
004017BE    90              NOP
004017BF    8945 08         MOV DWORD PTR SS:[EBP+0x8],EAX
004017C2    FF75 08         PUSH DWORD PTR SS:[EBP+0x8]
004017C5    8D85 00FAFFFF   LEA EAX,DWORD PTR SS:[EBP-0x600]
004017CB    50              PUSH EAX
004017CC    E8 6F020000     CALL 11-ToBeB.00401A40                                  ; 算法流程(带坑)

00401A40    55              PUSH EBP
00401A41    8BEC            MOV EBP,ESP
00401A43    E9 BB080000     JMP 11-ToBeB.00402303                                   ; 父进程对此处进行流程修改

// 跟踪改变后的流程,动态申请内存0x5000字节,用于存放待解密数据

00402657    6A 40           PUSH 0x40
00402659    68 00100000     PUSH 0x1000
0040265E    68 00500000     PUSH 0x5000
00402663    6A 00           PUSH 0x0
00402665    FFD7            CALL EDI                                                ; ZwAllocateVirtualMemory
00402667    8945 F4         MOV DWORD PTR SS:[EBP-0xC],EAX

// 用于读取自身0x5000字节待解密数据

004029B7    6A 00           PUSH 0x0
004029B9    8D45 80         LEA EAX,DWORD PTR SS:[EBP-0x80]
004029BC    50              PUSH EAX
004029BD    68 00500000     PUSH 0x5000
004029C2    FF75 F4         PUSH DWORD PTR SS:[EBP-0xC]
004029C5    FF75 D0         PUSH DWORD PTR SS:[EBP-0x30]
004029C8    FFD7            CALL EDI                                                ; ZwReadFile
004029CA    8B75 80         MOV ESI,DWORD PTR SS:[EBP-0x80]

// 获取输入并转换格式十六进制

004029D5    8B5D 08         MOV EBX,DWORD PTR SS:[EBP+0x8]           ; input
004029D8    8D55 F8         LEA EDX,DWORD PTR SS:[EBP-0x8]
004029DB    8BCB            MOV ECX,EBX
004029DD    C745 F8 0000000>MOV DWORD PTR SS:[EBP-0x8],0x0
004029E4    E8 D7F1FFFF     CALL 11-ToBeB.00401BC0                   ; 检查格式,转16进制
004029E9    85C0            TEST EAX,EAX
004029EB    0F84 49010000   JE 11-ToBeB.00402B3A                     ; 不能跳
004029F1    8D4B 10         LEA ECX,DWORD PTR DS:[EBX+0x10]
004029F4    C645 F3 00      MOV BYTE PTR SS:[EBP-0xD],0x0
004029F8    8D55 F3         LEA EDX,DWORD PTR SS:[EBP-0xD]
004029FB    E8 10F1FFFF     CALL 11-ToBeB.00401B10                   ; 检查格式,转16进制

00401BC0    56              PUSH ESI                                 ; 转十六进制
00401BC1    57              PUSH EDI
00401BC2    8BF9            MOV EDI,ECX
00401BC4    C702 00000000   MOV DWORD PTR DS:[EDX],0x0
00401BCA    33F6            XOR ESI,ESI
00401BCC    8D6424 00       LEA ESP,DWORD PTR SS:[ESP]
00401BD0    0FB70477        MOVZX EAX,WORD PTR DS:[EDI+ESI*2]        ; input
00401BD4    83F8 30         CMP EAX,0x30
00401BD7    72 05           JB X11-ToBeB.00401BDE
00401BD9    83F8 39         CMP EAX,0x39
00401BDC    76 0A           JBE X11-ToBeB.00401BE8
00401BDE    83F8 41         CMP EAX,0x41
00401BE1    72 3E           JB X11-ToBeB.00401C21
00401BE3    83F8 5A         CMP EAX,0x5A
00401BE6    77 39           JA X11-ToBeB.00401C21                    ; 要求输入 0 - 9 或 A - Z
00401BE8    8B0A            MOV ECX,DWORD PTR DS:[EDX]               ; 取变量,初值0
00401BEA    C1E1 04         SHL ECX,0x4                              ; *16 -- > 进位,转十六进制
00401BED    890A            MOV DWORD PTR DS:[EDX],ECX               ; 结果存回变量
00401BEF    83F8 30         CMP EAX,0x30
00401BF2    72 0A           JB X11-ToBeB.00401BFE
00401BF4    83F8 39         CMP EAX,0x39
00401BF7    77 05           JA X11-ToBeB.00401BFE
00401BF9    83E8 30         SUB EAX,0x30                             ; -0x30 数字
00401BFC    EB 11           JMP X11-ToBeB.00401C0F
00401BFE    83F8 41         CMP EAX,0x41
00401C01    72 0A           JB X11-ToBeB.00401C0D
00401C03    83F8 5A         CMP EAX,0x5A
00401C06    77 05           JA X11-ToBeB.00401C0D
00401C08    83E8 37         SUB EAX,0x37                             ; -0x37 大写字母
00401C0B    EB 02           JMP X11-ToBeB.00401C0F
00401C0D    33C0            XOR EAX,EAX
00401C0F    03C1            ADD EAX,ECX                              ; 加到累加变量
00401C11    46              INC ESI
00401C12    8902            MOV DWORD PTR DS:[EDX],EAX
00401C14    83FE 08         CMP ESI,0x8
00401C17  ^ 7C B7           JL X11-ToBeB.00401BD0
00401C19    5F              POP EDI                                  ; 01389DBE
00401C1A    B8 01000000     MOV EAX,0x1
00401C1F    5E              POP ESI
00401C20    C3              RETN

// 第一段算法暴露(SMC)

00402A12    0FB645 F3       MOVZX EAX,BYTE PTR SS:[EBP-0xD]          ; 第9、10位
00402A16    8B7D F8         MOV EDI,DWORD PTR SS:[EBP-0x8]           ; 第1-8位
00402A19    8B5D F4         MOV EBX,DWORD PTR SS:[EBP-0xC]
00402A1C    69F0 01010101   IMUL ESI,EAX,0x1010101                   ; ESI = EAX 第9、10位 * 0x1010101
00402A22    8B0493          MOV EAX,DWORD PTR DS:[EBX+EDX*4]         ; 表数组,准备解密
00402A25    8D0C3A          LEA ECX,DWORD PTR DS:[EDX+EDI]           ; 初值第1-8位,后面就是对其累加1
00402A28    03C6            ADD EAX,ESI                              ; 表数组 + (第9、10位 * 0x1010101)
00402A2A    33C8            XOR ECX,EAX                              ; 第1-8位 xor (表 + (第9、10位 * 0x1010101))
00402A2C    890C93          MOV DWORD PTR DS:[EBX+EDX*4],ECX         ; 异或结果存回地址
00402A2F    42              INC EDX                                  ; 计数器+1
00402A30    8B45 80         MOV EAX,DWORD PTR SS:[EBP-0x80]          ; 0x5000
00402A33    C1E8 02         SHR EAX,0x2                              ; /4 (>>2)
00402A36    3BD0            CMP EDX,EAX                              ; 5120次
00402A38  ^ 72 E8           JB X11-ToBeB.00402A22

if ( v68 & 0xFFFFFFFC )
  {
   v71 = v168;
   v72 = v167;
   v73 = 0x1010101 * v166;
   do
     {
      *(_DWORD *)(v72 + 4 * v70) = (v73 + *(_DWORD *)(v72 + 4 * v70)) ^ (v70 + v71);
        ++v70;
     }
   while ( v70 < v135 >> 2 );
     v69 = a1;
  }

// 异或解密后比较

00402A50    8A81 B0404300   MOV AL,BYTE PTR DS:[ECX+0x4340B0]                       ; 解密后数据必须与 [4340B0] 相同
00402A56    3A8411 B0404300 CMP AL,BYTE PTR DS:[ECX+EDX+0x4340B0]                   ; 比较异或解密后的数据是否正确
00402A5D    0F85 D7000000   JNZ 11-ToBeB.00402B3A
00402A63    41              INC ECX
00402A64    83F9 60         CMP ECX,0x60                                            ; 0x60字节
00402A67  ^ 72 E7           JB X11-ToBeB.00402A50
// 待解密代码:(后面省略一堆)
004F0000  83F08EA7
004F0004  3F0FBA29
004F0008  E747E97C
004F000C  93D03647
004F0010  EC72CD2C
004F0014  93C0BA2E
004F0018  90A578A3
004F001C  2A40BA2F
004F0020  DB3FF233
004F0024  9031FB09
004F0028  D1477258
004F002C  905E3DAC
004F0030  AB817C35
004F0034  6BD43434
004F0038  C49E84E4
004F003C  83B426AF
004F0040  51C0BA3A
004F0044  280080B8
004F0048  93BE3FF3
004F004C  8E36BA3B
004F0050  E9C0BA3C
004F0054  93C0BA29
004F0058  93C0B2C5
004F005C  1680CD3F
004F0060  92D0D6BC

// 解密后必须为:(后面省略一堆)
004340B0  1070EC81
004340B4  55530000
004340B8  BC8B5756
004340BC  00108424
004340C0  BBF63300
004340C4  00000001
004340C8  0725C68B
004340CC  79800000
004340D0  C8834805
004340D4  07B140F8
004340D8  C68BC82A
004340DC  07E28399
004340E0  F8C1C203
004340E4  38148A03
004340E8  D322FAD2
004340EC  10349488
004340F0  46000002
004340F4  7C40FE83
004340F8  0002BDCF
004340FC  05BA0000
00434100  BE000000
00434104  00000014
00434108  000008B9
0043410C  8DC03300

// 据此写Python穷举第一段注册码(穷举第9、10位,组合很少,秒出)

code_x = [0x83F08EA7,0x3F0FBA29]
code_m = [0x1070EC81,0x55530000]
for sn_9_10 in range(0x00,0x100):
    #注册码第9、10位只有这几百种
    y = code_m[0] ^ ((code_x[0] + sn_9_10 * 0x1010101) & 0xffffffff)
    y = y + 1
    if code_m[1] ^ y == ((code_x[1] + sn_9_10 * 0x1010101) & 0xffffffff):
        print ('找到 sn_9_10: %x' % sn_9_10)
        print ('找到 sn_1_8: %x' % (y-1))

// 找到 sn_9_10: E1,找到 sn_1_8: 75A29C09,因此第一段注册码(sn_1-10):“75A29C09E1”


六、分析第二段算法

// 上述解密后的动态申请的内存作为函数运行(DES+奇数位取反

00402ABE    C745 84 3139393>MOV DWORD PTR SS:[EBP-0x7C],0x33393931
00402AC5    50              PUSH EAX
00402AC6    8BC2            MOV EAX,EDX
00402AC8    C745 88 3039303>MOV DWORD PTR SS:[EBP-0x78],0x37303930
00402ACF    50              PUSH EAX                                                ; 除前10位之外的其他注册码
00402AD0    8D45 84         LEA EAX,DWORD PTR SS:[EBP-0x7C]
00402AD3    56              PUSH ESI
00402AD4    50              PUSH EAX                                                ; DES 密钥 "19930907"
00402AD5    F3:             PREFIX REP:                                             ; 多余的前缀
00402AD6    0F7F85 DCFEFFFF MOVQ QWORD PTR SS:[EBP-0x124],MM0
00402ADD    FFD7            CALL EDI                                                ; 调用刚才解密的代码,DES+奇数位取反

// 接下来跟进 CALL EDI,痛苦就此开始,第一眼判断可能位DES算法,使用工具计算结果不符,无奈跟进去分析,巨复杂
// 初步判断作者可能对DES的几个表做了调整,因此我花费了时间自己码了DES代码,对每一个DES步骤、表格进行检查(这工作量你懂得)
// 结果......DES解密过程没有问题,与标准的相同!!!真正的手脚在于DES末尾IP-1逆置换、交换左右32bit结束后,作者竟然将bit的奇数位取反!!!
// DES 解密末尾的坑:

003B2494   /79 05               JNS X003B249B                                        ; 非负数跳走
003B2496   |4A                  DEC EDX
003B2497   |83CA FE             OR EDX,0xFFFFFFFE
003B249A   |42                  INC EDX                                              ; 即bit奇数位取反,偶数位不变
003B249B   \75 11               JNZ X003B24AE                                        ; 下标偶数不跳,下标奇数跳
003B249D    389C34 10020000     CMP BYTE PTR SS:[ESP+ESI+0x210],BL                   ; 比较当前bit是否为1
003B24A4    0F95C1              SETNE CL                                             ; 为1时,CL=0;为0时,CL=1
003B24A7    888C34 10020000     MOV BYTE PTR SS:[ESP+ESI+0x210],CL                   ; 为1时就替换为0,为0时就替换为1,即取反
003B24AE    46                  INC ESI                                              ; 指向下一位
003B24AF    83FE 40             CMP ESI,0x40
003B24B2  ^ 7C D8               JL X003B248C

// 所以破解算法思路变成了将解密结果先bit奇数位取反,然后用“19930907”密钥进行DES加密,结果就是注册码的第二段
// CALL EDI 必须返回“!HelloHaniella!” 字符串

00402AF2    8A10            MOV DL,BYTE PTR DS:[EAX]
00402AF4    3A11            CMP DL,BYTE PTR DS:[ECX]
00402AF6    75 30           JNZ X11-ToBeB.00402B28                                  ; 比较"!HelloHaniella!"
00402AF8    84D2            TEST DL,DL
00402AFA    74 12           JE X11-ToBeB.00402B0E                                   ; 末尾0x00结束符
00402AFC    8A50 01         MOV DL,BYTE PTR DS:[EAX+0x1]
00402AFF    3A51 01         CMP DL,BYTE PTR DS:[ECX+0x1]                            ; 比较"!HelloHaniella!"
00402B02    75 24           JNZ X11-ToBeB.00402B28
00402B04    83C0 02         ADD EAX,0x2
00402B07    83C1 02         ADD ECX,0x2
00402B0A    84D2            TEST DL,DL                                              ; 末尾0x00结束符
00402B0C  ^ 75 E4           JNZ X11-ToBeB.00402AF2

// 比如,要使 CALL EDI 返回:

21 48 65 6C 6C 6F 48 61 6E 69 65 6C 6C 61 21 00  !HelloHaniella!
--> 0x2148656C6C6F48616E69656C6C612100

// 二进制

0 0 1 0 0 0 0 1 0 1 0 0 1 0 0 0
0 1 1 0 0 1 0 1 0 1 1 0 1 1 0 0
0 1 1 0 1 1 0 0 0 1 1 0 1 1 1 1
0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 1
0 1 1 0 1 1 1 0 0 1 1 0 1 0 0 1
0 1 1 0 0 1 0 1 0 1 1 0 1 1 0 0
0 1 1 0 1 1 0 0 0 1 1 0 0 0 0 1
0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0

// --> 奇数位取反(下标偶数位)

1 0 0 0 1 0 1 1 1 1 1 0 0 0 1 0
1 1 0 0 1 1 1 1 1 1 0 0 0 1 1 0
1 1 0 0 0 1 1 0 1 1 0 0 0 1 0 1
1 1 1 0 0 0 1 0 1 1 0 0 1 0 1 1
1 1 0 0 0 1 0 0 1 1 0 0 0 0 1 1
1 1 0 0 1 1 1 1 1 1 0 0 0 1 1 0
1 1 0 0 0 1 1 0 1 1 0 0 1 0 1 1
1 0 0 0 1 0 1 1 1 0 1 0 1 0 1 0

// 转回十六进制和字符串

--> 0x8BE2CFC6C6C5E2CBC4C3CFC6C6CB8BAA
--> 字符串:“嬧掀婆馑拿掀扑嫪”

// 以“19930907”为密钥加密“嬧掀婆馑拿掀扑嫪”得到“80217C048420956C15DA309FF2B69170”



// 与第一段注册码连接:75A29C09E180217C048420956C15DA309FF2B69170

// 这就是正确注册码!


七、分析算法多解

由于作者没有对输入的字符串长度做明确要求,而是对DES+奇数位取反结果的字符串做比较,且以0x00作为比较终止符,终止符之后的数据忽略,因此存在无数解。前42位保持“75A29C09E180217C048420956C15DA309FF2B69170”不变,在后面添加任意符合DES解密规则的字符都可以,提交了包含140组答案的附件。

八、总结

一个设计理念跟我的(第三题)接近的作品,防护手段太显眼,算法存在太多解,可惜了一个好作品。


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

上传的附件:
收藏
点赞2
打赏
分享
最新回复 (2)
雪    币: 161
活跃值: (813)
能力值: ( LV10,RANK:173 )
在线值:
发帖
回帖
粉丝
kaoyange 1 2017-6-23 13:53
2
0

如果是我引用DES加密算法,我也会动动手脚的,不会照搬过来;否则一旦被识破是何种加密算法,直接就工具跑了。

对于多解问题,我想只有作者在思路设计中给出“唯一性”证明才可避免,否则很难限制多解的问题。

雪    币: 609
活跃值: (197)
能力值: ( LV13,RANK:480 )
在线值:
发帖
回帖
粉丝
维一零 4 2017-6-23 14:01
3
0
最后到了这里已经没有耐心了,还是要多一点耐心去逆des的流程才行。
游客
登录 | 注册 方可回帖
返回