最近一直与失败为伍

1.
解决UltraDict之后,想试试商业软件
正好有一款国内著名的ciba软件,虽然有Crack补丁,不过有bug
每次上网之后她就不能用了,要再打一次补丁才可以,估计她会自动连接网络验证激活码
把她更新的网络设置改成代理方式,设置一个不能连接网络的代理地址
想把她困死在代理里,结果还是失败,猜想她是正常连接失败才会通过代理连接,或者相反
好,捆不住你是吧,把你关起来看你还能咋办!
用系统自带的防火墙阻止她的网络连接
或者把她拉到其他防火墙的黑名单里,禁止她一切的网络通信
(这个方法还没有测试,估计100%可行)

回到ciba的分析上,开始对她束手无策
反汇编之后,查看串式参考没有找到任何有用的信息(当时只会这种方法)
之后又看别人Crack其他软件的文章,开始接触OD,学习动态调试(动态调试关键是找准断点)
学会通过函数参考找断点,终于找到ciba的入口了,虽然只是Crack的起点

分析代码,找到关键跳转,修改之后跳到保存注册信息的窗口
再下一步却返回到注册窗口,没有跳进主界面
反复分析代码(当时读懂的代码少之又少,边学边看)
反反复复修改,还是进不去主界面,暴力Crack暂时失败

之后采取跟踪程序,试着能否跟踪出激活码,这个过程非常累
程序在关键call里call了又call,往往跟踪一两个小时就眼花,不知东南西北
关键在于没有抓到最关键的代码,反反复复都是事倍功半,只好暂时放手

分析的过程中注意到一点:
注册ciba先是输入通行证的用户名和密码
再把用户名通过算法转成数字,而密码只是通过简单的移位
字母不变,数字虽然通过算法转变了,但是通过黑盒测试就能够找到转变的规律
用户名和密码变换之后连接成请求码,最后转成激活码


2.
之后转战Ashampoo的刻录软件(下载的网站提供了一个可用的SN)
试用期是10天,一直都没有想到要Crack她的时间限制,因为当时脑海根本没有这个概念
一开始还是老办法,反汇编,查看串式参考,发现不少有用的信息
爆破点很快就找到了,修改关键跳转,输入任意注册码都能成功跳进主界面!
不过马上发现问题,每次启动都要输入注册码,查看注册表,注册码的信息已经创建了
估计有一部分注册相关的代码没有正确执行
或者是软件在启动的时候存在一个判断注册码的机制(这条经过对比分析暂时排除)
当时分析代码还是很困难,暂时没有去管哪些代码没有执行了
紧接着转去跟踪注册码的生成过程,只能说她的过程比ciba稍微容易点
断断续续的跟踪N次,没有跟踪出完整的注册码
不过已经有了大概的了解,进一步分析需要读懂更多的代码
于是返回分析UltraDict的代码,逐渐学习汇编语言


3.
分析UltraDict的结果如下:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:         ;假设用户名的每个字符的十进制用X表示
|:00401CA8(C)
|
:00401C33 0FBE7C3420              movsx edi, byte ptr [esp+esi+20]         ;根据用户名计算注册码算法开始,edi一直保存用户名的字符
:00401C38 8BC7                    mov eax, edi
:00401C3A B90A000000              mov ecx, 0000000A         ;0Ah=10
:00401C3F 99                      cdq         ;edx清零
:00401C40 F7F9                    idiv ecx         ;X/10,商没有进入下面的计算,edx保存余数,实际上是把X的个位数存入edx
:00401C42 8BCA                    mov ecx, edx         ;X的个位数也存入ecx,这里为后面一个关系ecx的运算做准备
:00401C44 81E201000080            and edx, 80000001         ;edx是奇数则为1,偶数则为0
:00401C4A 7905                    jns 00401C51         ;结果为正则跳,因为edx不可能为负,所以必跳
:00401C4C 4A                      dec edx         ;如果edx为负,从这里开始的三行代码是为了防止溢出?
:00401C4D 83CAFE                  or edx, FFFFFFFE
:00401C50 42                      inc edx

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401C4A(C)
|
:00401C51 7516                    jne 00401C69         ;edx是奇数就跳,即X是奇数就跳
:00401C53 8BC7                    mov eax, edi
:00401C55 B91A000000              mov ecx, 0000001A         ;1Ah=26
:00401C5A 99                      cdq         ;edx清零
:00401C5B F7F9                    idiv ecx         ;X/26
:00401C5D 80C241                  add dl, 41         ;41h=65,X/26的余数+65=注册码的十进制数
:00401C60 88943448010000          mov byte ptr [esp+esi+00000148], dl         ;算出的注册码是大写字母,保存在这里
:00401C67 EB2E                    jmp 00401C97

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401C51(C)
|
:00401C69 8BC1                    mov eax, ecx         ;ecx保存着X的个位数
:00401C6B BB03000000              mov ebx, 00000003
:00401C70 99                      cdq
:00401C71 F7FB                    idiv ebx         ;X的个位数/3
:00401C73 85D2                    test edx, edx         ;判断是否整除,整除则edx=0
:00401C75 7516                    jne 00401C8D         ;不整除就跳
:00401C77 8BC7                    mov eax, edi
:00401C79 B91A000000              mov ecx, 0000001A
:00401C7E 99                      cdq         ;edx清零
:00401C7F F7F9                    idiv ecx
:00401C81 80C261                  add dl, 61         ;61h=97,X/26的余数+97=注册码的十进制数
:00401C84 88943448010000          mov byte ptr [esp+esi+00000148], dl         ;算出的注册码是小写字母,保存在这里
:00401C8B EB0A                    jmp 00401C97

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401C75(C)
|
:00401C8D 80C131                  add cl, 31         ;31h=49,X的个位数+49=注册码的十进制数
                                                     ;个位数最大是9,结果会出现58,不是数字?不会,前面个位数/3排除这个结果
