好友
阅读权限30
听众
最后登录1970-1-1
|
红绡枫叶
发表于 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用.
注册按钮是灰色的.在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.
注册后发现,关闭再打开又是没注册的了.我这里试用期已过,所以用上面的注册码注册的,虽然
提示注册成功,但是在保存的时候又提示试用期已过,请注册.
看来上面的验证只是正确注册码的一部分啊.而且验证注册码的地方很多.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在应对大规模的逆向分析时,是个很棒的辅助工具,但是做主要工具很吃力.
附上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的.
注册机仅供学习研究用,出于其他目的,就不要下载了.
|
免费评分
-
查看全部评分
|