agang 发表于 2006-8-11 21:18:48

学习逍遥风对HappyTown兄弟的crackme_13(CRC32)的算法分析,超菜级笔记

选自逍遥风算法精华集第2篇,自己没能力分析出来,照搬学习也是一种途径呀,就怕眼高手低。
自我安慰,呵呵!

前几天天下了逍遥兄的算法分析,把这篇跟了几次,还是有点看不明白。好不容易等到周五,终于有空了,于是有了下面的学习笔记。
OD载入后,断点非常容易找到,超级字符串查找到'GOOD JOB,MAN!'在以下处F2下断。

0040116E|.68 FF000000   PUSH 0FF                                 ; /Count = FF (255.)
00401173|.68 8C344000   PUSH CrackMe_.0040348C                   ; |Buffer = CrackMe_.0040348C
00401178|.68 EC030000   PUSH 3EC                                 ; |ControlID = 3EC (1004.)
0040117D|.FF75 08       PUSH DWORD PTR SS:                ; |hWnd
00401180|.E8 6D020000   CALL <JMP.&user32.GetDlgItemTextA>       ; \GetDlgItemTextA
00401185|.83F8 04       CMP EAX,4
00401188|.73 05         JNB SHORT CrackMe_.0040118F
0040118A|.E9 11020000   JMP CrackMe_.004013A0
0040118F|>A3 8F354000   MOV DWORD PTR DS:,EAX
00401194|.68 FF000000   PUSH 0FF                                 ; /Count = FF (255.)
00401199|.68 93354000   PUSH CrackMe_.00403593                   ; |Buffer = CrackMe_.00403593
0040119E|.68 ED030000   PUSH 3ED                                 ; |ControlID = 3ED (1005.)
004011A3|.FF75 08       PUSH DWORD PTR SS:                ; |hWnd
004011A6|.E8 47020000   CALL <JMP.&user32.GetDlgItemTextA>       ; \GetDlgItemTextA
004011AB|.BA 1B344000   MOV EDX,CrackMe_.0040341B                ;上面这两个CALL是取字符串长度的,结果放在EAX里
004011B0|.8902          MOV DWORD PTR DS:,EAX
004011B2|.E8 49FEFFFF   CALL CrackMe_.00401000                   ;初始化CRC32数据表
004011B7|.8D1D 1B344000 LEA EBX,DWORD PTR DS:            ;储存注册码的位数准备进行CRC32计算
004011BD|.E8 78FEFFFF   CALL CrackMe_.0040103A                   ;开始对注册码的位数进行CRC32计算
004011C2|.3D F716602F   CMP EAX,2F6016F7
004011C7|.0F85 D3010000 JNZ CrackMe_.004013A0
004011CD|.6A 08         PUSH 8                                 ; /Length = 8
004011CF|.68 7F344000   PUSH CrackMe_.0040347F                   ; |Destination = CrackMe_.0040347F
004011D4|.E8 FB010000   CALL <JMP.&kernel32.RtlZeroMemory>       ; \RtlZeroMemory
004011D9|.8D05 93354000 LEA EAX,DWORD PTR DS:            ;试练码存放地址403593放在
004011DF|.83C0 08       ADD EAX,8                              ;指向试练码第9位
004011E2|.8A18          MOV BL,BYTE PTR DS:               ;使BL等于试练码的第9位
004011E4|.8D15 7F344000 LEA EDX,DWORD PTR DS:
004011EA|.881A          MOV BYTE PTR DS:,BL
004011EC|.42            INC EDX
004011ED|.83C0 09       ADD EAX,9                              ;指向试练码的第18位,EAX原值为9
004011F0|.8A18          MOV BL,BYTE PTR DS:
004011F2|.881A          MOV BYTE PTR DS:,BL
004011F4|.E8 07FEFFFF   CALL CrackMe_.00401000
004011F9|.8D1D 7F344000 LEA EBX,DWORD PTR DS:            ;注册码第9位与第18位合并在一起储存在EDX中
004011FF|.E8 36FEFFFF   CALL CrackMe_.0040103A                   ;注册码的第9位与第18位连接为一个整体,对整体做CRC32计算。
00401204|.3D 65142C24   CMP EAX,242C1465                         ;这里就知道第9和18位为什么是'-'了,呵呵
00401209|.0F85 91010000 JNZ CrackMe_.004013A0                  ;计算的结果等于0x242C1465?不等跳完
0040120F|.6A 0A         PUSH 0A                                  ; /Length = A (10.)
00401211|.8D45 EA       LEA EAX,DWORD PTR SS:            ; |
00401214|.50            PUSH EAX                                 ; |Destination
00401215|.E8 BA010000   CALL <JMP.&kernel32.RtlZeroMemory>       ; \RtlZeroMemory
0040121A|.6A 0A         PUSH 0A                                  ; /Length = A (10.)
0040121C|.8D45 E0       LEA EAX,DWORD PTR SS:            ; |
0040121F|.50            PUSH EAX                                 ; |Destination
00401220|.E8 AF010000   CALL <JMP.&kernel32.RtlZeroMemory>       ; \RtlZeroMemory
00401225|.33C9          XOR ECX,ECX
00401227|.8D05 93354000 LEA EAX,DWORD PTR DS:            ;试练码
0040122D|>8A1401      /MOV DL,BYTE PTR DS:            ;逐位取试练码ASCII码放在EAX低位
00401230|.80FA 39       |CMP DL,39                               ;与0x39(对应字符9的ASCII)比较
00401233|.76 05         |JBE SHORT CrackMe_.0040123A             ;小于等于就跳下来,我的是第一次是0x31('1'),所以跳
00401235|.80FA 41       |CMP DL,41
00401238|.72 0A         |JB SHORT CrackMe_.00401244
0040123A|>80FA 30       |CMP DL,30                               ;与0x30(对应字符0的ASCII)比较,
0040123D|.72 05         |JB SHORT CrackMe_.00401244            ;小于就跳,我的第一次是'1',就不跳了
0040123F|.80FA 46       |CMP DL,46                               ;与0x46(对应字符F的ASCII)比较
00401242|.76 05         |JBE SHORT CrackMe_.00401249             ;小于等于就跳下来,我的是第一次是0x31('1'),所以跳
00401244|>E9 57010000   |JMP CrackMe_.004013A0
00401249|>41            |INC ECX                                 ;ECX计数器+1
0040124A|.83F9 08       |CMP ECX,8                               ;取注册码的前8位(注册码第一部分)
0040124D|.^ 75 DE         \JNZ SHORT CrackMe_.0040122D             ;上面两句实现了只对试练码的前8位比较
0040124F|.33C9          XOR ECX,ECX
00401251|.33DB          XOR EBX,EBX
00401253|.8D75 EA       LEA ESI,DWORD PTR SS:
00401256|>C1E3 04       /SHL EBX,4                               ;左移4位,相当于乘10
00401259|.8A1401      |MOV DL,BYTE PTR DS:            ;逐位取注册码第一部分(即前8位)
0040125C|.8816          |MOV BYTE PTR DS:,DL
0040125E|.80FA 41       |CMP DL,41                               ;与0x41(对应字符A的ASCII)比较,
00401261|.72 0A         |JB SHORT CrackMe_.0040126D            ;小于就跳,我的第一位是0x31,就跳了
00401263|.80FA 46       |CMP DL,46
00401266|.77 05         |JA SHORT CrackMe_.0040126D
00401268|.80EA 37       |SUB DL,37
0040126B|.EB 03         |JMP SHORT CrackMe_.00401270
0040126D|>80EA 30       |SUB DL,30
00401270|>80E2 0F       |AND DL,0F
00401273|.0ADA          |OR BL,DL
00401275|.46            |INC ESI
00401276|.41            |INC ECX
00401277|.83F9 08       |CMP ECX,8                               ;这两句好眼熟,与前一循环作用相同,呵呵
0040127A|.895D FC       |MOV DWORD PTR SS:,EBX
0040127D|.^ 75 D7         \JNZ SHORT CrackMe_.00401256             ;将注册码的第一部分转换成对应的16进制
0040127F|.E8 7CFDFFFF   CALL CrackMe_.00401000                   ;运行到这里时可以看到EBX是转为16进制的前8位试练码
00401284|.8D1D 8C344000 LEA EBX,DWORD PTR DS:            ;用户名的存放地址放在EBX
0040128A|.E8 ABFDFFFF   CALL CrackMe_.0040103A                   ;开始对用户名作CRC32计算
0040128F|.3945 FC       CMP DWORD PTR SS:,EAX             ;对用户名CRC32计算的结果与试练码的前8位比较,不等就跳完
00401292      0F85 08010000 JNZ CrackMe_.004013A0                  ;真码第一部分就是CRC32(用户名)。我的用户名是agang,CRC32后是A53C759F
00401298|.33C9          XOR ECX,ECX
0040129A|.8D05 93354000 LEA EAX,DWORD PTR DS:            ;试练码又呼出来,准备第二部分的计算了
004012A0|.83C0 09       ADD EAX,9
004012A3|>8A1401      /MOV DL,BYTE PTR DS:            ;从这里开始。。。
004012A6|.80FA 39       |CMP DL,39
004012A9|.76 05         |JBE SHORT CrackMe_.004012B0
004012AB|.80FA 41       |CMP DL,41
004012AE|.72 0A         |JB SHORT CrackMe_.004012BA
004012B0|>80FA 30       |CMP DL,30
004012B3|.72 05         |JB SHORT CrackMe_.004012BA
004012B5|.80FA 46       |CMP DL,46
004012B8|.76 05         |JBE SHORT CrackMe_.004012BF
004012BA|>E9 E1000000   |JMP CrackMe_.004013A0
004012BF|>41            |INC ECX
004012C0|.83F9 08       |CMP ECX,8
004012C3|.^ 75 DE         \JNZ SHORT CrackMe_.004012A3
004012C5|.33C9          XOR ECX,ECX                              ;       逍遥兄称上述循环为'格式检验',说得够精练的,我等菜菜要慢慢分析才能看出来
004012C7|.33DB          XOR EBX,EBX
004012C9|.8D75 E0       LEA ESI,DWORD PTR SS:
004012CC|>C1E3 04       /SHL EBX,4
004012CF|.8A1401      |MOV DL,BYTE PTR DS:
004012D2|.8816          |MOV BYTE PTR DS:,DL
004012D4|.8810          |MOV BYTE PTR DS:,DL
004012D6|.80FA 41       |CMP DL,41
004012D9|.72 0A         |JB SHORT CrackMe_.004012E5
004012DB|.80FA 46       |CMP DL,46
004012DE|.77 05         |JA SHORT CrackMe_.004012E5
004012E0|.80EA 37       |SUB DL,37
004012E3|.EB 03         |JMP SHORT CrackMe_.004012E8
004012E5|>80EA 30       |SUB DL,30
004012E8|>80E2 0F       |AND DL,0F
004012EB|.0ADA          |OR BL,DL
004012ED|.46            |INC ESI
004012EE|.41            |INC ECX
004012EF|.83F9 08       |CMP ECX,8
004012F2|.895D F8       |MOV DWORD PTR SS:,EBX
004012F5|.^ 75 D5         \JNZ SHORT CrackMe_.004012CC             ;       以上循环将试练第二部分转为16进制,记为S1吧
004012F7|.E8 04FDFFFF   CALL CrackMe_.00401000                   ;初始化CRC32数据表
004012FC|.8D5D EA       LEA EBX,DWORD PTR SS:            ;取出试练码前8位
004012FF|.E8 36FDFFFF   CALL CrackMe_.0040103A                   ;对注册码的前8位进行CRC32计算
00401304|.3945 F8       CMP DWORD PTR SS:,EAX             ;S1与CRC32(CRC32(agang))[也就是真码第一部分的CRC32结果]比较,
00401307|.0F85 93000000 JNZ CrackMe_.004013A0                  ;注册码的第一部分CRC32计算后的值即为注册码的第2部分
0040130D|.E8 EEFCFFFF   CALL CrackMe_.00401000                   ;又是初始化CRC32数据表
00401312|.8D5D E0       LEA EBX,DWORD PTR SS:            ;取真码的第二部分,就是刚刚算出来那个。我的是39944E09
00401315|.E8 20FDFFFF   CALL CrackMe_.0040103A                   ;对其再进行CRC32计算,我这里的结果是AD14BD73
0040131A|.8945 F8       MOV DWORD PTR SS:,EAX
0040131D|.33C9          XOR ECX,ECX
0040131F|.8D05 93354000 LEA EAX,DWORD PTR DS:
00401325|.83C0 12       ADD EAX,12                               ;从试练码的第19位开始取值(取注册码的第3部分)
00401328|>8A1401      /MOV DL,BYTE PTR DS:
0040132B|.80FA 39       |CMP DL,39
0040132E|.76 05         |JBE SHORT CrackMe_.00401335
00401330|.80FA 41       |CMP DL,41
00401333|.72 0A         |JB SHORT CrackMe_.0040133F
00401335|>80FA 30       |CMP DL,30
00401338|.72 05         |JB SHORT CrackMe_.0040133F
0040133A|.80FA 46       |CMP DL,46
0040133D|.76 02         |JBE SHORT CrackMe_.00401341
0040133F|>EB 5F         |JMP SHORT CrackMe_.004013A0
00401341|>41            |INC ECX
00401342|.83F9 08       |CMP ECX,8
00401345|.^ 75 E1         \JNZ SHORT CrackMe_.00401328             ;格式验证,再多说也没意义了,呵呵
00401347|.33C9          XOR ECX,ECX
00401349|.33DB          XOR EBX,EBX
0040134B|>C1E3 04       /SHL EBX,4
0040134E|.8A1401      |MOV DL,BYTE PTR DS:
00401351|.8810          |MOV BYTE PTR DS:,DL
00401353|.80FA 41       |CMP DL,41
00401356|.72 0A         |JB SHORT CrackMe_.00401362
00401358|.80FA 46       |CMP DL,46
0040135B|.77 05         |JA SHORT CrackMe_.00401362
0040135D|.80EA 37       |SUB DL,37
00401360|.EB 03         |JMP SHORT CrackMe_.00401365
00401362|>80EA 30       |SUB DL,30
00401365|>80E2 0F       |AND DL,0F
00401368|.0ADA          |OR BL,DL
0040136A|.41            |INC ECX
0040136B|.83F9 08       |CMP ECX,8
0040136E|.895D F4       |MOV DWORD PTR SS:,EBX
00401371|.^ 75 D8         \JNZ SHORT CrackMe_.0040134B             ;转为16进制
00401373|.8B45 FC       MOV EAX,DWORD PTR SS:             ;里是A53C759F
00401376|.3345 F8       XOR EAX,DWORD PTR SS:             ;是真码第二部分再作CRC32计算的结果,与A53C759F异或
00401379|.3B45 F4       CMP EAX,DWORD PTR SS:             ;我这里的结果是0828C8EC,存的是试练码第三部分,两者比较
0040137C      75 22         JNZ SHORT CrackMe_.004013A0
0040137E|.68 D2204000   PUSH CrackMe_.004020D2                   ; /good job, man!
00401383|.FF75 08       PUSH DWORD PTR SS:                ; |hWnd
00401386|.E8 8B000000   CALL <JMP.&user32.SetWindowTextA>      ; \SetWindowTextA
0040138B|.68 EE030000   PUSH 3EE                                 ; /ControlID = 3EE (1006.)
00401390|.FF75 08       PUSH DWORD PTR SS:                ; |hWnd
00401393|.E8 54000000   CALL <JMP.&user32.GetDlgItem>            ; \GetDlgItem
00401398|.6A 00         PUSH 0                                 ; /Enable = FALSE
0040139A|.50            PUSH EAX                                 ; |hWnd
0040139B|.E8 40000000   CALL <JMP.&user32.EnableWindow>          ; \EnableWindow
004013A0|>33C0          XOR EAX,EAX

