re5-packed-movement

先脱壳
img
ida打开全是mov 指令,movfuscator混淆
img

法一

shift+F12 搜索字符串找到’Wrong Flag!',
交叉引用可以看到有70多处引用,可能就是逐字符比较
img
随便点一个看看,从每一个’Wrong Flag!'处往上翻可以看到都有一个
mov R2 xxh的指令
img
img
直接搜索R2寄存器内容
Alt+B搜索C7 05 68 20 06 08即mov R2对应的16进制编码
img
得到flagALEXCTF{M0Vfusc4t0r_w0rk5_l1ke_m4g1c}
img

法二

使用idac脚本。
shift+F2打开脚本界面
img
运行得到flag
img

flag

ALEXCTF{M0Vfusc4t0r_w0rk5_l1ke_m4g1c}

reverse-for-the-holy-grail-350

直接看主函数,有点多重点锁定这几个语句
v4 = stringmod(v9),v4小于0就输出auuuuuuugh,大于0才输出tuctf{}
应该就是v4>0时输出flag,跟进一下stringmod
img
stringmod有三个部分先看第一个
img
v4要大于0所以if 要不执行,那么v3就要是3的倍数,并且
v12读取的字符必须等于firstchar[v3 / 3]
img
第二部分是一个异或
img
第三部分
img
查看一下thirdchar和masterArray,有点乱不好提取
img
用idapython导出一下

1
2
3
4
5
6
7
from idc_bc695 import *
addr=[0x601840,0x601860,0x601880]
for a in addr:
list=[]
for i in range(6):
list.append(Dword(a+4*i))
print(list)

前三行时对应16进制结果,后三行则是10进制
img

exp

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
firstchar = [0x41, 0x69, 0x6e, 0x45, 0x6f, 0x61] 
thirdchar = [0x2ef, 0x2c4, 0x2dc, 0x2c7, 0x2de, 0x2fc]
masterarray = [0x1d7, 0xc, 0x244, 0x25e, 0x93, 0x6c]


list = [666,]
v7 = 666
for i in range(17):
v7 = v7 + v7 % 5
list.append(v7)
print(list)

#求flag第0,3,6,9,12,15位
flag = [0 for i in range(18)]
index1 = 0
for i in range(0,16,3):
flag[i] = firstchar[index1]
index1 += 1
# print(flag)
#求第2,5,8,11,14,17位
index2=0
for i in range(2,18,3):
flag[i] = list[i] ^ thirdchar[index2]
index2 += 1
# print(flag)
# 暴力求第1,4,7,10,13,16位
index3 = 0
for i in range(1,17,3):
for j in range(32,127):
if ((list[i-1] ^ firstchar[index3]) * (list[i] ^ j)) % thirdchar[index3] == masterarray[index3]:
flag[i] = j
index3 +=1
break
print('tuctf{'+"".join(map(chr,flag))+'}')
#[666, 667, 669, 673, 676, 677, 679, 683, 686, 687, 689, 693, 696, 697, 699, 703, 706, 707]
#tuctf{AfricanOrEuropean?}

flag

tuctf{AfricanOrEuropean?}

[XCTF]reverse_box

题目描述

挑战描述
$ ./reverse_box ${FLAG}
95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a
flag格式:TWCTF{}

分析

主函数很短,简单分析如下
img
运行一下可以发现,输入相同的flag输出不同,说明v4是动态变化的
img
跟进sub_804858D函数,可以看到是根据时间生成随机数种子v1
再生成随机数,构建出v4
img

法一

通过查看汇编代码,知道随机生成的范围不超过0xff,那这个构建v4的函数应该可以模拟出来。现在需要知道的就是__ROR1__的作用
img
这是一个IDA内置定义的函数,通过查看ida/plugins/defs.h查看__ROR1__定义
img
可以看到这是一个调用__ROL__的函数,查找__ROL__
img

有两个参数:(value, int count)
第一个参数为左移的数,第二个参数为左移的位数。
如果第二个参数值为负数,则实际上为循环右移 -count位。
该函数的实现细节为:
先得到value的位数,然后count对位数取模。
如果count值大于0,则先右移-count取模的结果,然后在左移取模的结果,得到的两个数相或,即为循环左移的结果。
如果count值小于0,先左移在右移即可。
举例来说: value = 0110, count = 6
value为4位数, 6 % 4 = 2,
0110先右移4-2=2位,得到0001,然后在左移2位,得到1000,0001 | 1000结果为1001,即循环左移结果为1001。

