简单分析:
1、使用读写禁止读取的内存来触发异常,异常中进行反调试检测及相应处理。
代码:
mov [ebp+SEH_trigger_ep], 29Ch mov ecx, [ebp+SEH_trigger] mov byte ptr [ecx], 6
2、反调试,使用查找父进程的方法,如找到父进程非explorer.exe,则终止进程。同时代码中,
有反HideOD的Process32NextW的代码,当发现Process32NextW返回异常,同样终止进程,因此无
法使用HideOD来bypass该反调试。
处理方式为修改一个跳转使程序不要TerminateProcess就可。
3、花指令
通过花指令来阻碍调试。花指令只使用了一种。
代码:
__asm
{
jmp dword ptr[label1]
label2: stc
jnb dword ptr[label3]
retn
label3:__emit 0xff
label1: call dword ptr[label2]
}
4、MessageBoxA
在通往成功的路径上,使用MessageBoxA拦截
代码:
if ( MessageBoxA((HWND)(Regcode[10] == 'U'), "继续努力", "错了", 0); )
{
pID_currentprocess = GetCurrentProcess();
TerminateProcess(pID_currentprocess, 0);
}
5、算法
按钮事件,进行用户名和注册码的合法性初步校验,如合法,进入chkreg()
代码:
int __cdecl onCheckBtnClick()
{
int ecx0; // ecx@0
int result; // eax@2
char *regcode; // ST08_4@1
int regcodeItem; // eax@1
int len_regcode; // eax@1
int regnameItem; // eax@1
int len_regname; // eax@1
int regname1; // eax@1
int this; // [sp+5Ch] [bp-4h]@1
int regcode1; // [sp+58h] [bp-8h]@1
char *regname2; // [sp+54h] [bp-Ch]@1
signed int tmpi; // [sp+50h] [bp-10h]@5
int v12; // [sp+0h] [bp-60h]@12
int Func_addr; // [sp+4Ch] [bp-14h]@12
this = ecx0;
regcode = (char *)(ecx0 + 0x60);
regcodeItem = CWnd__GetDlgItem(1001);
CWnd__GetWindowTextA(regcodeItem, regcode);
len_regcode = getstrlen1();
regcode1 = CString__GetBuffer(this + 96, len_regcode);
regnameItem = CWnd__GetDlgItem(1002);
CWnd__GetWindowTextA(regnameItem, this + 0x64);
len_regname = getstrlen1();
regname1 = CString__GetBuffer(this + 100, len_regname);
regname2 = (char *)regname1;
if ( strlen((const char *)regname1) )
{
if ( strlen((const char *)regcode1) )
{
tmpi = 0;
while ( regname2[tmpi] && regname2[tmpi] > 'a' && regname2[tmpi] < 'z' )
++tmpi;
if ( tmpi == 6 )
{
strcpy(&Name, regname2);
strcpy(Regcode, (const char *)regcode1);
Func_addr = (int)&v12;
result = chkreg((int)&v12, (int)regname2, regcode1);
}
else
{
result = CWnd__MessageBoxA(this, "非法用户", 0, 0);
}
}
else
{
result = CWnd__MessageBoxA(this, "请输入注册码!", 0, 0);
}
}
else
{
result = CWnd__MessageBoxA(this, "请输入用户名", 0, 0);
}
return result;
}
要求:regcode[2n+1]+0x1B=regname[n];
regcode[2n+2]+0x20<regname[n];
如不符合,修改函数的返回地址为错误提示函数wrong()的地址。
如正确,设置返回地址为chkregcode()。
在函数最后,使用mov [29c],6来触发异常,进入父进程检验的反调试函数。
代码:
int __cdecl chkreg(int Func_addr, int regname, int regcode)
{
int result; // eax@8
int v4; // [sp+0h] [bp-6Ch]@1
int *v5; // [sp+5Ch] [bp-10h]@1
int chkregcode_addr; // [sp+58h] [bp-14h]@1
signed int tmpi; // [sp+54h] [bp-18h]@1
int tmpj; // [sp+50h] [bp-1Ch]@1
int v9; // [sp+68h] [bp-4h]@8
v5 = &v4;
chkregcode_addr = Func_addr - 16;
*(_DWORD *)(Func_addr - 16) = chkregcode;
tmpi = 0;
tmpj = 0;
loc_401BF4(Func_addr - 16);
while ( tmpi < 6 )
{
if ( *(_BYTE *)(tmpi + regname) != *(_BYTE *)(tmpj + regcode) + 0x1B )
wrong();
loc_401C30();
if ( *(_BYTE *)(tmpi + regname) > *(_BYTE *)(tmpj + regcode + 1) + 0x20 )
*(_DWORD *)chkregcode_addr = wrong;
tmpj += 2;
++tmpi;
}
v9 = 0;
result = loc_401C7A();
v29c = 6;
return result;
}
跨过父进程检测的反调试后,进入第二级算法chkregcode()
代码:
int __cdecl chkregcode()
{
int tmpB; // ecx@4
signed int tmpChar; // eax@15
int v3; // edx@6
int v4; // eax@10
HANDLE pID_currentprocess; // eax@11
signed int v6; // edx@15
int v7; // ecx@23
char *charlist; // [sp+88h] [bp-4h]@1
char *charlist1; // [sp+84h] [bp-8h]@1
signed int tmpi; // [sp+64h] [bp-28h]@1
_BYTE chartable[28]; // [sp+68h] [bp-24h]@6
HANDLE hProcess; // [sp+60h] [bp-2Ch]@11
signed int v13; // [sp+5Ch] [bp-30h]@12
signed int tmpj; // [sp+58h] [bp-34h]@12
int v15; // [sp+54h] [bp-38h]@14
char *v16; // [sp+50h] [bp-3Ch]@20
char *v17; // [sp+4Ch] [bp-40h]@20
charlist = "ABCDEFGHIJKLMNOPQRSTUVWXY";
charlist1 = "ABCDEFGHIJKLMNOPQRSTUVWXY";
tmpi = 0;
while ( tmpi != 24 )
{
loc_40182B();
if ( Name[0] == *charlist + 32 || Name[5]== *charlist + 32) )
tmpB = (int)(charlist++ + 1);
LOBYTE(tmpB) = *charlist;
chartable[tmpi] = *charlist;
v3 = tmpi++ + 1;
charlist += 2;
loc_401893(tmpB, v3);
if ( !*charlist )
{
if ( tmpi < 24 )
charlist = charlist1 + 1;
}
}
if ( MessageBoxA((HWND)(Regcode[10] == 'U'), "继续努力", "错了", 0))
{
pID_currentprocess = GetCurrentProcess();
TerminateProcess(pID_currentprocess, 0);
}
tmpi = 0;
v13 = 5;
tmpj = 0;
while ( tmpi != 12 )
{
v15 = 4 * v13 - 4;
do
{
tmpChar = Regcode[tmpi];
v6 = chartable[v15++];
if ( tmpChar == v6 )
break;
++tmpj;
}
while ( tmpj <= 4 );
if ( tmpj == 5 )
{
v16 = "ABCDEFGHIJKLMN";
v17 = "OPQRSTUVWXYZ";
tmpj = tmpChar;
if ( tmpChar > 0 )
wrong();
wrong();
}
tmpi += 2;
v7 = v13-- - 1;
loc_4019B2(v7);
if ( !v13 )
v13 = 6;
tmpj = 0;
}
return succeed();
}
取其中的24个字母,每4个为一组,共六组,分别对应名字的6个字符。
注册机
根据以上算法,计算得出名字有如下限制:
1、名字必须为6位字母,名字中字母不能为'a'
2、Name[0]必须为defghij,
3、Name[5]必须为p
4、所有字符必须为打乱的字母表对应组(4个1组)中的一个字母.见算法。
由于不是所有名字均有解。因此采用注册机随机生成注册码的方法,每点一次随机生成一个。
具体算法为
代码:
void CkeygenDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知处理程序代码
char Name[7];
char RegCode[13];
char Ctable[26]="ABCDEFGHIJKLMNOPQRSTUVWXY";
char KeyCtable[26]="";
char NameTable[6][4];
char tmpB;
int tmpi,tmpj,tmpk;
int tmpRand;
Name[5]='p';
Name[6]=0x00;
//GetDlgItemText(IDC_NAME,&Name[0],2);
srand( (unsigned)time( NULL ) );
tmpRand=rand()%7;
Name[0]='d'+tmpRand;
if(Name[0]<'d' || Name[0]>'j')
{
AfxMessageBox("请设置名字的首字母为defghij其中的一个,否则无解");
return;
}
//Create Keytable
tmpj=0;
for(tmpi=0;tmpi<=24;tmpi++)
{
if(Name[0]==(Ctable[tmpj]+0x20)||Name[5]==(Ctable[tmpj]+0x20))
{
tmpj=(tmpj+1)%25;
}
tmpB=Ctable[tmpj];
KeyCtable[tmpi]=tmpB;
tmpj=(tmpj+2)%25;
}
//Create Nametable
for(tmpj=0;tmpj<4;tmpj++)
NameTable[0][tmpj]=KeyCtable[16+tmpj]+0x1B;
for(tmpj=0;tmpj<4;tmpj++)
NameTable[1][tmpj]=KeyCtable[12+tmpj]+0x1B;
for(tmpj=0;tmpj<4;tmpj++)
NameTable[2][tmpj]=KeyCtable[8+tmpj]+0x1B;
for(tmpj=0;tmpj<4;tmpj++)
NameTable[3][tmpj]=KeyCtable[4+tmpj]+0x1B;
for(tmpj=0;tmpj<4;tmpj++)
NameTable[4][tmpj]=KeyCtable[0+tmpj]+0x1B;
for(tmpj=0;tmpj<4;tmpj++)
NameTable[5][tmpj]=KeyCtable[20+tmpj]+0x1B;
//Generate Name
for(tmpi=1;tmpi<5;tmpi++)
{
srand( (unsigned)time( NULL ) );
tmpRand=rand()%4;
for(tmpj=tmpRand;tmpj<(tmpRand+4);tmpj++)
{
if(NameTable[tmpi][tmpj%4]>'a'&&NameTable[tmpi][tmpj%4]<'z')
Name[tmpi]=NameTable[tmpi][tmpj%4];
}
}
Name[5]='p';
Name[6]=0x00;
SetDlgItemText(IDC_NAME,&Name[0]);
//Generate Regcode
for(tmpi=0;tmpi<6;tmpi++)
{
RegCode[2*tmpi]=Name[tmpi]-0x1B;
RegCode[2*tmpi+1]=Name[tmpi]-0x20;
}
RegCode[12]=0x00;
SetDlgItemText(IDC_REGCODE,&RegCode[0]);
SetDlgItemText(IDOK,"NextOne");
}