本文为看雪论坛精华文章
看雪论坛作者ID:yyjeqhc
1.结果:
2.过程
(1)入手点:注册机,肯定对文本有操作,所以要找到和文本相关的地方。
0x29304 0x2AB SetWindowTextA
0x29270 0xC6 DrawTextExA
0x29264 0xC5 DrawTextA
0x29252 0x2C6 TabbedTextOutA
0x2923A 0x18D GetWindowTextLengthA
0x29228 0x18C GetWindowTextA
0x29632 0x29F TextOutA
0x296A4 0x258 ScaleViewportExtEx
0x29690 0x28F SetViewportExtEx
0x295CE 0x28D SetTextColor
0x2963E 0x122 ExtTextOutA
MFC控件编程之按钮编辑框(https://www.cnblogs.com/iBinary/p/9652668.html)
void encrystUsernameFirst(string username)
{
unsigned char data[0x14];
memset(data, 0, 0x14);
int eax = 0;
int ebp = 0x1339E7E;
int esp = (int)&data - 0x88;
int edx = (int)&data;
int ecx = 0;
int edi = username.length();
int* esi = 0;
ebp = ebp - edx;
while (ecx<0x10)
{
eax = ecx;
edx = eax%edi;
esi = (int*)&data[ecx];
ecx++;
eax = username[edx];
edx = (int)esi + ebp;
eax = eax*edx;
eax = eax*edi;
(*esi) = (*esi) + eax;
}
for (int i = 0; i<0x14; i++)
{
printf("%2X ", data[i]);
if ((i + 1) % 16 == 0)
{
cout << endl;
}
}
}
if(a&&b&&c&&d&&e)
{
success;
}
else
{
fail;
}
mov eax,dword ptr ds:[esi+edi]
cmp edx,14
void imul(int& eax, int &ecx, int &edx)
{
long long tmp = eax;
tmp = tmp*ecx;
eax = tmp & 0xffffffff;
edx = tmp >> 32;
}
void encrystUsernameSecond(unsigned char data[0x14])
{
unsigned char total[0x92];
memcpy(total + 0x88, data, 0x14);
memset(total + 0x30, 0, 0x24);
memset(total + 0x2c, 0, 0x14);
int esi = 0;
int ecx = 0;
int eax = 0;
int edx = 0x14;
int i24 = 0x2995c04;
unsigned char ediData[0x14] = { 0xD7,0x6D,0xF8,0xE7,0xAE,0xFC,0xF7,0x4D,0x76,0xDF,0x8E,0x7A,0xEF,0xCF,0x74,0xD7,0x6D,0xF8,0xE7,0xAE };//这个只和输入的序列号有关系。
int* edi = (int*)&ediData;
int unknowndata = 0x3156c04;
while (esi<0x14)
{
ecx = *(int*)&data[esi];
eax = 0x66666667;
imul(eax, ecx, edx);
edx >>= 2; //sar eax,2
ecx = edx;
unsigned int tmp = ecx;
tmp >>= 31;
//shr ecx,0x1f
//__asm {
// shr tmp,0x1F
//}
ecx = tmp;
ecx += edx;
edx = 0x14; //合并起来,固定为0x14
printf("ecx = %Xn", ecx);
memcpy(total + 0x2c + esi, (unsigned char*)&ecx, 4);
if (esi<edx)
{
eax = *(int*)&ediData[esi];
memcpy(total + 0x40 + esi, (unsigned char*)&eax, 4);
esi += 4;
}
cout << endl;
}
}
这个imux还挺麻烦的,直接写成一个函数了。
void test3(unsigned char data[])
{
int ecx = 0;
int eax = 0;
int edx = 0;
int esi = 0;
ecx = *(int*)&data[0x2c];
eax = *(int*)&data[0x50];
edx = eax + ecx;
ecx = *(int*)&data[0x48];
if (edx == ecx)
{
edx = *(int*)&data[0x30];
edx += ecx;
eax += eax;
if (edx == eax)
{
ecx = *(int*)&data[0x4c];
eax = *(int*)&data[0x34];
edx = *(int*)&data[0x40];
esi = ecx + eax;
if (esi == edx)
{
esi = *(int*)&data[0x38];
esi += edx;
ecx += ecx;
if (esi == ecx)
{
edx = *(int*)&data[0x3c];
ecx = *(int*)&data[0x44];
ecx += edx;
edx = eax + eax * 2;
if (ecx == edx)
{
cout << "successn";
return;
}
}
}
}
}
cout << "fail1n";
}
void test4(unsigned char data[])
{
int arr1[5];
int arr2[5];
memcpy((unsigned char*)&arr1, data + 0x2c, 0x14);
memcpy((unsigned char*)&arr2, data + 0x40, 0x14);
int ecx = 0;
int eax = 0;
int edx = 0;
int esi = 0;
ecx = arr1[0];
eax = arr2[4];
edx = ecx + eax;
ecx = arr2[2];
if (edx == ecx)//(a[0] + b[4] == b[2])
{
edx = arr1[1];
edx += ecx;
eax += eax;
if (edx == eax)//(a[1] + b[2] == 2*b[4])
{
ecx = arr2[3];
eax = arr1[2];
edx = arr2[0];
esi = ecx + eax;
if (esi == edx)//(a[2] + b[3] == b[0])
{
esi = arr1[3];
esi += edx;
ecx += ecx;
if (esi == ecx)//(a[3] + b[0] == 2*b[3])
{
edx = arr1[4];
ecx = arr2[1];
ecx += edx;
edx = eax + eax * 2;
if (ecx == edx) // (a[4] + b[1] == 3*a[2])
{
cout << "successn";
return;
}
}
}
}
}
cout<<"failn";
}
void test5(unsigned char data[])
{
int a[5];
int b[5];
memcpy((unsigned char*)&a, data + 0x2c, 0x14);
memcpy((unsigned char*)&b, data + 0x40, 0x14);
//if (((a[0] + b[4])== b[2]) && ((a[1] + b[2]) == (2 * b[4])) && ((a[2] + b[3]) == b[0]) && ((a[3] + b[0]) == (2 * b[3])) && ((a[4] + b[1]) == (3 * a[2])))
if((2*a[2]+a[3]==b[0])&&(3*a[2]-a[4]==b[1])&&(2*a[0]+a[1]==b[2])&&(a[2]+a[3]==b[3])&&(a[0]+a[1]==b[4]))
{
cout << "successn";
}
else
{
cout << "failn";
}
}
if (true)
{
//if ((2 * a[2] + a[3] == b[0]) && (3 * a[2] - a[4] == b[1]) && (2 * a[0] + a[1] == b[2]) && (a[2] + a[3] == b[3]) && (a[0] + a[1] == b[4]))
int a[5];
int b[5];
memcpy((unsigned char*)&a, total + 0x2c, 0x14);
memcpy((unsigned char*)&b, total + 0x40, 0x14);
b[0] = 2 * a[2] + a[3];
b[1] = 3 * a[2] - a[4];
b[2] = 2 * a[0] + a[1];
b[3] = a[2] + a[3];
b[4] = a[0] + a[1];
memcpy(total + 0x2c, (unsigned char*)&a, 0x14);
memcpy(total + 0x40, (unsigned char*)&b, 0x14);
}
cout << endl;
cout << "after 0x40" << endl;
for (int i = 0; i<0x14; i++)
{
printf("%02X,", total[i + 0x40]);
}
测试一下,我们直接把这个call改成nop。等第二次加密用户名的时候,可以看见加密后的序列号还是和之前一样。所以可以知道这一个call和加密序列号无关,直接忽略就好了。
int littleProc(unsigned char ch)
{
unsigned char data[0x100] = {0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x28,0x00,0x28,0x00,0x28,0x00,0x28,0x00,0x28,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x48,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x84,0x00,0x84,0x00,0x84,0x00,0x84,0x00,0x84,0x00,0x84,0x00,0x84,0x00,0x84,0x00,0x84,0x00,0x84,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x81,0x00,0x81,0x00,0x81,0x00,0x81,0x00,0x81,0x00,0x81,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x82,0x00,0x82,0x00,0x82,0x00,0x82,0x00,0x82,0x00,0x82,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x20,0x00};
int eax = ch;
short*p = (short*)&data;
unsigned short temp = (unsigned short)*((unsigned char*)p + eax*2);
eax = temp;
eax&=0x107;
return eax;
}
void change(char base64[64],char ch,char flag)
{
int eax = flag;
int edx = (int)&base64;
unsigned int ebx = 0;
ebx = ch;
int edi = 0;
unsigned int ecx;
if(edx&&3)
{
eax-=4;
edi = ebx;
ebx<<=8;
ebx+=edi;
edi = ebx;
ebx<<=16;
ebx+=edi;
edx = 0;
{
ecx = *(int*)&base64[edx];
ecx^=ebx;
edi = 0x7EFEFEFF;
edi+=ecx;
ecx^=0xffffffff;
ecx^=edi;
edx+=4;
while(ecx&0x81010100)
{
loop:
eax-=4;
ecx = *(int*)&base64[edx];
ecx^=ebx;
edi = 0x7EFEFEFF;
edi+=ecx;
ecx^=0xffffffff;
ecx^=edi;
edx+=4;
}
ecx = *(int*)&base64[edx-4];
if((char)ecx^(char)ebx)
{
eax = *(int*)&base64[edx-4];
}
else if((char)(ecx>>8)^(char)ebx)
{
eax = *(int*)&base64[edx-3];
}
else if((char)(ecx>>16)^(char)ebx)
{
eax = *(int*)&base64[edx-2];
}
else if((char)(ecx>>24)^(char)ebx)
{
}
else
{
goto loop;
}
}
}
}
0xD7,0x6D,0xF8,0xE7,0xAE,0xFC,0xF7,0x4D,0x76,0xDF,0x8E,0x7A,0xEF,0xCF,0x74,0xD7,0x6D,0xF8,0xE7,0xAE
前有64个字符串的字符替换,后有4字节到3字节的压缩。我们可以猜测是否为base64解码的过程。
using namespace std;
void encode(unsigned char data[4])
{
unsigned int ecx = *(int*)&data;
unsigned char al = data[0];
al+=al;
unsigned char dl = data[1];
dl>>=4;
dl&=3;
al+=al;
dl+=al;
al = data[2];
unsigned char result[4];
memset(result,0,4);
result[0] = dl;
dl = al;
dl>>=2;
unsigned char cl = data[1];
al<<=6;
al+=data[3];
dl&=0xf;
cl<<=4;
dl^=cl;
result[1] = dl;
result[2] = al;
for(int i=0;i<3;i++)
{
printf("%2Xn",result[i]);
char tmp[16];
itoa(result[i],tmp,2);
printf("%sn",tmp);
}
}
int main()
{
unsigned char data[4] = {0x35,0x36,0x37,0x38};
encode(data);
cout<<"datan";
for(int i=0;i<4;i++)
{
char tmp[16];
itoa(data[i],tmp,2);
printf("%sn",tmp);
printf("%Xn",data[i]);
}
}
D7
11010111
6D
01101101
F8
11111000
data
110101
35
110110
36
110111
37
111000
38
再不断的带入注册机的内存结果到程序,发现转换没问题,说明程序翻译得没错。
且都满足base64编解码的中间过程,也就是上述的运算结果可以互相转换。
直接写个完整的base64编码,把我们求出来的加密后序列号带进去看看。
using namespace std;
char base64str[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@%=";
map<unsigned char,unsigned char> m;
void encode(unsigned char str[],int len)
{
string s = "";
unsigned char tmp[3];
unsigned char t[4];
for(int i=0;i<=len;i+=3)
{
memcpy(tmp,str+i,3);
char a[10];
memset(t,0,4);
itoa(tmp[0],a,2);
itoa(tmp[1],a,2);
itoa(tmp[2],a,2);
t[0] = tmp[0]>>2;
itoa(t[0],a,2);
unsigned char f1 = tmp[0]<<6;
f1>>=2;
t[1] = (f1)^(tmp[1]>>4);
itoa(t[1],a,2);
f1 = tmp[1]<<4;
f1>>=2;
t[2] = (f1)^(tmp[2]>>6);
itoa(t[2],a,2);
f1 = tmp[2]<<2;
f1 >>=2;
t[3] = f1;
itoa(t[3],a,2);
t[0] = base64str[t[0]];
t[1] = base64str[t[1]];
t[2] = base64str[t[2]];
t[3] = base64str[t[3]];
for(int i=0;i<4;i++)
{
s+=t[i];
}
}
// cout<<s.length()<<endl;
for(int i=0;i<27;i++)
{
printf("%c",s[i]);
}
cout<<endl;
}
int main()
{
for(int i=0;i<64;i++)
{
m[base64str[i]]=i;
}
unsigned char data[21] = {0xD7,0x6D,0xF8,0xE7,0xAE,0xFC,0xF7,0x4D,0x76,0xDF,0x8E,0x7A,0xEF,0xCF,0x74,0xD7,0x6D,0xF8,0xE7,0xAE,0x0};
encode(data,21);
return 0;
}
0x54,0x04,0x9D,0x05,0xCB,0x0A,0x1A,0x13,0x43,0x31,0x1C,0x0F,0x5E,0xFF,0x39,0xFF,0xAD,0x4C,0x35,0x04
VASdBcsKGhNDMRwPXv85%61MNQQ
for(ecx = 0;ecx<0x1B;)
{
for(eax = 0;eax<4;eax++)
{
change()
}
}
本来就调试得头昏眼花了,就不想再多看了。大家有兴趣可以带着整体思路来看一下程序的完整流程。
using namespace std;
char base64str[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@%*";
map<unsigned char,unsigned char> m;
/*
2c 0x14 保存对用户名二次加密的数据
40 0x14 保存对序列号加密的数据
88 0x14 存储第一次用户名加密的数据
9C 用户名
1A0 密码
*/
void encode(unsigned char str[],int len)
{
string s = "";
unsigned char tmp[3];
unsigned char t[4];
for(int i=0;i<=len;i+=3)
{
memcpy(tmp,str+i,3);
char a[10];
memset(t,0,4);
itoa(tmp[0],a,2);
itoa(tmp[1],a,2);
itoa(tmp[2],a,2);
t[0] = tmp[0]>>2;
itoa(t[0],a,2);
unsigned char f1 = tmp[0]<<6;
f1>>=2;
t[1] = (f1)^(tmp[1]>>4);
itoa(t[1],a,2);
f1 = tmp[1]<<4;
f1>>=2;
t[2] = (f1)^(tmp[2]>>6);
itoa(t[2],a,2);
f1 = tmp[2]<<2;
f1 >>=2;
t[3] = f1;
itoa(t[3],a,2);
t[0] = base64str[t[0]];
t[1] = base64str[t[1]];
t[2] = base64str[t[2]];
t[3] = base64str[t[3]];
for(int i=0;i<4;i++)
{
s+=t[i];
}
}
cout<<"序列号"<<endl;
for(int i=0;i<27;i++)
{
printf("%c",s[i]);
}
cout<<endl;
}
void imul(int& eax, int &ecx, int &edx)
{
long long tmp = eax;
tmp = tmp*ecx;
eax = tmp & 0xffffffff;
edx = tmp >> 32;
}
void test5(unsigned char data[])
{
int a[5];
int b[5];
memcpy((unsigned char*)&a, data + 0x2c, 0x14);
memcpy((unsigned char*)&b, data + 0x40, 0x14);
//if (((a[0] + b[4])== b[2]) && ((a[1] + b[2]) == (2 * b[4])) && ((a[2] + b[3]) == b[0]) && ((a[3] + b[0]) == (2 * b[3])) && ((a[4] + b[1]) == (3 * a[2])))
if((2*a[2]+a[3]==b[0])&&(3*a[2]-a[4]==b[1])&&(2*a[0]+a[1]==b[2])&&(a[2]+a[3]==b[3])&&(a[0]+a[1]==b[4]))
{
cout << "successn";
}
else
{
cout << "failn";
}
}
void encyptUsernameSecond(unsigned char data[0x14])
{
unsigned char total[0x92];
memcpy(total + 0x88, data, 0x14);
memset(total + 0x30, 0, 0x24);
memset(total + 0x2c, 0, 0x14);
int esi = 0;
int ecx = 0;
int eax = 0;
int edx = 0x14;
int i24 = 0x2995c04;
unsigned char ediData[0x14] = { 0xD7,0x6D,0xF8,0xE7,0xAE,0xFC,0xF7,0x4D,0x76,0xDF,0x8E,0x7A,0xEF,0xCF,0x74,0xD7,0x6D,0xF8,0xE7,0xAE };//这个只要只和输入的序列号有关系。
memset(ediData,0,0x14);
int* edi = (int*)&ediData;
int unknowndata = 0x3156c04;
while (esi<0x14)
{
ecx = *(int*)&data[esi];
eax = 0x66666667;
imul(eax, ecx, edx);
edx >>= 2; //sar eax,2
ecx = edx;
unsigned int tmp = ecx;
tmp >>= 31; //基础要打好,总是一知半解的,用的时候还要重新查。移位很多,涉及到符号之类的。
//shr ecx,0x1f
//__asm {
// shr tmp,0x1F
//}
ecx = tmp;
ecx += edx;
edx = 0x14; //合并起来,固定为0x14
memcpy(total + 0x2c + esi, (unsigned char*)&ecx, 4);
if (esi<edx)
{
eax = *(int*)&ediData[esi];
memcpy(total + 0x40 + esi, (unsigned char*)&eax, 4);
esi += 4;
}
}
if (true)
{
//if ((2 * a[2] + a[3] == b[0]) && (3 * a[2] - a[4] == b[1]) && (2 * a[0] + a[1] == b[2]) && (a[2] + a[3] == b[3]) && (a[0] + a[1] == b[4]))
int a[5];
int b[5];
memcpy((unsigned char*)&a, total + 0x2c, 0x14);
memcpy((unsigned char*)&b, total + 0x40, 0x14);
b[0] = 2 * a[2] + a[3];
b[1] = 3 * a[2] - a[4];
b[2] = 2 * a[0] + a[1];
b[3] = a[2] + a[3];
b[4] = a[0] + a[1];
memcpy(total + 0x2c, (unsigned char*)&a, 0x14);
memcpy(total + 0x40, (unsigned char*)&b, 0x14);
}
cout << "after 0x40" << endl;
for (int i = 0; i<0x14; i++)
{
printf("0x%02X,", total[i + 0x40]);
}
cout<<endl;
total[0x54] = 0x2A;
encode(total+0x40,21);
// test5(total);
}
void encyptUsernameFirst(string username)
{
unsigned char data[0x14];
memset(data, 0, 0x14);
int eax = 0;
int ebp = 0x1339E7E;
int esp = (int)&data - 0x88;
int edx = (int)&data;
int ecx = 0;
int edi = username.length();
int* esi = 0;
ebp = ebp - edx;
while (ecx<0x10)
{
eax = ecx;
edx = eax%edi;
esi = (int*)&data[ecx];
ecx++;
eax = username[edx];
edx = (int)esi + ebp;
eax = eax*edx;
eax = eax*edi;
(*esi) = (*esi) + eax;
}
encyptUsernameSecond(data);
}
int main()
{
for(int i=0;i<64;i++)
{
m[base64str[i]]=i;
}
cout << "请输入username" << endl;
string str = "helloworld";
cout<<"username = "<<str<<endl;
encyptUsernameFirst(str);
system("pause");
}
看雪ID:yyjeqhc
https://bbs.pediy.com/user-home-951761.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
原文始发于微信公众号(看雪学苑):2016腾讯游戏安全技术竞赛题PC第一题