其实简单理解就是把所有位都向左移。最高位复制到进位标志位和最低位。
知道了__ROR1__的作用,接下来根据提示输入flag会输出
95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a
而flag格式为TWCTF{}对应的ASCII码是84,87,67,84,70,123,125
可以推出v4[84]=0x95,v4[87]=0xee,v4[67]=0xaf,v4[84]=0x95,v4[70]=0xef,v4[123]=0x94,v4[125]=0x4a,这就是我们模拟爆破出v4的依据
最后根据表反推出flag

exp

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int __ROR1__(unsigned __int8 value,int x){
value= (value>>x) | (value<<(8-x));
return value;
}

int main(){
unsigned int v1; // eax
char v2; // al
char v3; // al
int v4; // ecx
int v5; // eax
int v6; // ecx
int v7; // eax
int v8; // ecx
int v9; // eax
int v10; // ecx
int result; // eax
char v12; // [esp+1Ah] [ebp-Eh]
unsigned __int8 v13; // [esp+1Bh] [ebp-Dh]
char v14; // [esp+1Bh] [ebp-Dh]
char v15; // [esp+1Bh] [ebp-Dh]
int v16; // [esp+1Ch] [ebp-Ch]
unsigned __int8 a1[256]={0};
int i;
for(i=1; i<256; i++){
memset(a1,0,sizeof(a1));
a1[0] = i;
v12 = 1;
v13 = 1;
do
{
if ( v12 >= 0 )
v2 = 0;
else
v2 = 27;
v12 ^= (2 * v12) ^ v2;
v14 = (4 * ((2 * v13) ^ v13)) ^ (2 * v13) ^ v13;
v15 = (16 * v14) ^ v14;
if ( v15 >= 0 )
v3 = 0;
else
v3 = 9;
v13 = v15 ^ v3;
v4 = v13;
v4 = __ROR1__(v13, 7);
v5 = v4 ^ (v13 ^ *a1);
v6 = v13;
v6 = __ROR1__(v13, 6);
v7 = v6 ^ v5;
v8 = v13;
v8 = __ROR1__(v13, 5);
v9 = v8 ^ v7;
v10 = v13;
v10 = __ROR1__(v13, 4);
result = v10 ^ v9;
a1[v12] = result;
}while ( v12 != 1 );
if(a1[84]==0x95 && a1[87]==0xee && a1[67]==0xaf && a1[84]==0x95 && a1[70]==0xef && a1[123]==0x94 && a1[125]==0x4a){
printf("随机种子为%d\n",i);
break;
}
}
for(i=0; i<256; i++){
if(i%16 == 0) printf("\n");
printf("0x%02x ",a1[i]);

}
}
/*
随机种子为214

0xd6 0xc9 0xc2 0xce 0x47 0xde 0xda 0x70 0x85 0xb4 0xd2 0x9e 0x4b 0x62 0x1e 0xc3
0x7f 0x37 0x7c 0xc8 0x4f 0xec 0xf2 0x45 0x18 0x61 0x17 0x1a 0x29 0x11 0xc7 0x75
0x02 0x48 0x26 0x93 0x83 0x8a 0x42 0x79 0x81 0x10 0x50 0x44 0xc4 0x6d 0x84 0xa0
0xb1 0x72 0x96 0x76 0xad 0x23 0xb0 0x2f 0xb2 0xa7 0x35 0x57 0x5e 0x92 0x07 0xc0
0xbc 0x36 0x99 0xaf 0xae 0xdb 0xef 0x15 0xe7 0x8e 0x63 0x06 0x9c 0x56 0x9a 0x31
0xe6 0x64 0xb5 0x58 0x95 0x49 0x04 0xee 0xdf 0x7e 0x0b 0x8c 0xff 0xf9 0xed 0x7a
0x65 0x5a 0x1f 0x4e 0xf6 0xf8 0x86 0x30 0xf0 0x4c 0xb7 0xca 0xe5 0x89 0x2a 0x1d
0xe4 0x16 0xf5 0x3a 0x27 0x28 0x8d 0x40 0x09 0x03 0x6f 0x94 0xa5 0x4a 0x46 0x67
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
*/
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
list = [
0xd6,0xc9,0xc2,0xce,0x47,0xde,0xda,0x70,
0x85,0xb4,0xd2,0x9e,0x4b,0x62,0x1e,0xc3,
0x7f,0x37,0x7c,0xc8,0x4f,0xec,0xf2,0x45,
0x18,0x61,0x17,0x1a,0x29,0x11,0xc7,0x75,
0x02,0x48,0x26,0x93,0x83,0x8a,0x42,0x79,
0x81,0x10,0x50,0x44,0xc4,0x6d,0x84,0xa0,
0xb1,0x72,0x96,0x76,0xad,0x23,0xb0,0x2f,
0xb2,0xa7,0x35,0x57,0x5e,0x92,0x07,0xc0,
0xbc,0x36,0x99,0xaf,0xae,0xdb,0xef,0x15,
0xe7,0x8e,0x63,0x06,0x9c,0x56,0x9a,0x31,
0xe6,0x64,0xb5,0x58,0x95,0x49,0x04,0xee,
0xdf,0x7e,0x0b,0x8c,0xff,0xf9,0xed,0x7a,
0x65,0x5a,0x1f,0x4e,0xf6,0xf8,0x86,0x30,
0xf0,0x4c,0xb7,0xca,0xe5,0x89,0x2a,0x1d,
0xe4,0x16,0xf5,0x3a,0x27,0x28,0x8d,0x40,
0x09,0x03,0x6f,0x94,0xa5,0x4a,0x46,0x67]

