强网杯,哎…….我还是太菜了!

下面这些题还算是可以出的,就一起写了,后面的那些比较难的题我就慢慢复现吧!
ps:很抱歉之前的wp写的有点乱,这边重新整理一下

Babyre【强网先锋】

(xtea魔改)

先通过字符串定位到main函数

img

1
2
for ( j = 0; j < 4; ++j )
sub_14001106E(&v5[2 * j], &v5[2 * j + 1]);

有一个四次循环的调用函数,并且传入的参数是v5,v5之前有和input一起调用的函数,可能v5就是cmp的input

img

里面是一个xtea byte_14001E000里面的key也就是内容存放的地方:

img

但是经过代入脚本发现这个key有问题,可能是在函数运行的时候对key做了手脚

动调看看 因为程序在直接动调的时候怪怪的,所以就通过载入进程的方式调,然后找到xtea的位置进行查看,找到了key:

img

del和sum也找到了,在图里,密文如下

img

并且这个加密的调用方式也是四个一循环,然后2i和2i+1的方式进行加密,解密也这样就可以了 最终脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>

void decrypt(unsigned int* data, int key[]) {
int i, j;
unsigned int Y, Z, sum;
sum = 0xD192C263;
Y = *data;
Z = *(data + 1);

for (i = 0; i < 4; ++i) {
for (j = 0; j <= 32; ++j) {
sum += 0x77BF7F99;
Z -= (((32 * Y) ^ (Y >> 4)) + Y) ^ (sum + key[(sum >> 11) & 3]);
Y -= (((32 * Z) ^ (Z >> 4)) + Z) ^ (sum + key[sum & 3]) ^ sum;
}
}

*data = Y;
*(data + 1) = Z;
}

int main() {
int key[8] = { 0x62, 0x6F, 0x6D, 0x62, 0 , 0 , 0 , 0 };
unsigned int encryptedData[] = { 0x9523f2e0, 0x8ed8c293, 0x8668c393, 0xddf250bc, 0x510e4499, 0x8c60bd44, 0x34dcabf2, 0xc10fd260, 0x00000000 };

for (int i = 0; i < 4; i ++) {
decrypt((unsigned int*)&encryptedData[i*2], key);
}

printf("flag{%s}\n", (char*)encryptedData);

return 0;
}

flag{W31com3_2_Th3_QwbS7_4nd_H4v3_Fun}

ezre【强网先锋】

(ollvm控制流平坦化、base来回加解密、判断动态调试执行函数)

都是o简单的llvm控制流平坦化,先用脚本都把ollvm混淆去掉

img

这个函数是一个判断有没有进入调试状态,如果进入了调试状态,那么就会给

dword_4062C0 = 1 这个参数在后面也有在被用到

img

在后面进行操作,如果是调试的话就会对密文进行一个异或操作,以及改了很多次的码表,所以调试的话会和非调试的时候运行的代码不一样

然后就是一系列的base加解密

加密过程:

循环之前:

l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr

enc

0

FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8

dec

1

Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA

enc

2

pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a

dec

3

plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6

enc

然后通过手动计算出basetable的数值,即上面的字符串异或0x27

img

img

拿到新的table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <stdio.h>
#include <string.h>

