首页
社区
课程
招聘
bxm的第6个CrackMe破解分析
发表于: 2007-3-3 12:53 4535

bxm的第6个CrackMe破解分析

2007-3-3 12:53
4535
【文章标题】: bxm的第6个CrackMe破解分析
【文章作者】: netwind
【作者QQ号】: 群:8601428
【下载地址】: http://bbs.pediy.com/attachment.php?s=&attachmentid=4612
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  od载入,查找-》所有参考文本串-》双击“good job!"
  可以看到如下代码:
  004016F0   .  83EC 60       sub     esp, 60
  004016F3   .  53            push    ebx
  004016F4   .  55            push    ebp
  004016F5   .  BB 01000000   mov     ebx, 1
  004016FA   .  57            push    edi
  004016FB   .  8BE9          mov     ebp, ecx
  004016FD   .  53            push    ebx
  004016FE   .  E8 B5780100   call    00418FB8
  00401703   .  8D4424 0C     lea     eax, [esp+C]
  00401707   .  6A 14         push    14                    ; /Arg3 = 00000014
  00401709   .  50            push    eax                   ; |Arg2
  0040170A   .  68 E9030000   push    3E9                   ; |Arg1 = 000003E9
  0040170F   .  8BCD          mov     ecx, ebp              ; |
  00401711   .  E8 1F820100   call    00419935              ; \crackme_.00419935
  00401716   .  83F8 03       cmp     eax, 3                ;  用户名长度需大于3
  00401719   .  0F8C A7000000 jl      004017C6
  0040171F   .  8D4C24 48     lea     ecx, [esp+48]
  00401723   .  6A 21         push    21                    ; /Arg3 = 00000021
  00401725   .  51            push    ecx                   ; |Arg2
  00401726   .  68 EA030000   push    3EA                   ; |Arg1 = 000003EA
  0040172B   .  8BCD          mov     ecx, ebp              ; |
  0040172D   .  E8 03820100   call    00419935              ; \crackme_.00419935
  00401732   .  8D7C24 0C     lea     edi, [esp+C]          ;  将用户名字符串地址给edi
  00401736   .  83C9 FF       or      ecx, FFFFFFFF
  00401739   .  33C0          xor     eax, eax
  0040173B   .  33D2          xor     edx, edx
  0040173D   .  F2:AE         repne   scas byte ptr es:[edi>
  0040173F   .  F7D1          not     ecx
  00401741   .  49            dec     ecx
  00401742      74 25         je      short 00401769        ;  用户名串长度为0则跳(感觉多余代码,前面已经判断要大于3了)
  00401744   .  56            push    esi                   ;  保存esi
  00401745   .  BE 14000000   mov     esi, 14
  0040174A   >  0FBE4414 10   movsx   eax, byte ptr [esp+edx+10>;  从头开始取用户名字符asc码
  0040174F   .  0FAFC6        imul    eax, esi              ;  将取得的asc码乘以esi
  00401752   .  03D8          add     ebx, eax
  00401754   .  42            inc     edx
  00401755   .  8D7C24 10     lea     edi, [esp+10]
  00401759   .  83C9 FF       or      ecx, FFFFFFFF
  0040175C   .  33C0          xor     eax, eax
  0040175E   .  4E            dec     esi                   ;  esi-1
  0040175F   .  F2:AE         repne   scas byte ptr es:[edi>;  从头开始扫描edi指的字符串,同时ecx-1。
  00401761   .  F7D1          not     ecx
  00401763   .  49            dec     ecx
  00401764   .  3BD1          cmp     edx, ecx              ;  ecx 实际上是edi指的字符串的长度。
  00401766   .^ 72 E2         jb      short 0040174A        ;  因此这里循环次数为用户名的长度
  00401768   .  5E            pop     esi
  00401769      8B15 C0D04200 mov     edx, [42D0C0]         ;  这里获取通过GetStartupInfo(&si)运算得到的随机数
  0040176F   .  8D4C24 24     lea     ecx, [esp+24]
  00401773   .  6A 0A         push    0A
  00401775   .  03D3          add     edx, ebx              ;  ebx为上面的那个循环计算得到的数
  00401777   .  51            push    ecx
  00401778   .  52            push    edx
  00401779   .  E8 687F0000   call    004096E6              ;  将edx对应的十进制数转为字符串
  0040177E   .  8D7C24 30     lea     edi, [esp+30]         ;  取得字符串地址
  00401782   .  83C9 FF       or      ecx, FFFFFFFF
  00401785   .  33C0          xor     eax, eax
  00401787   .  83C4 0C       add     esp, 0C
  0040178A   .  33D2          xor     edx, edx
  0040178C   .  F2:AE         repne   scas byte ptr es:[edi>
  0040178E   .  F7D1          not     ecx
  00401790   .  49            dec     ecx                   ;  执行后ecx 实际上是edi指的字符串的长度。
  00401791      74 23         je      short 004017B6        ;  若串长度为0提示正确信息(感觉这里的代码是多余的,它不可能为0)
  00401793   >  0FBE4414 24   movsx   eax, byte ptr [esp+ed>;  取所得串第一个字符
  00401798   .  0FBE4C14 48   movsx   ecx, byte ptr [esp+ed>;  取输入的注册码第一个字符
  0040179D   .  03C2          add     eax, edx              ;  将串第一个字符asc码加其序号(从0开始)
  0040179F   .  3BC1          cmp     eax, ecx              ;  再与注册码第一个字符比较
  004017A1      75 23         jnz     short 004017C6        ;  不相等,则错
  004017A3   .  8D7C24 24     lea     edi, [esp+24]
  004017A7   .  83C9 FF       or      ecx, FFFFFFFF
  004017AA   .  33C0          xor     eax, eax
  004017AC   .  42            inc     edx
  004017AD   .  F2:AE         repne   scas byte ptr es:[edi>
  004017AF   .  F7D1          not     ecx
  004017B1   .  49            dec     ecx
  004017B2   .  3BD1          cmp     edx, ecx
  004017B4   .^ 72 DD         jb      short 00401793        ;  再对第二个字符比较,循环次数为所得串的长度
  004017B6   >  6A 00         push    0                     ;  两串匹配则提示正确消息
  004017B8   .  6A 00         push    0
  004017BA   .  68 34B14200   push    0042B134              ;  ASCII "Good job!"
  004017BF   .  8BCD          mov     ecx, ebp
  004017C1   .  E8 96700100   call    0041885C
  004017C6   >  5F            pop     edi
  004017C7   .  5D            pop     ebp
  004017C8   .  5B            pop     ebx
  004017C9   .  83C4 60       add     esp, 60
  004017CC   .  C3            retn
  
  下面看看42D0C0里的随机数是怎么得到的:
  重新用od载入,载od左下角内存窗口右键-转到-表达式 填42D0C0 然后转到这个地址;现在这个地址是四个0
  将这四个0选中,右键-断点-内存写入。
  然后运行程序断在:0040141E   .  8915 C0D04200 mov     [42D0C0], edx         ; |
  其附近有如下代码:
  00401405   .  51            push    ecx                   ; /pStartupinfo
  00401406   .  FF15 78224200 call    [<&KERNEL32.GetStartu>; \GetStartupInfoA
  0040140C   .  8B5424 30     mov     edx, [esp+30]         ;  startupinfo结构第9个参数
  00401410   .  8B4424 24     mov     eax, [esp+24]         ;  第6个参数
  00401414   .  8B5C24 20     mov     ebx, [esp+20]         ;  第5个参数
  00401418   .  03D0          add     edx, eax
  0040141A   .  03D3          add     edx, ebx              ;  将三个参数相加就得到随机数
  0040141C   .  6A 00         push    0                     ; /Revert = FALSE
  0040141E   .  8915 C0D04200 mov     [42D0C0], edx         ; |
  
  
  大致算法如下:
  1、将用户名每一位乘以esi(esi=esi-1初始值esi=0x14)所得的和相加,再加1,得到一个数。
  2、将此数加上随机数,得到另一个数。
  3、将得到数的十进制每一位加上它对应的序号(比如123加序号后为135)得到的数的十进制,作为一个字符串就是注册码。
  根据bxm提示,随机数是在用od载入时才产生,那么第二步则可省略,就剩下第一步和第三步。
  算法c程序表示如下:
  #include <stdio.h>
  #include <string.h>
  void main()
  {
          char name[]="netwind";
          char temp[20];
          long c=1,esi=0x14;
          int i,l;
          for(i=0;i<strlen(name);i++,esi--)
                  c=c+name[i]*esi;
          i=0;
      while(c)                     //将数字十进制转字符串。
          {        temp[i++]=c%10+'0';      //这里得的是逆序的。
              c/=10;
          }
          temp[i]=0;
          l=strlen(temp);
      for(i=0;i<strlen(temp);i++,l--) //每一位加上其正序时的序号。
          printf("%c",temp[l-1]+=i);      //将逆序串从最后一为开始输出,就是注册码。
          printf("\n");
   }
  过程与汇编代码一样,只是汇编代码中间有一步把逆序串转正序,这里节省了这一步。
感想:
这个crackme很特别,很容易让人感觉写不出注册机.
程序应该加了异常处理吧,
od载入时,则会读取本进程STARTUPINFO 结构.
lpStartupInfo,参数结构
typedef struct _STARTUPINFO { // si
    DWORD   cb; //结构长度
    LPTSTR  lpReserved; //保留
    LPTSTR  lpDesktop; //保留
    LPTSTR  lpTitle; //如果为控制台进程则为显示的标题
    DWORD   dwX; //窗口位置...........
    DWORD   dwY; //窗口位置...........
    DWORD   dwXSize; //窗口大小
    DWORD   dwYSize; //窗口大小
    DWORD   dwXCountChars; //控制台窗口字符号宽度 .........
    DWORD   dwYCountChars; //控制台窗口字符号高度
    DWORD   dwFillAttribute; //控制台窗口填充模式
    DWORD   dwFlags; //创建标记
    WORD    wShowWindow; //窗口显示标记如同ShowWindow中的标记
    WORD    cbReserved2; //
    LPBYTE  lpReserved2; //
    HANDLE  hStdInput; //标准输入句柄
    HANDLE  hStdOutput; //标准输出句柄
    HANDLE  hStdError; //标准错误句柄
} STARTUPINFO, *LPSTARTUPINFO;
将读出的结构的第9,6,5个参数的值相加得到随机数,然后加上前面运算得到的数字,得到一个数,把该数十进制每一位加该位的序号(从0开始)得到一个数,把该数十进制表示的数转为字符串形式便是注册码.
如果单用od跟踪,而没考虑到异常的话,很难想象可以做出一个注册机.
因为:
netwind  (2007-03-02 22:42:07)
问下:
GetStartupInfo(&si);获得当前进程的STARTUPINFO 结构
如何获得指定某个进程的STARTUPINFO 结构
大牛(2007-03-02 22:43:16)
那些信息都存放在那个进程的 PEB 里。
大牛 (2007-03-02 22:43:30)
PEB 不公开的。去找些资料来看
netwind  (2007-03-02 22:44:06)
恩,谢谢了
没有其他的简单方法了吗?
大牛  (2007-03-02 22:44:17)

大牛 (2007-03-02 22:44:27)
或者说,我不知道

crackme的算法分析较容易,爆破点也很容易找到,对异常处理那块较感兴趣.
希望有人能贴出更好的破文,谢谢!
这个crackme应该公布 源代码 希望如此,bxm大牛辛苦了,多谢!  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2007年03月03日 12:51:45

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (9)
雪    币: 297
活跃值: (21)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
2
关键地方没找到,为什么OD载入时就会产生随机数,直接运行时就不会产生呢?
2007-3-3 13:19
0
雪    币: 10948
活跃值: (3298)
能力值: (RANK:520 )
在线值:
发帖
回帖
粉丝
3
是的,贴的只是算法部分,我尝试跟踪过了,从头开是一步一步f8下去,我对异常处理几乎不了解,期待高人分析异常处理那部分.
2007-3-3 13:32
0
雪    币: 297
活跃值: (21)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
4
netwind兄的QQ群不让加人?
2007-3-3 13:34
0
雪    币: 10948
活跃值: (3298)
能力值: (RANK:520 )
在线值:
发帖
回帖
粉丝
5
可以加啊,只是有验证啊
2007-3-3 14:02
0
雪    币: 263
活跃值: (10)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
6
好,开始我也错了,后来 BXM 公布了正确的信息后,拿着正版序列号,才发觉原来是这样的
顶。。。。。。。。。。。。。这贴值得顶
2007-3-3 14:52
0
雪    币: 1969
活跃值: (46)
能力值: (RANK:550 )
在线值:
发帖
回帖
粉丝
7
最初由 netwind 发布
crackme的算法分析较容易,爆破点也很容易找到,对异常处理那块较感兴趣.
希望有人能贴出更好的破文,谢谢!

其实没有什么异常处理,也不存在什么更关键的地方了,您已经全都分析到了
关于GetStartupInfo的问题可以看一看dummy兄的《使用 GetStartupInfo 检查自己是否被"调试"》一文。(http://bbs.pediy.com/showthread.php?threadid=31447)
2007-3-3 15:58
0
雪    币: 297
活跃值: (21)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
8
哦,原来是这样,明白了,谢谢楼上的各位,谢谢netwind的破文,也谢谢bxm的CM
2007-3-3 18:47
0
雪    币: 10948
活跃值: (3298)
能力值: (RANK:520 )
在线值:
发帖
回帖
粉丝
9
谢谢hawking,你怎么找到着篇文章的,强.
谢谢《使用 GetStartupInfo 检查自己是否被"调试"》文章作者.
谢谢bxm
谢谢大家的讨论.
2007-3-3 21:29
0
雪    币: 207
活跃值: (12)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
10
学习中 thanks.
2007-3-5 05:11
0
游客
登录 | 注册 方可回帖
返回
//