[NPUCTF2020]Baby Obfuscation

分析

首先查壳,64位无壳,放入IDA中看看

image-20221106192308361

image-20221106192552471

看到十分的复杂,然后里头还有很多的函数调用,那么先一个个分析每个调用的函数在干嘛

F0X1

image-20221106192649528

欧几里得算法也称为辗转相除法,用于计算两个数的最大公因数,一般用gcd(a,b)来表示a和b的最大公因数。设a、b均为正整数,则gcd(a,b)=gcd(b,a%b)

F0X2和F0X3

image-20221106193901414

image-20221106193914357

F0X4

image-20221106193938069

这个相当于a-b

F0X5

image-20221106194742540

这个表示a的b次方(a^b)

几个重要的函数分析完了,那么接下来分析一下整个在做什么

int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int v4; // ebx
int v5; // esi
int v6; // ebx
int v7; // ebx
int v8; // esi
int v9; // ebx
int v10; // ebx
int v11; // ebx
int v12; // esi
int v13; // eax
int v14; // ebx
int v15; // esi
int v16; // ebx
int v17; // eax
bool v18; // bl
int v19; // eax
int v20; // esi
int v21; // ebx
int v22; // ebx
int v23; // eax
int v24; // eax
int v25; // eax
int v26; // ebx
int A0X3[65]; // [rsp+20h] [rbp-60h] BYREF
char A0X2[1001]; // [rsp+130h] [rbp+B0h] BYREF
int A0X1[1001]; // [rsp+520h] [rbp+4A0h] BYREF
int A0X5[4]; // [rsp+14D0h] [rbp+1450h]
int A0X4[4]; // [rsp+14E0h] [rbp+1460h]
int V0X1; // [rsp+14F0h] [rbp+1470h]
int i_1; // [rsp+14F4h] [rbp+1474h]
int i_0; // [rsp+14F8h] [rbp+1478h]
int i; // [rsp+14FCh] [rbp+147Ch]

