MistHill 发表于 2013-12-24 22:11

【吾爱2013CM大赛解答】-- White Decrypt & CrackMe 解题思路和方法

题目:【吾爱2013CM大赛题目】-- White Decrypt & CrackMe

分析文章(以发帖时间为序):
playboysen:【吾爱2013CM大赛解答】-- White Decrypt & CrackMe -- White 算法还原(复杂)
ja3klyTim9k:【吾爱2013CM大赛解答】 -- White Decrypt & CrackMe -- White
Thend:【吾爱2013CM大赛解答】-- White Decrypt & CrackMe -- White 算法详细分析+还原

题目要求很简单:找出合适的Name和Key组合,使得INFO显示“Something Is always Changing!”时,解题成功!

分析就不再重复了。贴段我的笔记来说明问题:
***************************************************************************************************************
Offset:         0123456789ABCDEF10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
Key:            44 6F 5F 59 6F 75 5F 4C 69 6B 65 5F 49 74 5F 4F 72 5F 4E 4F 54 5F 5F 3A 29 5F   "Do_You_Like_It_Or_NOT__:)_"
Key Variation #1: 00401577
                ALGO: ^= Key, i = strlen(Key)-1 ~ 0
                44 2B 30 06 36 1A 2A 13 25 02 0E 3A 16 3D 2B 10 3D 2D 11 01 1B 0B 00 65 13 76

Name:         57 68 69 74 65 5F 43 72 61 63 6B 4D 65 5F 4A 55 53 54 5F 46 6F 72 5F 46 75 6E   "White_CrackMe_JUST_For_Fun"
Name Variation #1: 0040159A
                ALGO: ^= Name, i = strlen(Name)-1 ~ 0
                57 3F 01 1D 11 3A 1C 31 13 02 08 26 28 3A 15 1F 06 07 0B 19 29 1D 2D 19 33 1B

                .D]cBrC}B"OU.[yhg~x9caaot{ng   !
Preset:         1F 44 5D 63 42 72 43 7D 42 22 4F 55 08 5B 79 68 67 7E 78 39 63 61 61 6F 74 7B 6E 67 20 21 00 00
at address 004160B0

