一道简单的SMC逆向题目复现
再不学逆向👶真要被开了
SMC(self-Modifying Code)可以理解为程序在执行一段机器码之前会对其进行修改,修改后的机器码才是可执行的正常汇编。在此之前,静态分析只能看到无法识别的汇编指令。
校赛re3是个带linux下smc的题目,只能说我完全看不懂,先做个纯smc的套题来练练手。
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
int i; // [esp+4Ch] [ebp-70h]
char Str[97]; // [esp+54h] [ebp-68h] BYREF
__int16 v6; // [esp+B5h] [ebp-7h]
char v7; // [esp+B7h] [ebp-5h]
size_t v8; // [esp+B8h] [ebp-4h]
v8 = 0;
memset(Str, 0, sizeof(Str));
v6 = 0;
v7 = 0;
scanf("%100s", Str);
v8 = strlen(Str);
if ( v8 == 28 )
{
if ( Str[27] == '}' )
{
for ( i = 0; i < 67; ++i )
byte_414C3C[i] ^= 0x7Du;
(*(void (__cdecl **)(char *, void *))byte_414C3C)(Str, &unk_414BE0);
return 0;
}
else
{
return 0;
}
}
else
{
printf("Try Again......\n");
return 0;
}
}
我们在这里可以发现flag的最后一位为'}',没啥用
第一次解密
关键在于这个奇怪的for循环处。在进行异或操作后对byte_414C3C数组进行了类似函数调用的操作,这里就是本题的第一层SMC加密
我们可以想到这里是将byte_414C3C数组异或后变成一段函数的机器码,然后再调用该函数。那么用考虑idc来实现函数的解密
static decrypt_1()
{
auto st = 0x00414C3C; // byte_414C3C的起始地址
auto i = 0;
for (i = 0; i < 67; ++i)
{
PatchByte(st + i,Byte(st + i) ^ 0x7D);
}
}
运行该脚本后对新的byte_414C3C数组(现在就可以看做是函数的机器码了)按C转化成代码,再在起始处按P转化成函数(直接按P好像也行)
int __cdecl sub_414C3C(_BYTE *a1, _BYTE *a2)
{
int i; // ecx
if ( *a1 == 102 && a1[1] == 108 && a1[2] == 97 && a1[3] == 103 && a1[4] == 123 )
{
for ( i = 0; i < 90; ++i )
a2[i] ^= 0x43u;
((void (__cdecl *)(_BYTE *, void *))a2)(a1 + 5, &unk_414A84);
}
return 0;
}
我们在这里可以发现flag的前五位为'flag{',没啥用
第二次解密
从sub_414c3c内我们再次发现了SMC的痕迹,继续这次的解密,a2的值我们可以回到主函数内看到参数byte_414BE0,用其解密即可
static decrypt_2()
{
auto st = 0x00414BE0;
auto i = 0;
for (i = 0; i < 90; ++i)
{
PatchByte(st + i,Byte(st + i) ^ 0x43);
}
}
重复上述方法得到函数sub_414BE0
int __cdecl sub_414BE0(int a1, void (__cdecl *a2)(int, void *))
{
int v2; // edx
int v3; // esi
int v5; // [esp+Ch] [ebp-8h] BYREF
char v6; // [esp+10h] [ebp-4h]
v2 = 0;
v5 = -1817598824;
v6 = 0;
v3 = 0;
while ( (*((_BYTE *)&v5 + v3 + a1 - (_DWORD)&v5) ^ 0xCC) == *((_BYTE *)&v5 + v3) )
{
if ( ++v3 >= 4 )
{
do
*((_BYTE *)a2 + v2++) ^= 0x55u;
while ( v2 < 347 );
a2(a1 + 4, &unk_414A30);
return 0;
}
}
return 0;
}
注意一下调用该函数时
((void (__cdecl *)(_BYTE *, void *))a2)(a1 + 5, &unk_414A84);
也就是说,这里的a1是除去了前五位的flag。
此处的限制条件简化一下*(a1 + v3) ^ 0xCC) == (&v5 + v3)
,简单解密一下可以知道是The_
第三次解密
这里显然还是有smc,只不过是将for换成了while的样子。
static decrypt_3()
{
auto st = 0x00414A84;
auto i = 0;
for (i = 0; i < 347; ++i)
{
PatchByte(st + i,Byte(st + i) ^ 0x55);
}
}
然后拿到sub_414A84函数
int __cdecl sub_414A84(int a1, void (__cdecl *a2)(_DWORD))
{
char v2; // dl
int v3; // eax
char i; // dl
char j; // dl
char *v6; // esi
int v7; // edi
unsigned __int8 v8; // bl
unsigned __int8 v9; // dl
int v10; // ecx
int v11; // eax
unsigned __int8 v12; // dh
int v13; // eax
unsigned __int8 v14; // dl
_BYTE *v15; // esi
int v16; // eax
int v17; // ecx
char v18; // dl
__int16 v20[34]; // [esp+0h] [ebp-64h]
char v21; // [esp+44h] [ebp-20h] BYREF
char v22[15]; // [esp+45h] [ebp-1Fh] BYREF
char v23[16]; // [esp+54h] [ebp-10h] BYREF
v2 = 65;
v3 = 0;
do
*((_BYTE *)v20 + v3++) = v2++;
while ( v2 <= 90 );
for ( i = 97; i <= 122; ++i )
*((_BYTE *)v20 + v3++) = i;
for ( j = 48; j <= 57; ++j )
*((_BYTE *)v20 + v3++) = j;
*(__int16 *)((char *)v20 + v3) = 12075;
*((_BYTE *)&v20[1] + v3) = 0;
v6 = &v21;
v21 = 0;
memset(v22, 0, 12);
strcpy(v23, "cmVhbEN0Rl8=");
v7 = 0;
do
{
v8 = *(_BYTE *)(a1 + v7 + 1);
v9 = *(_BYTE *)(a1 + v7 + 2);
v10 = *(_BYTE *)(a1 + v7) & 3;
v11 = *(unsigned __int8 *)(a1 + v7) >> 2;
v7 += 3;
*v6 = *((_BYTE *)v20 + v11);
v6[1] = *((_BYTE *)v20 + ((v8 >> 4) | (16 * v10)));
v6[2] = *((_BYTE *)v20 + ((v9 >> 6) | (4 * (v8 & 0xF))));
v6[3] = *((_BYTE *)v20 + (v9 & 0x3F));
v6 += 4;
}
while ( v7 < 6 );
if ( v7 < 8 )
{
v12 = *(_BYTE *)(v7 + a1);
*v6 = *((_BYTE *)v20 + (v12 >> 2));
v13 = v12 & 3;
if ( v7 == 7 )
{
v6[1] = v20[8 * v13];
v6[2] = 61;
}
else
{
v14 = *(_BYTE *)(v7 + a1 + 1);
v6[1] = *((_BYTE *)v20 + ((16 * v13) | (v14 >> 4)));
v6[2] = v20[2 * (v14 & 0xF)];
}
v15 = v6 + 3;
*v15 = 61;
v6 = v15 + 1;
}
v16 = 0;
*v6 = 0;
v17 = 0;
v18 = v21;
while ( v18 == v23[v17] )
{
v18 = v22[v17++];
if ( !v18 )
{
do
*((_BYTE *)a2 + v16++) ^= 0x4Du;
while ( v16 < 83 );
a2(a1 + 8);
return 0;
}
}
return 0;
}
显然是个Base64,解密出来得到realCtF_
第四次解密
然后继续套smc
static decrypt_4()
{
auto st = 0x00414A30;
auto i = 0;
for (i = 0; i < 83; ++i)
{
PatchByte(st + i,Byte(st + i) ^ 0x4D);
}
}
拿到sub_414A30
int __cdecl sub_414A30(char *a1)
{
int v1; // ebx
int v2; // edx
char *v3; // esi
char v5[12]; // [esp+Ch] [ebp-Ch] BYREF
v1 = 0;
strcpy(v5, "kvtu`C4h\"o");
v2 = -1;
do
++v2;
while ( v5[v2] );
if ( v2 > 0 )
{
v3 = a1;
do
{
if ( *v3 != v3[v5 - a1] - 1 )
break;
++v1;
++v3;
}
while ( v1 < v2 );
}
return 0;
}
简单解密一下得到just_B3g!n
综上,得到flag:
flag{The_realCtF_just_B3g!n}