逆向----有趣的位操作---笔记
结构很简单,
一个关键函数
伪代码
int __cdecl check_password(char *input)
{
void *v1; // esp
int v2; // eax
int v3; // eax
int v4; // eax
int v5; // eax
int v6; // eax
int v7; // eax
int v8; // eax
int v9; // eax
int v10; // eax
int v11; // eax
int v13; // [esp+0h] [ebp-48h]
char *s; // [esp+Ch] [ebp-3Ch]
int v15; // [esp+10h] [ebp-38h]
int v16; // [esp+14h] [ebp-34h]
int i; // [esp+18h] [ebp-30h]
int v18; // [esp+1Ch] [ebp-2Ch]
size_t n; // [esp+20h] [ebp-28h]
int v20; // [esp+24h] [ebp-24h]
char *s1; // [esp+28h] [ebp-20h]
int v22; // [esp+2Ch] [ebp-1Ch]
int v23; // [esp+30h] [ebp-18h]
int v24; // [esp+34h] [ebp-14h]
unsigned int v25; // [esp+38h] [ebp-10h]
unsigned int v26; // [esp+3Ch] [ebp-Ch]
s = input;
v26 = __readgsdword(0x14u);
v18 = strlen(input);
n = 4 * ((v18 + 2) / 3);
v20 = 4 * ((v18 + 2) / 3);
v1 = alloca(16 * ((4 * ((v18 + 2) / 3) + 16) / 0x10u));
s1 = (char *)&v13;
v15 = 0;
v16 = 0;
while ( v15 < v18 )
{
if ( v15 >= v18 )
{
v3 = 0;
}
else
{
v2 = v15++;
v3 = (unsigned __int8)s[v2];
}
v22 = v3;
if ( v15 >= v18 )
{
v5 = 0;
}
else
{
v4 = v15++;
v5 = (unsigned __int8)s[v4];
}
v23 = v5;
if ( v15 >= v18 )
{
v7 = 0;
}
else
{
v6 = v15++;
v7 = (unsigned __int8)s[v6];
}
v24 = v7;
v25 = (v23 << 8) + (v22 << 16) + v7;
v8 = v16++;
s1[v8] = alphabet[(v25 >> 18) & 0x3F];
v9 = v16++;
s1[v9] = alphabet[(v25 >> 12) & 0x3F];
v10 = v16++;
s1[v10] = alphabet[(v25 >> 6) & 0x3F];
v11 = v16++;
s1[v11] = alphabet[v25 & 0x3F];
}
for ( i = 0; mod[v18 % 3] > i; ++i )
s1[n - 1 - i] = 61;
return strncmp(s1, "cGljb0NURntiQXNFXzY0X2VOQ29EaU5nX2lTX0VBc1lfMjk1ODA5OTN9", n);
}
简化后
char alphabet[] = "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F"
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x61\x62\x63\x64"
"\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73"
"\x74\x75\x76\x77\x78\x79\x7A\x30\x31\x32\x33\x34\x35\x36\x37"
"\x38\x39\x2B\x2F";
int mod[3] = { 0,2,1 };
int getIndex(char c)
{
int len = strlen(alphabet);
for (int i = 0; i < len; i++)
{
if (alphabet[i] == c)
{
return i;
}
}
return -1;
}
int __cdecl check_password(char *input)
{
int v13;
char *input_back;
int j;
int v16;
int len;
size_t n;
char *s1;
int v22;
int v23;
int v24;
unsigned int v25;
input_back = input;
len = strlen(input);
s1 = (char *)&v13;
j = 0;
v16 = 0;
while (j < len)
{
if (j >= len)
{
v22 = 0;
}
else
{
v22 = (unsigned __int8)input_back[j];
j++;
}
if (j >= len)
{
v23 = 0;
}
else
{
v23 = (unsigned __int8)input_back[j];
j++;
}
if (j >= len)
{
v24 = 0;
}
else
{
v24 = (unsigned __int8)input_back[j];
j++;
}
v25 = (v23 << 8) + (v22 << 16) + v24;
s1[v16] = alphabet[(v25 >> 18) & 0x3F];
v16++;
s1[v16] = alphabet[(v25 >> 12) & 0x3F];
v16++;
s1[v16] = alphabet[(v25 >> 6) & 0x3F];
v16++;
s1[v16] = alphabet[v25 & 0x3F];
v16++;
}
//0 2 1
n = 4 * ((len + 2) / 3);
for (int i = 0; mod[len % 3] > i; ++i)
s1[n - 1 - i] = 61;
return strncmp(s1, "cGljb0NURntiQXNFXzY0X2VOQ29EaU5nX2lTX0VBc1lfMjk1ODA5OTN9", n);
//cout << input << endl;
return true;
}
重点分析一下这段代码
v25 = (v23 << 8) + (v22 << 16) + v24;
s1[v16] = alphabet[(v25 >> 18) & 0x3F];
v16++;
s1[v16] = alphabet[(v25 >> 12) & 0x3F];
v16++;
s1[v16] = alphabet[(v25 >> 6) & 0x3F];
v16++;
s1[v16] = alphabet[v25 & 0x3F];
其实v22-v24 取了输入字符串的3个字符,
接着就开始了位操作,分析下来
v25 = (v23 << 8) + (v22 << 16) + v24; 这行
使得v25的32个bit分布如下
v22 v23 v24
00000000 11111111 11111111 11111111
这七行
s1[v16] = alphabet[(v25 >> 18) & 0x3F];
v16++;
s1[v16] = alphabet[(v25 >> 12) & 0x3F];
v16++;
s1[v16] = alphabet[(v25 >> 6) & 0x3F];
v16++;
s1[v16] = alphabet[v25 & 0x3F];
使得s1连续的四个字符取到了alphabet字母表的四个字符。
且v25分别取不同的bit
讲述起来比较麻烦,分析一下就知道了
假设原来是这样
00000000 11111111 11111111 11111111
>>18
00000000 111111
00000000 00000000 00000000 00111111
>>12
00000000 11111111 1111
00000000 00000000 00001111 11111111
>>6
00000000 11111111 11111111 11
00000000 00000011 11111111 11111111
然后都去 与3f
还原思路
第一个数 左移18
第二个数 左移12
第三个数 左移6
第四个数 | 第三个数 |第二|第一个数
先找到cGljb0NURntiQXNFXzY0X2VOQ29EaU5nX2lTX0VBc1lfMjk1ODA5OTN9这个在字母表中的索引
/*char cmp[] = "cGljb0NURntiQXNFXzY0X2VOQ29EaU5nX2lTX0VBc1lfMjk1ODA5OTN9";
int arry[57] = { 0 };
for (int i = 0; i < 57; i++)
{
arry[i]=getIndex(cmp[i]);
cout << arry[i] << " ";
if (i != 0 && i % 10 == 0)
cout << endl;
}*/
为了清晰 我直接复制下来了
这是所有的索引 为了统一下标多加了一个-1
int arry[] = { -1,28,6,37,35,27,52,13,20,17,39,45,
34,16,23,13,5,23,51,24,52,23,
54,21,14,16,54,61,4,26,20,57,
39,23,54,37,19,23,52,21,1,28,
53,37,31,12,35,36,53,14,3,0,
57,14,19,13,61 };
最后
string str;
for (int i = 1; i < 57; i+=4)
{
arry[i] = arry[i] << 18;
arry[i+1] = arry[i+1] << 12;
arry[i+2] = arry[i+2] << 6;
arry[i + 3] |= arry[i + 2] | arry[i + 1] | arry[i];
str += ((char*)&arry[i + 3])[2];
str += ((char*)&arry[i + 3])[1];
str += ((char*)&arry[i + 3])[0];
}
cout << str << endl;
flag=picoCTF{bAsE_64_eNCoDiNg_iS_EAsY_29580993}