[安洵杯 2019]game

知识点

  • OLLVM

反编译

对源文件反编译

int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int *v4; // rsi
int i; // [rsp+2Ch] [rbp-54h]
int v7; // [rsp+38h] [rbp-48h]
char v8[56]; // [rsp+40h] [rbp-40h] BYREF
int v9; // [rsp+78h] [rbp-8h]
int v10; // [rsp+7Ch] [rbp-4h]

v9 = 0;
printf("input your flag:");
gets(v8, argv);
v10 = general_inspection((int (*)[9])sudoku);
for ( i = -1804515313; ; i = -303742386 )
{
while ( 1 )
{
while ( i == -2071121728 )
{
v7 = blank_num((int (*)[9])sudoku);
v4 = (int *)mem_alloc(v7);
trace((__int64)sudoku, v4, v7); // 写数独
check((int (*)[9])sudoku); // 检查填写是否正确
check1(v8); // 加密
check3(v8); // 与写好数独对比
v9 = 0;
i = -303742386;
}
if ( i != -1804515313 )
break;
v3 = -2071121728;
if ( v10 )
v3 = 664169471;
i = v3;
}
if ( i == -303742386 )
break;
printf("error");
check((int (*)[9])sudoku);
v9 = 0;
}
return v9;
}

发现将flag转入v8,带入执行check1和check3,进入check1发现

__int64 __fastcall check1(char *a1)
{
__int64 result; // rax
size_t v2; // rax
int v3; // ecx
size_t v4; // rax
int v5; // ecx
size_t v6; // rax
int v7; // ecx
int v8; // [rsp+68h] [rbp-18h]
char v9; // [rsp+6Eh] [rbp-12h]
char v10; // [rsp+6Fh] [rbp-11h]
int v11; // [rsp+70h] [rbp-10h]
int v12; // [rsp+74h] [rbp-Ch]

v12 = strlen(a1) >> 1;
v11 = 0;
v8 = 1519002972;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( v8 == -2084833488 )
{
v6 = strlen(a1);
v7 = -67245798;
if ( v12 < v6 )
v7 = 1974939745;
v8 = v7;
}
if ( v8 != -1988665894 )
break;
v12 = 0;
v8 = -2084833488;
}
if ( v8 != -1393133668 )
break;
v4 = strlen(a1);
v5 = -1988665894;
if ( v12 < v4 )
v5 = -1018472136;
v8 = v5;
}
if ( v8 != -1018472136 )
break;
v9 = a1[v12];
a1[v12] = a1[v12 + 1];
a1[v12 + 1] = v9;
v8 = -146751883;
}
if ( v8 != -831482631 )
break;
++v12;
v8 = -2084833488;
}
if ( v8 != -291294424 )
break;
++v11;
++v12;
v8 = 1519002972;
}
if ( v8 != -146751883 )
break;
v12 += 2;
v8 = -1393133668;
}
result = (unsigned int)(v8 + 67245798);
if ( v8 == -67245798 )
break;
switch ( v8 )
{
case 75381312:
v10 = a1[v12]; // 交换
a1[v12] = a1[v11];
a1[v11] = v10;
v8 = -291294424;
break;
case 1519002972:
v2 = strlen(a1);
v3 = 1555725255;
if ( v11 < v2 >> 1 )
v3 = 75381312;
v8 = v3;
break;
case 1555725255:
v12 = 0;
v8 = -1393133668;
break;
default:
a1[v12] = (a1[v12] & 0xF3 | ~a1[v12] & 0xC) - 20;
v8 = -831482631;
break;
}
}
return result;
}

很明显为OLLVM,这样也能分析

有三个加密过程

1、交换前后的顺序 0-20,1-21

2、两个一组交换

3、 a1[v12] = (a1[v12] & 0xF3 | ~a1[v12] & 0xC) - 20;

def check1():
v12 = len(flag)>>1
for i in range(len(flag)>>1):
(flag[i],flag[v12+1]) = (flag[v12+1],flag[i])
#前后两部分互换
for i in range(0,len(flag),2):
(flag[i],flag[i+1]) = (flag[i+1],flag[i])
#两位之间互换
for i in range(len(flag)):
flag[i] = ((flag[i]&0xf3)|(~flag[i]&0xc)) - 20

继续看check3

__int64 __fastcall check3(char *a1)
{
__int64 result; // rax
int v2; // eax
int v3; // [rsp+28h] [rbp-18h]
int v4; // [rsp+3Ch] [rbp-4h]

v4 = check2(a1);
v3 = 16123822;
while ( 1 )
{
while ( v3 == 16123822 )
{
v2 = 1478060410;
if ( !v4 )
v2 = 1274132590;
v3 = v2;
}
result = (unsigned int)(v3 - 824643665);
if ( v3 == 824643665 )
break;
if ( v3 == 1274132590 )
{
v3 = 824643665;
printf("error!\n");
}
else
{
v3 = 824643665;
printf("you get it!\n");
}
}
return result;
}

check2