--------------------逍遥兄的算法总结-------------------
  1)注册码必须为26位,第9位与第18位必须是:- 
  注册码的形式为12345678-01234567-90123456
  2)将注册名进行CRC32计算,将注册码的第1部分转换成对应的16进制。两个值必须相等
  3)将注册码的第1部分进行CRC32计算(设为A),将注册码的第2部分转换成对应的16进制。两个值必须相等
  4)将注册码的第2部分进行CRC32计算(设为B)。
  5)将A,B做XOR运算,将注册码的第3部分转换成对应的16进制。两个值必须相等
  即。注册码
  第一部分:CRC(注册名)
  第二部分:CRC(注册码第一部分)
  第三部分:XOR [  第一部分,CRC(第二部分)]
------------------------------------------------------------
我的试练信息说明
用户名:agang
试练码:12345678-abcdefgh-98765432
真码:A53C759F-39944E09-0828C8EC


此CRACKME分三部分对注册码检验,建议采用逐步替换的方式进行调试。
即:用户名:agang,
   注册码这样逐步替换:
    第一次:12345678-abcdefgh-98765432;
    第二次:A53C759F-abcdefgh-98765432
    第三次:A53C759F-39944E09-98765432
    第四次:A53C759F-39944E09-0828C8EC

这样可以清晰的看到逍遥兄算法分析的结果,
    第一部分:CRC(注册名)------------------------我的是CRC32(agang),结果是A53C759F(真码的第一部分)
  第二部分:CRC(注册码第一部分)-----------------我的是CRC32(A53C759F),结果是39944E09(真码的第二部分)
  第三部分:XOR [  第一部分,CRC(第二部分)]----我的是XOR(A53C759F,CRC32(39944E09)),结果是0828C8EC(真码的第三部分)


