网鼎杯青龙组部分RE WP

[网鼎杯 2020 青龙组]jocker

1
2
3
4
5
6
7
8
9
10
11
12
13
cmp=[0x00000066, 0x0000006B, 0x00000063, 0x00000064, 0x0000007F, 0x00000061, 0x00000067, 0x00000064, 0x0000003B, 0x00000056, 0x0000006B, 0x00000061, 0x0000007B, 0x00000026, 0x0000003B, 0x00000050, 0x00000063, 0x0000005F, 0x0000004D, 0x0000005A, 0x00000071, 0x0000000C, 0x00000037, 0x00000066, 0x00000000]
flag=[0]*25
flag_str=""
for i in range (24):

if((i&1)!=0):
flag[i]=cmp[i]+i
else:
flag[i]=cmp[i]^i
flag_str+=chr(flag[i])
print(flag_str)


获得

flag{fak3_alw35_sp_me!!}

image-20240617180033775

image-20240617180012150

之前的步骤需要处理一下IDA识别里面的堆栈不平衡的问题

其实不解决这个问题也能做,但是遇到了就学下吧

来源:

IDA为什么产生 sp-analysis failed 错误?-CSDN博客

sp-analysis failed-CSDN博客

总结一下:

1、确定本函数的栈大小、传入参数数量、是否保存寄存器,对本函数做响应修改

img

2、在本函数内部分析每一行的栈指针,看ida有没识别错误的,如果有识别错误的通过Alt+K修改过来

这个地方还要注意一下最开始得到假flag的时候提取数据的时候没有去注意具体的数据的类型

[网鼎杯 2020 青龙组]singal

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<windows.h>

using namespace std;//注意要把中间的数据0都去掉,避免出现死循环
unsigned char opcode[114] = {
0x0000000A, 0x00000004, 0x00000010, 0x00000008, 0x00000003, 0x00000005, 0x00000001, 0x00000004,
0x00000020, 0x00000008, 0x00000005, 0x00000003, 0x00000001, 0x00000003, 0x00000002, 0x00000008,
0x0000000B, 0x00000001, 0x0000000C, 0x00000008, 0x00000004, 0x00000004, 0x00000001, 0x00000005,
0x00000003, 0x00000008, 0x00000003, 0x00000021, 0x00000001, 0x0000000B, 0x00000008, 0x0000000B,
0x00000001, 0x00000004, 0x00000009, 0x00000008, 0x00000003, 0x00000020, 0x00000001, 0x00000002,
0x00000051, 0x00000008, 0x00000004, 0x00000024, 0x00000001, 0x0000000C, 0x00000008, 0x0000000B,
0x00000001, 0x00000005, 0x00000002, 0x00000008, 0x00000002, 0x00000025, 0x00000001, 0x00000002,
0x00000036, 0x00000008, 0x00000004, 0x00000041, 0x00000001, 0x00000002, 0x00000020, 0x00000008,
0x00000005, 0x00000001, 0x00000001, 0x00000005, 0x00000003, 0x00000008, 0x00000002, 0x00000025,
0x00000001, 0x00000004, 0x00000009, 0x00000008, 0x00000003, 0x00000020, 0x00000001, 0x00000002,
0x00000041, 0x00000008, 0x0000000C, 0x00000001, 0x00000007, 0x00000022, 0x00000007, 0x0000003F,
0x00000007, 0x00000034, 0x00000007, 0x00000032, 0x00000007, 0x00000072, 0x00000007, 0x00000033,
0x00000007, 0x00000018, 0x00000007, 0xFFFFFFA7, 0x00000007, 0x00000031, 0x00000007, 0xFFFFFFF1,
0x00000007, 0x00000028, 0x00000007, 0xFFFFFF84, 0x00000007, 0xFFFFFFC1, 0x00000007, 0x0000001E,
0x00000007, 0x0000007A
};

unsigned char input[1024]="flag{hbcacjnicejwklmlkmkmbXHcb}";

// unsigned char opcode[114]={0};



int result; // eax