flag = ""
enc = "95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a"
for i in range(0, len(enc), 2):
s1 = int(enc[i:i+2], 16)
flag += chr(list.index(s1))
print(flag)
#TWCTF{5UBS717U710N_C1PH3R_W17H_R4ND0M123D_5-B0X}

法二

参考reverse_box gdb调试
生成随机数后第一个断点
img
此时rand()随机数生成函数刚生成一个随机数与0xFF后,从eax寄存器取出存放在[ebp+var_C]
查看栈中var_C的地址相对于栈顶是-0000000C所以当时加载到栈中的地址就是ebp-0xc
img
第二个断点
img
正是printf函数开始输入的位置,这时候我们判断它输入的第一个16进制值是不是0x95,如果是那么我们就认为找到了正确的解;然后输出找到解的随机值和v4数组的所有值,查看起始地址从esp+0x1c开始的256个16进制字节的内容,在上图的汇编中数组地址是esp+eax+1Ch,但是当输出数组的第一个值时i为0即eax为0,所以此时数组起始地址esp+eax+1Ch=esp+1Ch,之所以输出256字节就够了,实际上也许用不上256字节,因为数组的下标是我们输入的字符的ASCII码,又我们输入的字符只能是可打印的字符,所以范围就是0-255中哪些可打印的字符就行了,这里我们输出完整的256字节是没有具体去区分可打印字符了,因为也不会影响到结果。

exp

采用gdb调试

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
#设置从0到255的随机值爆破
set $i=0
set $total=256
while($i<$total)
#在生成随机值之后的地址下第一个断点
b *0x80485b1
#在main函数中printf输出的位置下第二个断点
b *0x8048707
#运行程序,并输入参数"TWCTF"
run TWCTF
#$i变量的值递加1
set $i=$i+1
#程序在第一个断点停下来的时候,把当前的随机值赋值给eax寄存器
set $eax=$i
#继续运行程序
continue
#程序在第二个断点停下来的时候,判断此时的输出的第一个16进制值是否是0x95
if ($eax==0x95)
#如果是,则认为找到了正确的解,输出此时的随机值
print $i
#查看起始地址从esp+0x1c开始的256个16进制字节的内容,这里就是存放数组的地方
x/256xb $esp+0x1c
#令变量的值为256,退出循环
set $i=256
end
stop
end
end

img
img
img

flag

TWCTF{5UBS717U710N_C1PH3R_W17H_R4ND0M123D_5-B0X}

[SUCTF2019]hardCPP