最后有两个地方想说一下:
1、连接符'-'的穷举法问题。
此crackme的第9位和第18位均要求是'-',其中在
004011F9 |. 8D1D 7F344000 LEA EBX,DWORD PTR DS:;处,
把注册码第9位与第18位作为一个整体,也就是'--',再对它进行CRC32计算。
这里我在想,当我们只有CRC32结果,要对其逆推并穷举出原字符串是多大的工程啊?
逍遥兄是不是应用了社会工程学原理呀?
要不就是爆CRC32原码的专用工具(像MD5爆破一样)?如果是,请兄弟们不要金屋藏娇了,拿出来大家共享下吧,在下先谢过了。。。
2、提示成功窗口的问题。
我按照逍遥兄的分析,得出自己的正确注册码并载入OD。当第三部分验证结束后,我以为会弹出提示成功的窗口信息,所以一直F8,就等着听响,过了好久,仍不见动静,反而又跳到不知道是什么地方。我以为是我的注册码不对,遂关掉OD,直接运行软件注册。
这才明白了,原来我们在OD中超级字符串找到的'GOOD JOB, MAN!'并不是提示窗口,而是标题栏的信息。也就是说注册成功后,软件会把标题栏原来的信息'Coded by '改成为'GOOD JOB ,MAN!',并且设置Check按钮不可用。
这里我想说的是,Crackme作者HappyTown兄弟的思路,非常适合应用到实际。防止我等菜鸟上来就是窗口拦截断点打到入手处。如果字符再加密,恐怕我等菜辈下断都成问题了。必竟我等菜菜还是占多数嘛!呵呵

