吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 21179|回复: 67
收起左侧

[原创] IcoFx2.11算法分析&keygen编写

  [复制链接]
红绡枫叶 发表于 2015-5-26 11:18
本帖最后由 红绡枫叶 于 2015-5-26 11:25 编辑

IcoFX是我个人比较喜欢的软件,做图标很方便.语言:delphi xe2(2012).
它的注册方式是本地验证.只不过本地验证有些陷阱,有虚假注册(只是验证一部分注册码,在其他地方才验证全部).
首先应该明白delphi编译器的特点.参数三个之内用寄存器,多的用堆栈.即
delphi(eax,edx,ecx,stack...). 在 IDA中的调用叫做 __fastcall.
所有的符号分析由IDR完成,生成map给IDA和od用.
注册.png
注册按钮是灰色的.在IDR里面很容易找到注册所在的form:
_TfrmRegister_btnBuyClick
_TfrmRegister_btnCancelClick  
_TfrmRegister_btnPasteClick   
_TfrmRegister_btnRegisterClick
_TfrmRegister_FormClose      
_TfrmRegister_FormCreate   
_TfrmRegister_FormShow        
_TfrmRegister_Timer1Timer      
其中有一个Timer1Timer,很可能就是验证数据是否合法来激活注册按钮的.
其实就是定时对剪切板内容进行提取,符合验证就让paste按钮有效.

看Timer1Timer代码:
_Unit216::TfrmRegister.Timer1Timer
....
[Asm] 纯文本查看 复制代码
 00A3ADB8    push       ebp
 00A3ADB9    push       0A3AF37
 00A3ADBE    push       dword ptr fs:[eax]
 00A3ADC1    mov        dword ptr fs:[eax],esp
 00A3ADC4    call       Clipboard
 00A3ADC9    mov        dx,1
 00A3ADCD    call       TClipboard.HasFormat //剪切板是否是一定的格式.初始化的时候他设置的格式为字符串不为空.具体地方自己找吧.
 00A3ADD2    test       al,al
>00A3ADD4    je         00A3AF1A //剪切板没有有字符串就不用验证.
 00A3ADDA    call       Clipboard
 00A3ADDF    lea        edx,[ebp-4C]
 00A3ADE2    call       TClipboard.GetAsText //获取剪切板内容
 00A3ADE7    mov        eax,dword ptr [ebp-4C]
 00A3ADEA    lea        edx,[ebp-48]
 00A3ADED    call       Trim        //去掉空格等.
 00A3ADF2    mov        edx,dword ptr [ebp-48]
 00A3ADF5    mov        eax,dword ptr [ebp-4]
 00A3ADF8    mov        eax,dword ptr [eax+3D8]; TfrmRegister.KeyChecker:TPKVSCheckKey
 00A3ADFE    add        eax,8; TPKVSCheckKey.FKey:string
 00A3AE01    call       @UStrAsg //这是字符串复制用的函数.
 00A3AE06    mov        eax,dword ptr [ebp-4]
 00A3AE09    mov        eax,dword ptr [eax+3D8]; TfrmRegister.KeyChecker:TPKVSCheckKey
 00A3AE0F    mov        dword ptr [ebp-0C],eax
 00A3AE12    lea        eax,[ebp-10]
 00A3AE15    mov        edx,dword ptr [ebp-4]
 00A3AE18    mov        edx,dword ptr [edx+3D8]; TfrmRegister.KeyChecker:TPKVSCheckKey
 00A3AE1E    mov        edx,dword ptr [edx+8]; TPKVSCheckKey.FKey:string
 00A3AE21    call       @UStrLAsg
 00A3AE26    mov        byte ptr [ebp-11],0
 00A3AE2A    mov        al,[00A3B144]; 0x1
 00A3AE2F    push       eax
 00A3AE30    lea        eax,[ebp-50]
 00A3AE33    push       eax
 00A3AE34    xor        ecx,ecx
 00A3AE36    mov        edx,0A3B154; '-'
 00A3AE3B    mov        eax,dword ptr [ebp-10]
 00A3AE3E    call       StringReplace //去掉'-'
 00A3AE43    mov        eax,dword ptr [ebp-50]
 00A3AE46    lea        edx,[ebp-18]
 00A3AE49    call       UpperCase
 00A3AE4E    mov        eax,dword ptr [ebp-18]
 00A3AE51    mov        dword ptr [ebp-20],eax
 00A3AE54    cmp        dword ptr [ebp-20],0 //去掉'-',空格等之后,判断是否为空.
>00A3AE58    je         00A3AE65
 00A3AE5A    mov        eax,dword ptr [ebp-20]
 00A3AE5D    sub        eax,4
 00A3AE60    mov        eax,dword ptr [eax]
 00A3AE62    mov        dword ptr [ebp-20],eax
 00A3AE65    cmp        dword ptr [ebp-20],19 //判断是否是25个字符,测试的时候输入假验证码不就是25个嘛,要联想.
