由于re-part-5字数太多了,所以另开一个帖子,也是记录剩下的难度5的题解。


asong

 题目一共3个文件,asong是一个64位的ELF、out文件是一个二进制文件、that_girl是一个有好多句子的文件。

 这个题目,就是使用that_girl文件,结合flag,最后生成一个out文件,题目思路比较简单,就是函数逆向有点麻烦,花费了大概6h,脚本如下:

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
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
# @Time : 2023/03/29 10:56:16
# @Author: wd-2711
'''

def change_char(a1):
result = a1 - 10
if a1 == 10:
result = a1 + 35
return result
elif a1 == 32 or a1 == 33 or a1 == 34:
result = a1 + 10
return result
elif a1 == 39:
result = a1 + 2
return result
elif a1 == 44:
result = a1 - 4
return result
elif a1 == 46:
result = a1 - 7
return result
elif a1 == 58 or a1 == 59:
result = a1 - 21
return result
elif a1 == 63:
result = a1 - 27
return result
elif a1 == 95:
result = a1 - 49
return result
else:
if a1 <= 47 or a1 > 48:
if a1 <= 64 or a1 > 90:
if a1 > 96 and a1 <= 122:
result = a1 - 87
else:
result = a1 - 55
else:
result = a1 - 48
return result

if __name__ == "__main__":

def sub_400AAA():
a2 = [0 for i in range(0xff)]
with open("./asong/that_girl", "r") as f:
that_girl = f.read()
for c in that_girl:
v2 = change_char(ord(c))
a2[v2] += 1
return a2 # Double Word

def sub_400E54(a2):
with open("./asong/out", "rb") as f:
# out -> byte
out = f.read()
out = [int(hex(o)[2:], 16) for o in out]

# sub_400DB4
new_a1 = out
old_a1 = []
for i in range(len(new_a1)):
ind = (i-1) % len(new_a1)
tmp = (new_a1[i] >> 3) | ((new_a1[ind] << 5) & 0xff)
old_a1.append(tmp)

# sub_400D33
a1 = old_a1
arr = [0x00000016, 0x00000000, 0x00000006, 0x00000002, 0x0000001E, 0x00000018, 0x00000009, 0x00000001, 0x00000015, 0x00000007, 0x00000012, 0x0000000A, 0x00000008, 0x0000000C, 0x00000011, 0x00000017, 0x0000000D, 0x00000004, 0x00000003, 0x0000000E, 0x00000013, 0x0000000B, 0x00000014, 0x00000010, 0x0000000F, 0x00000005, 0x00000019, 0x00000024, 0x0000001B, 0x0000001C, 0x0000001D, 0x00000025, 0x0000001F, 0x00000020, 0x00000021, 0x0000001A, 0x00000022, 0x00000023]
v2 = [0 for i in range(5)]
v2[1] = arr.index(0)
v2[0] = a1[v2[1]]
while True:
v2[1] = arr.index(v2[1])
a1[arr[v2[1]]] = a1[v2[1]]
if v2[1] == 0:
break
a1[0] = v2[0]

v5 = a1
a1 = []
for i in v5:
a1_slice = []
ind = a2.index(i)
for c in range(256):
if change_char(c) == ind and ((c >= ord("a") and c <= ord("z")) or c == ord("_")):
a1_slice.append(c)
a1.append(a1_slice)
a1_slice = []

print("QCTF{", end = "")
for i in a1:
for ii in i:
print(chr(ii), end = "")
print("}")

a2 = sub_400AAA()
sub_400E54(a2)
# QCTF{that_girl_saying_no_for_your_vindicate}

pseudorandom

 看题目,假随机,猜测是要生成一段随机数,种子是flag。是64位的ELF。仔细调研后发现,C语言中,如果不提前使用srand来设置种子值,而是直接使用rand来生成随机数,此时生成的随机数序列是固定的。

 C语言中配置openssl库的方法为:https://blog.csdn.net/CHTXRT/article/details/128771974

有一个坑,就是在Linux下,使用C语言使用rand产生随机数,第一个数总是1804289383;然而在windows中,第一个数总是41。这是因为在不同的操作系统中,rand函数使用的随机数生成算法不同。在Linux下,rand函数使用的是线性同余算法(linear congruential generator),其计算公式是:next = (prev * a + c) % m。其中,prev是上一个随机数,a、c、m是常数。在Linux下,a=1103515245, c=12345, m=2147483648,因此第一个随机数总是1804289383。而在Windows下,rand函数使用的是另一种随机数生成算法,其计算公式也是线性同余算法,但参数不同。在Windows下,a=214013, c=2531011, m=2^31,因此第一个随机数总是41。

补充:ubuntu下编译含有openssl的程序g++ -o pseudorandom pseudorandom.cpp -lssl -lcrypto

方法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
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include<cstdio>
#include<stdlib.h>
#include<iostream>
#include<cstring>
#include<openssl/md5.h>
#include<openssl/sha.h>
#pragma comment(lib,"libssl.lib")
#pragma comment(lib,"libcrypto.lib")
using namespace std;

#define LOBYTE(w) ((unsigned char)(w))

unsigned int sub_400C30(int a1){
int v1;
int v2;
int v3;
int v5;
int v6;
int v7;
unsigned int v8;

v7 = 1;
v6 = 0;
v5 = 1686268861;
while(1){
while(1){
while(v5 <= -649527273){
v8 = 0;
v5 = 654278669;
}
if(v5 > 654278668)
break;
v8 = v6;
v5 = 654278669;
}
if(v5 <= 1010088293)
break;
if(v5 > 1759468863){
v3 = 1164544472;
if((a1 & (~v7 ^ a1)) != 0)
v3 = 1060084854;
v5 = v3;
}
else if(v5 > 1686268860){
v1 = -785991200;
if(a1)
v1 = 1010088294;
v5 = v1;
}
else if(v5 > 1567797097){
v2 = -649527272;
if(v7)
v2 = 1759468864;
v5 = v2;
}
else if(v5 == 1010088294){
v5 = 1567797098;
}
else if (v5 == 1060084854){
v6 = ~(~v7 | ~a1);
v5 = 1164544472;
}
else{
v7 *= 2;
v5 = 1567797098;
}
}
return v8;
}

unsigned int sub_400B40(int a1){
int v1;
int v3;
unsigned int v4;

v4 = 0;
v3 = 1523738799;
while(v3 != -1781392209){
if (v3 == -321715599){
a1 = ~(~(a1 - 1) | ~a1);
++v4;
v3 = 1523738799;
}
else{
v1 = -1781392209;
if(a1)
v1 = -321715599;
v3 = v1;
}
}
return v4;
}

char sub_400EA0(int a1, int a2){
int v2;
int v3;
int v4;
int v6;
int v7;
char v8;

v7 = -1460804643;
while(1){
while(v7 <= -857087489){
v2 = 218564280;
if(a2)
v2 = 527506268;
v7 = v2;
}
if(v7 <= 218564279)
break;
if(v7 == 218564280)
return 0;
if(v7 == 484338753){
v6 = a1 & (sub_400C30(a1) ^ a1);
v4 = sub_400C30(a1);
v8 = ((2 * v4) ^ v6 | (2 * v4) & v6) == a2 + a1;
v7 = -857087488;
}
else{
v3 = 218564280;
if ( a1 )
v3 = 484338753;
v7 = v3;
}
}
return v8;
}

int main(){
size_t len_s_1, len_s_2;
char s1[48], s[140], v8[48];
MD5_CTX md5_ctx;
SHA_CTX sha1_ctx;
int v3 = rand();
unsigned int v20 = rand();
int v16 = sub_400C30(v20);
unsigned int v17 = (1 << sub_400B40(v20)) - 1;
int v19 = 0;
unsigned int v18;
unsigned char v11[16], v10[20];

unsigned int dword_6020D0[40] = {
0x0000000D, 0x00000052, 0x00000067, 0x00000053, 0x00000044, 0x00000040, 0x00000016, 0x00000008,
0x00000051, 0x00000067, 0x00000006, 0x0000000B, 0x00000052, 0x00000003, 0x00000000, 0x00000000,
0x0000005F, 0x00000001, 0x0000000B, 0x0000006F, 0x00000053, 0x00000055, 0x00000043, 0x0000006A,
0x00000053, 0x00000050, 0x0000005B, 0x00000005, 0x00000051, 0x00000004, 0x00000010, 0x0000003A,
0x00000001, 0x00000054, 0x0000005C, 0x00000007, 0x0000004E, 0x00000041, 0x00000009, 0x00000046
};

MD5_Init(&md5_ctx);
SHA1_Init(&sha1_ctx);
while(v19 != v20){
for(v18 = 0; ; v18++){
if(sub_400EA0(v17, v18)){
printf("%d\n", v18);
break;
}
}

v17 += v18;
sprintf(s, "%d", v18);
len_s_1 = strlen(s);
MD5_Update(&md5_ctx, s, len_s_1);
len_s_2 = strlen(s);
SHA1_Update(&sha1_ctx, s, len_s_2);
while((~v16 | ~v17) != -1){
v19 = v16 ^ v19 | v16 & v19;
v17 &= v16 ^ v17;
v16 = sub_400C30(v20 & (v19 ^ v20));
}
}
MD5_Final(v11, &md5_ctx);
for(int i = 0; i < 16; ++i)
sprintf(&s1[2 * i], "%02x", v11[i]);
cout << s1 << endl;

printf("Good Job!!\n");
printf("Wait till I fetch your reward...");
int v6 = rand();
SHA1_Final(v10, &sha1_ctx);
for(int i = 0; i < 20; ++i )
sprintf(&v8[2 * i], "%02x", v10[i]);
printf("OK. Here it is\n");
for (int i = 0; i < 40; ++i )
v8[i] = (dword_6020D0[i] & 0x4A | ~LOBYTE(dword_6020D0[i]) & 0xB5) ^ (v8[i] & 0x4A | ~v8[i] & 0xB5);
printf("The flag is:nullcon{%s}\n", v8);
return 0;
}
# nullcon{50_5tup1d_ch4113ng3_f0r_e1i73er_71k3-y0u}

方法2:Angr模拟

 主要是看:https://blog.csdn.net/qq_43547885/article/details/113831507

 脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

import angr

# 导入项目
proj=angr.Project("./pseudorandom")
# 初始 state 为 sub_400EA0 函数的地址
state=proj.factory.blank_state(addr=0x400EA0)
# BVS 可以理解为符号的意思,也就是为输入->输出的映射指定一个自变量(32位)。一般来说这就是我们要求解的值
arg2=state.solver.BVS('arg2',32)
# 通过动调知道 sub_400EA0 的第一个参数为 0xffff
state.regs.edi=0xffff
state.regs.esi=arg2
# 初始化 simulation_manager
simgr=proj.factory.simulation_manager(state)
# 找到一条 0x400ea0 -> 0x401039 的路径
simgr.explore(find=0x401039)
# 获得 found 的 state
found=simgr.found[0]
# 增加限制条件,即存放返回值的内存单元为 True
found.add_constraints(found.memory.load(found.regs.rbp-8,4)!=0)
# 对输入进行求解
value=found.solver.eval(arg2)
print(hex(value))

 注:上述脚本不全,只是涉及了如何找出值,后续操作没跟上,我也懒得补上了。太懒了呀!

first

0x00 自己的解法

 对程序进行分析,如下:

image-20230417225658147

image-20230417225739300

 程序流程如下:(用python写出流程代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 计算v10
v10 = 0
for idx, i in enumerate(input):
v10 ^= (i + idx)
# 2. 开了 6 个线程
for _ in range(6):
start_routine()
# 3. 计算 output
for i in range(len(input)):
output[i - 1] = v10 ^ para_arr[i] ^ output[i]
# 4. output值的合理性
for i in output:
assert output[i] <= 122

start_routine()函数流程如下:(start_routine()函数中v2 = 4*i, i=0,1,2,3,4,5

1
2
sub_400E10(input[v4], 4, v8)
assert v8 == qword_602120[v1]

 尝试使用angr脚本解,但是一直有问题,脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def angr_solve():
ret = {}
# input 地址
inp_addr_base = 0x602180
# output[v6] = input[v4] 的地址
good_addr = 0x400dc6
proj = angr.Project("./first")
# 0x400D58 为 sub_400E10 call的地址
state = proj.factory.blank_state(addr=0x400D58)
arg = claripy.BVS('0', 32)
state.memory.store(inp_addr_base, arg)
state.regs.rbx = 0
state.regs.rsp = 0x00007F67575D9EC0
simgr = proj.factory.simulation_manager(state)
simgr.explore(find=good_addr)
if simgr.found:
print(len(simgr.found))
res = simgr.found[0]
value = res.solver.eval(arg)
ret['0'] = hex(value)
return ret

0x01 大佬wp

题目链接:https://blog.csdn.net/weixin_45055269/article/details/106157485

python密码算法:https://www.cnblogs.com/wangyujian/p/11774027.html

 总结了一下自己忽略的点:

(1)线程的启动是按时间顺序来的,这个时间是随机产生的。

image-20230418095551291

image-20230418095623111

(2)sub_400E10是一个MD5哈希函数。(没看出来竟然

不理解的点:为什么自己的angr脚本不行。感觉还是得自己精进一下angr基础

 大佬脚本如下:

1
2
3
4
5
6
7
8
9
10
11
import hashlib
check="4746bbbd02bb590fbeac2821ece8fc5cad749265ca7503ef4386b38fc12c4227b03ecc45a7ec2da7be3c5ffe121734e8"
for w in range(0,6):
for i in range(48,123):
for j in range(48,123):
for m in range(48,123):
for n in range(48,123):
temp=chr(i)+chr(j)+chr(m)+chr(n)
hashvalue=hashlib.md5(temp.encode()).hexdigest()
if hashvalue[0:16]==check[w*16:w*16+16]:
print(w,temp)

 因为不确定数组顺序,所以得试:(最终确定是juhuhfenlapsdunuhjifiuer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
input1='juhuhfenlapsiuerhjifdunu'
check=[0xfe,0xe9,0xf4,0xe2,0xf1,0xfa,0xf4,0xe4,0xf0,0xe7,0xe4,0xe5,0xe3,0xf2,0xf5,0xef,0xe8,0xff,0xf6,0xf4,0xfd,0xb4,0xa5,0xb2]
len=24
i=0
v11=0
while(i!=len):
v12=ord(input1[i])+i
v11=v11^v12
i=i+1

input2='juhuhfenlapsdunuhjifiuer'
flag=''
for i in range(24):
temp=ord(input2[i])^v11^check[i]
flag+=chr(temp)
print(flag)
# goodjobyougetthisflag233

0x02 总结

 其实感觉可以做出来,耐心!专注!

babydsp

 在re-part-5中没做出来的题,由于wp也很少,所以还是得自己多琢磨。题目中有ClimbToTop.exe.16248.dmpClimbToTop.pdb

0x00 自己的探索

 想找dmp文件修复工具,未果。看到网上大都用windbg调试,本题目中还有ClimbToTop.pdb,所以感觉考察的是windbg的使用。

 加载pdb文件,根据x ClimbToTop!*main*找到模块ClimbToTop!main模块,之后u ClimbToTop!main L30查看汇编命令,如下所示:

image-20230515135203608

 使用db 00007ff7 aefe7470查看上图绿框存的字符串,发现:

image-20230515135339833

 说明应该跳转到上图红框位置ClimbToTop!main+0x60 (00007ff7 aefe11a0)

image-20230515135730519

 继续跳转到上述红框位置(原因与之前一样):00007ff7 aefe11ef

image-20230515140515079

 上图中,第一个红框表示输出字符串:OK, if I don't messed up these heap, I can climb these hill。之后调用了两遍:

1
2
3
4
5
DetourTransactionBegin
DetourUpdateThread
DetourAttach
DetourDetach
DetourTransactionCommit

 最后两个红框代表程序走向,程序不应该走蓝框,因为蓝框输出字符串:LockResource ERROR

 下面逐步分析上述函数:

DetourTransactionBegin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// DetourTransactionBegin
00007ff7`aefe37a0 4883ec38 sub rsp,38h
00007ff7`aefe37a4 833d5d59000000 cmp dword ptr [ClimbToTop!s_nPendingThreadId (00007ff7`aefe9108)],0
00007ff7`aefe37ab 7407 je ClimbToTop!DetourTransactionBegin+0x14 (00007ff7`aefe37b4)
00007ff7`aefe37ad b8dd100000 mov eax,10DDh
00007ff7`aefe37b2 eb57 jmp ClimbToTop!DetourTransactionBegin+0x6b (00007ff7`aefe380b)
00007ff7`aefe37b4 e842290000 call ClimbToTop!GetCurrentThreadId (00007ff7`aefe60fb)
00007ff7`aefe37b9 89442420 mov dword ptr [rsp+20h],eax
00007ff7`aefe37bd 488d0d44590000 lea rcx,[ClimbToTop!s_nPendingThreadId (00007ff7`aefe9108)]
00007ff7`aefe37c4 33c0 xor eax,eax
00007ff7`aefe37c6 8b542420 mov edx,dword ptr [rsp+20h]
00007ff7`aefe37ca f00fb111 lock cmpxchg dword ptr [rcx],edx
00007ff7`aefe37ce 85c0 test eax,eax
00007ff7`aefe37d0 7407 je ClimbToTop!DetourTransactionBegin+0x39 (00007ff7`aefe37d9)
00007ff7`aefe37d2 b8dd100000 mov eax,10DDh
00007ff7`aefe37d7 eb32 jmp ClimbToTop!DetourTransactionBegin+0x6b (00007ff7`aefe380b)
00007ff7`aefe37d9 48c7053c59000000000000 mov qword ptr [ClimbToTop!s_pPendingOperations (00007ff7`aefe9120)],0
00007ff7`aefe37e4 48c7052959000000000000 mov qword ptr [ClimbToTop!s_pPendingThreads (00007ff7`aefe9118)],0
00007ff7`aefe37ef 48c7051659000000000000 mov qword ptr [ClimbToTop!s_ppPendingError (00007ff7`aefe9110)],0
00007ff7`aefe37fa e821f5ffff call ClimbToTop!detour_writable_trampoline_regions (00007ff7`aefe2d20)
00007ff7`aefe37ff 890507590000 mov dword ptr [ClimbToTop!s_nPendingError (00007ff7`aefe910c)],eax
00007ff7`aefe3805 8b0501590000 mov eax,dword ptr [ClimbToTop!s_nPendingError (00007ff7`aefe910c)]
00007ff7`aefe380b 4883c438 add rsp,38h
00007ff7`aefe380f c3 ret

 分析DetourTransactionBegin,其逻辑如下:

1
2
3
4
5
if (CurrentThreadId() == s_nPendingThreadId){
return detour_writable_trampoline_regions()
} else {
return 0x10DD
}

DetourUpdateThread

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
// DetourUpdateThread
00007ff7`aefe3cc0 48894c2408 mov qword ptr [rsp+8],rcx
00007ff7`aefe3cc5 4883ec48 sub rsp,48h
...
00007ff7`aefe3cf0 b910000000 mov ecx,10h
00007ff7`aefe3cf5 e85e170000 call ClimbToTop!operator new (00007ff7`aefe5458)
00007ff7`aefe3cfa 4889442430 mov qword ptr [rsp+30h],rax
00007ff7`aefe3cff 488b442430 mov rax,qword ptr [rsp+30h]
00007ff7`aefe3d04 4889442428 mov qword ptr [rsp+28h],rax
00007ff7`aefe3d09 48837c242800 cmp qword ptr [rsp+28h],0
00007ff7`aefe3d0f 754d jne ClimbToTop!DetourUpdateThread+0x9e (00007ff7`aefe3d5e)
00007ff7`aefe3d11 c744242008000000 mov dword ptr [rsp+20h],8
00007ff7`aefe3d19 48837c242800 cmp qword ptr [rsp+28h],0
00007ff7`aefe3d1f 7422 je ClimbToTop!DetourUpdateThread+0x83 (00007ff7`aefe3d43)
00007ff7`aefe3d21 488b442428 mov rax,qword ptr [rsp+28h]
00007ff7`aefe3d26 4889442438 mov qword ptr [rsp+38h],rax
00007ff7`aefe3d2b ba10000000 mov edx,10h
00007ff7`aefe3d30 488b4c2438 mov rcx,qword ptr [rsp+38h]
00007ff7`aefe3d35 e85a170000 call ClimbToTop!operator delete (00007ff7`aefe5494)
00007ff7`aefe3d3a 48c744242800000000 mov qword ptr [rsp+28h],0
00007ff7`aefe3d43 8b442420 mov eax,dword ptr [rsp+20h]
...
00007ff7`aefe3d5c eb45 jmp ClimbToTop!DetourUpdateThread+0xe3 (00007ff7`aefe3da3)
00007ff7`aefe3d5e 488b4c2450 mov rcx,qword ptr [rsp+50h]
00007ff7`aefe3d63 e899230000 call ClimbToTop!SuspendThread (00007ff7`aefe6101)
00007ff7`aefe3d68 83f8ff cmp eax,0FFFFFFFFh
00007ff7`aefe3d6b 750b jne ClimbToTop!DetourUpdateThread+0xb8 (00007ff7`aefe3d78)
00007ff7`aefe3d6d e8e1d7ffff call ClimbToTop!GetLastError (00007ff7`aefe1553)
00007ff7`aefe3d72 89442420 mov dword ptr [rsp+20h],eax
00007ff7`aefe3d76 eba1 jmp ClimbToTop!DetourUpdateThread+0x59 (00007ff7`aefe3d19)
00007ff7`aefe3d78 488b442428 mov rax,qword ptr [rsp+28h]
00007ff7`aefe3d7d 488b4c2450 mov rcx,qword ptr [rsp+50h]
00007ff7`aefe3d82 48894808 mov qword ptr [rax+8],rcx
00007ff7`aefe3d86 488b442428 mov rax,qword ptr [rsp+28h]
00007ff7`aefe3d8b 488b0d86530000 mov rcx,qword ptr [ClimbToTop!s_pPendingThreads (00007ff7`aefe9118)]
00007ff7`aefe3d92 488908 mov qword ptr [rax],rcx
00007ff7`aefe3d95 488b442428 mov rax,qword ptr [rsp+28h]
00007ff7`aefe3d9a 48890577530000 mov qword ptr [ClimbToTop!s_pPendingThreads (00007ff7`aefe9118)],rax
00007ff7`aefe3da1 33c0 xor eax,eax
00007ff7`aefe3da3 4883c448 add rsp,48h
00007ff7`aefe3da7 c3 ret

 其逻辑如下:

1
2
3
4
5
6
7
8
if (operator_new != 0){
if (SuspendThread != 0FFFFFFFFh){
print("success")
} else {
operator_delete()
}
}
return

DetourAttach

1
2
3
4
5
6
7
8
9
10
11
12
// DetourAttach
00007ff7`aefe2d80 4889542410 mov qword ptr [rsp+10h],rdx
00007ff7`aefe2d85 48894c2408 mov qword ptr [rsp+8],rcx
00007ff7`aefe2d8a 4883ec38 sub rsp,38h
00007ff7`aefe2d8e 48c744242000000000 mov qword ptr [rsp+20h],0
00007ff7`aefe2d97 4533c9 xor r9d,r9d
00007ff7`aefe2d9a 4533c0 xor r8d,r8d
00007ff7`aefe2d9d 488b542448 mov rdx,qword ptr [rsp+48h]
00007ff7`aefe2da2 488b4c2440 mov rcx,qword ptr [rsp+40h]
00007ff7`aefe2da7 e814000000 call ClimbToTop!DetourAttachEx (00007ff7`aefe2dc0)
00007ff7`aefe2dac 4883c438 add rsp,38h
00007ff7`aefe2db0 c3 ret

意外发现

 分析来分析去,发现DetourTransactionBegin等函数竟然是Microsoft Detours库中的函数…无语住了,搜了搜相关资料:

1
2
3
Microsoft Detours 是一个 Windows 平台下的二进制重定向库,能够在运行时修改二进制程序的函数调用,实现函数钩子等操作。它是一个强大的工具,常用于动态调试、API Hooking、代码注入和病毒分析等领域。

Microsoft Detours 可以用于对二进制程序进行函数钩子操作。它的实现方式是通过构造一个跳板函数(Trampoline)替换原始函数的入口地址,然后在跳板函数中调用原始函数或者钩子函数,从而实现函数调用的重定向。在跳板函数中,可以执行一些额外的代码来拦截、修改或替换原始函数的行为,实现函数钩子的功能。

 一个Detours的例子如下,发现这个例子和我们的题目,是多么的契合!!

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
#include <Windows.h>
#include <detours.h>

typedef void (*pMessageBoxA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

pMessageBoxA TrueMessageBoxA = MessageBoxA;

void HookedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
TrueMessageBoxA(hWnd, "Hooked: Hello from HookedMessageBoxA", lpCaption, uType);
}

int main()
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)TrueMessageBoxA, HookedMessageBoxA);
DetourTransactionCommit();

MessageBoxA(NULL, "Test Message", "Test Caption", MB_OK);

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)TrueMessageBoxA, HookedMessageBoxA);
DetourTransactionCommit();

return 0;
}

 OK,那我们现在就要查找:(1)Detour钩取的函数是什么?(2)函数主代码是什么?

 可以看到,Detourattach阶段时,将pfnLockResource变成NewLockResource,然后主函数是一个_imp_LockResource

1
2
补充:
1. _imp_ 开头通常表示这是一个导入函数指针。导入函数指针是一种用于在运行时动态加载和调用 DLL(动态链接库)函数的技术。

 通过跟踪pfnLockResource,最终其指向了kernel32!_imp_LockResource

1
2
补充:
1. kernel32!LockResource 函数的作用是将一个资源对象的数据加载到内存中,并返回指向该数据的指针。资源对象可以是应用程序中的任何类型资源,例如位图、图标、字符串、音频文件等。

 再跟踪NewLockResource,其代码如下:

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
// NewLockResource
00007ff7`aefe1080 4883ec28 sub rsp,28h
00007ff7`aefe1084 ff158e860000 call qword ptr [ClimbToTop!pfnLockResource (00007ff7`aefe9718)]
00007ff7`aefe108a 4c8b057f860000 mov r8,qword ptr [ClimbToTop!g_Size (00007ff7`aefe9710)]
00007ff7`aefe1091 33c9 xor ecx,ecx
00007ff7`aefe1093 4c8bc8 mov r9,rax
00007ff7`aefe1096 498d4001 lea rax,[r8+1]
00007ff7`aefe109a 4883f840 cmp rax,40h
00007ff7`aefe109e 726b jb ClimbToTop!NewLockResource+0x8b (00007ff7`aefe110b)
00007ff7`aefe10a0 660f6f1598640000 movdqa xmm2,xmmword ptr [ClimbToTop!_xmm (00007ff7`aefe7540)]
00007ff7`aefe10a8 498d5110 lea rdx,[r9+10h]
00007ff7`aefe10ac 83e03f and eax,3Fh
00007ff7`aefe10af 4d8bd0 mov r10,r8
00007ff7`aefe10b2 4c2bd0 sub r10,rax
00007ff7`aefe10b5 6666660f1f840000000000 nop word ptr [rax+rax]
00007ff7`aefe10c0 f30f6f42f0 movdqu xmm0,xmmword ptr [rdx-10h]
00007ff7`aefe10c5 83c140 add ecx,40h
00007ff7`aefe10c8 488d5240 lea rdx,[rdx+40h]
00007ff7`aefe10cc 4863c1 movsxd rax,ecx
00007ff7`aefe10cf 660fefc2 pxor xmm0,xmm2
00007ff7`aefe10d3 f30f7f42b0 movdqu xmmword ptr [rdx-50h],xmm0
00007ff7`aefe10d8 f30f6f4ac0 movdqu xmm1,xmmword ptr [rdx-40h]
00007ff7`aefe10dd 660fefca pxor xmm1,xmm2
00007ff7`aefe10e1 f30f7f4ac0 movdqu xmmword ptr [rdx-40h],xmm1
00007ff7`aefe10e6 660f6fca movdqa xmm1,xmm2
00007ff7`aefe10ea f30f6f42d0 movdqu xmm0,xmmword ptr [rdx-30h]
00007ff7`aefe10ef 660fefc2 pxor xmm0,xmm2
00007ff7`aefe10f3 f30f7f42d0 movdqu xmmword ptr [rdx-30h],xmm0
00007ff7`aefe10f8 f30f6f42e0 movdqu xmm0,xmmword ptr [rdx-20h]
00007ff7`aefe10fd 660fefc8 pxor xmm1,xmm0
00007ff7`aefe1101 f30f7f4ae0 movdqu xmmword ptr [rdx-20h],xmm1
00007ff7`aefe1106 493bc2 cmp rax,r10
00007ff7`aefe1109 76b5 jbe ClimbToTop!NewLockResource+0x40 (00007ff7`aefe10c0)
00007ff7`aefe110b 4863c1 movsxd rax,ecx
00007ff7`aefe110e 493bc0 cmp rax,r8
00007ff7`aefe1111 771e ja ClimbToTop!NewLockResource+0xb1 (00007ff7`aefe1131)
00007ff7`aefe1113 4a8d1408 lea rdx,[rax+r9]
00007ff7`aefe1117 660f1f840000000000 nop word ptr [rax+rax]
00007ff7`aefe1120 803276 xor byte ptr [rdx],76h
00007ff7`aefe1123 488d5201 lea rdx,[rdx+1]
00007ff7`aefe1127 ffc1 inc ecx
00007ff7`aefe1129 4863c1 movsxd rax,ecx
00007ff7`aefe112c 493bc0 cmp rax,r8
00007ff7`aefe112f 76ef jbe ClimbToTop!NewLockResource+0xa0 (00007ff7`aefe1120)
00007ff7`aefe1131 498bc1 mov rax,r9
00007ff7`aefe1134 4883c428 add rsp,28h
00007ff7`aefe1138 c3 ret
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
00007ff7`aefe1080 4883ec28        sub     rsp,28h
00007ff7`aefe1084 ff158e860000 call qword ptr [ClimbToTop!pfnLockResource (00007ff7`aefe9718)]
; r8=0x4600
00007ff7`aefe108a 4c8b057f860000 mov r8,qword ptr [ClimbToTop!g_Size (00007ff7`aefe9710)]
00007ff7`aefe1091 33c9 xor ecx,ecx ; ecx=0
00007ff7`aefe1093 4c8bc8 mov r9,rax ; r9=pfnLockResource,r9 是指向某数据的指针
00007ff7`aefe1096 498d4001 mov rax,r8+1 ; rax=0x4601
00007ff7`aefe109a 4883f840 cmp rax,40h
00007ff7`aefe109e 726b jb ClimbToTop!NewLockResource+0x8b (00007ff7`aefe110b)
; xmm2=76767676`76767676
00007ff7`aefe10a0 660f6f1598640000 movdqa xmm2,xmmword ptr [ClimbToTop!_xmm (00007ff7`aefe7540)]
00007ff7`aefe10a8 498d5110 lea rdx,[r9+10h] ; rdx=r9+10
00007ff7`aefe10ac 83e03f and eax,3Fh ; 保留 eax 后 6 位
00007ff7`aefe10af 4d8bd0 mov r10,r8 ; r10=r8 | r10=0x4600
00007ff7`aefe10b2 4c2bd0 sub r10,rax ; r10 -= rax | r10 = ffffffff`ffffffff
00007ff7`aefe10b5 6666660f1f840000000000 nop word ptr [rax+rax]
00007ff7`aefe10c0 f30f6f42f0 movdqu xmm0,xmmword ptr [rdx-10h] ; xmm0=某数据
00007ff7`aefe10c5 83c140 add ecx,40h ; ecx=0x40=64
00007ff7`aefe10c8 488d5240 lea rdx,[rdx+40h] ; rdx=rdx+0x40
00007ff7`aefe10cc 4863c1 movsxd rax,ecx ; rax=ecx,rax 高位扩展为 0 or 1
00007ff7`aefe10cf 660fefc2 pxor xmm0,xmm2 ; xmm0 = xmm0 xor xmm2
00007ff7`aefe10d3 f30f7f42b0 movdqu xmmword ptr [rdx-50h],xmm0 ; r9 指向 xmm0
00007ff7`aefe10d8 f30f6f4ac0 movdqu xmm1,xmmword ptr [rdx-40h] ; xmm1=[rdx-0x40]
00007ff7`aefe10dd 660fefca pxor xmm1,xmm2 ; xmm1 = xmm1 xor xmm2
00007ff7`aefe10e1 f30f7f4ac0 movdqu xmmword ptr [rdx-40h],xmm1 ; [rdx-0x40]=xmm1
00007ff7`aefe10e6 660f6fca movdqa xmm1,xmm2 ; xmm1=xmm2
00007ff7`aefe10ea f30f6f42d0 movdqu xmm0,xmmword ptr [rdx-30h] ; xmm0=[rdx-0x30]
00007ff7`aefe10ef 660fefc2 pxor xmm0,xmm2 ; xmm0 = xmm0 xor xmm2
00007ff7`aefe10f3 f30f7f42d0 movdqu xmmword ptr [rdx-30h],xmm0 ; [rdx-0x30]=xmm0
00007ff7`aefe10f8 f30f6f42e0 movdqu xmm0,xmmword ptr [rdx-20h] ; xmm0=[rdx-0x20]
00007ff7`aefe10fd 660fefc8 pxor xmm1,xmm0 ; xmm1=xmm0
00007ff7`aefe1101 f30f7f4ae0 movdqu xmmword ptr [rdx-20h],xmm1 ; [rdx-0x20]=xmm1
00007ff7`aefe1106 493bc2 cmp rax,r10 ; cmp rax, r10
00007ff7`aefe1109 76b5 jbe ClimbToTop!NewLockResource+0x40 (00007ff7`aefe10c0)
00007ff7`aefe110b 4863c1 movsxd rax,ecx ; rax=ecx
00007ff7`aefe110e 493bc0 cmp rax,r8
00007ff7`aefe1111 771e ja ClimbToTop!NewLockResource+0xb1 (00007ff7`aefe1131)
00007ff7`aefe1113 4a8d1408 lea rdx,[rax+r9] ; rdx=rax+r9
00007ff7`aefe1117 660f1f840000000000 nop word ptr [rax+rax]
00007ff7`aefe1120 803276 xor byte ptr [rdx],76h ; [rdx] = [rdx] xor 0x76
00007ff7`aefe1123 488d5201 lea rdx,[rdx+1] ; rdx += 1
00007ff7`aefe1127 ffc1 inc ecx ; ecx += 1
00007ff7`aefe1129 4863c1 movsxd rax,ecx ; rax=ecx
00007ff7`aefe112c 493bc0 cmp rax,r8
00007ff7`aefe112f 76ef jbe ClimbToTop!NewLockResource+0xa0 (00007ff7`aefe1120)
00007ff7`aefe1131 498bc1 mov rax,r9
00007ff7`aefe1134 4883c428 add rsp,28h
00007ff7`aefe1138 c3 ret

程序崩溃点

 使用.ecxr命令,查看程序崩溃点:

1
2
3
4
5
6
7
8
9
10
rax=0000000000000040 rbx=0000000000004600 rcx=0000000000000040
rdx=00007ff7aefef100 rsi=0000000000000000 rdi=00007ff7aefef0b0
rip=00007ff7aefe10d3 rsp=000000c9b416fd90 rbp=0000000000000000
r8=0000000000004600 r9=00007ff7aefef0b0 r10=00000000000045ff
r11=000000c9b416f7b0 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl nz na pe nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
ClimbToTop!NewLockResource+0x53:
00007ff7`aefe10d3 f30f7f42b0 movdqu xmmword ptr [rdx-50h],xmm0 ds:00007ff7`aefef0b0=76768989767676727676767576e62c3b

 使用!analyze -v,查看程序:

image-20230515215731933

 错误是由于程序段不可写造成的。

如何做

 之后,打算用python重构NewLockResource。首先,将NewLockResource使用到的内存区域dump下来:.writemem data.bin 7ff7aefef0b0 7ff7 aeef 47b0。之后,写出NewLockResource对应的python脚本:

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
131
132
133
134
135
136
137
138
139
140
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
# @Time : 2023/05/16 10:19:45
# @Author: wd-2711
'''