int main() {
char a1[] = "WZqSWcUtWBLlOriEfcajWBSRstLlkEfFWR7j/R7dMCDGnp==";

char a2[100]; // 根据实际需求设置合适的大小
char v5[50];
char base_table[] = {0x57,0x4B,0x5F,0x7F,0x68,0x7D,0x53,0x46,0x4E,0x72,0x49,0x42,0x6D,0x6E,0x4F,0x4C,0x10,0x56,0x74,0x7E,0x62,0x4D,0x63,0x16,0x6C,0x4A,0x1E,0x13,0x48,0x17,0x61,0x73,0x52,0x12,0x15,0x71,0x76,0x40,0x69,0x6B,0x14,0x51,0x64,0x65,0x6F,0x1F,0x5D,0x54,0x66,0x08,0x45,0x0C,0x43,0x5E,0x44,0x60,0x77,0x75,0x6A,0x50,0x70,0x41,0x55,0x11};

int v6, v7, v8;
long long result;
int v3, v4;

char* s = a1;
char* v9 = a2;

v8 = strlen(a1);
//printf("v8=%d\n",v8);
v7 = 2023;
v6 = 0;
memset(v5, 0, sizeof(v5));
strncpy(v5, &base_table[6], 0x15);
int i;
printf("v5=");
for(i=0;i<strlen(v5);i++)
{
printf("0x%02X,",(unsigned int)v5[i] & 0xFF);
}
printf(" \n");
v5[21] = 0;
strcpy(v9, s);
printf("v3数组的数值是:");
while (1) {
result = 0x1AD711FD;

if ( v6 >= v8 - 1 )
break;
if ( v6 % 3 == 1 )
{
v7 = (v7 + 5) % 20;
v3 = v5[v7 + 1];
}
else if ( v6 % 3 == 2 )
{
v7 = (v7 + 7) % 19;
v3 = v5[v7 + 2];
}
else
{
v7 = (v7 + 3) % 17;
v3 = v5[v7 + 3];
}

v9[v6] ^= v3;
v4 = v9[v6++];
v9[v6] ^= v4;
printf("0x%x,",(unsigned int)v3 & 0xFF);
// printf("v6=%x v3=%x v4=%x\n",v6-1,(unsigned int)v3 & 0xFF,(unsigned int)v4 & 0xFF);
//printf("%d,",v3);
}

return 0;
}

通过c代码进行的加密复现,并且拿到正常运行情况下的v3和v5 然后python的解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def xor_array_backward(arr):
key = [0x6d,0x4c,0x16,0x49,0x6e,0x4d,0x4a,0x4e,0x10,0x62,0x16,0x6d,0x10,0x7e,0x4e,0x6d,0x4c,0x16,0x49,0x6e,0x4d,0x4a,0x4e,0x10,0x62,0x16,0x6d,0x10,0x7e,0x4e,0x6d,0x4c,0x16,0x49,0x6e,0x4d,0x4a,0x4e,0x10,0x62,0x16,0x6d,0x10,0x7e,0x4e,0x6d,0x4c]
arr[len(arr)-1]^=arr[len(arr)-2]
for i in range(len(arr) - 2, 0, -1):
xor_value = arr[i] ^ arr[i - 1] ^ key[i]
arr[i]=xor_value

# 添加数组的第一位
arr[0]^=0x6d
return arr

# 示例用法
input_array = [0x3A, 0x2C, 0x4B, 0x51, 0x68, 0x46, 0x59, 0x63, 0x24, 0x04,
0x5E, 0x5F, 0x00, 0x0C, 0x2B, 0x03, 0x29, 0x5C, 0x74, 0x70,
0x6A, 0x62, 0x7F, 0x3D, 0x2C, 0x4E, 0x6F, 0x13, 0x06, 0x0D,
0x06, 0x0C, 0x4D, 0x56, 0x0F, 0x28, 0x4D, 0x51, 0x76, 0x70,
0x2B, 0x05, 0x51, 0x68, 0x48, 0x55, 0x24, 0x19]
output_array = xor_array_backward(input_array)
print((output_array))
#[87, 90, 113, 83, 87, 99, 85, 116, 87, 66, 76, 108, 79, 114, 105, 69, 102, 99, 97, 106, 87, 66, 83, 82, 115, 116, 76, 108, 107, 69, 102, 70, 87, 82, 55, 106, 47, 82, 55, 100, 77, 67, 68, 71, 110, 112, 61, 61]

img

然后拿到赛博厨子上逆一下上面的base

img

拿到flag

去ollvm混淆

首先配置defalt环境,然后再去用指令 python defalt.py -f ezre –addr 0x0000(地址)

image-20231217210445763

这是去除之前的main函数,哎,一眼ollvm

