小白一枚,膜拜大佬,欢迎指正
总结:
- RC4加密
- ida远程动调
[GUET-CTF2019]encrypt
exeinfope看一下,是64位无壳程序
导入ida64分析,贴上主函数伪代码:
__int64 __fastcall main(int a1, char **a2, char **a3)
{
unsigned int v3; // eax
unsigned int v4; // eax
char v6[4]; // [rsp+4h] [rbp-93Ch] BYREF
int i; // [rsp+8h] [rbp-938h]
int v8; // [rsp+Ch] [rbp-934h]
_DWORD v9[260]; // [rsp+10h] [rbp-930h] BYREF
char v10[16]; // [rsp+420h] [rbp-520h] BYREF
char s[256]; // [rsp+430h] [rbp-510h] BYREF
char v12[1032]; // [rsp+530h] [rbp-410h] BYREF
unsigned __int64 v13; // [rsp+938h] [rbp-8h]
v13 = __readfsqword(0x28u);
v10[0] = 16;
v10[1] = 32;
v10[2] = 48;
v10[3] = 48;
v10[4] = 32;
v10[5] = 32;
v10[6] = 16;
v10[7] = 64;
memset(s, 0, sizeof(s));
v8 = strlen(s);
memset(v12, 0, 0x400uLL);
printf("please input your flag:");
scanf("%s", s);
memset(v9, 0, 0x408uLL);
sub_4006B6(v9, v10, 8); // 动态调试获取v9的值
v3 = strlen(s);
sub_4007DB(v9, s, v3);
v4 = strlen(s);
sub_4008FA(s, v4, v12, v6);
for ( i = 0; i <= 50; ++i )
{
if ( v12[i] != byte_602080[i] )
{
puts("Wrong");
return 0LL;
}
}
puts("Good");
return 0LL;
}
整体来说很直接的一道题,考察算法逆向分析能力。
先来看第一个函数sub_4006B6()
bool __fastcall sub_4006B6(_DWORD *a1, __int64 a2, int a3)
{
bool result; // al
int i; // [rsp+1Ch] [rbp-18h]
int j; // [rsp+1Ch] [rbp-18h]
int v6; // [rsp+20h] [rbp-14h]
int v7; // [rsp+24h] [rbp-10h]
int v8; // [rsp+28h] [rbp-Ch]
_DWORD *v9; // [rsp+2Ch] [rbp-8h]
*a1 = 0;
a1[1] = 0;
v9 = a1 + 2;
for ( i = 0; i <= 255; ++i )
v9[i] = i;
v7 = 0;
result = 0;
LOBYTE(v6) = 0;
for ( j = 0; j <= 255; ++j )
{
v8 = v9[j];
v6 = (v6 + v8 + *(v7 + a2));
v9[j] = v9[v6];
v9[v6] = v8;
result = ++v7 >= a3;
if ( v7 >= a3 )
v7 = 0;
}
return result;
}
获取v9的值对后面的解密起到了至关重要的作用,我们可以直接linux下ida动态调试, 在return result;指令处下断点,然后F9运行到断点处,双击v9进入栈窗口可以获取v9内的值。
【后来学习别的博主的wp知道了这几个函数是RC4加密,这个函数是RC4算法里的生成S盒,再打乱S盒】
0x00, 0x00, 0xB0, 0x31, 0x75, 0x70, 0xF8, 0xDF,
0x07, 0x3C, 0x78, 0x71, 0x50, 0x29, 0x2C, 0x16,
0x69, 0x12, 0xC8, 0x2B, 0x3B, 0x7F, 0xB2, 0xE7,
0x4B, 0x68, 0x8C, 0xC5, 0xA6, 0x15, 0x03, 0x58,
0x47, 0x04, 0x13, 0x8D, 0x87, 0x26, 0x09, 0xED,
0x17, 0x8A, 0xC2, 0xF2, 0x43, 0xC0, 0xAC, 0x59,
0x97, 0xF5, 0x3F, 0x67, 0x5E, 0x39, 0x86, 0xD5,
0x72, 0x61, 0xDA, 0xF7, 0x01, 0x05, 0x8B, 0xC3,
0xB1, 0x77, 0xAF, 0x1D, 0x30, 0xC6, 0x45, 0x0E,
0x5F, 0xEE, 0xAE, 0xF0, 0x28, 0xCE, 0xCD, 0xA7,
0x9B, 0x2A, 0x19, 0x48, 0x08, 0x44, 0x20, 0xFE,
0x6D, 0xB5, 0x2E, 0x6A, 0xF1, 0x34, 0xBC, 0x1E,
0x3E, 0xCC, 0x41, 0x92, 0xD8, 0xBD, 0xA5, 0xE8,
0x4D, 0x0A, 0x49, 0x0D, 0xA2, 0xFA, 0x62, 0x74,
0xD4, 0x83, 0x96, 0x94, 0x3D, 0xCB, 0x18, 0x63,
0x99, 0x46, 0xCA, 0xB7, 0x8E, 0xCF, 0xFB, 0xA3,
0x6C, 0x7E, 0x51, 0x27, 0x60, 0x9A, 0x11, 0xF3,
0x5C, 0x6E, 0xBA, 0x42, 0x76, 0x2F, 0xEF, 0xBF,
0x21, 0xAA, 0xE4, 0xD6, 0x1B, 0x55, 0x7D, 0xBE,
0xEA, 0xD3, 0x10, 0xF4, 0xC7, 0x4A, 0x23, 0x79,
0x84, 0xA4, 0x1C, 0xAB, 0x14, 0xDB, 0x4C, 0x3A,
0xB8, 0x52, 0xEC, 0x37, 0x38, 0xB6, 0xD2, 0xA0,
0x5A, 0x5B, 0x98, 0x66, 0x54, 0x9E, 0x4E, 0x4F,
0xB4, 0xC4, 0xC9, 0xD0, 0x25, 0x9C, 0x80, 0xDE,
0x2D, 0x06, 0x22, 0x0B, 0x91, 0x6B, 0x9F, 0xF6,
0xE6, 0xE2, 0xC1, 0x0F, 0x93, 0x90, 0x7B, 0x9D,
0x8F, 0xDD, 0xE5, 0x65, 0x35, 0xAD, 0xA9, 0xDC,
0x82, 0xBB, 0x00, 0x53, 0xD1, 0xA8, 0x33, 0xE9,
0x40, 0x1A, 0xFF, 0xA1, 0x95, 0x36, 0xD9, 0xEB,
0x89, 0xE3, 0x7C, 0x73, 0x85, 0x88, 0x7A, 0xE0,
0xFD, 0x64, 0x0C, 0x57, 0x32, 0xB3, 0xB9, 0x1F,
0xD7, 0xFC, 0x81, 0xE1, 0x02, 0xF9, 0x5D, 0x56,
0x6F, 0x24
接下来我们看sub_4007DB函数,这个函数主要是对上面的v9进行了一些更改,然后把输入的字符串每一个字符都与改过的v9进行异或。
_DWORD *__fastcall sub_4007DB(_DWORD *a1, __int64 a2, int a3)
{
_DWORD *result; // rax
int i; // [rsp+18h] [rbp-1Ch]
int v5; // [rsp+1Ch] [rbp-18h]
int v6; // [rsp+20h] [rbp-14h]
int v7; // [rsp+24h] [rbp-10h]
int v8; // [rsp+28h] [rbp-Ch]
_DWORD *v9; // [rsp+2Ch] [rbp-8h]
v5 = *a1;
v6 = a1[1];
v9 = a1 + 2;
for ( i = 0; i < a3; ++i )
{
v5 = (v5 + 1);
v7 = v9[v5];
v6 = (v6 + v7);
v8 = v9[v6];
v9[v5] = v8;
v9[v6] = v7;
*(i + a2) ^= LOBYTE(v9[(v7 + v8)]);//对输入字符串做异或操作
}
*a1 = v5;
result = a1;
a1[1] = v6;
return result;
}
Tab查看 *(i + a2) ^= LOBYTE(v9[(v7 + v8)]);的汇编代码如下。
不难看出esi里存放的是输入的字符,edx里存放的是待异或的数据,所以要想得到输入字符串与什么做了异或,我们只需要利用动态调试在“xor edx, esi”处下断点,统计edx寄存器里的值就行。
0x10,0x59,0x9C,0x92,0x06,0x22,0xCF,0xA5,
0x72,0x1E,0x45,0x6A,0x06,0xCB,0x08,0xC3,
0xE4,0x49,0x5A,0x63,0x0C,0xDF,0xF6,0x5F,
0x08,0x28,0xBD,0xE2,0x10,0x15,0x1F,0x6E,
0xAA,0x5A,0xCA,0xEC,0x80,0xAF,0x9B,0x16,
0xBB,0x3D,0x13,0x2F,0x6A,0xA4,0xC7,0x2E,
0xBC,0x4B,0x60,0x9A,0xAF,0xE9,0xCE,0xDA,
0x67,0x39,0xBA,0x3B,0x85,0xEB,0xD2,0x6B,
0xAB,0x06,0x6B,0x10,0x57,0x2C,0x88,0x70,
0xF7,0x4F,0xAA,0x7F,0x12,0x47,0xD6,0xDE,
0x74,0xB2,0x1D,0xA4,0xD7,0x76,0x9A,0xE0,
看最后一个函数sub_4008FA;
_DWORD *__fastcall sub_4008FA(__int64 a1, int a2, const char *a3, _DWORD *a4)
{
int v4; // eax
int v5; // eax
unsigned __int8 v6; // al
int v7; // eax
unsigned __int8 v8; // al
int v9; // eax
int v10; // edx
_DWORD *result; // rax
char v13; // [rsp+2Dh] [rbp-13h]
unsigned __int8 v14; // [rsp+2Eh] [rbp-12h]
unsigned __int8 v15; // [rsp+2Fh] [rbp-11h]
int v16; // [rsp+30h] [rbp-10h]
int v17; // [rsp+34h] [rbp-Ch]
v16 = 0;
v17 = 0;
while ( v17 < a2 )
{
v4 = v17++;
v13 = *(v4 + a1);
if ( v17 >= a2 )
{
v6 = 0;
}
else
{
v5 = v17++;
v6 = *(v5 + a1);
}
v14 = v6;
if ( v17 >= a2 )
{
v8 = 0;
}
else
{
v7 = v17++;
v8 = *(v7 + a1);
}
v15 = v8;
a3[v16] = ((v13 >> 2) & 0x3F) + '=';
a3[v16 + 1] = (((v14 >> 4) | (16 * v13)) & 0x3F) + 61;
a3[v16 + 2] = (((v8 >> 6) | (4 * v14)) & 0x3F) + 61;
v9 = v16 + 3;
v16 += 4;
a3[v9] = (v15 & 0x3F) + 61;
}
if ( a2 % 3 == 1 )
{
a3[--v16] = 61;
}
else if ( a2 % 3 != 2 )
{
goto LABEL_15;
}
a3[v16 - 1] = 61;
LABEL_15:
v10 = strlen(a3);
result = a4;
*a4 = v10;
return result;
}
上述代码简化之后如下:
_DWORD *__fastcall sub_4008FA(__int64 a1, int a2, const char *a3, _DWORD *a4)
{
int v4; // eax
int v5; // eax
unsigned __int8 v6; // al
int v7; // eax
unsigned __int8 v8; // al
int v9; // eax
int v10; // edx
_DWORD *result; // rax
char v13; // [rsp+2Dh] [rbp-13h]
unsigned __int8 v14; // [rsp+2Eh] [rbp-12h]
unsigned __int8 v15; // [rsp+2Fh] [rbp-11h]
int v16; // [rsp+30h] [rbp-10h]
int v17; // [rsp+34h] [rbp-Ch]
v16 = 0;
v17 = 0;
while (v17 < a2)
{
v13 = a1[v17++];
v14 = a1[v17++];
v15 = a1[v17++];//取三个字符
a3[v16] = (( v13 >> 2 ) & 0x3F) + 61;
a3[v16 + 1] = (( (v14 >> 4) | ( v13 << 4) ) & 0x3F) + 61;
a3[v16 + 2] = (( (v15 >> 6) | ( v14 << 2) ) & 0x3F) + 61;
a3[v16 + 3] = (v15 & 0x3F) + 61;
v16 += 4;
}
//等号填充
if (a2 % 3 == 1)
{
a3[--v16] = 61;
}
else if (a2 % 3 != 2)
{
goto LABEL_15;
}
a3[v16 - 1] = 61;
LABEL_15:
v10 = strlen(a3);
result = a4;
*a4 = v10;
return result;
}
最后是跟byte_602080作比较
把byte_602080变成16进制后整理如下:
0x5a, 0x60, 0x54, 0x7A, 0x7A, 0x54, 0x72, 0x44,
0x7C, 0x66, 0x51, 0x50, 0x5B, 0x5F, 0x56, 0x56,
0x4C, 0x7C, 0x79, 0x6E, 0x65, 0x55, 0x52, 0x79,
0x55, 0x6D, 0x46, 0x6B, 0x6C, 0x56, 0x4A, 0x67,
0x4C, 0x61, 0x73, 0x4A, 0x72, 0x6F, 0x5A, 0x70,
0x48, 0x52, 0x78, 0x49, 0x55, 0x6C, 0x48, 0x5C,
0x76, 0x5A, 0x45, 0x3D
下面我们就可以写exp了:
data=[0x5a, 0x60, 0x54, 0x7A, 0x7A, 0x54, 0x72, 0x44,0x7C, 0x66, 0x51, 0x50, 0x5B, 0x5F, 0x56, 0x56,0x4C, 0x7C, 0x79, 0x6E, 0x65, 0x55, 0x52, 0x79,0x55, 0x6D, 0x46, 0x6B, 0x6C, 0x56, 0x4A, 0x67,0x4C, 0x61, 0x73, 0x4A, 0x72, 0x6F, 0x5A, 0x70,0x48, 0x52, 0x78, 0x49, 0x55, 0x6C, 0x48, 0x5C,0x76, 0x5A, 0x45, 0x3D]
flag=''
for i in range(0,len(data),4):
flag+=chr((((data[i]-0x3D)&0x3F)<<2)|(((data[i+1]-0x3D)&0x30)>>4))
flag+=chr((((data[i+1]-0x3D)&0x0F)<<4)|(((data[i+2]-0x3D)&0x3C)>>2))
flag+=chr(((data[i+3]-0x3D)&0x3F)|((data[i+2]-0x3D)&0x03)<<6)
j=0
l=[0x10,0x59,0x9C,0x92,0x06,0x22,0xCF,0xA5,0x72,0x1E,0x45,0x6A,0x06,0xCB,0x08,0xC3,0xE4,0x49,0x5A,0x63,0x0C,0xDF,0xF6,0x5F,0x08,0x28,0xBD,0xE2,0x10,0x15,0x1F,0x6E,0xAA,0x5A,0xCA,0xEC,0x80,0xAF,0x9B,0x16,0xBB,0x3D,0x13,0x2F,0x6A,0xA4,0xC7,0x2E,0xBC,0x4B,0x60,0x9A,0xAF,0xE9,0xCE,0xDA,0x67,0x39,0xBA,0x3B,0x85,0xEB,0xD2,0x6B,0xAB,0x06,0x6B,0x10,0x57,0x2C,0x88,0x70,0xF7,0x4F,0xAA,0x7F,0x12,0x47,0xD6,0xDE,0x74,0xB2,0x1D,0xA4,0xD7,0x76,0x9A,0xE0]
a=list(flag)
flag=''
for i in a:
flag+=chr(ord(a[j])^l[j])
j+=1
print(flag)
flag{e10adc3949ba59abbe56e057f20f883e}