能力值:
( LV9,RANK:490 )
19 楼
算法来源于数独游戏,数独原形为:
int shudu[9][9]={
0,7,0,0,0,0,0,0,2,
0,0,2,7,0,3,0,0,6,
8,3,0,6,0,0,0,0,0,
0,0,0,4,0,0,6,5,7,
0,0,5,0,0,0,8,0,0,
4,1,7,0,0,5,0,0,0,
0,0,0,0,0,4,0,6,9,
9,0,0,3,0,8,1,0,0,
1,0,0,0,0,0,0,7,0
};
思路:用用户名定位坐标,将注册码转换成数字填入数独中的0位。 void CCrackMe01Dlg::OnOK()
{
// TODO: Add extra validation here
int sum=0,sum1=0,sum2=1,sum3=0,x=0,y=0;
int nUsize=m_cUserName.GetWindowTextLength();
int nSsize=m_cSn.GetWindowTextLength();
CString cSn;
CString chSn;
UpdateData(TRUE);
//利用用户名的定位:
for(int i=0;i<nUsize;i++)
{
sum+=m_UserName[i];
}
x=(sum%81)/9;
y=(sum%81)%9;
//不用解释了吧,很简单的定位,x,y即为定位出来的坐标。
//注册码的转换:
for(i=0;i<nSsize;i++)
{
if(m_Sn[i]!=0x2d)
{
cSn=cSn+m_Sn[i];
}
else
{
i++;
break;
}
}
if (i>8)
{
MessageBox("注册失败!");
CDialog::OnOK();
return;
}
for(int k=0;k<5;k++)
{
for(chSn="";i<nSsize;i++)
{
if(m_Sn[i]!=0x2d)
{
chSn=chSn+m_Sn[i];
}
else
{
i++;
break;
}
}
chSn.Format("%d",(strtoul(chSn,NULL,16)==-1)?1:strtoul(chSn,NULL,16));
cSn+=chSn;
}
for(;i<nSsize;i++)
{
if(m_Sn[i]!=0x2d)
{
cSn=cSn+m_Sn[i];
}
else
{
i++;
break;
}
}
//正确注册码第一部分为(以"-"分割)十进制数字,中间5部分为十六进制数字,第7部分为十进制数字(不足7部分可以,再长就不处理了)。
//本想不这么麻烦的,用户直接写入10进制数字填进表里就OK的,后来为了迷惑观众,写的复杂了一点。
//下面工作就是写入表格了:
for(k=0,i=0;i<nSsize;i++)
{
for(;x<9;x++)
{
for(;y<9;y++)
{
if (shudu1[x][y]==0) break;
}
if (y==9)
{
y=0;
continue;
}
if (shudu1[x][y]==0) break;
}
if (x==9)
{
x=0;
k++;
if (k<2)
{
i--;
continue;
}
else break;
}
shudu1[x][y]=cSn[i]-0x30;
}
//下面的代码想给大家留下反调试的印象,实际上在为新进程运行赢得时间。
//正如sessiondiy提到的,50ms在反应慢的机子上可能达不到效果,也就是永远也看不到注册成功。
i=GetTickCount();
::CreateThread(NULL,NULL,ThreadProc,this,0,NULL); //次线程中也有验证代码
Sleep(50);
k=GetTickCount();
//下面为验证代码:(验证方法有些猥琐,没敢直接判断9个数字各不相同)
//验证横竖格中的数字
for (x=0;x<9;x++)
{
for(sum1=0,sum2=1,sum3=0,y=0;y<9;y++)
{
sum1+=shudu1[x][y];//45
sum2*=shudu1[x][y];//362880
sum3=sum3+shudu1[y][x]*shudu1[y][x];//=285
}
if((sum2/sum1)*sum3!=0x231180 || (k-i)<50 )
{
MessageBox("注册失败!");
CDialog::OnOK();
return;
}
else //本想用它迷惑观众,后来才发现被C++优化掉了
{
if((sum2/sum1)*sum3==0x231180&& (k-i)<50)
MessageBox("恭喜你,成功!");
CDialog::OnOK();
return;
}
}
MessageBox("注册失败!");
CDialog::OnOK();
}
//线程函数代码
DWORD WINAPI ThreadProc(LPVOID lpPara)
{
int data[256]=
{ 0x119BA09E, 0x6649E324, 0x2942065D, 0x08F16AAD, 0x354B283F, 0x463F6AE3,0xe7
//0x42D0C8BA, 0x35028B00, 0x7A096E79, 0x5BBA0289, 0x6600401B, 0x157402C7,0xc3
};
int shu[9][9];
memcpy(shu,(((CCrackMe01Dlg *)lpPara)->shudu1),sizeof shu);//这里有些浪费
//下面是验证9个小方格中的数字
for(int x=0,y=0,i=0;i<9;i++)
{
x=(i/3)*3;
y=(i%3)*3;
for(int j=0,sum1=0,sum2=1;j<3;j++)
{
for(int k=0;k<3;k++)
{
sum1+=shu[x+j][y+k];
sum2*=shu[x+j][y+k];
}
}
if (sum2/sum1!=8064)
{
return 0;
}
}
//利用部分数字得到解码key
int sum=0,temp , k;
for(k=0;k<4;k++)
{
temp=shu[6][k];
_asm
{
mov eax,temp
mov edx,eax
rol eax,13
xor eax,0x14d28209
add sum,eax
}
}
//解码
for(i=0;i<7;i++)
{
data[i]=data[i]^sum;
}
//分配内存并执行,刚学vc++,好多的实现方法都不会,中间加了汇编
void *newaddr=VirtualAlloc(NULL,0x100,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
{
memcpy(newaddr,data,25);
_asm
{
mov eax,newaddr
call eax
}
}
//下面就是要运行的代码
/* _asm
{
mov edx,0x42d0c8
mov eax,dword ptr[edx]
xor eax,0x7a096e79 //将提示中的"失败"改为"成功"
mov dword ptr [edx],eax
mov edx,0x401b5b
mov word ptr[edx],0x1574 //修改关键代码
ret
}*/
return 0;
}
//构造函数中的代码
CCrackMe01Dlg::CCrackMe01Dlg(CWnd* pParent /*=NULL*/)
: CDialog(CCrackMe01Dlg::IDD, pParent)
{
//省略了无关部分
int shudu[9][9]={
0,7,0,0,0,0,0,0,2,
0,0,2,7,0,3,0,0,6,
8,3,0,6,0,0,0,0,0,
0,0,0,4,0,0,6,5,7,
0,0,5,0,0,0,8,0,0,
4,1,7,0,0,5,0,0,0,
0,0,0,0,0,4,0,6,9,
9,0,0,3,0,8,1,0,0,
1,0,0,0,0,0,0,7,5
};
//可能大家已经注意到了最后一个数字是5而不是我前面给出的0,这是我另一个感觉猥琐的地方,下面会提到
memcpy(shudu1,shudu,sizeof shudu1);
DWORD oldProtect=0;
void *addr=(LPVOID)0x401b5b;
VirtualProtect(addr,8,PAGE_EXECUTE_READWRITE,&oldProtect);
_asm
{
mov edx,addr
mov word ptr[edx],0x1575
}
//上面的代码是在防爆破,也为我在线程中更改此处代码埋下伏笔。
}//下面是猥琐的代码,请原谅我在说明中没有提到anti 及自校验的字眼,这个函数被用在了CCrackMe01Dlg::OnInitDialog()中
void CCrackMe01Dlg::ImportCheck()
{
PIMAGE_NT_HEADERS pNtHeader=NULL;
PIMAGE_DOS_HEADER pDosHeader=NULL;
PIMAGE_IMPORT_DESCRIPTOR pIpt=NULL;
DWORD pImageBase=NULL;
//下面的代码将检查程序导入表中的函数是否被下了断点(前5个字节)
_asm //当时是为了不留下痕迹才这样写的
{
mov eax,4
shl eax,20
mov pImageBase,eax
}
pDosHeader=(PIMAGE_DOS_HEADER)pImageBase;
if(pDosHeader)
{
pNtHeader=(PIMAGE_NT_HEADERS)((pDosHeader->e_lfanew)+pImageBase);
pIpt=(PIMAGE_IMPORT_DESCRIPTOR)((pNtHeader->OptionalHeader.DataDirectory[1].VirtualAddress)+pImageBase);
for(PIMAGE_THUNK_DATA pIat;pIpt->FirstThunk||pIpt->ForwarderChain||pIpt->Name\
||pIpt->OriginalFirstThunk||pIpt->TimeDateStamp;pIpt++)
{
pIat=(PIMAGE_THUNK_DATA)(pIpt->FirstThunk+pImageBase);
for(;pIat->u1.Function;pIat++)
{
char* iscc=(char*)(pIat->u1.Function);
for(int i=0;i<5;++i,iscc++)
{
if((BYTE)(*iscc^0x12)==0xde)
{
return;
//Cracker found!
}
}
}
}
}
//下面的代码是检测程序载入内存后代码段的校验值是否正确
int codes=0x401,codee=0x424; //其实这样没效果,被优化了。
codes*=0x1000;
codee*=0x1000;
for(int sum=0;codes<codee;codes++)
{
_asm
{
mov ecx,codes
movzx eax,byte ptr[ecx]
mov edx,eax
rol eax,19
xor eax,edx
add sum,eax
}
}
if (sum==pNtHeader->OptionalHeader.CheckSum)
shudu1[8][8]=0;//correct
//看见了吗?这就是猥琐的所在,如果没通过检查,你面对的数独是一个死局!
//请原谅我这么做,这可能浪费了你的时间,再次对此猥琐做法致歉。
}
如有疑问或建议,请跟帖,再次谢谢关注 !