ida打开注意到流程图比较不一样,是OLLVM混淆控制流平坦化
img
用脚本deflat.py去控制流平坦化,需要用到angr,版本为8.19.4.5
pip install angr==8.19.4.5
python3 deflat.py hardCpp 0x4007E0
img
得到去控制流平坦化之后的主函数如下

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v3; // al
char v4; // al
char v5; // al
char v6; // al
char v8; // al
char v9; // al
char v10; // al
char v11; // al
char v12[8]; // [rsp+A0h] [rbp-90h] BYREF
char v13[8]; // [rsp+A8h] [rbp-88h] BYREF
char v14[8]; // [rsp+B0h] [rbp-80h] BYREF
char v15[8]; // [rsp+B8h] [rbp-78h] BYREF
char v16[8]; // [rsp+C0h] [rbp-70h] BYREF
char v17[7]; // [rsp+C8h] [rbp-68h] BYREF
char v18; // [rsp+CFh] [rbp-61h]
int v19; // [rsp+D0h] [rbp-60h]
int v20; // [rsp+D4h] [rbp-5Ch]
int v21; // [rsp+D8h] [rbp-58h]
int v22; // [rsp+DCh] [rbp-54h]
char s; // [rsp+E0h] [rbp-50h] BYREF
char v24[23]; // [rsp+E1h] [rbp-4Fh] BYREF
char v25[8]; // [rsp+F8h] [rbp-38h] BYREF
char v26[8]; // [rsp+100h] [rbp-30h] BYREF
char v27[8]; // [rsp+108h] [rbp-28h] BYREF
char v28[4]; // [rsp+110h] [rbp-20h] BYREF
int v29; // [rsp+114h] [rbp-1Ch]
const char **v30; // [rsp+118h] [rbp-18h]
int v31; // [rsp+120h] [rbp-10h]
int v32; // [rsp+124h] [rbp-Ch]
int v33; // [rsp+128h] [rbp-8h]
bool v34; // [rsp+12Eh] [rbp-2h]

v32 = 0;
v31 = argc;
v30 = argv;
v29 = time(0LL);
puts("func(?)=\"01abfc750a0c942167651c40d088531d\"?");
s = getchar();
fgets(v24, 21, stdin);
v22 = time(0LL);
v21 = v22 - v29;
v33 = v22 - v29;
if ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
goto LABEL_13;
while ( 1 )
{
v20 = strlen(&s);
v34 = v20 != 21;
if ( y < 10 || ((((_BYTE)x - 1) * (_BYTE)x) & 1) == 0 )
break;
LABEL_13:
v20 = strlen(&s);
}
while ( 1 )
{
v19 = 1;
if ( y < 10 || ((((_BYTE)x - 1) * (_BYTE)x) & 1) == 0 )
break;
v19 = 1;
}
while ( v19 < 21 )
{
if ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
{
v18 = v21 ^ *(&s + v19);
v17[0] = main::$_0::operator()(v27, (unsigned int)v18);
v16[0] = main::$_1::operator()(v25, (unsigned int)*(&s + v21 + v19 - 1));
v8 = main::$_1::operator() const(char)::{lambda(int)#1}::operator()(v16, 7LL);
v18 = main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v17, (unsigned int)v8);
v15[0] = main::$_2::operator()(v28, (unsigned int)v18);
v14[0] = main::$_2::operator()(v28, (unsigned int)*(&s + v21 + v19 - 1));
v9 = main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v14, 18LL);
v13[0] = main::$_3::operator()(v26, (unsigned int)v9);
v10 = main::$_3::operator() const(char)::{lambda(char)#1}::operator()(v13, 3LL);
v12[0] = main::$_0::operator()(v27, (unsigned int)v10);
v11 = main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v12, 2LL);
v18 = main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v15, (unsigned int)v11);
}
do
{
v18 = v21 ^ *(&s + v19);
v17[0] = main::$_0::operator()(v27, (unsigned int)v18);
v16[0] = main::$_1::operator()(v25, (unsigned int)*(&s + v21 + v19 - 1));
v3 = main::$_1::operator() const(char)::{lambda(int)#1}::operator()(v16, 7LL);
v18 = main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v17, (unsigned int)v3);
v15[0] = main::$_2::operator()(v28, (unsigned int)v18);
v14[0] = main::$_2::operator()(v28, (unsigned int)*(&s + v21 + v19 - 1));
v4 = main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v14, 18LL);
v13[0] = main::$_3::operator()(v26, (unsigned int)v4);
v5 = main::$_3::operator() const(char)::{lambda(char)#1}::operator()(v13, 3LL);
v12[0] = main::$_0::operator()(v27, (unsigned int)v5);
v6 = main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v12, 2LL);
v18 = main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v15, (unsigned int)v6);
}
while ( enc[v19 - 1] != v18 );
while ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
;
++v19;
}
if ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
goto LABEL_16;
while ( 1 )
{
puts("You win");
if ( y < 10 || ((((_BYTE)x - 1) * (_BYTE)x) & 1) == 0 )
break;
LABEL_16:
puts("You win");
}
return 0;
}