agang好啰嗦,谢谢兄弟能坚持看这里。

lhl8730 发表于 2006-8-11 21:32:10

好文,学习了/:D!!!

agang 发表于 2006-8-11 23:47:50

兄弟所讲的凭经验,道上也叫社会工程学,呵呵

作者都说了是CRC32,其结果是8位,共分三组比较,再加两个连接符,这也是经验吧,

musoft 发表于 2007-1-3 23:56:30

/:D 学习,社会工程学,呵呵~~穷举可是个工程了

冷血书生 发表于 2007-1-4 14:23:10

agang好像是黑道高手!:lol: :lol:

无意中见过!

playboyjin 发表于 2007-1-4 15:49:46

看的不是很懂,,,,我不会算法的哦,,,呵呵。。。。努力中

binbinbin 发表于 2007-1-4 19:40:12

加密算法
........

nieufo 发表于 2007-1-4 20:34:13

好呀,很详细!

dewar 发表于 2007-1-11 15:52:03

学习一下,共同进步:handshake

agang 发表于 2007-1-11 23:16:54

属于自己的时间越来越少了,每天睁眼忙到现在才有空触网,急得要死.

TO书生兄弟:你在PYG讲话很有影响力的,在PYG我是密界菜菜,其余的暂且不谈.哈哈
页: [1] 2
查看完整版本: 学习逍遥风对HappyTown兄弟的crackme_13(CRC32)的算法分析,超菜级笔记