int main(void){

int i;
//for(i=0;i<456;i++)
//{
// memcpy(&opcode[i],&ida_chars[i*4],4);
// }
char v4; // [esp+DBh] [ebp-1Dh]
int v5; // [esp+DCh] [ebp-1Ch]
int v6; // [esp+E0h] [ebp-18h]
int v7; // [esp+E4h] [ebp-14h]
int v8; // [esp+E8h] [ebp-10h]
int _eip; // [esp+ECh] [ebp-Ch]

_eip = 0;
v8 = 0;
v7 = 0;
v6 = 0;
v5 = 0;

while ( 1 )
{

if ( _eip >= 114 )
return 0;
switch ( opcode[_eip] )
{
case 1:
printf("%x\t#1:\t input[%d]=%d;\n",_eip,v6,v4);
input[v6 + 100] = v4;
++_eip;
++v6;
++v8;
break;
case 2:
printf("%x\t#2:\t v4(%d)=opcode[%d](%d)+input[%d](%d)\n",_eip,opcode[_eip+1]+input[v8],_eip+1,opcode[_eip+1],v8,input[v8]);
v4 = opcode[_eip + 1] + input[v8];
_eip += 2;
break;
case 3:
printf("%x\t#3:\t v4(%d)=input[%d](%d)-opcode[%d](%d)\n",_eip,input[v8]-opcode[_eip+1],v8,input[v8],_eip+1,opcode[_eip+1]);
v4 = input[v8] - opcode[_eip + 1];
_eip += 2;
break;
case 4:
printf("%x\t#4:\t v4(%d)=opcode[%d](%d)^input[%d](%d)\n",_eip,opcode[_eip+1]^input[v8],_eip+1,opcode[_eip+1],v8,input[v8]);
v4 = opcode[_eip + 1] ^ input[v8];
_eip += 2;
break;
case 5:
printf("%x\t#5:\t v4(%d)=opcode[%d](%d)*input[%d](%d)\n",_eip,opcode[_eip+1]*input[v8],_eip+1,opcode[_eip+1],v8,input[v8]);
v4 = opcode[_eip + 1] * input[v8];
_eip += 2;
break;
case 6:
printf("%x\t#6:\t eip++\n",_eip);
++_eip;
break;
case 7:
printf("%x\t #7:\t input[%d](%d)!=opcode[%d](%d)\n",_eip,v7+100,input[v7+100],_eip+1,opcode[_eip+1]);
if ( input[v7 + 100] != opcode[_eip + 1] )
{
printf("what a shame...\n\n");
//exit(0);
}
++v7;
_eip += 2;
break;
case 8:
printf("%x\t#8:\t input[%d]=v4(%d)\n",_eip,v5,v4);
input[v5] = v4;
++_eip;
++v5;
break;
case 10:
printf("%x\t#10:\tinput flag\n",_eip);
//read(input);
++_eip;
break;
case 11:
printf("%x\t#11:\t v4(%d)=input[%d]-1\n",_eip,input[v8]-1,v8);
v4 = input[v8] - 1;
++_eip;
break;
case 12:
printf("%x\t#12:\t v4(%d)=input[%d]+1\n",_eip,input[v8]+1,v8);
v4 = input[v8] + 1;
++_eip;
break;
default:
continue;
}
}

}

运行得到结果:

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
0       #10:    input flag
1 #4: v4(118)=opcode[2](16)^input[0](102)
3 #8: input[0]=v4(118)
4 #3: v4(113)=input[0](118)-opcode[5](5)
6 #1: input[0]=113;

7 #4: v4(76)=opcode[8](32)^input[1](108)
9 #8: input[1]=v4(76)
a #5: v4(228)=opcode[11](3)*input[1](76)
c #1: input[1]=-28;

d #3: v4(95)=input[2](97)-opcode[14](2)
f #8: input[2]=v4(95)
10 #11: v4(94)=input[2]-1
11 #1: input[2]=94;

12 #12: v4(104)=input[3]+1
13 #8: input[3]=v4(104)
14 #4: v4(108)=opcode[21](4)^input[3](104)
16 #1: input[3]=108;

17 #5: v4(369)=opcode[24](3)*input[4](123)
19 #8: input[4]=v4(113)
1a #3: v4(80)=input[4](113)-opcode[27](33)
1c #1: input[4]=80;

1d #11: v4(103)=input[5]-1
1e #8: input[5]=v4(103)
1f #11: v4(102)=input[5]-1
20 #1: input[5]=102;

21 #4: v4(107)=opcode[34](9)^input[6](98)
23 #8: input[6]=v4(107)
24 #3: v4(75)=input[6](107)-opcode[37](32)
26 #1: input[6]=75;

27 #2: v4(180)=opcode[40](81)+input[7](99)
29 #8: input[7]=v4(-76)
2a #4: v4(144)=opcode[43](36)^input[7](180)
2c #1: input[7]=-112;