:00401C90 888C3448010000          mov byte ptr [esp+esi+00000148], cl         ;算出的注册码是数字,保存在这里

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00401C67(U), :00401C8B(U)
|
:00401C97 8D7C2420                lea edi, dword ptr [esp+20]
:00401C9B 83C9FF                  or ecx, FFFFFFFF
:00401C9E 33C0                    xor eax, eax
:00401CA0 46                      inc esi
:00401CA1 F2                      repnz
:00401CA2 AE                      scasb
:00401CA3 F7D1                    not ecx
:00401CA5 49                      dec ecx
:00401CA6 3BF1                    cmp esi, ecx         ;esi的值是已经循环的次数,ecx的值是注册码字符串长度
:00401CA8 7289                    jb 00401C33         ;算法末尾,不等就跳转,循环次数为注册码字符串长度
:00401CAA 33DB                    xor ebx, ebx

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401C31(C)
|
:00401CAC 55                      push ebp
:00401CAD 8D8C24EC000000          lea ecx, dword ptr [esp+000000EC]
:00401CB4 889C344C010000          mov byte ptr [esp+esi+0000014C], bl
:00401CBB E8F0010000              call 00401EB0
:00401CC0 899C24B4010000          mov dword ptr [esp+000001B4], ebx
:00401CC7 8DB42484000000          lea esi, dword ptr [esp+00000084]         ;信息窗口显示输入的注册码的ASCII码
:00401CCE 8D842448010000          lea eax, dword ptr [esp+00000148]         ;如果断点设在这里,信息窗口直接显示正确的注册码的ASCII码

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401CF3(C)
|
:00401CD5 8A10                    mov dl, byte ptr [eax]         ;开始判断输入的注册码与算出来的注册码是否一致
:00401CD7 8ACA                    mov cl, dl
:00401CD9 3A16                    cmp dl, byte ptr [esi]
:00401CDB 751C                    jne 00401CF9
:00401CDD 3ACB                    cmp cl, bl
:00401CDF 7414                    je 00401CF5
:00401CE1 8A5001                  mov dl, byte ptr [eax+01]
:00401CE4 8ACA                    mov cl, dl
:00401CE6 3A5601                  cmp dl, byte ptr [esi+01]
:00401CE9 750E                    jne 00401CF9
:00401CEB 83C002                  add eax, 00000002
:00401CEE 83C602                  add esi, 00000002
:00401CF1 3ACB                    cmp cl, bl
:00401CF3 75E0                    jne 00401CD5

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401CDF(C)
|
:00401CF5 33C0                    xor eax, eax
:00401CF7 EB05                    jmp 00401CFE

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00401CDB(C), :00401CE9(C)
|
:00401CF9 1BC0                    sbb eax, eax
:00401CFB 83D8FF                  sbb eax, FFFFFFFF

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401CF7(U)
|
:00401CFE 3BC3                    cmp eax, ebx
:00401D00 0F85DF000000            jne 00401DE5         ;判断之后的关键跳转
:00401D06 6801100000              push 00001001

注册码的算法很简单,里面一个call也没有
刚开始只是想简单地注释一些代码,没想到把算法弄出来了
算法总结如下:

X是偶数:      X/26的余数+65=注册码的十进制数
X是奇数,且个位数能被3整除:      X/26的余数+97=注册码的十进制数
X是奇数,且个位数不能被3整除:      X的个位数+49=注册码的十进制数

C programming Language 是彻底生疏了,翻书+调试,搞半天才把注册机写出来,尽管糟糕,将就用吧,源码如下,错误请指出:

#include <stdio.h>
#include <string.h>

main()
{
  char s[40];
  int i=0;
  int j=0;
  printf("Please enter your name:");
  gets(s);
  i=strlen(s);
  j=i;
  for(i=0;i<j;i++)
    {
      if(s[i]%2==0)
        s[i]=s[i]%26+65;
      else
        if(s[i]%10%3==0)
          s[i]=s[i]%26+97;
        else
          s[i]=s[i]%10+49;
    }
  printf("Your SN is:\n");
  for(i=0;i<j;i++)
    {
      printf("%c",s[i]);
    }
  printf("\n");
}

测试使用的用户名和注册码如下:
注册名:gardener      注册码:z8KW2G2K

回想第一次Crack就成功了,真是碰上超级好运气!
后面遇到的都是硬骨头啊!