使用IDA打开,通过搜索字符串找到主函数:
int __thiscall sub_AC2810(void *this)
{
int v1; // eax
int v2; // ecx
int v3; // eax
char *v4; // esi
signed int v5; // edi
unsigned int v6; // kr00_4
char *v7; // ecx
char **v8; // ecx
char *v9; // ecx
char *v10; // edx
unsigned int v11; // edi
int v12; // eax
int v13; // eax
bool v14; // cf
unsigned __int8 v15; // al
unsigned __int8 v16; // al
unsigned __int8 v17; // al
const char *v18; // edx
int v19; // eax
char *v20; // eax
int v22; // [esp-14h] [ebp-D8h]
int v23; // [esp-10h] [ebp-D4h]
char *v24; // [esp+24h] [ebp-A0h]
int len; // [esp+34h] [ebp-90h]
unsigned int v26; // [esp+38h] [ebp-8Ch]
__int128 v27; // [esp+3Ch] [ebp-88h]
__int128 v28; // [esp+4Ch] [ebp-78h]
int v29; // [esp+5Ch] [ebp-68h]
__int128 key; // [esp+60h] [ebp-64h]
__int128 v31; // [esp+70h] [ebp-54h]
int v32; // [esp+80h] [ebp-44h]
__int64 v33; // [esp+84h] [ebp-40h]
int v34; // [esp+8Ch] [ebp-38h]
int v35; // [esp+94h] [ebp-30h]
int v36; // [esp+98h] [ebp-2Ch]
int v37; // [esp+9Ch] [ebp-28h]
int v38; // [esp+A0h] [ebp-24h]
int v39; // [esp+A4h] [ebp-20h]
int v40; // [esp+A8h] [ebp-1Ch]
int v41; // [esp+ACh] [ebp-18h]
int v42; // [esp+B0h] [ebp-14h]
int v43; // [esp+C0h] [ebp-4h]
len = 0;
v26 = 15;
LOBYTE(v24) = 0;
v43 = 0;
LOBYTE(v43) = 1;
v1 = printf(this, "Please input your flag: ");
sub_AC3050(v1);
scanf(&dword_AF0068, &v24);
strcpy((char *)&v34, "9_CTF");
v33 = *(_QWORD *)aSwpu2019Ctf; // SWPU_2019_CTF
if ( len == 32 )
{
v39 = -1173078761;
v40 = 494076752;
v41 = -1811652486;
v42 = 688582768;
v5 = 0;
key = 0i64;
v32 = 0;
v31 = 0i64;
v6 = strlen((const char *)&v33);
do // 1:亦或
{
v7 = (char *)&v24;
if ( v26 >= 16 )
v7 = v24;
v7[v5] ^= *((_BYTE *)&v33 + v5 % v6);
++v5;
}
while ( v5 < 32 );
v8 = &v24;
v4 = v24;
if ( v26 >= 16 )
v8 = (char **)v24;
v29 = 0;
v27 = 0i64;
v28 = 0i64;
*(_QWORD *)&key = *(_QWORD *)v8;
*((_QWORD *)&key + 1) = *((_QWORD *)v8 + 1);
*(_QWORD *)&v31 = *((_QWORD *)v8 + 2);
*((_QWORD *)&v31 + 1) = *((_QWORD *)v8 + 3);
sub_AC25C0(v22, v23, 256, (unsigned int)&key, (unsigned int)&v27);// 一种加密
v35 = 0xF80F37B3;
v36 = 0x5DAEBCBC;
v9 = (char *)&v35;
v37 = 0x864D5ABA;
v10 = (char *)&v27;
v38 = 0xD3629744;
v11 = 28;
v39 = 0x1624BA4F;
v40 = 0x1A729F0B;
v41 = 0x266D6865;
v42 = 0x67C86BBA;
while ( 1 )
{
v12 = *(_DWORD *)v9;
if ( *(_DWORD *)v9 != *(_DWORD *)v10 )
break;
v9 += 4;
v10 += 4;
v14 = v11 < 4;
v11 -= 4;
if ( v14 )
{
v13 = 0;
goto LABEL_19;
}
}
v14 = (unsigned __int8)v12 < (unsigned __int8)*v10;
if ( (_BYTE)v12 != *v10
|| (v15 = v9[1], v14 = v15 < (unsigned __int8)v10[1], v15 != v10[1])
|| (v16 = v9[2], v14 = v16 < (unsigned __int8)v10[2], v16 != v10[2])
|| (v17 = v9[3], v14 = v17 < (unsigned __int8)v10[3], v17 != v10[3]) )
{
v13 = -v14 | 1;
}
else
{
v13 = 0;
}
LABEL_19:
if ( v13 )
v18 = "Try again!\r\n";
else
v18 = "Congratulations! I always knew you could do it.";
v19 = printf(v9, v18);
sub_AC3050(v19);
sub_ACADBE("pause");
}
else
{
v3 = printf(v2, "Try again!\r\n");
sub_AC3050(v3);
sub_ACADBE("pause");
v4 = v24;
}
if ( v26 >= 0x10 )
{
v20 = v4;
if ( v26 + 1 >= 0x1000 )
{
v4 = (char *)*((_DWORD *)v4 - 1);
if ( (unsigned int)(v20 - v4 - 4) > 0x1F )
sub_ACAFF7(v26 + 36);
}
sub_AC64DE(v4);
}
return 0;
}
先静心浏览代码,先与一个值亦或,再把亦或后的数据放到了一个函数进行加密,然后比较;
这里注意几点:
1.后面比较字符串因为是整数类型,要注意大小端(后面OD动态调试在寄存器里找亦或的数据也要注意大小端)。
2.前面虽然只传入了四次数据,但是一次是QWORD,也就是4 * 8 = 32。
当然流程不知道是什么,可以先动态调试,一般是同时进行的。
因为看了加密函数里面很多常量的混淆,下面使用动态调试:
OD打开(IDA报错):
在IDA中可以看到函数地址是,AC2810
找到地址(注意基地址可能不一样,后面相同就行),下个断点
F9运行,F8跟踪
printf
scanf
随便输入数据,32位
check长度是否位0x20(32),不是输出错误
这里应该是第一个亦或(xor),知道亦或的数据,就不用记录了:
看到这里有一片移动数据:
[ecx]地址的值移动到[EBP-64]
ecx的值是前面亦或后的数据,就相当于把数据移动到了新的地址,ds是数据段寄存器,ss是栈段寄存器然后是加密函数:
EBP-64的数据就是上面转移的新地址,这个数据需要在栈中查看。也可以在数据窗口中跟随。
栈段:
数据窗口跟随方法:
先跟踪到要跟随的数据操作地址,比如这里是ss:[EBP-64]
下面有个提示堆栈,右键选择跟随;
32字节刚好两排,数据应该和上面段中的地址一样,这里不一样是因为我这次输入的数据不一样,这个功能相当于段内存映射
然后因为这个加密函数我跟进去看了一下,发现思路不明确,然后可以使用一个内存断点的功能;
如下
只要有指令改变这个地方的数据OD就会给我们短下来,F9运行
断到了一个亦或
EAX-4是刚刚那个数据
我们搜寻每次亦或ECX的数据:
CA3E0C86
19AED798
A66B77E2
B077A16A
05379169
307BF97A
104B5A43
28D47D86
这里有个重点,前面说了。
因为xor每次亦或,后面使用的dword(2字节),产生大小端的问题,所以要就行处理,可以将ECX数据重新排列,也可以将另外一个字符串重新排列;
继续,后面还有一个比较:
数据在上面:
脚本:
#输入的永远是一个字节一个字节的输入,所以没有大小端。但是比较的时候会大小端,所以要将比较的数据还原就行了
data = [0xB3, 0x37, 0x0F, 0xF8, 0xBC, 0xBC, 0xAE, 0x5D, 0xBA, 0x5A, 0x4D, 0x86, 0x44, 0x97, 0x62, 0xD3, 0x4F, 0xBA, 0x24, 0x16, 0x0B ,0x9F, 0x72, 0x1A, 0x65, 0x68, 0x6D, 0x26, 0xBA, 0x6B, 0xC8, 0x67]
flag = ""
#字符形式不考虑大小端
check1 = "SWPU_2019_CTF"
#本来是4个字节4个字节的亦或,也可以转换为一个字节一个字节的亦或
#比较是dwrod类型,所以每四个字节需要使用大小端转换
check2 = [0X86,0X0C,0X3E,0XCA,0X98,0XD7,0XAE,0X19,0XE2,0X77,0X6B,0XA6,0X6A,0XA1,0X77,0XB0,0X69,0X91,0X37,0X05,0X7A,0XF9,0X7B,0X30,0X43,0X5A,0X4B,0X10,0X86,0X7D,0XD4,0X28]
for i in range(len(data)):
#每个数据只亦或一下,所以可以写一起
flag += chr(data[i] ^ check2[i] ^ ord(check1[i%13]))
print(flag)