>00A3AE69    jne        00A3AEFE
 00A3AE6F    lea        eax,[ebp-1C]
 00A3AE72    push       eax
 00A3AE73    mov        ecx,3
 00A3AE78    mov        edx,17
 00A3AE7D    mov        eax,dword ptr [ebp-18]
 00A3AE80    call       @UStrCopy  //字符串复制到 dword ptr [ebp-18]
 00A3AE85    lea        eax,[ebp-18]
 00A3AE88    mov        ecx,3
 00A3AE8D    mov        edx,17
 00A3AE92    call       @UStrDelete //删除第23,24,25个字符.也就是删除最后三个字符.汇编中数字都是16进制的!
 00A3AE97    lea        eax,[ebp-54]
 00A3AE9A    push       eax
 00A3AE9B    lea        ecx,[ebp-58]
 00A3AE9E    mov        edx,dword ptr [ebp-18]
 00A3AEA1    mov        eax,dword ptr [ebp-0C]
 00A3AEA4    call       0091D170
 00A3AEA9    mov        edx,dword ptr [ebp-58]
 00A3AEAC    mov        ecx,3
 00A3AEB1    mov        eax,dword ptr [ebp-0C]
 00A3AEB4    call       00921B44
 00A3AEB9    mov        edx,dword ptr [ebp-54]
 00A3AEBC    mov        eax,dword ptr [ebp-1C]
 00A3AEBF    call       @UStrEqual //比较字符串
>00A3AEC4    je         00A3AEF9 //相等就跳,看下面跳的地方有TfrmRegister.btnPaste:TPngSpeedButton,TControl.SetEnabled
 00A3AEC6    lea        eax,[ebp-5C] //就知道让粘贴的图标按钮有效.
 00A3AEC9    push       eax
 00A3AECA    lea        ecx,[ebp-60]
 00A3AECD    mov        edx,dword ptr [ebp-18]
 00A3AED0    mov        eax,dword ptr [ebp-0C]
 00A3AED3    call       0091D338
 00A3AED8    mov        edx,dword ptr [ebp-60]
 00A3AEDB    mov        ecx,3
 00A3AEE0    mov        eax,dword ptr [ebp-0C]
 00A3AEE3    call       00921B44
 00A3AEE8    mov        edx,dword ptr [ebp-5C]
 00A3AEEB    mov        eax,dword ptr [ebp-1C]
 00A3AEEE    call       @UStrEqual
>00A3AEF3    je         00A3AEF9
 00A3AEF5    xor        eax,eax
>00A3AEF7    jmp        00A3AEFB
 00A3AEF9    mov        al,1
 00A3AEFB    mov        byte ptr [ebp-11],al
 00A3AEFE    mov        al,byte ptr [ebp-11]
 00A3AF01    mov        byte ptr [ebp-5],al
 00A3AF04    mov        eax,dword ptr [ebp-4]
 00A3AF07    mov        eax,dword ptr [eax+3D0]; TfrmRegister.btnPaste:TPngSpeedButton
 00A3AF0D    mov        dl,byte ptr [ebp-5]
 00A3AF10    mov        ecx,dword ptr [eax]
 00A3AF12    call       dword ptr [ecx+80]; TControl.SetEnabled
>00A3AF18    jmp        00A3AF2D
 00A3AF1A    mov        eax,dword ptr [ebp-4]
 00A3AF1D    mov        eax,dword ptr [eax+3D0]; TfrmRegister.btnPaste:TPngSpeedButton
 00A3AF23    xor        edx,edx
 00A3AF25    mov        ecx,dword ptr [eax]
 00A3AF27    call       dword ptr [ecx+80]; TControl.SetEnabled
 00A3AF2D    xor        eax,eax
 00A3AF2F    pop        edx
 00A3AF30    pop        ecx
 00A3AF31    pop        ecx
 00A3AF32    mov        dword ptr fs:[eax],edx
>00A3AF35    jmp        00A3AF54
<00A3AF37    jmp        @HandleAnyException
 00A3AF3C    mov        eax,dword ptr [ebp-4]
 00A3AF3F    mov        eax,dword ptr [eax+3D0]; TfrmRegister.btnPaste:TPngSpeedButton
 00A3AF45    xor        edx,edx
 00A3AF47    mov        ecx,dword ptr [eax]
 00A3AF49    call       dword ptr [ecx+80]; TControl.SetEnabled
 00A3AF4F    call       @DoneExcept
 00A3AF54    lea        edx,[ebp-64]
 00A3AF57    mov        eax,dword ptr [ebp-4]
 00A3AF5A    mov        eax,dword ptr [eax+390]; TfrmRegister.KeyEdit1:TKeyEdit
 00A3AF60    call       TKeyEdit.GetKey
 00A3AF65    mov        edx,dword ptr [ebp-64]
 00A3AF68    mov        eax,dword ptr [ebp-4]
 00A3AF6B    mov        eax,dword ptr [eax+3D8]; TfrmRegister.KeyChecker:TPKVSCheckKey
 00A3AF71    add        eax,8; TPKVSCheckKey.FKey:string
 00A3AF74    call       @UStrAsg
 00A3AF79    lea        edx,[ebp-6C]
 00A3AF7C    mov        eax,dword ptr [ebp-4]
 00A3AF7F    mov        eax,dword ptr [eax+3A8]; TfrmRegister.edtName:TEdit
 00A3AF85    call       TControl.GetText
 00A3AF8A    mov        eax,dword ptr [ebp-6C]
 00A3AF8D    lea        edx,[ebp-68]
 00A3AF90    call       Trim
 00A3AF95    cmp        dword ptr [ebp-68],0