2d #12: v4(98)=input[8]+1
2e #8: input[8]=v4(98)
2f #11: v4(97)=input[8]-1
30 #1: input[8]=97;

31 #5: v4(198)=opcode[50](2)*input[9](99)
33 #8: input[9]=v4(-58)
34 #2: v4(235)=opcode[53](37)+input[9](198)
36 #1: input[9]=-21;

37 #2: v4(160)=opcode[56](54)+input[10](106)
39 #8: input[10]=v4(-96)
3a #4: v4(225)=opcode[59](65)^input[10](160)
3c #1: input[10]=-31;

3d #2: v4(142)=opcode[62](32)+input[11](110)
3f #8: input[11]=v4(-114)
40 #5: v4(142)=opcode[65](1)*input[11](142)
42 #1: input[11]=-114;

43 #5: v4(315)=opcode[68](3)*input[12](105)
45 #8: input[12]=v4(59)
46 #2: v4(96)=opcode[71](37)+input[12](59)
48 #1: input[12]=96;

49 #4: v4(106)=opcode[74](9)^input[13](99)
4b #8: input[13]=v4(106)
4c #3: v4(74)=input[13](106)-opcode[77](32)
4e #1: input[13]=74;

4f #2: v4(166)=opcode[80](65)+input[14](101)
51 #8: input[14]=v4(-90)
52 #12: v4(167)=input[14]+1
53 #1: input[14]=-89;

54 #7: input[100](113)!=opcode[85](34)
what a shame...

56 #7: input[101](228)!=opcode[87](63)
what a shame...

58 #7: input[102](94)!=opcode[89](52)
what a shame...

5a #7: input[103](108)!=opcode[91](50)
what a shame...

5c #7: input[104](80)!=opcode[93](114)
what a shame...

5e #7: input[105](102)!=opcode[95](51)
what a shame...

60 #7: input[106](75)!=opcode[97](24)
what a shame...

62 #7: input[107](144)!=opcode[99](167)
what a shame...

64 #7: input[108](97)!=opcode[101](49)
what a shame...

66 #7: input[109](235)!=opcode[103](241)
what a shame...

68 #7: input[110](225)!=opcode[105](40)
what a shame...

6a #7: input[111](142)!=opcode[107](132)
what a shame...

6c #7: input[112](96)!=opcode[109](193)
what a shame...

6e #7: input[113](74)!=opcode[111](30)
what a shame...

70 #7: input[114](167)!=opcode[113](122)
what a shame...

只有case7处是在直接比较输入的数据和最后已知的opcode的指令(opcode[i+1])

那么就是取opcode指令里面的是7的下一个数据

下面得到的[34,63,52,50,114,51,24,167,49,241,40,132,193,30,122]数据则为输入正确的flag以后经过处理最后得到的数据

然后再根据最后每一位得到的input挨个推出就行

也可以选择angr一把梭

1
2
3
4
5
6
7
8
9
import angr
project = angr.Project('signal.exe') #创建项目,加载二进制文件
state = project.factory.entry_state() #创建state
sim = project.factory.simgr(state) #创建sim
sim.explore(find=0x40175e,avoid=0x4016e6) # 希望到达的和避免的分支
if sim.found:
res = sim.found[0]
res = res.posix.dumps(0)
print("[+] Success! Solution is: {}".format(res.decode("utf-8")))

详情

[BUUCTF-网鼎杯 2020 青龙组]singal——angr学习记录_退出angr环境-CSDN博客

image-20240619010216074

注意angr脚本中的地址

此处判断循环结束,故find地址为0x40175e,避免出现what a shame…,故avoid地址为0x4016e6

image-20240619012543353

[网鼎杯 2022 青龙组]fakeshell

image-20240619013219664

image-20240619124204767

image-20240619124238057

UPX防脱壳机脱壳、去除特征码、添加花指令小探 - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

UPX壳的修改 - Clovershrub - 博客园 (cnblogs.com)

里面有的upx标志位写的是FUK0和FUK1,分别修改为UPX0和UPX1,55 50 58 30,55 50 58 31

【C/C++ 跳转函数】setjmp 和 longjmp 函数的巧妙运用: C 语言错误处理实践-CSDN博客

j___intrinsic_setjmp

1
2
setjmp:保存当前程序的执行状态,并返回一个标记,用于后续判断是第一次调用还是通过 longjmp 返回。
longjmp:将程序状态恢复到之前由 setjmp 所保存的状态,从而直接跳转到 setjmp 处继续执行。