Decoding #1, Key Variation #2: 00401644
for Preset:   ALGO: ^= Key, i = 00 ~ 0A;
for Key:      ALGO: Key ^= Key, i = 0A ~ strlen(Key)-0A if strlen(Key)-0A >= 0A
                <------------------------------>
                [omething   AU.[yhg~x9caaot{ng   !
Preset:         5B 6F 6D 65 74 68 69 6E 67 20 41 55 08 5B 79 68 67 7E 78 39 63 61 61 6F 74 7B 6E 67 20 21 00 00
                                                 <------>
Key:            44 2B 30 06 36 1A 2A 13 25 02 0E 34 22 1F 2B 10 3D 2D 11 01 1B 0B 00 65 13 76

Decoding #2, Name Variation #2: 00401668
for Preset:   ALGO: ^= Name, i = 0A ~ 14
for Name:       ALGO: Name &= Name, i = 14 ~ strlen(Name)-14 if strlen(Name)-14 >= 14
                                              <------------------------------>
                [omething   Is   always   Jaaot{ng   !
Preset:         5B 6F 6D 65 74 68 69 6E 67 20 49 73 20 61 6C 77 61 79 73 20 4A 61 61 6F 74 7B 6E 67 20 21 00 00
                                                                            <----------------, ...
Name:         57 3F 01 1D 11 3A 1C 31 13 02 08 26 28 3A 15 1F 06 07 0B 19 29 1D 2D 19 33 1B

Name Variation #3: 00401695
                ALGO: ^= Key, i = 00 ~ 0A
                <------------------------------>
Name:         13 14 31 1B 27 20 36 22 36 00 06 26 28 3A 15 1F 06 07 0B 19 29 1D 2D 19 33 1B

Key Variation #3: 004016A9
                ALGO: &= Name, i = 0A ~ 14 <------------------------------>
Key:            44 2B 30 06 36 1A 2A 13 25 02 06 24 20 1A 01 10 04 05 01 01 09 0B 00 65 13 76

Name Variation #4: 004016BE
                ALGO: &= Key, i = 14 ~ 19                              <--------------->
Name:         13 14 31 1B 27 20 36 22 36 00 06 26 28 3A 15 1F 06 07 0B 19 09 09 00 01 13 12

Decoding #3: 004016CF
                ALGO: ^= Name, i = 14 ~ 19                               <--------------->
                [omething   Is   always   Changing   !
Preset/Answer:5B 6F 6D 65 74 68 69 6E 67 20 49 73 20 61 6C 77 61 79 73 20 43 68 61 6E 67 69 6E 67 20 21 00 00
***************************************************************************************************************
Conclusion
Related:      KKKKKKKKKKKN NNNNNNNNNNK NK NK NK NK NK
Key status:   CCCCCCCCCCVvvvvvvvvvVVVVVV
Offset:         00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
                <-----------------------------<>----------------------------<>-------------->
                ^= Key                        ^= Name                     ^= Name(#4)
ALGO:         1) 00~09: ^= Key
                2) 0A: ^= Key, ^= Name
                                              3) 0B~13: ^= Name
                                              4) 14: ^= Name, ^= Name & (Key & Name)
                                                                            5) 15~19: ^= (Name & Key)
***************************************************************************************************************
前半部分是Name和Key的变换、答案的解码过程;重点在后半部分的分析总结,那些无关的运算完全可以无视:

[*]Key的00~09位是固定的,Preset和Answer决定了它不可变化。在08的机器上为"Do_You_Lik",在00的机器上为"LgWQg}WDac"。“xx的机器上”这种说法有点儿诡异!
[*]Key的0A位:对给定的Name,是确定的,没有太多变数。
[*]Key的0B~13位:因为正确的答案只受Name的影响,则Key是可自定义的,随便了。可以弄得有意义一点,比如:"_MistHill","_52PoJie_","_It_Or_NO"等等。
[*]Key的14位:这个比较讨厌,“与”运算使得结果有相当的不确定性:用Name、Preset和Answer三个条件对Key进行枚举,找出所有的可能;在当前Name下,可能得不到任何结果,还需要修改Name的对应位,重复这个枚举过程。
[*]Key的15~19位:与第14位类似,但少一个与第一次变换后Name的“异或”。

对Name来说,其00~0A位不对结果产生影响(只需调整Key),也是可自定义的。比如:"White_Crack","Weare52PoJi","Weare吾爱__"等等。当然得满足Name和Name的强制要求。
Key:
0123456789ABCDEF0123456789ABCDEF
Do_You_Lik
LgWQg}WDac
         _MistHill
         _52PoJie_
         _It_Or_NO

Name:
0123456789ABCDEF0123456789ABCDEF
White_Crack
Weare52PoJi
Weare吾爱__
理解了这些就可以写程序了,用ODbgScript比较方便。本来只想简单地写一个,结果越整越复杂,搞得平安夜还在码字。{:1_937:}

脚本对给定的Name,枚举出有效的Key。在某个Name无解时,会自动调整Name。
因为Key的0B~13位是自定义的。对一个给定的合格Name,仅Key在这里的变化就有256的9次方,这个数是很大的。脚本不处理这种简单的罗列!关键在Key~Key的求解上。
结合脚本的输入参数,说一下Keygen使用。
    mov szName, "White_CrackMe_JUST_For_Fun"

    mov char_mallocPtrMinus1, 8, 1
    mov dwCountLimitFor1419, 2
    mov bFilterOn, 1
    mov bOutputToFile, 0
先什么都不用改,直接跑一下脚本,看看你能得到什么结果。

变量szName可以指定任意Name,脚本会自动调整的。{:1_921:}
长度有要求:至少0x1A(26.)字符,最多0x27(39.)字符。设置最长字符数是为了简化问题(参见上面的Name Variation #2)。Key是要求解的,长度固定为0x1A可省去不少麻烦。

char_mallocPtrMinus1是为解决“xx的机器上”造成答案的差异,即VC运行库函数_malloc返回的指针-1那个字节的值。正常地,任何时候都不应该访问不属于自己请求的地址范围。不过,_malloc大多数情况是从Heap里分配空间,非法访问不易察觉。
我和ja3klyTim9k的这个值为0x00,其它人多为0x08。感谢playboysen在多个平台的测试!

dwCountLimitFor1419用来限制Key的14~19位各种有效组合的输出数量。脚本已经计算出了各位的有效值,完全的组合数同样巨大,没必要全部输出显示。
以题目的示例Name为例,各位的有效个数及取值如下:
位    个数    有效值
--    ----    ------
14    20      09 0B 0D 0F 19 1B 1D 1F 49 4B 4D 4F 59 5B 5D 5F 89 8B 8D 8F 99 9B 9D 9F C9 CB CD CF D9 DB DD DF
15    10      09 0B 29 2B 49 4B 69 6B 89 8B A9 AB C9 CB E9 EB
16    10      00 02 10 12 40 42 50 52 80 82 90 92 C0 C2 D0 D2
17    20      01 03 05 07 21 23 25 27 41 43 45 47 61 63 65 67 81 83 85 87 A1 A3 A5 A7 C1 C3 C5 C7 E1 E3 E5 E7
18    10      13 17 1B 1F 53 57 5B 5F 93 97 9B 9F D3 D7 DB DF
19    10      12 16 32 36 52 56 72 76 92 96 B2 B6 D2 D6 F2 F6
这6位的有效数据完全组合为0x40000000 (1,073,741,824)个,就其中的可打印字符组合保守地按1/3计算,也还有0x15555555 (357,913,941)个,保存为文件约有9GB。所以不要小看!

bFilterOn是一个开关,用于过滤那些不可能正确输入到题目编辑框的不可打印组合。只保留Key编码后字符范围在0x20~0x7E之间的组合。也会过滤掉Key为一些怪异的有效中文组合,题目能够通过。
Thend的帖子里有个还原得较好的C源码,可视为题目的命令行版本,稍微完善一下也可以作为验证程序。
其中有两处需要修改补充:1) Key第二次变换的条件及位数;2) 增加可能发生的Name的第二次变换。
我也写了一个验证脚本,奇怪的是脚本验证通过了,有极少数Key在题目里却显示错误答案。后来才发现它是用_sprintf函数来复制对话框的Name和Key,很奇怪的用法。
而这些出问题的Key包含字符'%',这就带来不确定性,要处理的话很麻烦,干脆过滤掉了事。算是一个陷阱吧!