>00A3AF99    je         00A3B0A5
 00A3AF9F    mov        eax,dword ptr [ebp-4]
 00A3AFA2    mov        eax,dword ptr [eax+3D8]; TfrmRegister.KeyChecker:TPKVSCheckKey
 00A3AFA8    mov        dword ptr [ebp-28],eax
 00A3AFAB    lea        eax,[ebp-2C]
 00A3AFAE    mov        edx,dword ptr [ebp-4]
 00A3AFB1    mov        edx,dword ptr [edx+3D8]; TfrmRegister.KeyChecker:TPKVSCheckKey
 00A3AFB7    mov        edx,dword ptr [edx+8]; TPKVSCheckKey.FKey:string
 00A3AFBA    call       @UStrLAsg
 00A3AFBF    mov        byte ptr [ebp-2D],0
 00A3AFC3    mov        al,[00A3B144]; 0x1
 00A3AFC8    push       eax
 00A3AFC9    lea        eax,[ebp-70]
 00A3AFCC    push       eax
 00A3AFCD    xor        ecx,ecx
 00A3AFCF    mov        edx,0A3B154; '-'
 00A3AFD4    mov        eax,dword ptr [ebp-2C]
 00A3AFD7    call       StringReplace
 00A3AFDC    mov        eax,dword ptr [ebp-70]
 00A3AFDF    lea        edx,[ebp-34]
 00A3AFE2    call       UpperCase
 00A3AFE7    mov        eax,dword ptr [ebp-34]
 00A3AFEA    mov        dword ptr [ebp-3C],eax
 00A3AFED    cmp        dword ptr [ebp-3C],0
>00A3AFF1    je         00A3AFFE
 00A3AFF3    mov        eax,dword ptr [ebp-3C]
 00A3AFF6    sub        eax,4
 00A3AFF9    mov        eax,dword ptr [eax]
 00A3AFFB    mov        dword ptr [ebp-3C],eax
 00A3AFFE    cmp        dword ptr [ebp-3C],19
>00A3B002    jne        00A3B097
 00A3B008    lea        eax,[ebp-38]
 00A3B00B    push       eax
 00A3B00C    mov        ecx,3
 00A3B011    mov        edx,17
 00A3B016    mov        eax,dword ptr [ebp-34]
 00A3B019    call       @UStrCopy  //复制最后三位到dword ptr [ebp-34]
 00A3B01E    lea        eax,[ebp-34]
 00A3B021    mov        ecx,3
 00A3B026    mov        edx,17
 00A3B02B    call       @UStrDelete
 00A3B030    lea        eax,[ebp-74]
 00A3B033    push       eax
 00A3B034    lea        ecx,[ebp-78]
 00A3B037    mov        edx,dword ptr [ebp-34] //放的是验证注册码的最后三位
 00A3B03A    mov        eax,dword ptr [ebp-28]
 00A3B03D    call       0091D170  //比较关键的call
 00A3B042    mov        edx,dword ptr [ebp-78]
 00A3B045    mov        ecx,3
 00A3B04A    mov        eax,dword ptr [ebp-28]
 00A3B04D    call       00921B44//比较关键的call
 00A3B052    mov        edx,dword ptr [ebp-74]
 00A3B055    mov        eax,dword ptr [ebp-38]
 00A3B058    call       @UStrEqual  //上面一段和一开始的分析是一样的,所以就省略了.
>00A3B05D    je         00A3B092 //同理,这里跳转的话,注册按钮就会被激活.
 00A3B05F    lea        eax,[ebp-7C]
 00A3B062    push       eax
 00A3B063    lea        ecx,[ebp-80]
 00A3B066    mov        edx,dword ptr [ebp-34]
 00A3B069    mov        eax,dword ptr [ebp-28]
 00A3B06C    call       0091D338 //比较关键的call
 00A3B071    mov        edx,dword ptr [ebp-80]
 00A3B074    mov        ecx,3
 00A3B079    mov        eax,dword ptr [ebp-28]
 00A3B07C    call       00921B44 //比较关键的call
 00A3B081    mov        edx,dword ptr [ebp-7C]
 00A3B084    mov        eax,dword ptr [ebp-38]
 00A3B087    call       @UStrEqual
>00A3B08C    je         00A3B092 //同理,这里跳转的话,注册按钮就会被激活.
 00A3B08E    xor        eax,eax
>00A3B090    jmp        00A3B094
 00A3B092    mov        al,1
 00A3B094    mov        byte ptr [ebp-2D],al
 00A3B097    mov        al,byte ptr [ebp-2D]
 00A3B09A    mov        byte ptr [ebp-21],al
 00A3B09D    mov        al,byte ptr [ebp-21]
 00A3B0A0    mov        byte ptr [ebp-3D],al
>00A3B0A3    jmp        00A3B0A9
 00A3B0A5    mov        byte ptr [ebp-3D],0
 00A3B0A9    cmp        byte ptr [ebp-3D],0
>00A3B0AD    je         00A3B0C4
 00A3B0AF    mov        eax,dword ptr [ebp-4]
 00A3B0B2    mov        eax,dword ptr [eax+3D8]; TfrmRegister.KeyChecker:TPKVSCheckKey
 00A3B0B8    call       TPKVSCheckKey.IsBlacklisted
 00A3B0BD    xor        al,1
 00A3B0BF    mov        byte ptr [ebp-3E],al
>00A3B0C2    jmp        00A3B0C8
 00A3B0C4    mov        byte ptr [ebp-3E],0
 00A3B0C8    mov        eax,dword ptr [ebp-4]
 00A3B0CB    mov        eax,dword ptr [eax+3A0]; TfrmRegister.btnRegister:TButton
 00A3B0D1    mov        dl,byte ptr [ebp-3E]
 00A3B0D4    mov        ecx,dword ptr [eax]
 00A3B0D6    call       dword ptr [ecx+80]; TControl.SetEnabled
 00A3B0DC    xor        eax,eax
 00A3B0DE    pop        edx
 00A3B0DF    pop        ecx
 00A3B0E0    pop        ecx
 00A3B0E1    mov        dword ptr fs:[eax],edx
 00A3B0E4    push       0A3B13D
 00A3B0E9    lea        eax,[ebp-80]
 00A3B0EC    mov        edx,5
 00A3B0F1    call       @UStrArrayClr
 00A3B0F6    lea        eax,[ebp-6C]
 00A3B0F9    call       @UStrClr
 00A3B0FE    lea        eax,[ebp-68]
 00A3B101    mov        edx,9
 00A3B106    call       @UStrArrayClr
 00A3B10B    lea        eax,[ebp-38]
 00A3B10E    mov        edx,2
 00A3B113    call       @UStrArrayClr
 00A3B118    lea        eax,[ebp-2C]
 00A3B11B    call       @UStrClr
 00A3B120    lea        eax,[ebp-1C]
 00A3B123    mov        edx,2
 00A3B128    call       @UStrArrayClr
 00A3B12D    lea        eax,[ebp-10]
 00A3B130    call       @UStrClr
 00A3B135    ret
