playboysen 发表于 2013-12-17 13:51

【吾爱2013CM大赛解答】-- White Decrypt & CrackMe -- White 算法还原(复杂)

本帖最后由 playboysen 于 2013-12-25 07:40 编辑

本文是针对下面这个CM的解答:
http://www.52pojie.cn/thread-228419-1-1.html解压出WhiteDecrypt & CrackMe主程序,看到了MFC特有图标
再结合程序入口代码特有的kernel32.GetVersion标示一般可以认为编程语言是C系无疑,使用系统API下断点就相对方便了(C系底层和系统API结合最紧密),不错的开端,继续……
运行程序随便输入测试出现不同错误提示,OD加载主程序查看字符串后轻松定位关键代码

注册码初步验证函数功能如下(具体解释见注释)00401486|.E8 B38A0000               call White_De.00409F3E
0040148B|.8B8E D4000000             mov ecx,dword ptr ds:       ;userName "White_CrackMe_JUST_For_Fun"
00401491|.8BF8                      mov edi,eax
00401493|.8B86 D8000000             mov eax,dword ptr ds:       ;regCode "Do_You_Like_It_Or_NOT__:)_"
00401499|.83C4 08                   add esp,8
0040149C|.8B49 F8                   mov ecx,dword ptr ds:      ;ecx = len(userName)
0040149F|.33D2                      xor edx,edx
004014A1|.8B40 F8                   mov eax,dword ptr ds:      ;eax = len(regCode)
004014A4|.894D FC                   mov dword ptr ss:,ecx
004014A7|.85C9                      test ecx,ecx
004014A9|.0F94C2                  sete dl
004014AC|.33C9                      xor ecx,ecx
004014AE|.897D F0                   mov dword ptr ss:,edi
004014B1|.85C0                      test eax,eax
004014B3|.0F94C1                  sete cl
004014B6|.0BD1                      or edx,ecx
004014B8|.74 1A                     je short White_De.004014D4          ;二者不能为空
004014BA|.6A 10                     push 10
004014BC|.68 28614100               push White_De.00416128            ;错误
004014C1|.68 14614100               push White_De.00416114            ;请输入用户名和密码
004014C6|.8BCE                      mov ecx,esi
004014C8|.E8 6BB00000               call White_De.0040C538
004014CD|.5F                        pop edi
004014CE|.5E                        pop esi
004014CF|.5B                        pop ebx
004014D0|.8BE5                      mov esp,ebp
004014D2|.5D                        pop ebp
004014D3|.C3                        retn
004014D4|>8B4D FC                   mov ecx,dword ptr ss:
004014D7|.33D2                      xor edx,edx
004014D9|.83F9 17                   cmp ecx,17                        ;比较userName和regCode长度是不是23
004014DC|.0F9CC2                  setl dl
004014DF|.33C9                      xor ecx,ecx
004014E1|.83F8 17                   cmp eax,17
004014E4|.0F9CC1                  setl cl
004014E7|.0BD1                      or edx,ecx                        ;往下分析就知道23位不够解码完整key
004014E9|.74 1A                     je short White_De.00401505          ;下面的提示做了误导
004014EB|.6A 10                     push 10
004014ED|.68 28614100               push White_De.00416128            ;错误
004014F2|.68 00614100               push White_De.00416100            ;字符串长度等于23
004014F7|.8BCE                      mov ecx,esi
004014F9|.E8 3AB00000               call White_De.0040C538
004014FE|.5F                        pop edi
004014FF|.5E                        pop esi
00401500|.5B                        pop ebx
00401501|.8BE5                      mov esp,ebp
00401503|.5D                        pop ebp
00401504|.C3                        retn
00401505|>8B86 D4000000             mov eax,dword ptr ds:
0040150B|.50                        push eax
0040150C|.57                        push edi
0040150D|.E8 EC0D0000               call White_De.004022FE
00401512|.8B96 D8000000             mov edx,dword ptr ds:
00401518|.52                        push edx
00401519|.53                        push ebx
0040151A|.E8 DF0D0000               call White_De.004022FE
0040151F|.8B86 D4000000             mov eax,dword ptr ds:
00401525|.83C4 10                   add esp,10
00401528|.33C9                      xor ecx,ecx
0040152A|.8078 04 65                cmp byte ptr ds:,65          ;userName="e"
0040152E|.0F95C1                  setne cl
00401531|.33D2                      xor edx,edx
00401533|.8038 57                   cmp byte ptr ds:,57            ;userName="W"
00401536|.0F95C2                  setne dl
00401539|.0BCA                      or ecx,edx
0040153B|.74 1A                     je short White_De.00401557
0040153D|.6A 10                     push 10
0040153F|.68 28614100               push White_De.00416128            ;错误
00401544|.68 F4604100               push White_De.004160F4            ;错误字符串
00401549|.8BCE                      mov ecx,esi
0040154B|.E8 E8AF0000               call White_De.0040C538
从以上代码可以看出,userName和regCode长度起码不能为空或小于23位,其中userName第一位是“W”,第五位是“e”00401577|.8B4D F4                   mov ecx,dword ptr ss:      ;ecx = Len(regCode)-1
0040157A|.8B45 F8                   mov eax,dword ptr ss:      ;eax = regCode
0040157D|.03C1                      add eax,ecx
0040157F|.8D51 01                   lea edx,dword ptr ds:      ;edx = Len(regCode)
00401582|>8A48 FF                   /mov cl,byte ptr ds:
00401585|.8A18                      |mov bl,byte ptr ds:
00401587|.32D9                      |xor bl,cl
00401589|.8ACB                      |mov cl,bl
0040158B|.8818                      |mov byte ptr ds:,bl         ;regCode从最后一位xor前一位并依次替换原值
0040158D|.0FBE58 FF               |movsx ebx,byte ptr ds:
00401591|.0FBEC9                  |movsx ecx,cl
00401594|.03D9                      |add ebx,ecx
00401596|.48                        |dec eax
00401597|.4A                        |dec edx                            ;注意regCode第一位xor固定值0x08
00401598|.^ 75 E8                     \jnz short White_De.00401582      ;变形后的注册码设为regCode1
0040159A|.8B7D F0                   mov edi,dword ptr ss:       ;edi = userName
0040159D|.8B55 FC                   mov edx,dword ptr ss:      ;edx = len(userName)-1
004015A0|.33F6                      xor esi,esi
004015A2|.8D043A                  lea eax,dword ptr ds:      ;eax指向userName最后一字节的地址
004015A5|>8A48 FF                   /mov cl,byte ptr ds:
004015A8|.8A10                      |mov dl,byte ptr ds:
004015AA|.32D1                      |xor dl,cl
004015AC|.8810                      |mov byte ptr ds:,dl         ;userName从最后一位xor前一位并以次替换原值
004015AE|.0FBE0C3E                  |movsx ecx,byte ptr ds:
004015B2|.0FBED2                  |movsx edx,dl
004015B5|.03CA                      |add ecx,edx
004015B7|.8B55 FC                   |mov edx,dword ptr ss:
004015BA|.46                        |inc esi
004015BB|.48                        |dec eax
004015BC|.3BF2                      |cmp esi,edx                        ;注意userName第一位xor固定值0x08
004015BE|.^ 76 E5                     \jbe short White_De.004015A5      ;变形后的注册码设为userName1
004015C0|.8B45 F8                   mov eax,dword ptr ss:
004015C3|.23CB                      and ecx,ebx
004015C5|.51                        push ecx
004015C6|.57                        push edi                            ;edi = userName1
004015C7|.50                        push eax                            ;eax = regCode1
004015C8|.E8 13000000               call White_De.004015E0            ;(╰_╯)#
004015CD|.83C4 0C                   add esp,0C
004015D0|.5F                        pop edi
004015D1|.5E                        pop esi
004015D2|.5B                        pop ebx
004015D3|.8BE5                      mov esp,ebp
004015D5|.5D                        pop ebp
004015D6\.C3                        retn主程序读入userName和regCode后分别从最后一位xor前一位并替换原值(第一位xor 0x08),以此来变形userName和regCode,字节变形前后对比见图:regCode
regCode从最后一位依次xor前一位并替换原值(第一位xor 0x08),变形后的注册码设为regCode1
userName
userName从最后一位依次xor前一位并替换原值(第一位xor 0x08),变形后的用户名设为userName1
主程序内置了一组Bytes(见下图黄色选区),设为Key用合适的用户名和注册码参与解码后就是字符串“Something Is always Changing !”(作为注册成功提示MessageBoxA的内容)细心人可能发现了该字符串共30位,其中前26位是变形字节,最后4位是明文——也就是说主程序要做的就是解码前26字节
0040163F|.49                        dec ecx
00401640|.894C24 18               mov dword ptr ss:,ecx
00401644|>8A0C30                  /mov cl,byte ptr ds:       ;Key从首字节开始xor regCode1对应字节
00401647|.300C28                  |xor byte ptr ds:,cl
0040164A|.83F8 0A                   |cmp eax,0A                         ;共解码Key的前11字节
0040164D|.75 13                     |jnz short White_De.00401662
0040164F|.8D7B F6                   |lea edi,dword ptr ds:
00401652|.3BF8                      |cmp edi,eax
00401654|.72 0C                     |jb short White_De.00401662
00401656|>8A0C06                  |/mov cl,byte ptr ds:      ;regCode1从第十二字节开始依次xor前一字节
00401659|.304C06 01               ||xor byte ptr ds:,cl
0040165D|.40                        ||inc eax
0040165E|.3BC7                      ||cmp eax,edi
00401660|.^ 76 F4                     |\jbe short White_De.00401656       ;共处理regCode1第12-14字节
00401662|>40                        |inc eax
00401663|.83F8 0A                   |cmp eax,0A
00401666|.^ 76 DC                     \jbe short White_De.00401644
00401668|.8B4C24 18               mov ecx,dword ptr ss:       ;ecx = len(userName1)
0040166C|.B8 0A000000               mov eax,0A
00401671|>8A1C10                  /mov bl,byte ptr ds:       ;Key从第11字节开始xor userName1对应字节
00401674|.301C28                  |xor byte ptr ds:,bl
00401677|.83F8 14                   |cmp eax,14                         ;共解码Key的第11-21字节
0040167A|.75 13                     |jnz short White_De.0040168F
0040167C|.8D79 EC                   |lea edi,dword ptr ds:
0040167F|.3BF8                      |cmp edi,eax
00401681|.72 0C                     |jb short White_De.0040168F
00401683|>8A1C02                  |/mov bl,byte ptr ds:
00401686|.205C02 01               ||and byte ptr ds:,bl
0040168A|.40                        ||inc eax
0040168B|.3BC7                      ||cmp eax,edi
0040168D|.^ 76 F4                     |\jbe short White_De.00401683
0040168F|>40                        |inc eax
00401690|.83F8 14                   |cmp eax,14
00401693|.^ 76 DC                     \jbe short White_De.00401671
00401695|.8BCE                      mov ecx,esi第一段解码:Key从首字节开始xor regCode1对应字节,共处理Key的前11字节(截图是前十一字节解码后的明文)for j in range(1,12):
    key = chr(ord(key) ^ ord(regCode1))