bOutputToFile也是一个开关,决定是否将结果输出到文件,方便验证程序使用。
以下为一些输出示例。
Name:
White_CrackMe_JUST_For_Fun
Keys:
Do_You_Like_MistHill!*(/pf
Do_You_Like_MistHill!*(/pb
Do_You_Like_MistHill!*(/tb
Do_You_Like_MistHill!*(/tf
Do_You_Like_MistHill!*(-rd
Do_You_Like_MistHill!*(-r`
Do_You_Like_MistHill!*(-v`
Do_You_Like_MistHill!*(-vd
Do_You_Like_MistHill!**-rd
Do_You_Like_MistHill!**-r`
Do_You_Like_MistHill!**-v`
Do_You_Like_MistHill!**-vd
Do_You_Like_MistHill!**/pf
Do_You_Like_MistHill!**/pb
Do_You_Like_MistHill!**/tb
Do_You_Like_MistHill!**/tf
...

Name:
White_CrackMe_JUST_For_Fun
Keys:
Do_You_Like_52PoJie_Do=>!W
Do_You_Like_52PoJie_Do=>!S
Do_You_Like_52PoJie_Do=<#U
Do_You_Like_52PoJie_Do=<#Q
Do_You_Like_52PoJie_Do=<'Q
Do_You_Like_52PoJie_Do=<'U
Do_You_Like_52PoJie_Do?<#U
Do_You_Like_52PoJie_Do?<#Q
Do_You_Like_52PoJie_Do?<'Q
Do_You_Like_52PoJie_Do?<'U
Do_You_Like_52PoJie_Do?>!W
Do_You_Like_52PoJie_Do?>!S
Do_You_Like_52PoJie_Dm?<#U
Do_You_Like_52PoJie_Dm?<#Q
...

Name:
White_CrackMe_JUST_For_Fun
Keys:
Do_You_Like_It_Or_NOT__8/Y
Do_You_Like_It_Or_NOT__8/]
Do_You_Like_It_Or_NOT__8+]
Do_You_Like_It_Or_NOT__8+Y
Do_You_Like_It_Or_NOT__:-[      ; 我更喜欢这个
Do_You_Like_It_Or_NOT__:-_
Do_You_Like_It_Or_NOT__:)_      ; 题目示例Key
Do_You_Like_It_Or_NOT__:)[
Do_You_Like_It_Or_NOT_M*=K
Do_You_Like_It_Or_NOT_M*=O
Do_You_Like_It_Or_NOT_M*9O
Do_You_Like_It_Or_NOT_M*9K
...

Name:
Weare52PoJiOg]HWQV]Ddmml?-
Keys:
Do_You_LikNtfBX_cBGGFMLOXK
Do_You_LikNtfBX_cBGGFMLOXJ
Do_You_LikNtfBX_cBGGFMLO\O
Do_You_LikNtfBX_cBGGFMLO\N
Do_You_LikNtfBX_cBGGFMLMZI
Do_You_LikNtfBX_cBGGFMLMZH
Do_You_LikNtfBX_cBGGFMLM^M
Do_You_LikNtfBX_cBGGFMLM^L
Do_You_LikNtfBX_cBGGFMMNYJ
...

Name:
Weare吾爱__yQk~ag`krR[[ZI[
Keys:
Do_You_LikmWEa{|@addenol{h
Do_You_LikmWEa{|@addenol{i
Do_You_LikmWEa{|@addenonyj
Do_You_LikmWEa{|@addenonyk
Do_You_LikmWEa{|@addenon}n
Do_You_LikmWEa{|@addenon}o
Do_You_LikmWEa{|@addennmzi
Do_You_LikmWEa{|@addennmzh
...
最后一个,Name里有中文,题目会崩溃;而Key里含有中文却不会。不知道原因。

感谢White提供这个有趣的游戏!

祝各位圣诞快乐!

马斯维尔 发表于 2013-12-24 22:18

MistHill的每篇文章都值得好好研读,分析得不错。

Ylca 发表于 2013-12-24 23:58

MistHill 发表于 2013-12-26 17:49

本帖最后由 MistHill 于 2013-12-26 17:59 编辑

还是说一下吧!
这个CM暴露出的问题,是每个写程序的人都可能碰到、而且经常犯的错误,吸取经验和教训总是有益的。从某种程度上讲,我觉得比分析和解答CM自身更有意义!

前面提到Name里的中文字符会造成目标崩溃,仔细看下来后发现,原因不是中文引起的,西文同样可能使目标崩溃。
在程序对话框的编辑框里随便输入点儿字符串会“搞死”程序,这无论如何是说不过去的,看来程序有很严重的Bug。

这次问题出在Key和Name的第二次变换那两个循环:
0040161F|. 8B7424 20      MOV   ESI,              pKey(After Variation #1)
00401623|. 83C9 FF      OR      ECX, 0xFFFFFFFF
00401626|. 8BFE         MOV   EDI, ESI                  pKey
00401628|. 83C4 08      ADD   ESP, 0x8
0040162B|. F2:AE          REPNE   SCAS BYTE PTR ES:
0040162D|. 8B5424 1C      MOV   EDX,              pName(After Variation #1)
00401631|. F7D1         NOT   ECX
00401633|. 49             DEC   ECX                         ECX=strlen(pKey)
00401634|. 8BFA         MOV   EDI, EDX                  pName
00401636|. 8BD9         MOV   EBX, ECX                  EBX=strlen(pKey)
00401638|. 83C9 FF      OR      ECX, 0xFFFFFFFF
0040163B|. F2:AE          REPNE   SCAS BYTE PTR ES:
0040163D|. F7D1         NOT   ECX
0040163F|. 49             DEC   ECX                         ECX=strlen(pName)
00401640|. 894C24 18      MOV   , ECX             ECX->, save to local variable
00401644|> 8A0C30       / MOV   CL,                ESI=pKey, EAX=0 Init.
00401647|. 300C28       | XOR   , CL               EBP=pPreset, Decoding #1 with Key
0040164A|. 83F8 0A      | CMP   EAX, 0xA
0040164D|. 75 13      | JNZ   SHORT 00401662
0040164F|. 8D7B F6      | LEA   EDI,             EDI=strlen(pKey)-0A, EDI: unsigned int
00401652|. 3BF8         | CMP   EDI, EAX
00401654|. 72 0C      | JB      SHORT 00401662            <- *** BUG!
00401656|> 8A0C06       |/MOV   CL,                <- Memory access violation if strlen(pKey) < 0A
00401659|. 304C06 01    ||XOR   , CL         <- write
0040165D|. 40         ||INC   EAX
0040165E|. 3BC7         ||CMP   EAX, EDI                  <- *** Never stop!
00401660|. 76 F4      |\JBE   SHORT 00401656
00401662|> 40         | INC   EAX
00401663|. 83F8 0A      | CMP   EAX, 0xA
00401666|. 76 DC      \ JBE   SHORT 00401644
00401668|. 8B4C24 18      MOV   ECX,              ECX<-strlen(pName)
0040166C|. B8 0A000000    MOV   EAX, 0xA                  EAX=0A Init.
00401671|> 8A1C10       / MOV   BL,                EDX=pName
00401674|. 301C28       | XOR   , BL               EBP=pPreset, Decoding #2 with Name
00401677|. 83F8 14      | CMP   EAX, 0x14
0040167A|. 75 13      | JNZ   SHORT 0040168F
0040167C|. 8D79 EC      | LEA   EDI,              EDI=strlen(pName)-14, EDI: unsigned int
0040167F|. 3BF8         | CMP   EDI, EAX
00401681|. 72 0C      | JB      SHORT 0040168F            <- *** BUG!
00401683|> 8A1C02       |/MOV   BL,                <- Memory access violation if strlen(pName) < 14
00401686|. 205C02 01    ||AND   , BL         <- write
0040168A|. 40         ||INC   EAX
0040168B|. 3BC7         ||CMP   EAX, EDI                  <- *** Never stop!
0040168D|. 76 F4      |\JBE   SHORT 00401683
0040168F|> 40         | INC   EAX
00401690|. 83F8 14      | CMP   EAX, 0x14
00401693|. 76 DC      \ JBE   SHORT 00401671
00401695|. 8BCE         MOV   ECX, ESI在00401644和00401671处的外层循环是分别用Key和Name解码预设值得到答案;现在我们重点关注00401656和00401683两处内层循环。
搞这么个Key和Name的第二次变换无非是想增加一点解Key的难度。实际上也没啥作用,我在Keygen的脚本中写有一个针对Key第二次变换的逆过程,后来发现调用这个子过程与否对获得正确结果没太大影响,为节省运行时间就将那个调用注释掉了。

进入这两个循环的条件是:strlen(pKey)-0A >= 0和strlen(pName)-14 >= 0;换言之,即strlen(pKey) >= 0A和strlen(pName) >= 14。
“换言之”是我的第一反应,看起来strlen(pKey)=01~09,strlen(pName)=01~13都不会进入循环。可事实却完全不是我们想象的那样。
比如strlen(pKey)=09或strlen(pName)=13时,09-0A或13-14都等于-1,在32位系统里表示为0xFFFFFFFF。进入循环的条件就变成:FFFFFFFF与0A或14比较,紧接着的那条JB指令(00401654和00401681)就出问题了,这是一定要进循环的!
而结束循环的条件是:EAX++ > FFFFFFFF(0040165E和0040168B)。将永远不可能退出循环滴!于是它会读写4G内存空间的每一个地址,直到出现“内存访问冲突”,操作系统忍无可忍、不得不终止这个失控的进程。

由于Key和Name的第一次变换算法为两相邻字符进行异或,只要两个相邻字符相同,就得到一个字符串结尾NULL。在中文字符串的情况下不易察觉:比如“吾爱破解”中的“破”字,其两字节内码为0xC60 xC6,同样得到一个NULL字符。
下面这张表可用于对比测试:
      0123456789ABCDEF0123456789
      <-------->                     Key 紧邻字符相同崩溃区
         <----------------->            Name 紧邻字符相同崩溃区
Name:   White_CrackMe_JUST_For_Fun      标准输入
Key:    Do_You_Like_It_Or_NOT__:)_

会造成崩溃的Name:
      White_CrackMe_JUST__or_Fun      strlen(pName)=13
      White_CrackMe_JUSTFFor_Fun      strlen(pName)=13
      WWite_CrackMe_JUST_For_Fun      strlen(pName)=01
会造成崩溃的Key:
      Do_You_Liie_It_Or_NOT__:)_      strlen(pKey)=09
      Do_You_Lkke_It_Or_NOT__:)_      strlen(pKey)=09
      DD_You_Like_It_Or_NOT__:)_      strlen(pKey)=01
      oo_You_Like_It_Or_NOT__:)_      strlen(pKey)=01显然,写程序的人用错了数据类型声明,没有达到预期的设想。编译器只是忠实地将源代码变成机器指令。

先将目标这两处:
0040165472 0C      JB      SHORT 00401662
0040168172 0C      JB      SHORT 0040168F改为:
004016547C 0C      JL      SHORT 00401662
004016817C 0C      JL      SHORT 0040168F再试试,就不会出问题了。不要忽视这一个字节的差异:JB用于无符号数,而JL用于有符号数!

模仿题目写了一个命令行的东西,数据声明部分如下:
void __cdecl sub_4015E0(char *pKey, char *pName, int DisplayMode)
{
      char *pPreset;
      unsigned int i;
/* Correct data type declaration: */
      int nKeyLength;
      int nNameLength;
/* Wrong data type declaration: */
//      unsigned int nKeyLength;
//      unsigned int nNameLength;
...数据声明分别为int和unsigned int时,生成的代码对比如下:
正确(int):                                       #错误(unsigned int):
004015238A0C30       / MOV   CL,       #004015238A0C30       / MOV   CL,
00401526300C28       | XOR   , CL      #00401526300C28       | XOR   , CL
0040152983F8 0A      | CMP   EAX, 0xA         #0040152983F8 0A      | CMP   EAX, 0xA
0040152C75 16      | JNZ   SHORT 00401544   #0040152C75 13      | JNZ   SHORT 00401541
0040152E8D4B F6      | LEA   ECX,    #0040152E8D7B F6      | LEA   EDI,
004015313BC8         | CMP   ECX, EAX         #004015313BF8         | CMP   EDI, EAX
004015337C 0F      | JL      SHORT 00401544   #0040153372 0C      | JB      SHORT 00401541
004015358D7B F6      | LEA   EDI,    #004015358A0C06       |/MOV   CL,
004015388A0C06       |/MOV   CL,       #00401538304C06 01    ||XOR   , CL
0040153B304C06 01    ||XOR   , CL#0040153C40         ||INC   EAX
0040153F40         ||INC   EAX                #0040153D3BC7         ||CMP   EAX, EDI
004015403BC7         ||CMP   EAX, EDI         #0040153F76 F4      |\JBE   SHORT 00401535
0040154276 F4      |\JBE   SHORT 00401538   #0040154140         | INC   EAX
0040154440         | INC   EAX                #0040154283F8 0A      | CMP   EAX, 0xA
0040154583F8 0A      | CMP   EAX, 0xA         #0040154576 DC      \ JBE   SHORT 00401523
0040154876 D9      \ JBE   SHORT 00401523   #004015478B4C24 14      MOV   ECX,
0040154A8B4C24 14      MOV   ECX,     #0040154BB8 0A000000    MOV   EAX, 0xA
0040154EB8 0A000000    MOV   EAX, 0xA         #004015508A1C10       / MOV   BL,
004015538A1C10       / MOV   BL,       #00401553301C28       | XOR   , BL
00401556301C28       | XOR   , BL      #0040155683F8 14      | CMP   EAX, 0x14
0040155983F8 14      | CMP   EAX, 0x14          #0040155975 13      | JNZ   SHORT 0040156E
0040155C75 16      | JNZ   SHORT 00401574   #0040155B8D79 EC      | LEA   EDI,
0040155E8D79 EC      | LEA   EDI,     #0040155E3BF8         | CMP   EDI, EAX
004015613BF8         | CMP   EDI, EAX         #0040156072 0C      | JB      SHORT 0040156E
004015637C 0F      | JL      SHORT 00401574   #004015628A1C02       |/MOV   BL,
004015658D79 EC      | LEA   EDI,     #00401565205C02 01    ||AND   , BL
004015688A1C02       |/MOV   BL,       #0040156940         ||INC   EAX
0040156B205C02 01    ||AND   , BL#0040156A3BC7         ||CMP   EAX, EDI
0040156F40         ||INC   EAX                #0040156C76 F4      |\JBE   SHORT 00401562
004015703BC7         ||CMP   EAX, EDI         #0040156E40         | INC   EAX
0040157276 F4      |\JBE   SHORT 00401568   #0040156F83F8 14      | CMP   EAX, 0x14
0040157440         | INC   EAX                #0040157276 DC      \ JBE   SHORT 00401550
0040157583F8 14      | CMP   EAX, 0x14
0040157876 D9      \ JBE   SHORT 00401553从对比可以看到:1) JL和JB的差异;2) 右边的错误代码与目标代码几乎完全一致!

下面说说附件里的东西。
   Date      Time            SizeName
-----------------------------------------------
2013-12-25 18:23:00          5968WDC Verifier.c
2013-12-25 22:41:47         16384WDC Verifier.exe在命令提示符下不带参数运行会显示用法:
A Verification Program for "White Decrypt & CrackMe"
By MistHill, 25/12/2013

SYNTAX:
      "WDC Verifier.exe" "Name" "Key"
or
      "WDC Verifier.exe" "Log file created by my ODbgScript T228419.Keygen.osc"

You can get the script at:
      http://www.52pojie.cn/thread-230338-1-1.html第一种用法类似题目的命令行版本,即输入Name和Key参数会显示结果,自己检查与标准答案是否匹配。
运行实例:
"WDC Verifier.exe" "White_CrackMe_JUST_For_Fun" "Do_You_Like吾爱破解_V__^As"
INFO:
      Something Is always Changing !

"WDC Verifier.exe" "White_CrackMe_JUST_For_Fun" "Do_You_Like吾爱破解_V__\O]"
INFO:
      Something Is always Changing !

"WDC Verifier.exe" "Weare_ChildBjPEZ\[PII@@AR@" "Do_You_LikeMMwb}{|wn%,,->,"
INFO:
      Something Is always changing !

"WDC Verifier.exe" "Weare吾爱__yQk~ag`krR[[ZI[" "Do_You_LikmWEa{|@addenol{h"
INFO:
      Something Is always Changing !因为已经修正了已知Bug,所以不会崩溃,也能正确处理字符'%'。

但受命令提示符传递给程序main函数的命令行参数的影响,通常Name和Key字符串必须用西文字符'"'封闭。在Name或Key存在'"'字符的情况下,见以下示例:
Name:   Weare_ChildBjPEZ\[PII@@AR@
Key1:   Do_You_LikeMCQD[]ZQH*##"1#
Key2:   Do_You_LikeMCQD[]ZQH+""#0"

"WDC Verifier.exe" "Weare_ChildBjPEZ\ZQH*##""1#"
INFO:
      Something Is always changing !

"WDC Verifier.exe" "Weare_ChildBjPEZ\ZQH+\"\"#0\"
INFO:
      Something Is always changing !即在只有一个'"'的情况下,多加一个'"'字符;在有多个'"'的情况下,不使用'"'进行封闭,其中的'"'字符前加脱字符'\'。
上面例子中,有几个答案是不正确的,注意'c'和'C'的区别。这是最早调试脚本时的记录,现在一时找不到适合的样本。

总之,这时使用命令行是很麻烦的。对这个感兴趣的可参阅:Everyone quotes command line arguments the wrong way[http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx]。

还是把Name和Key放到文件比较方便,第二种用法:
只需一个参数,为Key的记录文件,其格式用Keygen脚本生成一个就清楚了。
文件里包含一个Name和多个Key,进行批量自动验证。合格的Key后面会显示'V'字符,而无效的Key会以'X'字符表示。

yangyongyoyo 发表于 2014-9-3 20:01

mark    下次看!
页: [1]
查看完整版本: 【吾爱2013CM大赛解答】-- White Decrypt & CrackMe 解题思路和方法