<00A3B136    jmp        @HandleFinally
<00A3B13B    jmp        00A3B0E9
 00A3B13D    pop        edi
 00A3B13E    pop        esi
 00A3B13F    pop        ebx
 00A3B140    mov        esp,ebp
 00A3B142    pop        ebp
 00A3B143    ret


看汇编挺累的,直接用IDA吧.
进入关键call 0091D170:
[C] 纯文本查看 复制代码
int __fastcall sub_91D170(int key_class, int key_1_23, int dest) //参数个数要自己分析指定
{
  void *v3; // edx@14
  unsigned __int32 v5; // [sp-Ch] [bp-48h]@1
  _UNKNOWN *v6; // [sp-8h] [bp-44h]@1
  int *v7; // [sp-4h] [bp-40h]@1
  char *Source; // [sp+4h] [bp-38h]@1
  unsigned int MaxLen; // [sp+8h] [bp-34h]@1
  int v10[2]; // [sp+Ch] [bp-30h]@13
  char *v11; // [sp+18h] [bp-24h]@4
  int v12; // [sp+1Ch] [bp-20h]@1
  char *v13; // [sp+20h] [bp-1Ch]@7
  int v14; // [sp+24h] [bp-18h]@7
  unsigned __int16 v15; // [sp+2Ah] [bp-12h]@13
  unsigned __int16 v16; // [sp+2Ch] [bp-10h]@1
  unsigned __int16 v17; // [sp+2Eh] [bp-Eh]@1
  char *v19; // [sp+34h] [bp-8h]@1
  int v20; // [sp+38h] [bp-4h]@1
  int savedregs; // [sp+3Ch] [bp+0h]@1

  Source = 0;
  MaxLen = 0;
  v19 = (char *)key_1_23;
  v20 = key_class;
  v7 = &savedregs;
  v6 = &loc_91D2D2;
  v5 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v5); //和异常处理有关
  v17 = 86;
  v16 = 175;
  v12 = key_1_23;
  if ( key_1_23 )
    v12 = *(_DWORD *)(v12 - 4); //获取字符串长度
  if ( v12 > 0 )
  {
    v11 = v19;
    if ( v19 )
      v11 = (char *)*((_DWORD *)v11 - 1);       // 获取字符串长度
    if ( (signed int)v11 > 0 )
    {
      v13 = v11;
      v14 = 1;
      do
      {
        v16 += (unsigned __int8)v19[2 * v14 - 2];
        if ( v16 > 0xFFu )
          v16 -= 255;
        v17 += v16;
        if ( v17 > 0xFFu )
          v17 -= 255;
        ++v14;
        --v13;
      }
      while ( v13 );
    }
  }
  v15 = (unsigned __int16)(v16 + (v17 << 8)) % 0xB63Fu;
  v10[0] = v15;
  v10[1] = 0;
  UStrClr(&MaxLen);
  do
  {
    v3 = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    LOWORD(v3) = a0123456789abcd[(unsigned int)(*(_QWORD *)v10 % 36i64)]; //64位求余,这里注意了,
    // 函数原型为__int64 __usercall _llmod@<edx:eax>(int numer_low@<eax>, int numer_hight@<edx>, int denom_low, int denom_hight);
    //IDA自己分析是失败的,所以要自己手动设置函数原型.
    UStrFromWChar(&Source, v3);
    UStrCat3((wchar_t *)&MaxLen, (const wchar_t *)Source, MaxLen);
    *(_QWORD *)v10 /= 36i64;//64位除法
    //此处也要注意,IDA自己分析是失败的
    //函数原型为 __int64 __usercall _lldiv@<edx:eax>(int numer_low@<eax>, int numer_hight@<edx>, int denom_low, int denom_hight);
  }
  while ( *(_QWORD *)v10 );
  UStrAsg((wchar_t *)dest, MaxLen); //复制给参数
  __writefsdword(0, 0x24u);
  return UStrArrayClr((int)&Source, 2, (int)&loc_91D2D9);
}

我再还原优化一下就是这样的:
[C++] 纯文本查看 复制代码
    unsigned int sum2 = 86;
    unsigned int sum1 = 175;
    if (int len = _key.length())
    {
        int iter = 1;
        do
        {
            sum1 += _key[iter - 1];
            if (sum1 > 0xFFu)
                sum1 -= 255;
            sum2 += sum1;
            if (sum2 > 0xFFu)
                sum2 -= 255;
            ++iter;
            --len;
        } while (len);
    }
    __int64 numr = (unsigned __int16)(sum1 + (sum2 << 8)) % 0xB63Fu;
    std::string result;
    do //注意,下面多处用到这里的代码.
    {
        result.insert(0, 1, m_alphabet[numr % 36i64]);
        numr /= 36i64;
    } while (numr);
    return result;