__int64 __fastcall check2(char *a1)
{
size_t v1; // rax
int v2; // ecx
int v3; // eax
int v4; // eax
int v5; // eax
int v6; // eax
int v7; // eax
int v8; // eax
int v9; // eax
int v11; // [rsp+8Ch] [rbp-C4h]
unsigned int v12; // [rsp+90h] [rbp-C0h]
int v13; // [rsp+94h] [rbp-BCh]
int v14; // [rsp+98h] [rbp-B8h]
int v15; // [rsp+9Ch] [rbp-B4h]
int v16[42]; // [rsp+A0h] [rbp-B0h]
char *s; // [rsp+148h] [rbp-8h]

s = a1;
v13 = 0;
v12 = 1;
v15 = 0;
v11 = -2671583;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( v11 == -2119125118 )
{
++v15;
v11 = -94879051;
}
if ( v11 != -1998111552 )
break;
v6 = 396170963;
if ( v15 < 9 )
v6 = -512482015;
v11 = v6;
}
if ( v11 != -1695072055 )
break;
++v15;
v11 = -1998111552;
}
if ( v11 != -1658909923 )
break;
v8 = -1129833658;
if ( D0g3[9 * v15 + v14] != sudoku[9 * v15 + v14] )// D0g3和数独比较
v8 = -528396247;
v11 = v8;
}
if ( v11 != -1613667829 )
break;
v11 = -2119125118;
}
if ( v11 != -1369143226 )
break;
v14 = 0;
v11 = -740861019;
}
if ( v11 != -1244045086 )
break;
D0g3[9 * v15 + v14] = v16[v13++];// 填充数组
v11 = 1611237474;
}
if ( v11 != -1129833658 )
break;
v11 = -90011013;
}
if ( v11 != -740861019 )
break;
v4 = -1613667829;
if ( v14 < 9 )
v4 = 705300330;
v11 = v4;
}
if ( v11 != -528396247 )
break;
v12 = 0;
v11 = 1954800504;
}
if ( v11 != -512482015 )
break;
v14 = 0;
v11 = 564268595;
}
if ( v11 != -334121999 )
break;
v15 = 0;
v11 = -1998111552;
}
if ( v11 != -94879051 )
break;
v3 = -334121999;
if ( v15 < 9 )
v3 = -1369143226;
v11 = v3;
}
if ( v11 != -90011013 )
break;
++v14;
v11 = 564268595;
}
if ( v11 != -2671583 )
break;
v1 = strlen(s);
v2 = 2101131376;
if ( v15 < v1 )
v2 = 441246003;
v11 = v2;
}
if ( v11 == 396170963 )
break;
switch ( v11 )
{
case 430996436:
++v15;
v11 = -2671583;
break;
case 441246003:
v16[v15] = s[v15] - 232084296 + 232084248;
v11 = 430996436;
break;
case 564268595:
v7 = 1954800504;
if ( v14 < 9 )
v7 = -1658909923;
v11 = v7;
break;
case 705300330:
v5 = 1611237474;
if ( !D0g3[9 * v15 + v14] )
v5 = -1244045086;
v11 = v5;
break;
case 1611237474:
v11 = 2119231421;
break;
case 1908623879:
v11 = -1695072055;
break;
case 1954800504:
v9 = 1908623879;
if ( !v12 )
v9 = 2014359934;
v11 = v9;
break;
case 2014359934:
v11 = 396170963;
break;
case 2101131376:
v15 = 0;
v11 = -94879051;
printf("\n");
break;
default:
++v14;
v11 = -740861019;
break;
}
}
return v12;
}

逆向函数

def check2():
v16 = []
for i in range(len(flag)):
v16.append(flag[v15] -48 )
#这个位置其实是数字得char和int转化
for i in range(9):
for j in range(9):
if dog3[9 * i + j] == 0:
dog3[9 *i + i] = v16[v13]
v13 += 1
#这里形成一个数独游戏得填入
for i in range(9):
for j in range(9):
if dog3[9 * i + j] != sudoku[9 * i + j]:
#注意这里
print("!!!")

d0g3可直接获取,最后根据check1逆向即可

sudoku = [1, 4, 5, 3, 2, 7, 6, 9, 8, 8, 3, 9, 6, 5, 4, 1, 2, 7, 6, 7, 2, 8, 1, 9, 5, 4, 3, 4, 9, 6, 1, 8, 5, 3, 7, 2, 2, 1, 8, 4, 7, 3, 9, 5, 6, 7, 5, 3, 2, 9, 6, 4, 8, 1, 3, 6, 7, 5, 4, 2, 8, 1, 9, 9, 8, 4, 7, 6, 1, 2, 3, 5, 5, 2, 1, 9, 3, 8, 7, 6, 4]
dog3 = [1, 0, 5, 3, 2, 7, 0, 0, 8, 8, 0, 9, 0, 5, 0, 0, 2, 0, 0, 7, 0, 0, 1, 0, 5, 0, 3, 4, 9, 0, 1, 0, 0, 3, 0, 0, 0, 1, 0, 0, 7, 0, 9, 0, 6, 7, 0, 3, 2, 9, 0, 4, 8, 0, 0, 6, 0, 5, 4, 0, 8, 0, 9, 0, 0, 4, 0, 0, 1, 0, 3, 0, 0, 2, 1, 0, 3, 0, 7, 0, 4]
arr = []
for i in range(81):
if sudoku[i] != dog3[i]:
tmp = ord(str(sudoku[i])) + 20
arr.append( tmp&0xf3 | ~tmp&0xc )
print(arr)
# print(len(arr))

for i in range(0,40,2):
(arr[i], arr[i+1]) = (arr[i+1], arr[i])
for i in range(20):
(arr[i],arr[i+20]) = (arr[i+20], arr[i])
for i in range(40):
print(chr(arr[i]),end='')

得到flag: KDEEIFGKIJ@AFGEJAEF@FDKADFGIJFA@FDE@JG@J

取反运算符逆向

tmp = ord(input())
print(bin(tmp))
print(bin(tmp & 0xf3))
print(bin(~tmp & 0xc))
print(bin((tmp & 0xf3)|(~tmp & 0xc)))
print(((tmp & 0xf3)|(~tmp & 0xc)))

解密OLLVM

说实话OLLVM混淆过的代码看不懂,这就需要解密了

其实在OLLVM混淆这片文章中写过了,这就不过多赘述了。