由于异或运算是可逆的,所以可以根据已知Key和最终对应的“Something Is always Changing !”前十一位逆推出regCode前十位(因为第十一位后续变幻多次,暂无法确定)

key = {1:"\x1F",2:"\x44",3:"\x5D",4:"\x63",5:"\x42",6:"\x72",7:"\x43",8:"\x7D",9:"\x42",10:"\x22",11:"\x4F",12:"\x55",13:"\x08",14:"\x5B",15:"\x79",16:"\x68",17:"\x67",18:"\x7E",19:"\x78",20:"\x39",21:"\x63",22:"\x61",23:"\x61",24:"\x6F",25:"\x74",26:"\x7B",27:"\x6E",28:"\x67",29:"\x20",30:"\x21"}
OK = {1:"S",2:"o",3:"m",4:"e",5:"t",6:"h",7:"i",8:"n",9:"g",10:" ",11:"I",12:"s",13:" ",14:"a",15:"l",16:"w",17:"a",18:"y",19:"s",20:" ",21:"C",22:"h",23:"a",24:"n",25:"g",26:"i",27:"n",28:"g",29:" ",30:"!"}

regCode = dict()
for j in range(1,12):
    regCode= chr(ord(key)^ord(OK))
    if i ==1:
      regCode = chr(ord(regCode)^ord('\x08'))
    else:
      regCode=chr(ord(regCode)^ord(regCode))