_main();
memset(A0X1, 0, 0xFA0ui64);
A0X1[1000] = 0;
memset(A0X3, 0, 0x100ui64);
A0X3[64] = 0;
for ( i = 0; i <= 64; ++i )
A0X3[i] = i + 1;
A0X4[0] = 2;
A0X4[1] = 3;
A0X4[2] = 4;
A0X4[3] = 5;
A0X5[0] = 2;
A0X5[1] = 3;
A0X5[2] = 4;
A0X5[3] = 5;
puts("WHERE IS MY KEY!?");
scanf("%32s", A0X2);
V0X1 = strlen(A0X2);
v3 = F0X1(A0X3[i_0], A0X3[i_0]); // v3=A0X3[i_0]
for ( i_0 = v3 / A0X3[i_0]; i_0 <= V0X1; ++i_0 )// for(i_0=1;i_0<=15;++i_0)
{
v4 = (A0X3[i_0] + A0X3[i_0 + 1]) * (A0X3[i_0] + A0X3[i_0 + 1]);// v4=(2(i_0)+3)^2
if ( v4 >= F0X5(2, 2) * A0X3[i_0] * A0X3[i_0 + 1] )// v4>=4*(i_0+1)*(i_0+2)恒满足
{
v5 = ~A0X2[F0X4(i_0, 1)]; // v5=~A0X2[i_0-1]
v6 = F0X4(i_0, 1); // v6=i_0-1
A0X1[i_0] = ~(v5 + A0X4[v6 % F0X5(2, 2)]);// A0x1[i_0]=~(~A0X2[i_0-1]+A0X4[i_0-1]%4)
}
v7 = F0X1(A0X3[i_0], A0X3[i_0 + 1]);
if ( v7 > F0X1(A0X3[i_0 + 1], ~(~A0X3[i_0 + 1] + A0X3[i_0])) )// 不成立
{
v8 = A0X1[i_0];
v9 = F0X4(i_0, 1);
A0X1[i_0] = ~(~v8 + A0X3[v9 % F0X5(2, 2)]) * v8;
}
v10 = A0X3[i_0 + 1]; // v10=i_0+2
v11 = F0X5(2, 1) * v10; // v11=2*(i_0)+2
v12 = A0X3[i_0]; // v12=i_0+1
v13 = F0X5(2, 1); // v13=2
v14 = F0X1(v12 * v13, v11); // v14=gcd(2*(i_0+1),2*(i_0)+2) ps:gcd表示求最大公因数
v15 = F0X5(2, 1); // v15=2
if ( v14 == v15 * F0X1(A0X3[i_0], A0X3[i_0 + 1]) )// 成立
{
v16 = F0X4(i_0, 1); // v16=i_0-1
A0X1[i_0] ^= A0X4[v16 % F0X5(2, 2)]; // A0X1^=A0X4[(i_0-1)%4]
}
v17 = F0X5(V0X3, A0X3[i_0]);
v18 = v17 < A0X3[i_0] + 1; // v18=0
v19 = F0X5(2, 4); // v19=16
if ( F0X3(v19 >= i_0, v18) ) // 不成立
{
v20 = ~A0X2[F0X4(i_0, 1)];
v21 = F0X4(i_0, 1);
A0X1[i_0] ^= ~(v20 + A0X4[v21 % F0X5(2, 2)]);
}
v22 = F0X5(2, 3); // v22=8
v23 = F0X1(A0X3[i_0], A0X3[i_0]);
A0X1[i_0] *= v22 + F0X5(2, v23 / A0X3[i_0]);// A0X1[i_0]*=8+2^1
}
v24 = F0X5(2, 4); // v24=16
if ( F0X4(v24, 1) != V0X1 ) // strlen=15
goto LABEL_23;
v25 = F0X1(A0X3[i_1], A0X3[i_1]);
for ( i_1 = v25 / A0X3[i_1]; i_1 <= V0X1; ++i_1 )// if(i_1=1;i_1<=15;++i_1)
{
v26 = A0X1[i_1];
if ( v26 == F0X4(A0X6[i_1], 1) / 10 )
++V0X2;
}
if ( V0X2 == V0X1 )
puts("\nPASS");
else
LABEL_23:
puts("\nDENIED");
return 0;
}
PS:这些成立和不成立的语句,可以通过赋值进行判断

分析完整段后可以知道要PASS的话就需要if ( v26 == F0X4(A0X6[i_1], 1) / 10 )这个语句成立,也就是说根A0X1数组有关,看了整个操作,能够成立的根A0X1有关的也就只有这三句

1.A0X1[i_0] = v22 + F0X5(2, v23 / A0X3[i_0]);// A0X1[i_0]=8+2^1

2.A0X1[i_0] ^= A0X4[v16 % F0X5(2, 2)];

3.A0X1[i_0] = ~(v5 + A0X4[v6 % F0X5(2, 2)]);

那么可以写脚本了

脚本
#include<stdio.h>
int main()
{
int A0X6[]={0x0, 0x1E79, 0x1E79, 0x2135, 0x170D, 0x1F41,0x1901, 0x2CED,0x11F9, 0x2649, 0x2581,0x2DB5, 0x14B5, 0x25E5, 0x2A31, 0x30D5,0x0};
int A0X4[4];
A0X4[0] = 2;
A0X4[1] = 3;
A0X4[2] = 4;
A0X4[3] = 5;
int flag[100];
int a;
for(int i=1;i<=15;++i)
{
a=(A0X6[i]-1)/100;//将得到加密后的A0X1数组与A0X1[i_0] *= v22 + F0X5(2, v23 / A0X3[i_0]);合并就是÷100
a^=A0X4[(i-1)%4];
a+=A0X4[(i-1)%4];
flag[i-1]=a;
printf("%c",flag[i-1]);
}
}
//NPUCTF{0bfu5er}