使用事例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
jmp_buf env;

int main() {
int ret = ___intrinsic_setjmp(env, NULL);
if (ret == 0) {
// Normal execution path
printf("Setting jump point...\n");
some_function(); // Calls longjmp to env
} else {
// After longjmp has jumped back here
printf("Returned from longjmp!\n");
}
return 0;
}

void some_function() {
printf("Jumping to setjmp point...\n");
longjmp(env, 1);
}

exp:

1
2
3
4
5
6
7
8
cmp=[0x0000004B, 0x00000048, 0x00000079, 0x00000013, 0x00000045, 0x00000030, 0x0000005C, 0x00000049, 0x0000005A, 0x00000079, 0x00000013, 0x00000070, 0x0000006D, 0x00000078, 0x00000013, 0x0000006F, 0x00000048, 0x0000005D, 0x00000064, 0x00000064]
key=[0]*20
flag=""
for i in range (20):
key[i]=(cmp[i]^0x50)-10
key[i]^=0x66
flag+=chr(key[i])
print(flag)

[2022网鼎杯青龙组部分wp–re两题(challenge,Fakeshell)_2022网鼎杯青龙组]fakeshell题目下载-CSDN博客

网鼎杯2022 青龙组 Fakeshell | Fight for the good in your heart. (mykonos-x.github.io)

image-20240619152538434

[网鼎杯 2022 青龙组]Handmake

image-20240619173005574

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
jjxXf=[37, 73, 151, 135, 65, 58, 241, 90, 33, 86, 71, 41, 102, 241, 213, 234, 67, 144, 139, 20, 112, 150, 41, 7, 158, 251, 167, 249, 24, 129, 72, 64, 83, 142, 166, 236, 67, 18, 211, 100, 91, 38, 83, 147, 40, 78, 239, 113, 232, 83, 227, 47, 192, 227, 70, 167, 201, 249, 156, 101, 216, 159, 116, 210, 152, 234, 38, 145, 198, 58, 24, 183, 72, 143, 136, 234, 246]
KdlaH=[191, 140, 114, 245, 142, 55, 190, 30, 161, 18, 200, 7, 21, 59, 17, 44, 34, 181, 109, 116, 146, 145, 189, 68, 142, 113, 0, 33, 46, 184, 21, 33, 66, 99, 124, 167, 201, 88, 133, 20, 211, 67, 133, 250, 62, 28, 138, 229, 105, 102, 125, 124, 208, 180, 50, 146, 67, 39, 55, 240, 239, 203, 230, 142, 20, 90, 205, 27, 128, 136, 151, 140, 222, 92, 152, 1, 222, 138, 254, 246, 223, 224, 236, 33, 60, 170, 189, 77, 124, 72, 135, 46, 235, 17, 32, 28, 245]
spVwk=[108, 39, 231, 242, 53, 26, 133, 50, 68, 118, 33, 64, 20, 130, 161, 202, 37, 229, 229, 119, 4, 255, 70, 105, 178, 219, 208, 145, 113, 226, 32, 96, 59, 239, 213, 204, 117, 50, 163, 5, 41, 71, 62, 246, 92, 43, 157, 2, 200, 50, 141, 75, 224, 151, 46, 194, 233, 141, 244, 12, 170, 251, 84, 188, 249, 135, 67, 245, 230, 93, 84, 254, 32, 221, 178, 202, 252]
out1=[0]*78
out2=[0]*98
input1=""
input2=""
for i in range(len(jjxXf)):
out1[i] = spVwk[i] ^ jjxXf[i]
input1+=chr(out1[i])
print(input1)
jutvH=[246, 226, 2, 128, 250, 23, 202, 118, 196, 50, 187, 98, 118, 84, 127, 72, 2, 211, 24, 26, 241, 229, 212, 43, 224, 93, 32, 86, 70, 209, 118, 73, 98, 11, 29, 212, 233, 107, 165, 119, 178, 47, 233, 159, 76, 111, 170, 132, 7, 2, 93, 21, 190, 194, 93, 249, 38, 84, 23, 132, 135, 174, 198, 232, 97, 52, 174, 111, 233, 231, 249, 172, 176, 61, 245, 100, 186, 170, 157, 190, 133, 150, 217, 78, 76, 146, 207, 2, 17, 36, 198, 69, 137, 39, 26, 60, 255]
for i in range (96):
out2[i] = jutvH[i] ^ KdlaH[i]
input2+=chr(out2[i])
print(input2)

得到提示