>>> print regCode
{1: 'D', 2: 'o', 3: '_', 4: 'Y', 5: 'o', 6: 'u', 7: '_', 8: 'L', 9: 'i', 10: 'k', 11: 'm'}

regCode1从第十二字节开始依次xor前一字节,共处理regCode1第12-14字节for j in range(11,14):
    regCode1 = chr(ord(regCode1) ^ ord(regCode1))

第二段解码:Key从第11字节开始xor userName1对应字节,共处理Key的第11-21字节(截图是Key解码后的明文)for j in range(11,22):
    key = chr(ord(key) ^ ord(userName1))
004016A0|>8A1C01                  /mov bl,byte ptr ds:       ;userName1从首字节开始依次xor regCode1对应字节
004016A3|.3018                      |xor byte ptr ds:,bl
004016A5|.40                        |inc eax
004016A6|.4F                        |dec edi                            ;共处理userName1的前11字节
004016A7|.^ 75 F7                     \jnz short White_De.004016A0
004016A9|.8BFA                      mov edi,edx                         ;edi指向变形后的userName1
004016AB|.8D46 0A                   lea eax,dword ptr ds:
004016AE|.2BFE                      sub edi,esi
004016B0|.BE 0B000000               mov esi,0B
004016B5|>8A1C07                  /mov bl,byte ptr ds:       ;regCode1从第11字节开始and userName1对应字节
004016B8|.2018                      |and byte ptr ds:,bl
004016BA|.40                        |inc eax
004016BB|.4E                        |dec esi                            ;共处理regCode1的11-21字节
004016BC|.^ 75 F7                     \jnz short White_De.004016B5
004016BE|.8D42 14                   lea eax,dword ptr ds:       ;eax指向变形后的userName1
004016C1|.BE 06000000               mov esi,6
004016C6|>8A1C08                  /mov bl,byte ptr ds:       ;userName1从第21字节开始and regCode1对应字节
004016C9|.2018                      |and byte ptr ds:,bl
004016CB|.40                        |inc eax
004016CC|.4E                        |dec esi                            ;共处理userName1的21-26字节
004016CD|.^ 75 F7                     \jnz short White_De.004016C6
004016CF|.8D45 14                   lea eax,dword ptr ss:       ;eax指向key的第21字节
004016D2|.2BD5                      sub edx,ebp
004016D4|.BE 06000000               mov esi,6
004016D9|>8A0C02                  /mov cl,byte ptr ds:       ;Key从第21字节开始xor userName1对应字节
004016DC|.8A18                      |mov bl,byte ptr ds:
004016DE|.32D9                      |xor bl,cl
004016E0|.8818                      |mov byte ptr ds:,bl
004016E2|.40                        |inc eax
004016E3|.4E                        |dec esi                            ;共解码Key的第21-26字节
004016E4|.^ 75 F3                     \jnz short White_De.004016D9
004016E6|.8B7424 10               mov esi,dword ptr ss:
004016EA|.55                        push ebp
004016EB|.68 38614100               push White_De.00416138            ;%s
004016F0|.56                        push esi
004016F1|.E8 080C0000               call White_De.004022FE
004016F6|.83C4 0C                   add esp,0C
004016F9|.6A 40                     push 40                           ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
004016FB|.68 30614100               push White_De.00416130            ; |info
00401700|.56                        push esi                            ; |Text
00401701|.6A 00                     push 0                              ; |hOwner = NULL
00401703|.FF15 58134100             call dword ptr ds:[<&USER32.Message>; \MessageBoxA
userName1从首字节开始xor regCode1对应字节,共处理userName1的前11字节for j in range(1,12):
    userName1 = chr(ord(userName1) ^ ord(regCode1))

regCode1从第11字节开始and userName1对应字节,共处理regCode1的11-21字节for j in range(11,22):
    regCode1 = chr(ord(regCode1) & ord(userName1))

userName1从第21字节开始and regCode1对应字节,共处理userName1的21-26字节for j in range(21,27):
    userName1 = chr(ord(userName1) & ord(regCode1))
第三段解码:Key从第21字节开始xor userName1对应字节,共处理Key的第21-26字节(截图是Key解码后的明文)for j in range(21,27):
    key = chr(ord(key) ^ ord(userName1))

整段解码算法并不复杂,简单来说就是程序预置了30位加密Bytes,需要填入合适的用户名和注册码来参与运算解码,最终生成需要的文字“Something Is always Changing !”,运算只是简单的xor和and,不过由于全部都是字节操作编码解码多次,故需全部理解、重现、调试和验证等等需耗大量时间,心力交瘁!我估计到大赛结束,这个“White Decrypt & CrackMe”解答的人也不会太多了{:1_923:}
放一段已还原的算法Python示例以供参考#!/usr/bin/env python
##Python 2.7 & Windows 7

import string,random

"""
随机生成用户名和注册码,考虑到后面计算方便,首字节忠于原程序设置为0x08
key是主程序内置的一组加密Bytes(用合适的用户名和注册码参与解密后就是最终的字符串“Something Is always Changing !”)
"""
txt = "ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*()_"
key = {1:"\x1F",2:"\x44",3:"\x5D",4:"\x63",5:"\x42",6:"\x72",7:"\x43",8:"\x7D",9:"\x42",10:"\x22",11:"\x4F",12:"\x55",13:"\x08",14:"\x5B",15:"\x79",16:"\x68",17:"\x67",18:"\x7E",19:"\x78",20:"\x39",21:"\x63",22:"\x61",23:"\x61",24:"\x6F",25:"\x74",26:"\x7B",27:"\x6E",28:"\x67",29:"\x20",30:"\x21"}
OK = {1:"S",2:"o",3:"m",4:"e",5:"t",6:"h",7:"i",8:"n",9:"g",10:" ",11:"I",12:"s",13:" ",14:"a",15:"l",16:"w",17:"a",18:"y",19:"s",20:" ",21:"C",22:"h",23:"a",24:"n",25:"g",26:"i",27:"n",28:"g",29:" ",30:"!"}

def bruteAttack():
    userName = "\x08" + "Wallet" + string.join(random.sample(txt,20)).replace(' ','')
    regCode = "\x08" + "Do_You_Lik" + string.join(random.sample(txt,16)).replace(' ','')

    """
    regCode最后一位xor前一位并替换原值(其它字节以此类推,第一位xor 0x08),变形后的注册码设为regCode1
    userName计算方法相同
    """
    regCode1 =dict()
    for j in range(26,0,-1):
      regCode1 = chr(ord(regCode) ^ ord(regCode))

    userName1 = dict()
    for j in range(26,0,-1):
      userName1 = chr(ord(userName) ^ ord(userName))

    """Key从首字节开始xor regCode1对应字节,共处理Key的前11字节"""
    for j in range(1,12):
      key = chr(ord(key) ^ ord(regCode1))

    """Key从第11字节开始xor userName1对应字节,共处理Key的第11-21字节"""
    for j in range(11,22):
      key = chr(ord(key) ^ ord(userName1))

    """regCode1从第十二字节开始xor前一字节,共处理regCode1第12-14字节"""
    for j in range(11,14):
      regCode1 = chr(ord(regCode1) ^ ord(regCode1))

    """userName1从首字节开始xor regCode1对应字节,共处理userName1的前11字节"""
    for j in range(1,12):
      userName1 = chr(ord(userName1) ^ ord(regCode1))

    """regCode1从第11字节开始and userName1对应字节,共处理regCode1的11-21字节"""
    for j in range(11,22):
      regCode1 = chr(ord(regCode1) & ord(userName1))

    """userName1从第21字节开始and regCode1对应字节,共处理UserName1的21-26字节"""
    for j in range(21,27):
      userName1 = chr(ord(userName1) & ord(regCode1))

    """Key从第21字节开始xor userName1对应字节,共处理Key的第21-26字节"""
    for j in range(21,27):
      key = chr(ord(key) ^ ord(userName1))

while True:
    bruteAttack()
    if key == OK:
      print "Success!"
      break


不能免俗,只为加分O(∩_∩)O~——做个爆破Loader

琦琦的爱 发表于 2013-12-17 14:01

给力啊稳坐沙发

夜的静night 发表于 2013-12-17 14:04

牛掰啊   见识了

hzg303 发表于 2013-12-17 14:11


牛掰啊   见识了

xiaobai 发表于 2013-12-17 14:50

膜拜...上午刚开始分析,中午吃完饭回来LZ就发帖了。。。

White 发表于 2013-12-17 14:52



算法 是在休息时间做的,主要的就是三个函数。
在这个CrackMe 修改很大,因为原版本Kegen比较难做,这个版本的相较简单点。
分析 很精彩。

xjun 发表于 2013-12-17 15:20

很精彩,感谢大牛分享优秀文章。

我是用户 发表于 2013-12-17 18:11

膜拜

brack 发表于 2013-12-17 18:14

厉害...学习了

playboysen 发表于 2013-12-17 18:23

xiaobai 发表于 2013-12-17 14:50 static/image/common/back.gif
膜拜...上午刚开始分析,中午吃完饭回来LZ就发帖了。。。

坐等分析结论,求指导……{:301_997:}
页: [1] 2
查看完整版本: 【吾爱2013CM大赛解答】-- White Decrypt & CrackMe -- White 算法还原(复杂)