点about按钮可以知道这个程序是让我们去掉按钮,让logo显示出来;首先用dede载入程序,看下有几个自定函数
由于OK按钮是灰色的,所以我们先看下CancellaClick按钮函数,使用IDA特定标签库来解析
使用F5分析函数
int __usercall TPrincipale_CancellaClick<eax>(int a1<eax>, int a2<ebx>)
{
int v2; // ebx@1
int v3; // ST00_4@1
int v4; // edx@2
unsigned int v6; // [sp-10h] [bp-14h]@1
int (*v7)(); // [sp-Ch] [bp-10h]@1
int (*v8)(); // [sp-8h] [bp-Ch]@1
int v9; // [sp-4h] [bp-8h]@1
int v10; // [sp+0h] [bp-4h]@1
int v11; // [sp+4h] [bp+0h]@1
v10 = 0;
v9 = a2;
v2 = a1;
v8 = (int (*)())&v11;
v7 = loc_442F32;
v6 = __readfsdword(0);
__writefsdword(0, (unsigned int)&v6);
TControl::GetText(*(_DWORD *)(a1 + 736), &v10);
v3 = sub_407670(v10);
TControl::GetText(*(_DWORD *)(v2 + 732), &v10);
if ( (unsigned __int8)sub_442AF4(v10, v3) )
{
sub_4231B0(*(_DWORD *)(v2 + 720), 0);
LOBYTE(v4) = 1;
(*(void (__fastcall **)(_DWORD, int))(**(_DWORD **)(v2 + 716) + 96))(*(_DWORD *)(v2 + 716), v4);
}
else
{
Controls::TControl::SetText(*(_DWORD *)(v2 + 736), &str_0_1[1]);
}
__writefsdword(0, v6);
v8 = loc_442F39;
return System::__linkproc___LStrClr(&v10);
}
上面的代码简单分析后结果如下
int __usercall TPrincipale_CancellaClick<eax>(int this<eax>, int a2<ebx>)
{
int this; // ebx@1
int v3; // ST00_4@1
int v4; // edx@2
unsigned int v6; // [sp-10h] [bp-14h]@1
int v9; // [sp-4h] [bp-8h]@1
char *pszBuf;
TControl::GetText(*(_DWORD *)(this + 736), &pszBuf);
v3 = sub_407670(pszBuf);
TControl::GetText(*(_DWORD *)(this + 732), &pszBuf);
if (sub_442AF4(pszBuf, v3) )
{
sub_4231B0(*(_DWORD *)(this + 720), 0);
LOBYTE(v4) = 1;
(*(void (__fastcall **)(_DWORD, int))(**(_DWORD **)(this + 716) + 96))(*(_DWORD *)(this + 716), v4);
}
else
{
Controls::TControl::SetText(*(_DWORD *)(this + 736), &str_0_1[1]);
}
}
这边无法得知this+736/732对应的控件是什么,以及sub_407670函数的作用是什么,这边可以用OD调试下辅助我们分析
可以看到第一次获取的是序列号
这边0x4D2的十进制是1234,正好是我们输入的序列号的值,我们可以推测这个函数的功能应该就是StrToInt
第二次获取的就是我们输入的用户名,现在我们已经知道sub_442AF4两个输入参数是什么了,分别是eax->用户名 edx->序列号值
使用F5分析函数
int __fastcall sub_442AF4(int a1, int a2)
{
int v2; // esi@2
int v3; // ebx@2
int v4; // eax@2
signed int v5; // edx@3
unsigned int v7; // [sp-Ch] [bp-1Ch]@1
int (*v8)(); // [sp-8h] [bp-18h]@1
int (*v9)(); // [sp-4h] [bp-14h]@1
int v10; // [sp+8h] [bp-8h]@1
int v11; // [sp+Ch] [bp-4h]@1
int v12; // [sp+10h] [bp+0h]@1
v10 = a2;
v11 = a1;
System::__linkproc___LStrAddRef();
v9 = (int (*)())&v12;
v8 = loc_442B90;
v7 = __readfsdword(0);
__writefsdword(0, (unsigned int)&v7);
if ( sub_403A34(v11) > 5 )
{
v2 = sub_442A20(*(_BYTE *)(v11 + 4) % 7u + 2);
v3 = 0;
v4 = sub_403A34(v11);
if ( v4 > 0 )
{
v5 = 1;
do
{
v3 += v2 * *(_BYTE *)(v11 + v5++ - 1);
--v4;
}
while ( v4 );
}
}
__writefsdword(0, v7);
v9 = loc_442B97;
return System::__linkproc___LStrClr(&v11);
}
函数初步分析结果如下,IDA的分析漏掉了最后的验证代码,即iResult – iSerial == 0x7a69
int __fastcall sub_442AF4(int szName, int iSerial)
{
int iMagic; // esi@2
int iResult; // ebx@2
int iNLen; // eax@2
int i; // edx@3
int iSerial; // [sp+8h] [bp-8h]@1
int szName; // [sp+Ch] [bp-4h]@1
iSerial = iSerial;
szName = szName;
if ( strlen(szName) > 5 )
{
iMagic = sub_442A20(*(_BYTE *)(szName + 4) % 7u + 2);
iResult = 0;
iNLen = strlen(szName);
if ( iNLen > 0 )
{
i = 1;
do
{
iResult += iMagic * *(_BYTE *)(szName + i++ - 1);
--iNLen;
}
while ( iNLen );
}
}
}
计算iMagic的函数算法如下
int __fastcall sub_442A20(int a1)
{
signed __int64 v1; // qax@2
if ( a1 )
v1 = a1 * (signed __int64)sub_442A20(a1 - 1);
else
LODWORD(v1) = 1;
return v1;
}
可以很容易看出,这就是一个递归函数,求的是n*(n-1)*...*1,所以CancellaClick按钮的伪代码以及注册算法如下:
#include <iostream>
using namespace std;
/*int __usercall TPrincipale_CancellaClick<eax>(int this<eax>, int iSerial<ebx>)
{
int this; // ebx@1
int v3; // ST00_4@1
int v4; // edx@2
unsigned int v6; // [sp-10h] [bp-14h]@1
int v9; // [sp-4h] [bp-8h]@1
char *pszBuf;
TControl::GetText(*(_DWORD *)(this + 736), &pszBuf);
v3 = sub_407670(pszBuf);
TControl::GetText(*(_DWORD *)(this + 732), &pszBuf);
if (sub_442AF4(pszBuf, v3) )
{
sub_4231B0(*(_DWORD *)(this + 720), 0);
LOBYTE(v4) = 1;
(*(void (__fastcall **)(_DWORD, int))(**(_DWORD **)(this + 716) + 96))(*(_DWORD *)(this + 716), v4);
}
else
{
Controls::TControl::SetText(*(_DWORD *)(this + 736), &str_0_1[1]);
}
}*/
/*int __fastcall sub_442AF4(int szName, int iSerial)
{
int iMagic; // esi@2
int iResult; // ebx@2
int iNLen; // eax@2
int i; // edx@3
int iSerial; // [sp+8h] [bp-8h]@1
int szName; // [sp+Ch] [bp-4h]@1
iSerial = iSerial;
szName = szName;
if ( strlen(szName) > 5 )
{
iMagic = sub_442A20(*(_BYTE *)(szName + 4) % 7u + 2);
iResult = 0;
iNLen = strlen(szName);
if ( iNLen > 0 )
{
i = 1;
do
{
iResult += iMagic * *(_BYTE *)(szName + i++ - 1);
--iNLen;
}
while ( iNLen );
}
}
}*/
int NMul(int n)
{
return n==1?1:n*NMul(n-1);
}
void main()
{
char szName[] = "@@@@@@";
int iSerial;
int iMagicA = 0x7A69;
int iMagicB = 0;
int iTemp = 0;
iMagicB = NMul(szName[4]%7+2);
for (int i=0; i<6; i++)
iTemp += iMagicB*szName[i];
iSerial = iTemp - iMagicA;
cout<<"The Serial:"<<iSerial<<endl;
}
这样可以获到CancellaClick按钮的通关用户名和密码即”@@@@@@”和”-29033”,这个时候OK按钮被激活了,CancellaClick按钮消失了,下面我们来看下OK按钮对应的函数
int __usercall TPrincipale_OkClick<eax>(int a1<eax>, int a2<ebx>)
{
int v2; // ebx@1
int v3; // ST00_4@3
unsigned int v5; // [sp-10h] [bp-14h]@1
int (*v6)(); // [sp-Ch] [bp-10h]@1
int (*v7)(); // [sp-8h] [bp-Ch]@1
int v8; // [sp-4h] [bp-8h]@1
int v9; // [sp+0h] [bp-4h]@1
int v10; // [sp+4h] [bp+0h]@1
v9 = 0;
v8 = a2;
v2 = a1;
v7 = (int (*)())&v10;
v6 = loc_442DED;
v5 = __readfsdword(0);
__writefsdword(0, (unsigned int)&v5);
if ( *(_BYTE *)(*(_DWORD *)(a1 + 720) + 71) == 1 )
{
Controls::TControl::SetText(*(_DWORD *)(a1 + 736), &str_0_0[1]);
}
else
{
TControl::GetText(*(_DWORD *)(a1 + 736), &v9);
v3 = sub_407670(v9);
TControl::GetText(*(_DWORD *)(v2 + 732), &v9);
if ( (unsigned __int8)sub_442BA0(v9, v3) )
sub_4231B0(*(_DWORD *)(v2 + 716), 0);
}
__writefsdword(0, v5);
v7 = loc_442DF4;
return System::__linkproc___LStrClr(&v9);
}
简单分析结果如下
int __usercall TPrincipale_OkClick<eax>(int this<eax>, int a2<ebx>)
{
int this; // ebx@1
int iSerial; // ST00_4@3
int pszBuf; // [sp+0h] [bp-4h]@1
if ( *(_BYTE *)(*(_DWORD *)(this + 720) + 71) == 1 )
{
Controls::TControl::SetText(*(_DWORD *)(this + 736), &str_0_0[1]);
}
else
{
TControl::GetText(*(_DWORD *)(this + 736), &pszBuf);
iSerial = StrToInt(pszBuf);
TControl::GetText(*(_DWORD *)(this + 732), &pszBuf);
if ( (unsigned __int8)sub_442BA0(pszBuf, iSerial) )
sub_4231B0(*(_DWORD *)(this + 716), 0);
}
}
我们会发现else这段和前面的CancellaClick的验证逻辑差不多,所以核心就是sub_442BA0这个函数,函数的输入参数为用户名和序列号值
int __usercall sub_442BA0<eax>(int a1<eax>, int a2<edx>, int a3<ebx>, int a4<esi>)
{
int v4; // esi@1
int v5; // esi@2
int v6; // eax@3
char v7; // zf@4
unsigned int v9; // [sp-14h] [bp-20h]@1
int (*v10)(); // [sp-10h] [bp-1Ch]@1
int (*v11)(); // [sp-Ch] [bp-18h]@1
int v12; // [sp-8h] [bp-14h]@1
int v13; // [sp+0h] [bp-Ch]@1
int v14; // [sp+4h] [bp-8h]@1
int v15; // [sp+8h] [bp-4h]@1
int v16; // [sp+Ch] [bp+0h]@1
v14 = 0;
v12 = a4;
v4 = a2;
v15 = a1;
System::__linkproc___LStrAddRef(v12, a3, 0);
v11 = (int (*)())&v16;
v10 = loc_442C67;
v9 = __readfsdword(0);
__writefsdword(0, (unsigned int)&v9);
Sysutils::IntToStr(v4, &v14);
System::__linkproc___LStrLAsg(&v13);
if ( sub_403A34(v14) > 5 )
{
v5 = sub_403A34(v14);
if ( v5 >= 1 )
{
do
{
v6 = System::UniqueString(&v13);
*(_BYTE *)(v6 + v5 - 1) = v5 * (signed __int16)(*(_BYTE *)(v14 + v5 - 1) * *(_BYTE *)(v14 + v5 - 1)) % 25 + 65;
--v5;
}
while ( v5 );
}
System::__linkproc___LStrCmp(v13, v15);
if ( v7 )
System::__linkproc___LStrCmp(v15, v13);
}
__writefsdword(0, v9);
v11 = loc_442C6E;
return System::__linkproc___LStrArrayClr(&v13, 3);
}
分析函数,得出伪代码以及注册算法如下
/*int __usercall sub_442BA0<eax>(int a1<eax>, int a2<edx>, int a3<ebx>, int a4<esi>)
{
int iSerial; // esi@1
int iSLen; // esi@2
int pszBuf; // eax@3
int pszTemp; // [sp+0h] [bp-Ch]@1
int szSerial; // [sp+4h] [bp-8h]@1
int pszName; // [sp+8h] [bp-4h]@1
Sysutils::IntToStr(iSerial, &szSerial);
if ( strlen(szSerial) > 5 )
{
iSLen = strlen(szSerial);
if ( iSLen >= 1 )
{
do
{
pszBuf = System::UniqueString(&pszTemp);
*(_BYTE *)(pszBuf + iSLen - 1) = iSLen * (signed __int16)(*(_BYTE *)(szSerial + iSLen - 1) * *(_BYTE *)(szSerial + iSLen - 1)) % 25 + 65;
--iSLen;
}
while ( iSLen );
}
System::__linkproc___LStrCmp(pszTemp, pszName);
}
}*/
void main()
{
char szSerial[] = "111111";
char szName[7] = {'\0'};
for (int i=5; i>=0; i--)
szName[i] = (i+1)*szSerial[i]*szSerial[i]%25+65;
cout<<"The Name:"<<szName<<endl;
}