流程图

image-20231217210520063

然后通过汇编,定位到这个函数的入口的posh点

image-20231217210613168

这里就是4025F0

指令:python deflat.py -f ezre –addr 0x4025f0(其他地方的类似)

image-20231217211219940

逆完之后的控制流就恢复正常了

image-20231217211407898

代码也是差不多可以看了!

image-20231217211429573

代码分析

都是简单的llvm控制流平坦化,先用脚本都把ollvm混淆去掉

img

这个函数是一个判断有没有进入调试状态,如果进入了调试状态,那么就会给

dword_4062C0 = 1 这个参数在后面也有在被用到

img

在后面进行操作,如果是调试的话就会对密文进行一个异或操作,以及改了很多次的码表,所以调试的话会和非调试的时候运行的代码不一样

然后就是一系列的base加解密

加密过程:

循环之前:

l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr

enc

0

FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8

dec

1

Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA

enc

2

pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a

dec

3

plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6

enc

然后通过手动计算出basetable的数值,即上面的字符串异或0x27

img

img

拿到新的table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <stdio.h>
#include <string.h>

int main() {
char a1[] = "WZqSWcUtWBLlOriEfcajWBSRstLlkEfFWR7j/R7dMCDGnp==";

char a2[100]; // 根据实际需求设置合适的大小
char v5[50];
char base_table[] = {0x57,0x4B,0x5F,0x7F,0x68,0x7D,0x53,0x46,0x4E,0x72,0x49,0x42,0x6D,0x6E,0x4F,0x4C,0x10,0x56,0x74,0x7E,0x62,0x4D,0x63,0x16,0x6C,0x4A,0x1E,0x13,0x48,0x17,0x61,0x73,0x52,0x12,0x15,0x71,0x76,0x40,0x69,0x6B,0x14,0x51,0x64,0x65,0x6F,0x1F,0x5D,0x54,0x66,0x08,0x45,0x0C,0x43,0x5E,0x44,0x60,0x77,0x75,0x6A,0x50,0x70,0x41,0x55,0x11};

int v6, v7, v8;
long long result;
int v3, v4;

char* s = a1;
char* v9 = a2;

v8 = strlen(a1);
//printf("v8=%d\n",v8);
v7 = 2023;
v6 = 0;
memset(v5, 0, sizeof(v5));
strncpy(v5, &base_table[6], 0x15);
int i;
printf("v5=");
for(i=0;i<strlen(v5);i++)
{
printf("0x%02X,",(unsigned int)v5[i] & 0xFF);
}
printf(" \n");
v5[21] = 0;
strcpy(v9, s);
printf("v3数组的数值是:");
while (1) {
result = 0x1AD711FD;

if ( v6 >= v8 - 1 )
break;
if ( v6 % 3 == 1 )
{
v7 = (v7 + 5) % 20;
v3 = v5[v7 + 1];
}
else if ( v6 % 3 == 2 )
{
v7 = (v7 + 7) % 19;
v3 = v5[v7 + 2];
}
else
{
v7 = (v7 + 3) % 17;
v3 = v5[v7 + 3];
}

v9[v6] ^= v3;
v4 = v9[v6++];
v9[v6] ^= v4;
printf("0x%x,",(unsigned int)v3 & 0xFF);
// printf("v6=%x v3=%x v4=%x\n",v6-1,(unsigned int)v3 & 0xFF,(unsigned int)v4 & 0xFF);
//printf("%d,",v3);
}

return 0;
}

通过c代码进行的加密复现,并且拿到正常运行情况下的v3和v5 然后python的解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def xor_array_backward(arr):
key = [0x6d,0x4c,0x16,0x49,0x6e,0x4d,0x4a,0x4e,0x10,0x62,0x16,0x6d,0x10,0x7e,0x4e,0x6d,0x4c,0x16,0x49,0x6e,0x4d,0x4a,0x4e,0x10,0x62,0x16,0x6d,0x10,0x7e,0x4e,0x6d,0x4c,0x16,0x49,0x6e,0x4d,0x4a,0x4e,0x10,0x62,0x16,0x6d,0x10,0x7e,0x4e,0x6d,0x4c]
arr[len(arr)-1]^=arr[len(arr)-2]
for i in range(len(arr) - 2, 0, -1):
xor_value = arr[i] ^ arr[i - 1] ^ key[i]
arr[i]=xor_value