先给了一个提示 md5 值
puts("func(?)=\"01abfc750a0c942167651c40d088531d\"?");
通过查找得到是#
img
之后有一对冗余代码,对加密不影响
重点看一下
img
点进去看看每个操作operator
main::$_0::operator()
直接返回第二个参数

1
2
3
4
char __fastcall main::$_0::operator()(__int64 a1, char a2)
{
return a2;
}

main::$_1::operator()
同样是直接返回第二个参数

1
2
3
4
char __fastcall main::$_1::operator()(__int64 a1, char a2)
{
return a2;
}

main::$_1::operator() const(char)::{lambda(int)#1}::operator()
返回的是a1%a2

1
2
3
4
__int64 __fastcall main::$_1::operator() const(char)::{lambda(int)#1}::operator()(char *a1, int a2)
{
return (unsigned int)(*a1 % a2);
}

main::$_0::operator() const(char)::{lambda(char)#1}::operator()
冗余代码挺多,关键就5句
v15 = *(&v5-16) + *v5 = a1 + a2
img
main::$_2::operator()
其实就是返回第二个参数
img
main::$_2::operator() const(char)::{lambda(char)#1}::operator()
返回a2 ^ a1

1
2
3
4
__int64 __fastcall main::$_2::operator() const(char)::{lambda(char)#1}::operator()(_BYTE *a1, char a2)
{
return (unsigned int)(char)(a2 ^ *a1);
}

main::$_3::operator()
返回第二个参数
img
main::$_3::operator() const(char)::{lambda(char)#1}::operator()
返回a2 * a1

1
2
3
4
__int64 __fastcall main::$_3::operator() const(char)::{lambda(char)#1}::operator()(char *a1, char a2)
{
return (unsigned int)(a2 * *a1);
}

最后整理一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
v18 = v21 ^ s[v19];
v17 = v18
v16 = s[v21 - 1 + v19]
v3 = v16[0] % 7
v18 = v17 + v3
v15 = v18
v14 = s[v21 - 1 + v19]
v4 = 18 ^ v14[0]
v13 = v4
v5 = v13[0] * 3
v12 = v5
v6 = v12[0] + 2
v18 = v15[0] ^ v6
//组合一下结果如下
v18 = ((v21 ^ s[v19]) + (s[v21 - 1 + v19] % 7)) ^ ((18 ^ s[v21 - 1 + v19]) * 3 + 2)

其中v21是time(0LL)即为0
s是输入的字符串
最后v18 = ((0 ^ input[i]) + (input[i-1] % 7)) ^ ((18 ^ input[i-1]) * 3 + 2)
最后判断v18是否等于enc
img

exp

1
2
3
4
5
6
enc = [0xF3, 0x2E, 0x18, 0x36, 0xE1, 0x4C, 0x22, 0xD1, 0xF9, 0x8C, 0x40, 0x76, 0xF4, 0x0E, 0x00, 0x05, 0xA3, 0x90, 0x0E, 0xA5]
flag = '#'
for i in range(20):
flag += chr(((enc[i] ^ ((ord(flag[i]) ^ 18) * 3 + 2)) - (ord(flag[i]) % 7)) & 0xff)
print(flag)
#flag{mY-CurR1ed_Fns}

flag

flag{mY-CurR1ed_Fns}

secret-galaxy-300

先运行看看
img
显示了五个星系的信息,用IDA打开
主函数是调用填充函数和打印函数
img
img
打印函数里面没什么内容,那我们就关注一下填充函数
跟踪一下galaxy_name,总共有6个星系名字
而刚刚运行结果只有前五个,最后一个DARK SECRET GALAXY会不坏就藏有flag信息呢
img
交叉引用看看,跟踪到一个__libc_csu_gala函数有点像flag的生成函数
img
应该是截取不同星系名字凭借而成一个byte_40DAXX这个串
手动拼接一下或者在return result处下断点
动态调试F9后跟踪一下byte串
img
按a将数据转换成字符串,得到flag
img

flag

aliens_are_around_us

[HCTF2018]seven

函数不多,直接定位到关键函数

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
__int64 __fastcall sub_1400012F0(__int64 a1, __int64 a2)
{
__int64 v3; // rsi
unsigned __int64 v4; // rdx
int v5; // ecx
__int16 *v6; // rdi
__int64 v7; // rbp
__int16 v8; // dx
char v9; // dl
const CHAR *v10; // rcx

if ( *(int *)(a2 + 48) >= 0 )
{
v3 = *(_QWORD *)(a2 + 24);
v4 = *(_QWORD *)(a2 + 56) / 0xCui64;
if ( (_DWORD)v4 )
{
v5 = dword_1400030E4;
v6 = (__int16 *)(v3 + 2);
v7 = (unsigned int)v4;
while ( *(_WORD *)(v3 + 4) )
{
LABEL_30:
v6 += 6;
if ( !--v7 )
goto LABEL_31;
}
aO[v5] = 46;
v8 = *v6;
if ( *v6 == 17 ) // 对应w
{
if ( (v5 & 0xFFFFFFF0) != 0 )
{
v5 -= 16; // 坐标移动,向上
goto LABEL_13;
}
v5 += 208;
dword_1400030E4 = v5;
}
if ( v8 != 31 ) // 对应s
goto LABEL_14;
if ( (v5 & 0xFFFFFFF0) == 208 )
v5 -= 208;
else
v5 += 16; // 坐标移动,向下
LABEL_13:
dword_1400030E4 = v5;
LABEL_14:
if ( v8 == 30 ) // 对应a
{
if ( (v5 & 0xF) != 0 )
--v5; // 向左
else
v5 += 15; // 边界移动,最右端0位置时,+15,向左移动到最右端
dword_1400030E4 = v5;
}
if ( v8 == 32 ) // 对应d
{
if ( (v5 & 0xF) == 15 )
v5 -= 15; // 向右
else
++v5; // 同理
dword_1400030E4 = v5;
}
v9 = aO[v5];
if ( v9 == 42 )
{
v10 = "-1s\n";
}
else
{
if ( v9 != '7' ) // 终点是一个字符'7'
{
LABEL_29:
aO[v5] = 'o'; // 起点是'o'
goto LABEL_30;
}
v10 = "The input is the flag!\n";
}
dword_1400030E4 = 16;
DbgPrint(v10);
v5 = dword_1400030E4;
goto LABEL_29;
}
}
LABEL_31:
if ( *(_BYTE *)(a2 + 65) )
*(_BYTE *)(*(_QWORD *)(a2 + 184) + 3i64) |= 1u;
return *(unsigned int *)(a2 + 48);
}

提取一下迷宫手动走一下得到flag
img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
****************
o..............*
**************.*
************...*
***********..***
**********..****
*********..*****
********..******
*******..*******
******..********
*****..*********
****..**********
****7***********
****************

flag

hctf{ddddddddddddddssaasasasasasasasasas}

[buu]Youngter-drive

先脱UPX壳,然后看主函数,有多个线程
img
sub_4110FF里是读入source
img
先看第一个线程hObject,它执行了startAddress
img
继续查看 sub_41112C 函数
如果不是大写字母或者小写字母就正常退出
如果是小写字母,则对off_418000的小写字母进行-38的加密操作
反之如果是大写字母,则对off_418000的大写字母进行-96的加密操作.
img
off_418000如下
img
看一下第二个线程sub_41119F
和第一个线程一样都调用了dword_418008看了一下是0x1D
img
两个线程执行一次都会把dword_418008减一
两个线程交替运行,由于sub_41119F是直接让dword_418008减一
意味着只有一半的dword_418008会传入sub_41112C

最后回到主函数有个 sub_411190,点开是比较flag
img
off_418004如下
img
已知第一个传入的dword_418008是0x1D(29)是奇数
那么只有奇数位会进行加密,偶数为应该就是off_418004的内容
解出来29位提交上去是错的,
仔细观察可以发现最后的sub_411880只比较了0~28位这29个字符
而根据分析主函数判断dword_418008!=-1,说明是从29~0总共应该有30位
没有更多信息去知道最后一位是什么,按理来说应该就是多解了
但是好像只有填充E才会是对的。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
enc ='QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
key = 'TOiZiZtOrYaToUwPnToBsOaOapsyS'
flag = ''
for i in range(len(key)):
if i%2 == 0:
flag += key[i]
else:
if key[i].isupper():
flag += chr(enc.find(key[i])+96)
else:
flag += chr(enc.find(key[i])+38)
print(flag)
#ThisisthreadofwindowshahaIsES

flag

ThisisthreadofwindowshahaIsESE