import binascii

def ret_128(addr):
addr -= 0x00007ff7aefef0b0
dd = data[addr:addr+0x10]
dd = dd[::-1]
dd_str = ""
for i in dd:
dd_str += str(i)[2:-1]
return int(dd_str, 16)

def ret_byte(addr):
addr -= 0x00007ff7aefef0b0
dd = data[addr:addr+0x1]
dd_str = str(i)[2:-1]
return int(dd_str, 16)

def set_128(addr, v):
global data
addr -= 0x00007ff7aefef0b0
v = hex(v)[2:].zfill(32).encode("utf-8")
vv_list = []
for i in range(len(v)//2):
vv = v[i*2:i*2+2]
vv_list.append(vv)
vv_list = vv_list[::-1]
for ind, i in enumerate(vv_list):
data[addr+ind] = i

def set_byte(addr, v):
global data
addr -= 0x00007ff7aefef0b0
v = hex(v)[2:].zfill(2).encode("utf-8")
vv_list = []
for i in range(len(v)//2):
vv = v[i*2:i*2+2]
vv_list.append(vv)
vv_list = vv_list[::-1]
for ind, i in enumerate(vv_list):
data[addr+ind] = i


if __name__ == "__main__":
with open("./babydsp/data.bin", "rb") as f:
data = binascii.hexlify(f.read(0x5700))

d = []
for i in range(len(data)//2):
d.append(data[i*2:i*2+2])
data = d
pre_data = data[:]

r8 = 0x4600 & 0xffffffffffffffff
ecx = 0x0 & 0xffffffff
r9 = 0x00007ff7aefef0b0 & 0xffffffffffffffff
rax = (r8 + 1) & 0xffffffffffffffff
if rax >= 0x40:
xmm2 = 0x76767676767676767676767676767676
rdx = (r9 + 0x10) & 0xffffffffffffffff
rax = rax & 0xffffffff0000003f
r10 = r8
r10 = (r10 - rax) & 0xffffffffffffffff
# print(hex(rdx)[2:].zfill(0x10), hex(r9)[2:].zfill(0x10))
# xmm0 = *(rdx - 0x10)
cir1 = cir2 = 0
while True:
cir1 += 1
xmm0 = ret_128(rdx - 0x10)
ecx = (ecx + 0x40) & 0xffffffff
rdx = (rdx + 0x40) & 0xffffffffffffffff
if ecx >= 0 and ecx <= 0x7FFFFFFF:
rax = ecx
else:
rax = ecx + 0xffffffff00000000
xmm0 = xmm0 ^ xmm2
# *(rdx - 0x50) = xmm0
set_128(rdx - 0x50, xmm0)
# xmm1 = *(rdx - 0x40)
xmm1 = ret_128(rdx - 0x40)
xmm1 = xmm1 ^ xmm2
# *(rdx - 0x40) = xmm1
set_128(rdx - 0x40, xmm1)
xmm1 = xmm2
# xmm0 = *(rdx - 0x30)
xmm0 = ret_128(rdx - 0x30)
xmm0 = xmm0 ^ xmm2
# *(rdx - 0x30) = xmm0
set_128(rdx - 0x30, xmm0)
# xmm0 = *(rdx - 0x20)
xmm0 = ret_128(rdx - 0x20)
xmm1 = xmm1 ^ xmm0
# *(rdx - 0x20) = xmm1
set_128(rdx - 0x20, xmm1)
if rax > r10:
break
# print("check-1")
if ecx >= 0 and ecx <= 0x7FFFFFFF:
rax = ecx
else:
rax = ecx + 0xffffffff00000000
if rax > r8:
rax = r9
exit(0)
rdx = (rax + r9) & 0xffffffffffffffff
while True:
cir2 += 1
# *(rdx) = *(rdx) ^ 0x76
set_byte(rdx, ret_byte(rdx) ^ 0x76)
rdx = (rdx + 1) & 0xffffffffffffffff
ecx = (ecx + 1) & 0xffffffff
if ecx >= 0 and ecx <= 0x7FFFFFFF:
rax = ecx
else:
rax = ecx + 0xffffffff00000000
if rax >= r8:
break
rax = r9

with open("./babydsp/data_decode.bin", "wb") as f:
write_data = []
for d in data:
dd = str(d)[2:-1]
dd = int(dd, 16)
write_data.append(dd)
write_data = bytes(write_data)
f.write(write_data)


# diff = 0
# for i in range(len(data)):
# if data[i] != pre_data[i]:
# diff += 1
# print(f"%.10f"% (diff/len(data)), diff)

 相当于对某段内存区域进行解密,然后将结果保存在data_decode.bin中。通过die.exe发现这是一个dll文件,使用ida打开,dllmain中发现函数,其中有:

image-20230516102237766

 可以确定,这就是我们要找的flag所在函数。

0x01 拿flag

image-20230516150843423

 经过对上述函数的分析,我们必须要知道输入的字符串序列,才能得出最后的flag。我们知道,dll加载到exe中会自动执行dllmain,虽然载入到file中的dll是加密的。所以下一步打算找一找crash文件中是否有字符串序列。

 并没有查找到crash文件中的字符串,满足长度为192,且每个元素都小于等于0x31,且能生成合适的flag。解密后的dll也查找了,并没有找到。

0x02 其他人

 网上的资料很少,并没有找到其他师傅的题解。难受。

maze

image-20230516200057398

 这是难度为5的题???感觉好简单。。

 8*8的迷宫:

1
2
3
4
5
6
7
8
  ******
* * *
*** * **
** * **
* *# *
** *** *
** *
********

o为向右,O为向左,.为向上,0为向下。起点为左上角,终点为#。因此,flag为:nctf{o0oo00O000oooo..OO}

reverse_box

 调试了很久,感觉不知道从何入手。它是一个程序,程序逻辑就是:输入字符串s,然后选取随机数r,对s+r进行一个加密,之后得到一个结果并输出16进制。无论输入的s是否是flag,它都会输出16进制的串?那??我应该怎么入手呢??

initfini函数也看不出来啥来,里面的函数没发现有用的内容,而且没有找到SEH函数,似乎这不是ELF的内容。

0x00 其他大佬wp

 我操了,攻防世界少了条件,条件是:95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a。flag格式:TWCTF{}。明天再看吧,服了。

 调试了很久,有一个坑,就是:

image-20230518151339733

 上述红框是一个有符号数比较,其汇编指令为jns,所以写脚本的时候应该是0-127范围内的正数。

 还有就是ror其实是循环移位。

 最后上脚本:

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
def alg(r):
a1 = [0x71, 0xEA, 0xB1, 0x07, 0x44, 0x1C, 0xF1, 0xB7, 0x30, 0xDC, 0xA0, 0xBF, 0x73, 0xFB, 0xF0, 0xB7, 0x70, 0xF4, 0xEE, 0xB7, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xF1, 0xB7, 0x08, 0x9C, 0xF1, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xDC, 0xA0, 0xBF, 0xC9, 0xDF, 0xEF, 0xB7, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x9A, 0xF1, 0xB7, 0x38, 0xDC, 0xA0, 0xBF, 0x80, 0xDC, 0xA0, 0xBF, 0x4B, 0xEB, 0xEF, 0xB7, 0x4C, 0x82, 0x04, 0x08, 0x38, 0xDC, 0xA0, 0xBF, 0x74, 0x9A, 0xF1, 0xB7, 0x01, 0x00, 0x00, 0x00, 0xA0, 0xF4, 0xEE, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x99, 0xF1, 0xB7, 0x00, 0x90, 0xF1, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xEA, 0xEF, 0xB7, 0x08, 0x9C, 0xF1, 0xB7, 0x00, 0x8D, 0xF1, 0xB7, 0x30, 0xDD, 0xA0, 0xBF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xF1, 0xB7, 0x18, 0x99, 0xF1, 0xB7, 0x30, 0xDC, 0xA0, 0xBF, 0xDD, 0x82, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xDC, 0xA0, 0xBF, 0x00, 0x50, 0xED, 0xB7, 0x37, 0x90, 0xE0, 0xB7, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x00, 0x00, 0xC8, 0xED, 0xD2, 0xB7, 0xB0, 0xF1, 0xEE, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x50, 0xED, 0xB7, 0xBD, 0x83, 0x04, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xA0, 0x04, 0x08, 0xB2, 0x87, 0x04, 0x08, 0x02, 0x00, 0x00, 0x00, 0x24, 0xDD, 0xA0, 0xBF, 0x30, 0xDD, 0xA0, 0xBF, 0x1B, 0x0C, 0xD5, 0xB7, 0xDC, 0x53, 0xED, 0xB7, 0x4C, 0x82, 0x04, 0x08, 0x6B, 0x87, 0x04, 0x08, 0x00, 0x4A, 0x5A, 0xB4, 0x00, 0x50, 0xED, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0xA6, 0xD3, 0xB7, 0x02, 0x00, 0x00, 0x00, 0x24, 0xDD, 0xA0, 0xBF, 0x30, 0xDD, 0xA0, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xED, 0xB7, 0x04, 0x9C, 0xF1, 0xB7, 0x00, 0x90, 0xF1, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xED, 0xB7, 0x00, 0x50, 0xED, 0xB7, 0x00, 0x00, 0x00, 0x00, 0xEB, 0xEB, 0xC2, 0x52, 0xFB, 0xC5, 0x37, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
a1[0] = r
v12 = 1
v13 = 1
while True:
if (v12 >= 0 and v12 <= 127):
v2 = 0
else:
v2 = 27
v12 ^= ((2 * v12) & 0xff) ^ v2
v12 = v12 & 0xff
v14 = (4 * ((2 * v13) ^ v13)) ^ (2 * v13) ^ v13
v14 = v14 & 0xff
v15 = (16 * v14) ^ v14
v15 = v15 & 0xff
if (v15 >= 0 and v15 <= 127):
v3 = 0
else:
v3 = 9
v13 = v15 ^ v3
v13 = v13 & 0xff
v4 = v13
v4 = (v13 >> 7) + ((v13 << 1) & 0xff)
v5 = v4 ^ ((v13 ^ a1[0]) & 0xff)
v6 = v13
v6 = (v13 >> 6) + ((v13 << 2) & 0xff)
v7 = v6 ^ v5
v8 = v13
v8 = (v13 >> 5) + ((v13 << 3) & 0xff)
v9 = v8 ^ v7
v10 = v13
v10 = (v13 >> 4) + ((v13 << 4) & 0xff)
result = v10 ^ v9
a1[v12 & 0xff] = result

if v12 == 1:
break

return a1

def main(a1, tar):
flag = ""
for i in range(len(tar)//2):
tt = tar[2*i:2*i+2]
for j in range(32, 128):
res = hex(a1[j] & 0xff)[2:].zfill(2)
if res == tt:
flag += chr(j)
break
print(flag)

if __name__ == "__main__":
target = "95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a"
for r in range(256):
a1 = alg(r)
main(a1, target)
# TWCTF{5UBS717U710N_C1PH3R_W17H_R4ND0M123D_5-B0X}

easygo

1400k的文件,有点小大。由于静态链接依赖的方法,最简单的 Go 二进制文件大小为数兆字节,而具有适当功能的二进制文件可以在 15-20mb范围内计算。

 相关的Go逆向的博客,有事件可以看一下:https://www.anquanke.com/post/id/214940

 如果不用单独的插件,ida显示出来就一片混乱,有很多函数(因为静态链接)。之后使用go_parse的ida插件对程序进行解析,如下图所示:

image-20230519123043007

 上图红框为长度检查,黄框为内容检查,如果检查通过就输出success,否则输出fail

 动态调试发现长度为0x2A=42,且格式为flag{xxx}。之后程序在memqbody下断点,成功发现flag{92094daf-33c9-431e-a85a-8bfbd5df98ad}

image-20230519123831877

riskv-and-reward

risc_v:开源精简指令集,本题为risv_v 64位的ELF,感觉动调需要docker

ida75不能直接加载,需要需要加载的需要新的loader:https://github.com/bingseclab/ida_riscv(不行),然后https://github.com/lcq2/riscv-ida(虽然支持ida7.x,但是只是32位的),但是也是只是静态调试。而ida7.7支持RISC_V

 然而,RISC-V的汇编并不好分析,函数名挺多而且都不知道是干啥的,而且不能转源代码(太菜咯)。所以打算用qemu(虚拟化软件,可以模拟多种硬件平台)动态调试。之前做babyArm(在re-part-5)中有搭建过qemu+ida的环境。

 这个题搭完qemu+risc环境一运行直接出结果了,栓q。

image-20230520154446406

polyre

 发现是使用LLVM 7.0.1混淆进行处理后的ELF64。直接打开:

image-20230520160431473

 CFG可以的。还是先看一下OLLVM吧。想直接在memcmp上打断点查看,发现输入经过变换之后才进行比较,输入长度应该为0x30。找到一个脚本,链接如下:https://github.com/cq674350529/deflat,主要是用angr做符号执行来做去控制流平坦化,之后生成文件:

image-20230521232811180

 之后写脚本,先写正向脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
inp = "".join(["98765432" for i in range(6)])


for i in range(6):
inp_item = inp[i*8:i*8+8][::-1]
inp_hex = 0
for j in inp_item:
inp_hex = inp_hex << 8
inp_hex += ord(j)
old_iter = inp_hex
v7 = 0
while True:
if v7 >= 64:
break
new_iter = (old_iter << 1) & 0xffffffffffffffff
if old_iter < 0 or old_iter > 0x7FFFFFFFFFFFFFFF:
new_iter ^= 0xB0004B7679FA26B3
v7 += 1
old_iter = new_iter
print(hex(old_iter))

 难点在于new_iter = (old_iter << 1) & 0xffffffffffffffff,一开始我总以为这样会失去信息。其实思考到后面,如果old_iter是奇数,那么就一定会走new_iter ^= 0xB0004B7679FA26B3,如果是偶数,就一定不会走上述语句。所以可以写解密脚本:

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
targ = [0x96, 0x62, 0x53, 0x43, 0x6D, 0xF2, 0x8F, 0xBC, 0x16, 0xEE, 0x30, 0x05, 0x78, 0x00, 0x01, 0x52, 0xEC, 0x08, 0x5F, 0x93, 0xEA, 0xB5, 0xC0, 0x4D, 0x50, 0xF4, 0x53, 0xD8, 0xAF, 0x90, 0x2B, 0x34, 0x81, 0x36, 0x2C, 0xAA, 0xBC, 0x0E, 0x25, 0x8B, 0xE4, 0x8A, 0xC6, 0xA2, 0x81, 0x9F, 0x75, 0x55, 0xB3, 0x26, 0xFA, 0x79, 0x76, 0x4B, 0x00]
for i in range(6):
inp_item = targ[i*8:i*8+8][::-1]
inp_hex = 0
for j in inp_item:
inp_hex = inp_hex << 8
inp_hex += j
old_iter = inp_hex
v7 = 0
while True:
new_iter = old_iter
v7 += 1
if new_iter % 2 == 0:
old_iter = new_iter >> 1
else:
new_iter ^= 0xB0004B7679FA26B3
old_iter = (new_iter >> 1) | 0x8000000000000000
if v7 >= 64:
break
flag = ""
for i in range(len(hex(old_iter)[2:])//2):
ii = hex(old_iter)[2:][i*2:i*2+2]
flag += chr(int(ii, 16))
flag = flag[::-1]
print(flag, end = "")
# flag{6ff29390-6c20-4c56-ba70-a95758e3d1f8}

补充:LLVM

 相当于一个虚拟机。传统编译器架构:

1
2
3
4
5
6
7
源代码
|
Frontend 前端:词法分析、语法分析、语义分析,生成中间代码
|
Optimizer 优化器:中间代码优化
|
Backend 后端:生成机器码

 LLVM架构:

image-20230520155153178

1
2
3
1. 不同前端后端使用统一的中间代码LLVM Intermediate Representation (LLVM IR)
2. 如果需要支持一种新的编程语言,那么只需要实现一个新的前端
3. 如果需要支持一种新的硬件设备,那么只需要实现一个新的后端

 LLVM混淆(LLVM Obfuscation,也叫OLLVM)是一种对LLVM IR进行变换的技术,旨在使生成的程序更难以阅读和分析。LLVM混淆技术可以应用于源代码、字节码、机器码等不同的层次。可以看一下博客:https://www.cnblogs.com/theseventhson/p/14861940.html

留言

2023-03-29

© 2024 wd-z711

⬆︎TOP