# 添加数组的第一位
arr[0]^=0x6d
return arr

# 示例用法
input_array = [0x3A, 0x2C, 0x4B, 0x51, 0x68, 0x46, 0x59, 0x63, 0x24, 0x04,
0x5E, 0x5F, 0x00, 0x0C, 0x2B, 0x03, 0x29, 0x5C, 0x74, 0x70,
0x6A, 0x62, 0x7F, 0x3D, 0x2C, 0x4E, 0x6F, 0x13, 0x06, 0x0D,
0x06, 0x0C, 0x4D, 0x56, 0x0F, 0x28, 0x4D, 0x51, 0x76, 0x70,
0x2B, 0x05, 0x51, 0x68, 0x48, 0x55, 0x24, 0x19]
output_array = xor_array_backward(input_array)
print((output_array))
#[87, 90, 113, 83, 87, 99, 85, 116, 87, 66, 76, 108, 79, 114, 105, 69, 102, 99, 97, 106, 87, 66, 83, 82, 115, 116, 76, 108, 107, 69, 102, 70, 87, 82, 55, 106, 47, 82, 55, 100, 77, 67, 68, 71, 110, 112,

img

然后拿到赛博厨子上逆一下上面的base

img

拿到flag

ezre【强网杯】

这题又是ollvm混淆,而且控制流更加离谱了

image-20231218181515764

一个是main函数的比较简单的混淆,然后再通过字符串索引,找到了一个更混乱的控制流

image-20231218181854218

好玩,爱玩!

image-20231218181955741

D810插件用起来怪怪的而且我个人编译起来特别卡(特别是动调的时候,非常卡)

还得是defalt,找到相对应的函数入口地址然后直接用angr跑一下了

但是这里的话是39F0,试过去ollvm但是没成功,问了问大佬才知道,原来是有偏移,一般来说前面都会有40为基地址,所以直接去去掉0x4039f0就可以了

python deflat.py -f chall –addr 0x4039F0

image-20231218182623228

程序的控制流很乱,得跑上一段时间

最喜欢看到的!

image-20231218183054196

main函数也顺便去一下

python deflat.py -f chall –addr 0x405290

还有一个地方,反正有混淆就去就行了

python deflat.py -f chall –addr 0x403580

image-20231218183803893

恢复了

但是巡视一圈,发现没啥加密,就有一个SM4加密
这里有一个好玩的地方:我是用defalt和D810去混淆,结果给我的结果不一样,哎,好像defalt的结果有点问题,但是好在影响不是很大:

defalt:

image-20231218191514196

D810:

image-20231218191526786

还得是b810,就按照D810的来分析了

SM4国产加密的特长,国产加密yyds

image-20231218192918959

v8很明显是秘钥,v7是密文,提取出来直接拿到赛博厨子直接梭哈

image-20231218194739438

半自动化转化端绪脚本:

顺便贴上一个端绪转化的半自动化脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def endian_conversion(hex_value):
byte_sequence = bytes.fromhex(hex_value)
reversed_byte_sequence = byte_sequence[::-1]
reversed_hex_value = reversed_byte_sequence.hex()
return reversed_hex_value.upper()

conversion_results = []

while True:
input_hex = input("请输入要进行端序转换的十六进制数(输入 'q' 退出):")
if input_hex.lower() == 'q':
break
output_hex = endian_conversion(input_hex)
conversion_results.append(output_hex)
print("转换结果:", output_hex)

result_string = ''.join(conversion_results)
print("所有转换结果:", result_string)

要不然一个一个转的话真的累了!