1
2
3
4
5
Input the first function, which has 6 parameters and the third named gLIhR: 
输入第一个函数,该函数有6个参数,第三个参数名为 gLIhR
Input the second function, which has 3 callers and invokes the function named cHZv5op8rOmlAkb6:
输入第二个函数,该函数有3个调用者,并调用名为 cHZv5op8rOmlAkb6 的函数

image-20240619174701813

针对提示要找的第一个函数有6个参数第三个参数是“gLIhR”,可以使用正则表达式

1
func \w+\(\w+\s+\[\]\w+,\s+\w+\s+\w+,\s+gLIhR\s+\w+,\s+\w+\s+\w+,\s+\w+\s+\w+,\s+\w+\s+\w+\)

\w代表一个单词字符,匹配以下字符:

  • 所有大小写字母(A-Z,a-z)
  • 所有数字(0-9)
  • 下划线(_)

\s代表空白字符,匹配以下字符:

  • 空格
  • 制表符
  • 换行符
  • 回车符
  • 垂直制表符
  • 换页符

参考:网鼎杯2022 青龙组 Handmake | Fight for the good in your heart. (mykonos-x.github.io)

第一个函数:ZlXDJkH3OZN4Mayd

1
2
3
4
5
6
7
8
9
10
func ZlXDJkH3OZN4Mayd(swlvV []byte, S5aOL string, gLIhR string, QIErK string, mxqoA string, hTBet string) string {
eBS9X := make([]byte, 20)
WX5YT := [16]byte{
169, 43, 100, 115, 202, 185, 130, 174, 238, 233, 147, 43, 149, 7, 229, 119}
for i := 0; i < 16; i++ {
eBS9X[i] += WX5YT[i] ^ swlvV[i]
}

return string(eBS9X)
}
1
2
3
4
YrXQd = bytes.hex('ZlXDJkH3OZN4Mayd'.encode())
flag1 = YrXQd[22] + YrXQd[19] + YrXQd[20] + YrXQd[21] + YrXQd[28] + YrXQd[10] + YrXQd[20] + YrXQd[7] + YrXQd[29] + YrXQd[14] + YrXQd[0] + YrXQd[18] + YrXQd[3] + YrXQd[24] + YrXQd[27] + YrXQd[31]

print(flag1)
1
flag1:3a4e76449355c414

第二个函数:UhnCm82SDGE0zLYO

1
2
3
4
5
6
func UhnCm82SDGE0zLYO() string {
SythK := []byte{
159, 141, 72, 106, 196, 62, 16, 205, 170, 159, 36, 232, 125, 239, 208, 3}
var Vw2mJ, Nij87, zVclR string
return cHZv5op8rOmlAkb6(SythK, Vw2mJ, Nij87, zVclR)
}
1
2
3
4
5
6
7
8
9
10
11
func cHZv5op8rOmlAkb6(HIGXt []byte, VGvny string, ZOkKV string, eU0uD string) string {
QTk4l := make([]byte, 20)
Ek08m := [16]byte{
167, 238, 45, 89, 160, 95, 34, 175, 158, 169, 20, 217, 68, 137, 231, 54}
for i := 0; i < 16; i++ {
QTk4l[i] += Ek08m[i] ^ HIGXt[i]
}

return string(QTk4l)
}

最后异或得到结果

1
2
3
4
5
6
7
Ek08m = [167, 238, 45, 89, 160, 95, 34, 175, 158, 169, 20, 217, 68, 137, 231, 54]
Sythk = [159, 141, 72, 106, 196, 62, 16, 205, 170, 159, 36, 232, 125, 239, 208, 3]
ans = []
for i in range(len(Ek08m)):
ans.append(Ek08m[i] ^ Sythk[i])
for i in ans:
print(chr(i), end="")
1
flag2:8ce3da2b46019f75

flag{3a4e76449355c4148ce3da2b46019f75}

网上查看别人的wp发现其实可以通过直接对go文件进行编译,或者直接运行,就可以在终端直接获得上述提示输出,然后直接找到对应的函数进行输入就可以得到正确的flag

(这里要注意一个细节,源码里面的scanf中间没有fmt.Scanln(),如果直接输入第一个函数回车然后输入第二个函数的时候会把回车当成第二个输入然后直接退出程序运行)

image-20240619210359330


网鼎杯青龙组部分RE WP
http://example.com/2024/06/17/[网鼎杯 2020 青龙组]jocker/
作者
John Doe
发布于
2024年6月17日
许可协议