很简单吧? 根据前22位得出最后3位的字符,最后三位和此处得到的3位字符相等,注册按钮就会被激活.
call       00921B44//比较关键的call 此处只是防止生成的字符不足3位,不足的话用'0'填充.这里就不分析了.
call       0091D338 //比较关键的call  他只是上面分析的算法的一个变种,非常相似,可以自己分析练习练习.
此处我计算了一个:M62DI-DQ0D4-D869F-7U8XA-D3A9M.
注册后发现,关闭再打开又是没注册的了.我这里试用期已过,所以用上面的注册码注册的,虽然
提示注册成功,但是在保存的时候又提示试用期已过,请注册
.
试用期.png

看来上面的验证只是正确注册码的一部分啊.而且验证注册码的地方很多.patch不方便.
于是,找到菜单的另存为事件处理地址:_TfrmMain_actSaveAsExecute
此段代码非常长,我先上一个缩减了的代码:
[C++] 纯文本查看 复制代码
int __usercall TfrmMain_actSaveAsExecute@<eax>(int a1@<eax>, int a2@<edx>, int a3@<ebx>, int a4@<edi>, int a5@<esi>)
{
  //IDA变量声明部分略掉.

  v348 = a2;
  v349 = a1;
  v92 = &savedregs;
  v91 = &loc_A52C5C;
  v90 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v90);
  v342 = 1;
  v341 = 1; //注意,这里是打开存放图标的对话框的判断标志,看下面的分析.
  LOBYTE(a2) = 1;
  v340 = TPKVSCheckKey_Create(TPKVSCheckKey, a2);
  v89 = &savedregs;
  v88 = &loc_A5251B;
  v87 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v87);
  UStrAsg((wchar_t *)(v340 + 8), *(_DWORD *)(*(_DWORD *)TGlobalSetting + 60));
  v338 = v340;
  v337 = 0;
  *(_BYTE *)(v340 + 4) = 0;
  *(_BYTE *)(v338 + 5) = 0;
  a1a = v338;
  UStrLAsg((int *)&v335, *(_DWORD *)(v338 + 8));
  v334 = 1;
  v328 = a1a;
  UStrLAsg((int *)&v327, v335);
  v326 = 0;
  LOBYTE(v5) = 1;
  v86 = v5;
  Sysutils::StringReplace(v327, (int)"-", 0, v179);
  UpperCase(*(int *)v179, (int *)&src);
  v323 = src;
  if ( src )
    v323 = (wchar_t *)*((_DWORD *)v323 - 1);
  if ( v323 == (wchar_t *)25 )
  {
    UStrCopy(src, 23, 3, v324);
    UStrDelete(&src, 23, 3);
    sub_91D170(v328, (int)src, (int)&v177);
    sub_921B44(v328, v177, 3, (int)&v178);
    UStrEqual(*(wchar_t **)v324, v178);
    v7 = 1;
    if ( !v6 )
    {
      sub_91D338(v328, (int)src, (int)&v175, (int)&v176);
      sub_921B44(v328, v175, 3, v86);
      UStrEqual(*(wchar_t **)v324, v176);
      if ( !v6 )
        v7 = 0;
    }
    v326 = v7;
  }
  if...                                         // business license
LABEL_31:
  v337 = v334 == 0; //从下面的分析来看,这里v334必须等于0.可以看下面的分析,上面折叠了的if语句是更改了v334的.
  if...                                         // site license
  if...                                         // home license
  if...                                         // business license
  if...                                         // site license
  if...                                         // home license
  if ( !v337 && *(_DWORD *)(*(_DWORD *)TGlobalSetting + 116) <= 0 ) //v337必须为1,不然存放图标的对话框的判断标志被置为0.也就是说出现试用期过期的提示.
  {
    if ( v342 )
    {
      TLanguage_ReadMessage(*TLanguage, (int)"m", (int)&off_A52CE4, (int)&v339); //试用期过期的提示
      UStrLAsg((int *)&cchLength, v339);
      UStrLAsg(&v185, (int)"|");
      UStrLAsg(&v184, (int)L"\r\n");
      AnsiReplaceStr(cchLength, v185, v184, (unsigned int)v183);
      UStrLAsg(&v339, *(int *)v183);
      LOWORD(v48) = 3;
      LOBYTE(v49) = 2;
      if ( sub_776E98(v339, v49, v48, 0) == 6 )
        CallDynaInst(*(_DWORD *)(dword_C31358 + 976));
    }
    v341 = 0; //只有这里是让v341=0,然后就不会出现存放图标的对话框.
  }
  __writefsdword(0, v79);
  TObject_Free(v340, &loc_A52522);
  __writefsdword(0, v79);
  TObject_Free(v340, &loc_A52522);
  if ( v341 )                                   // v343==1 --> 存放图标的对话框 ,目标明确了 v341必须为1
  {
    v50 = TWindowManager_Selected(*TWindowManager[0]);
    sub_A0A6F8(v50, (int)&v95);
    Sysutils::ExtractFileName(v95, v346); //从函数名称可以看出,这里是打开存放图标的对话框.
    ExtractFileExt(*(wchar_t **)v346, v345);
    if...
    TOpenDialog_SetFileName(*(_DWORD *)(v349 + 1140), *(_DWORD *)v346);
    v51 = TWindowManager_Selected(*TWindowManager[0]);
    sub_A0A6F8(v51, (int)&v93);
    ExtractFileDir(v93, v94);
    Dialogs::TOpenDialog::SetInitialDir(*(_DWORD *)(v349 + 1140), *(int *)v94);
    v344 = 0;
    v52 = TWindowManager_Selected(*TWindowManager[0]);
    if...
    *(_DWORD *)(*(_DWORD *)(v349 + 1140) + 124) = v344;
    if...
  }
  __writefsdword(0, v80);
  UStrArrayClr((int)&v93, 87, (int)&loc_A52C66);
  UStrClr(v312);
  ......
}

上面注释里说的business license是我后面加上去的,这得要自己分析.分析后面再说.
上面的分析是逆向思维式的分析方法,从头往下顺序分析会花掉很多时间并且找不到核心思想.
现在我们分析第一个折叠的if语句:
[C++] 纯文本查看 复制代码
if ( src )
    v323 = (wchar_t *)*((_DWORD *)v323 - 1);
  if ( v323 == (wchar_t *)25 )
  {
    UStrCopy(src, 23, 3, v324);
    UStrDelete(&src, 23, 3);
    sub_91D170(v328, (int)src, (int)&v177);
    sub_921B44(v328, v177, 3, (int)&v178);
    UStrEqual(*(wchar_t **)v324, v178);
    v7 = 1;
    if ( !v6 )
    {
      sub_91D338(v328, (int)src, (int)&v175, (int)&v176); //此处我们之前分析过
      sub_921B44(v328, v175, 3, v86);
      UStrEqual(*(wchar_t **)v324, v176); //验证最后三位是否与用前22位计算出来的字符串相等.
      if ( !v6 )
        v7 = 0;
    }
    v326 = v7;
  }
  if ( v326 )                                   // business license ,第一个折叠了的if语句.
  {
    Sysutils::StringReplace(v335, (int)"-", 0, v174); //去掉'-'.
    UpperCase(*(int *)v174, (int *)&v333);
    v330 = 0;
    do
    {
      if ( checkinlist(a1a, (wchar_t *)string_array_24_[v330], v333) ) //这个有点意思,分析起来很简单,只是一个黑名单验证.
      {
        v334 = 2;
        goto LABEL_31;
      }
      ++v330;
    }
    while ( v330 != 24 );
    v334 = 3;
    UStrCopy(v333, 1, 6, v173); //复制1-6位注册码到v173用于下面的计算
    v322 = *(_DWORD *)v173;
    hight[0] = 0;
    hight[1] = 0;
    *(_DWORD *)&v318[2] = *(_DWORD *)v173;
    if ( *(_DWORD *)v173 )
      *(_DWORD *)&v318[2] = *(_DWORD *)(*(_DWORD *)&v318[2] - 4);
    if ( *(_DWORD *)&v318[2] > 0 )
    {
      v319 = *(_DWORD *)&v318[2]; //上面复制的注册码长度.
      v320 = 1;
      do                                        // sum
      {
        *(_WORD *)v318 = *(_WORD *)(v322 + 2 * v320 - 2);
        HIWORD(v8) = HIWORD(v320);
        if ( (unsigned __int16)(*(_WORD *)(v322 + 2 * v320 - 2) - 'a') < 0x1Au )// if bigger than 'z',check if clause
        {
          HIWORD(v8) = HIWORD(v320);
          *(_WORD *)v318 = *(_WORD *)(v322 + 2 * v320 - 2) & 0xFFDF;// = key[i] & 0xffdf
        }
        LOWORD(v8) = *(_WORD *)v318;
        UStrFromWChar(&v172, v8);
        v9 = Pos(v172, (int)L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") - 1;// wrrong here! 查找字符位置
        //注意这里,IDA函数分析在我修正之后依然无法分析出来这里的正确结果
        //正确的应该是*(_QWORD *)hight = 36i64 * *(_QWORD *)hight + v9;
        *(_QWORD *)hight = 36i64 * *(_QWORD *)hight + 36;
        ++v320;
        --v319;
      }
      while ( v319 );
    }
    v331[0] = hight[0];
    v331[1] = hight[1];
    UStrCopy(v333, 9, 2, v332);
    v329 = sub_91D500(a1a, 10, 0, v331[1], v331[0], 0x5C04); //根据1-6位注册码计算出来的结果再次计算出一个数字.
    numer_low = v329;
    UStrClr(&v316);
    do //前面已经给出这个循环的C++源码了.o_o忘记了?
    {
      v10 = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
      LOWORD(v10) = a0123456789a_12[(unsigned int)(numer_low % 36)];
      UStrFromWChar(&Source, v10);
      UStrCat3((wchar_t *)&v316, (const wchar_t *)Source, v316); //生成字符串
      numer_low /= 36i64;
    }
    while ( numer_low );
    sub_921B44(a1a, v316, 2, (int)&v170); //此处只是防止生成的字符不足,不足的话用'0'填充.这里就不分析了.第三个参数为期待的长度.
    UStrEqual(*(wchar_t **)v332, v170); //必须相等.
    if ( v6 ) //这里v6是zf标志,由UStrEqual来决定,IDA分析不正确.IDA无法设置函数的返回在为zf标志,所以这里有问题,下同.
    {
      UStrCopy(v333, 11, 2, v332); //复制11-12位注册码到v332用于下面的最终比较
      v329 = sub_91D500(a1a, 1, 2, v331[1], v331[0], 0x1513C);//根据1-6位注册码计算出来的结果再次计算出一个数字.
      *(_QWORD *)numer_hight = v329;
      UStrClr(Dest);
      do//前面已经给出这个循环的C++源码了.o_o忘记了?
      {
        v11 = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        LOWORD(v11) = a0123456789a_12[(unsigned int)(*(_QWORD *)numer_hight % 36i64)];
        UStrFromWChar(&v169, v11);
        UStrCat3((wchar_t *)Dest, v169, *(unsigned int *)Dest);
        *(_QWORD *)numer_hight /= 36i64;
      }
      while ( *(_QWORD *)numer_hight );
      sub_921B44(a1a, *(signed __int32 *)Dest, 2, (int)&v168);//此处只是防止生成的字符不足,不足的话用'0'填充.这里就不分析了.第三个参数为期待的长度.
      UStrEqual(*(wchar_t **)v332, v168);
      if ( v6 )
      {
        UStrCopy(v333, 17, 2, v332);//复制17-18位注册码到v332用于下面最终比较
        v329 = sub_91D500(a1a, 13, 5, v331[1], v331[0], 0x9CF84);//根据1-6位注册码计算出来的结果再次计算出一个数字.
        *(_QWORD *)v313 = v329;
        UStrClr(v312);
        do//前面已经给出这个循环的C++源码了.o_o忘记了?
        {
          v12 = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
          LOWORD(v12) = a0123456789a_12[(unsigned int)(*(_QWORD *)v313 % 36i64)];
          UStrFromWChar(&v167, v12);
          UStrCat3(v312, v167, *(unsigned int *)v312);
          *(_QWORD *)v313 /= 36i64;
        }
        while ( *(_QWORD *)v313 );
        sub_921B44(a1a, *(signed __int32 *)v312, 2, (int)&v166);//此处只是防止生成的字符不足,不足的话用'0'填充.这里就不分析了.第三个参数为期待的长度.
        UStrEqual(*(wchar_t **)v332, v166);
        if ( v6 )
          v334 = 0; //看到了吧,这里v334等于0,也就是说下面的v337等于1-->不提示过期-->打开存储对话框.这是我们想要的效果.
      }
    }
  }
LABEL_31:
  v337 = v334 == 0;

...下面省略.
现在的重头戏就是 sub_91D500(a1a, 13, 5, v331[1], v331[0], 0x9CF84)的算法了.
同样用IDA:
[C++] 纯文本查看 复制代码
int __fastcall sub_91D500(int key_class, int a2, int a3, int a4, int a5, int a6)
{
  int v6; // ebx@2
  int v7; // ebx@3
  int v9; // [sp+4h] [bp-10h]@2
  int v10; // [sp+8h] [bp-Ch]@1
  int v11; // [sp+Ch] [bp-8h]@1

  v11 = a2 % 25;
  v10 = a3 % 3;
  if ( a2 % 25 % 2 )
  {
    v7 = (unsigned __int8)sub_40B67C(a5, a4, v11);
    v9 = a6 & sub_40B67C(a5, a4, v10) ^ v7;
  }
  else
  {
    v6 = (unsigned __int8)sub_40B67C(a5, a4, v11);
    v9 = (a6 | sub_40B67C(a5, a4, v10)) ^ v6;
  }
  return v9 % 0x50F;
}

再看sub_40B67C(a5, a4, v11)里面的反汇编代码结果:
[C++] 纯文本查看 复制代码
// local variable allocation has failed, the output may be wrong!  IDA的可能出错提示
int __fastcall sub_40B67C(int result, int a2, int a3)
{
  char v3; // cl@1
  unsigned int v4; // edx@2

  v3 = a3 & 0x3F;
  if ( v3 < 32 )
    *(_QWORD *)&result >>= v3 & 0x1F;
  else
    result = v4 >> v3;
  return result;
}

其实IDA对于处理64位运算的时候很容易出错,这些代码直接使用是有问题的.怎么办?我的确不想再分析了....直接用程序的汇编代码
写个内联的汇编函数来还原算法.如下:
[C++] 纯文本查看 复制代码
__int64 KeyGenerator::KeyGen::sum_mapping(int key_class, int a2, int a3, int a4, int a5, int a6)
{
    //汇编代码是用OD的ollyext插件弄下来的,注意选择recursive.
    __asm
    {
        push a4
            push a5
            push a6
            push 0h //模拟调用的假返回地址,原汇编肯定没有这句话.这里是为了让下面的本地变量存储正常.返回前需要清除.
            mov eax, key_class
            mov edx, a2
            mov ecx, a3 //上面这几句就是模拟调用原函数用的,下面的代码才是ollyext插件弄下来的.
        LOC_FUNCTION_ENTRY_POINT :
        jmp LOC_0x0091D500
        LOC_0x0040B67C :
        and cl, 3Fh
            cmp cl, 20h
            jl LOC_0x0040B68B
            mov eax, edx
            xor edx, edx
            shr eax, cl
            ret
        LOC_0x0040B68B :
        shrd eax, edx, cl
            shr edx, cl
            ret
        LOC_0x0091D500 :
        push ebp
            mov ebp, esp
            add esp, 0FFFFFFF0h
            push ebx
            mov dword ptr[ebp - 0Ch], ecx
            mov dword ptr[ebp - 08h], edx
            mov dword ptr[ebp - 04h], eax
            mov ecx, 00000019h
            mov eax, dword ptr[ebp - 08h]
            cdq
            idiv ecx
            mov dword ptr[ebp - 08h], edx
            mov ecx, 00000003h
            mov eax, dword ptr[ebp - 0Ch]
            cdq
            idiv ecx
            mov dword ptr[ebp - 0Ch], edx
            mov eax, dword ptr[ebp - 08h]
            and eax, 80000001h
            jns LOC_0x0091D53B
            dec eax
            or eax, 0FFFFFFFEh
            inc eax
        LOC_0x0091D53B :
        test eax, eax
            jne LOC_0x0091D56D
            mov eax, dword ptr[ebp + 0Ch]
            mov edx, dword ptr[ebp + 10h]
            mov ecx, dword ptr[ebp - 08h]
            call LOC_0x0040B67C
            mov ebx, eax
            and ebx, 000000FFh
            mov eax, dword ptr[ebp + 0Ch]
            mov edx, dword ptr[ebp + 10h]
            mov ecx, dword ptr[ebp - 0Ch]
            call LOC_0x0040B67C
            or eax, dword ptr[ebp + 08h]
            xor ebx, eax
            mov dword ptr[ebp - 10h], ebx
            jmp LOC_0x0091D599
        LOC_0x0091D56D :
        mov eax, dword ptr[ebp + 0Ch]
            mov edx, dword ptr[ebp + 10h]
            mov ecx, dword ptr[ebp - 08h]
            call LOC_0x0040B67C
            mov ebx, eax
            and ebx, 000000FFh
            mov eax, dword ptr[ebp + 0Ch]
            mov edx, dword ptr[ebp + 10h]
            mov ecx, dword ptr[ebp - 0Ch]
            call LOC_0x0040B67C
            and eax, dword ptr[ebp + 08h]
            xor ebx, eax
            mov dword ptr[ebp - 10h], ebx
        LOC_0x0091D599 :
        mov ecx, 0000050Fh
            mov eax, dword ptr[ebp - 10h]
            cdq
            idiv ecx
            mov dword ptr[ebp - 10h], edx
            mov eax, dword ptr[ebp - 10h]
            pop ebx
            mov esp, ebp
            pop ebp
            add esp, 10h //add esp, 0Ch+04h 把模拟的假返回地址清除,原始的是add esp, 0Ch.
    }
}

总结一下上面分析到的算法:
1.根据1-6位注册码计算出来一个数字A.
2.用数字A经过sub_91D500变换得出另外的一个数字B.
3.将B经过一个循环转换为字符串(2-3个字符).并且与注册码的9-10,11-12,17-18,23-25分别比较.
可以知道,一开始的虚假注册只是验证了注册码的23-25位,重启或者其他功能会进行全部的验证.
这只是business license的注册算法.要问我lincense type在哪里分析得到的?自己动手,很快就知道.
HOME license 和 SITE license 会对business license进行一个可逆变换,从而得出HOME license 和 SITE license.
具体地方自己找吧.我在上面的IDA代码已经有过注释.



不要好奇为什么没怎么用OD.OD在应对大规模的逆向分析时,是个很棒的辅助工具,但是做主要工具很吃力.

成功.png
附上IcoFX注册码前6位的黑名单(前六位在黑名单里一定不能注册):
ZZZZZZ
71XRKL
AD4QVP
23G0SP
C0HOCT
J3RAOV
DO9L9E
8PPP66
AD91NY
C0SE5Q
7221TF
FBON7B
C0LYAJ
C0HOFN
3QMIEO
72221T
23KB4D
8PH4J5
3QX84U
5EGKS6
DNYVGN
5EGKH3
23I5I1
000000

根据上面的分析和还原了的源代码,是不难写出keygen的.
注册机仅供学习研究用,出于其他目的,就不要下载了.

IcoFXKeyGen.7z

22.31 KB, 阅读权限: 10, 下载次数: 273, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 15吾爱币 +2 热心值 +15 收起 理由
610100 + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
daohang + 1 谢谢@Thanks!
JustPasserby + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
XQiang + 1 我很赞同!
3800CC + 1 谢谢@Thanks!
wu0687050 + 1 佩服 佩服
Tortoise + 1 谢谢@Thanks!
凡凡之呗 + 1 热心回复!
ppj975 + 1 鼓励转贴优秀软件安全工具和文档!
这只猪 + 1 已经处理,感谢您对吾爱破解论坛的支持!
fenghaoda + 1 已答复!
xindelangmaney + 1 热心回复!
无痕软件 + 1 学习,delphi不会弄
vigers + 1 热心回复!
bytzhh + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Hmily 发表于 2015-5-28 11:52
284406022 发表于 2015-5-28 09:26
IDA的F5我用过,IDR是什么来的?    map又是什么来的?映射文件?

这是个好东西,要善用搜索!http://www.52pojie.cn/thread-178147-1-1.html
kill 发表于 2015-6-2 19:54
月之恨 发表于 2015-6-2 11:03
注册成功
需要2个文件,放在软件目录里就可以,可在网上搜索查找。
msvcp120d.dll

非常感谢,测试了,有这两个文件跟注册机同一个目录下,就能正常运行了。
bytzhh 发表于 2015-5-26 12:59
Hmily 发表于 2015-5-26 17:26
IDR的map给IDA F5用很叼炸天啊!
 楼主| 红绡枫叶 发表于 2015-5-26 17:31
Hmily 发表于 2015-5-26 17:26
IDR的map给IDA F5用很叼炸天啊!

本来想做个xe2的sig签名,但是感觉有些麻烦,就放弃了.老大是刚下班吧
asd9988 发表于 2015-5-26 18:37
这么快这么多回复了?
先留名,肥了再看
vigers 发表于 2015-5-26 19:12
本帖最后由 vigers 于 2015-5-26 19:50 编辑

好长了,留下慢慢看注册机怎么运行不了??
ljrlove2008 发表于 2015-5-26 20:16
留名,慢慢欣赏。。。
zz100179 发表于 2015-5-26 21:08
注册码怎么复制出来啊,全是dos界面好陌生
MT小青菜 发表于 2015-5-26 21:14
谢谢你兄弟
研究技术 发表于 2015-5-26 23:41
感谢分享,,很牛叉
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-26 00:07

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表