dilemma
这是经典的100个囚徒问题,先根据反编译chal的主函数来写一个player的解密脚本
import sys
def solve():
try:
prompt = sys.stdin.readline().strip()
except EOFError:
return
try:
player_number = int(prompt.split()[4].strip('.'))
except:
return
current_box = player_number
for _ in range(50):
print(current_box)
sys.stdout.flush()
try:
response = sys.stdin.readline().strip()
except EOFError:
break
if not response.startswith("FOUND"):
break
try:
found_number = int(response.split()[1])
except:
break
if found_number == player_number:
return
try:
next_prompt = sys.stdin.readline().strip()
except EOFError:
break
current_box = found_number
solve()
EOF手动运行一下
nc chall.polygl0ts.ch 6667
Provide Python script for player 1 (end with string 'EOF' on its own line):
import sys
def solve():
try:
prompt = sys.stdin.readline().strip()
except EOFError:
return
try:
player_number = int(prompt.split()[4].strip('.'))
except:
return
current_box = player_number
for _ in range(50):
print(current_box)
sys.stdout.flush()
try:
response = sys.stdin.readline().strip()
except EOFError:
break
if not response.startswith("FOUND"):
break
try:
found_number = int(response.split()[1])
except:
break
if found_number == player_number:
return
try:
next_prompt = sys.stdin.readline().strip()
except EOFError:
break
current_box = found_number
solve()
EOF
You are player number 1. You have 50 attempts left. Which box do you want to open?
[python 1] 1
The box 1 contains number 99.
You are player number 1. You have 49 attempts left. Which box do you want to open?
[python 1] 99
The box 99 contains number 36.
You are player number 1. You have 48 attempts left. Which box do you want to open?
[python 1] 36
The box 36 contains number 60.
You are player number 1. You have 47 attempts left. Which box do you want to open?
[python 1] 60
The box 60 contains number 13.
You are player number 1. You have 46 attempts left. Which box do you want to open?
[python 1] 13
The box 13 contains number 27.
You are player number 1. You have 45 attempts left. Which box do you want to open?
[python 1] 27
The box 27 contains number 20.
You are player number 1. You have 44 attempts left. Which box do you want to open?
[python 1] 20
The box 20 contains number 4.
You are player number 1. You have 43 attempts left. Which box do you want to open?
[python 1] 4
The box 4 contains number 55.
You are player number 1. You have 42 attempts left. Which box do you want to open?
[python 1] 55
The box 55 contains number 1.
Player 1 succeeded.
Provide Python script for player 2 (end with string 'EOF' on its own line):成功运行,但是100个player去一个个运行明显不现实,于是需要一个自动化的脚本去上传
from pwn import *
HOST = "chall.polygl0ts.ch"
PORT = 6667
payload_script = r"""
import sys
def solve():
try:
prompt = sys.stdin.readline().strip()
except EOFError:
return
try:
player_number = int(prompt.split()[4].strip('.'))
except:
return
current_box = player_number
for _ in range(50):
print(current_box)
sys.stdout.flush()
try:
response = sys.stdin.readline().strip()
except EOFError:
break
if not response.startswith("FOUND"):
break
try:
found_number = int(response.split()[1])
except:
break
if found_number == player_number:
return
try:
next_prompt = sys.stdin.readline().strip()
except EOFError:
break
current_box = found_number
solve()
EOF
"""
def main():
r = remote(HOST, PORT)
buffer = b""
while True:
try:
data = r.recv(timeout=1)
except EOFError:
print("[*] connection closed by remote host")
break
except:
continue
if not data:
continue
print(data.decode(errors="ignore"), end="") # 打印服务器输出
buffer += data
# 检测上传请求
if b"Provide Python script for player" in buffer:
print("\n[*] 发送脚本...\n")
r.send(payload_script.encode())
buffer = b"" # 重置缓冲
r.close()
if __name__ == "__main__":
main()
All players succeeded!
Flag: EPFL{wow_such_puzzle_did_you_google_the_solution_or_did_you_just_came_up_with_it?}
Another Android Applaketion
通过主函数可知test调用了80次去检查,且test在native层,使用IDA打开so文件
test是通过拼接类名和方法名去调用方法,且通过寻找一个方法可知
每次调用之后会校验,且更新aaa,bbb的值。
于是保存java源文件,使用脚本将所有类的方法放在一起
import os
import json
JAVA_FILES_DIR = "\\新建文件夹\\sources\\com\\lake\\ctf\\"
# 存储所有校验数据的字典
all_check_data = {}
# ----------------------------------------------------
# 字符串分割提取函数 (比正则更可靠)
# ----------------------------------------------------
def extract_data_from_content(content, filename):
"""使用字符串查找和分割来提取 Class ID 和所有校验方法的数据"""
# 1. 提取 Class ID (AAA)
class_id = None
# 查找 com.lake.ctf.Check 后面的哈希值
class_id_start_key = "com.lake.ctf.Check"
start_index = content.find(class_id_start_key)
if start_index != -1:
start_index += len(class_id_start_key)
# 假设哈希值后面紧跟着空格或注释结束符
end_index = content.find(" ", start_index)
if end_index == -1:
end_index = content.find("*/", start_index)
if end_index != -1:
class_id = content[start_index:end_index].strip()
if not class_id:
# 对于 C0320R.java 和 MainActivity.java 等文件,这是正常的
print(f"警告: 无法从 {filename} 中提取 Class ID。跳过。")
return None, 0
# 2. 提取所有 Method Checks (遍历所有 Check 标识符)
extracted_count = 0
methods_data = {}
# 我们知道每个方法都以 /* renamed from: Check... */ 开头
method_marker = "/* renamed from: Check"
current_pos = content.find(method_marker)
while current_pos != -1:
# A. 提取 Method ID (BBB)
method_id_start = current_pos + len(method_marker)
method_id_end = content.find(" */", method_id_start)
method_id = content[method_id_start:method_id_end].strip()
# B. 查找 if 语句和 nop 调用
if_start = content.find("if (", method_id_end)
if if_start == -1:
current_pos = content.find(method_marker, method_id_end)
continue
# C. 提取校验逻辑 (Logic)
logic_start = if_start + len("if (")
logic_end = content.find(") {", logic_start)
logic = content[logic_start:logic_end].strip()
# D. 提取 Next AAA 和 Next BBB
nop_start = content.find("nop(", logic_end)
if nop_start != -1:
nop_args_start = nop_start + len("nop(")
nop_args_end = content.find(");", nop_args_start)
# 提取 nop(...) 中的所有参数字符串
nop_args_str = content[nop_args_start:nop_args_end].strip()
# 分割参数并去除引号
try:
# nop("AAA", "BBB") -> "AAA", "BBB"
args_parts = [arg.strip().replace('"', '') for arg in nop_args_str.split(',')]
next_aaa = args_parts[0]
next_bbb = args_parts[1]
# 规范化逻辑格式 (用于 Z3 转换)
logic = logic.replace("str.charAt", "F")
methods_data[method_id] = {
"logic": logic,
"next_aaa": next_aaa,
"next_bbb": next_bbb
}
extracted_count += 1
except IndexError:
# 忽略格式不正确的 nop 调用
pass
# 移动到下一个方法的开始位置
current_pos = content.find(method_marker, method_id_end + 1)
return class_id, extracted_count, methods_data
# ----------------------------------------------------
# 主函数
# ----------------------------------------------------
def extract_all_data():
global all_check_data
for filename in os.listdir(JAVA_FILES_DIR):
if filename.endswith(".java"):
filepath = os.path.join(JAVA_FILES_DIR, filename)
# 强制使用 UTF-8 编码读取文件,解决编码问题
try:
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
except UnicodeDecodeError:
# 如果 UTF-8 失败,尝试 Latin-1 或其他常用编码
try:
with open(filepath, 'r', encoding='latin-1') as f:
content = f.read()
except Exception as e:
print(f"致命错误: 无法读取文件 {filename},跳过。")
continue
# 提取数据
class_id, count, methods_data = extract_data_from_content(content, filename)
if class_id:
all_check_data[class_id] = methods_data
print(f"成功处理 {filename}。提取了 {count} 个校验方法。")
# 保存数据到 JSON 文件
with open('check_data.json', 'w') as f:
json.dump(all_check_data, f, indent=4)
print("\n--- 完成 ---")
print(f"所有校验数据已保存到 'check_data.json' 文件中。总共处理了 {len(all_check_data)} 个类。")
if __name__ == "__main__":
extract_all_data()import json
from z3 import *
# 初始状态 (从第一个文件 Check86e50149... 中的 JNI 调用 Init 中获取)
INITIAL_AAA = "86e50149658661312a9e0b35558d84f6c6d3da797f552a9657fe0558ca40cdef"
INITIAL_BBB = "031b4af5197ec30a926f48cf40e11a7dbc470048a21e4003b7a3c07c5dab1baa"
FLAG_LEN = 55 # 根据您提取的最高索引 F[i] 确定,此处假设为 55
TOTAL_CHECKS = 80 # 总共需要追踪 80 步
def solve_flag():
# 1. 加载数据
try:
with open('check_data.json', 'r') as f:
all_check_data = json.load(f)
except FileNotFoundError:
print("错误: 找不到 'check_data.json' 文件。请先运行 1_extract_data.py。")
return
# 2. 初始化 Z3 求解器和 Flag 变量
s = Solver()
# F[0] 到 F[FLAG_LEN - 1]
F = [Int(f'F_{i}') for i in range(FLAG_LEN)]
# 约束 1: 可见 ASCII 范围 (32 - 126)
for i in range(FLAG_LEN):
s.add(F[i] >= ord('!'))
s.add(F[i] <= ord('~'))
# 3. 追踪 80 步并添加约束
current_aaa = INITIAL_AAA
current_bbb = INITIAL_BBB
print("--- 追踪执行路径并构建 Z3 约束 ---")
for step in range(1, TOTAL_CHECKS + 1):
try:
check_info = all_check_data[current_aaa][current_bbb]
except KeyError:
print(f"!!! 追踪失败 !!! 找不到第 {step} 步的校验方法。")
print(f" 当前 Class ID: {current_aaa}")
print(f" 当前 Method ID: {current_bbb}")
return
logic_str = check_info["logic"]
try:
constraint = eval(logic_str)
s.add(constraint)
except Exception as e:
print(f"!!! Z3 转换失败 !!! 第 {step} 步的逻辑: {logic_str}")
print(f" 错误: {e}")
return
print(f"[{step:02d}/{TOTAL_CHECKS}] - 添加约束: {logic_str}")
# 更新下一个状态
current_aaa = check_info["next_aaa"]
current_bbb = check_info["next_bbb"]
# 4. 求解
print("\n--- 求解 Z3 约束 ---")
if s.check() == sat:
m = s.model()
flag = "".join(chr(m[F[i]].as_long()) for i in range(FLAG_LEN))
print("Flag: ", flag)
if __name__ == "__main__":
solve_flag()
#Flag: EPFL{Wh1_3v3n_b0th3r_w1th_J4v4_1n_th3_f1rst_Pl4c3?????}drum machine
## drum machine
通过ida分析 main 函数,可以知道:
1、读取用户输入字符串。
2、调用 decomposeInput 将字符串转换为 vector<Step>。
3、初始化 DrumMachine 对象(加载巨大的转移表)。
4、调用 DrumMachine::playBeat 执行状态机。
5、检查 DrumMachine::getState() == 181。如果成立,打印 "Congratulations"。
同时main函数还定义了字符到状态机输入的转换规则:
遍历输入字符串的每一个字符。
对每个字符的 8 个比特位(0-7)进行检查。
如果第 j 位是 1,则生成一个数值为 j 的 Hit。
关键特性:对于同一个字符,生成的 Hit 序列是严格递增的(从小到大遍历比特位)。一旦 Hit 数值变小,意味着开始处理下一个字符。
因此可以知道这是一个典型的 DFA (确定性有限自动机) 实现。
转移表硬编码在 DrumMachine 的构造函数中
void fastcall DrumMachine::DrumMachine(DrumMachine *this, int64 a2)
{
_DWORD *v2; // rdi
std::vector<Step>::vector(this, a2);
v2 = (_DWORD )((char )this + 24);
memset(v2, 0, 0x1700u);
*v2 = 1;
*((_DWORD *)this + 15) = 1;
*((_DWORD *)this + 16) = 2;
*((_DWORD *)this + 25) = 2;
*((_DWORD *)this + 26) = 4;
*((_DWORD *)this + 27) = 5;
*((_DWORD *)this + 28) = 3;
*((_DWORD *)this + 30) = 6;
*((_DWORD *)this + 31) = 3;
*((_DWORD *)this + 32) = 3;
*((_DWORD *)this + 34) = 4;
*((_DWORD *)this + 37) = 6;
*((_DWORD *)this + 43) = 4;
*((_DWORD *)this + 44) = 5;
*((_DWORD *)this + 46) = 5;
*((_DWORD *)this + 47) = 6;
*((_DWORD *)this + 53) = 5;
*((_DWORD *)this + 56) = 7;
*((_DWORD *)this + 65) = 10;
*((_DWORD *)this + 66) = 10;
*((_DWORD *)this + 67) = 7;
*((_DWORD *)this + 68) = 8;
*((_DWORD *)this + 70) = 4;
*((_DWORD *)this + 71) = 11;
*((_DWORD *)this + 72) = 9;
*((_DWORD *)this + 77) = 11;
*((_DWORD *)this + 81) = 10;
*((_DWORD *)this + 90) = 6;
*((_DWORD *)this + 91) = 12;
*((_DWORD *)this + 92) = 11;
*((_DWORD *)this + 94) = 12;
*((_DWORD *)this + 101) = 11;
*((_DWORD *)this + 103) = 13;
*((_DWORD *)this + 112) = 16;
*((_DWORD *)this + 113) = 14;
*((_DWORD *)this + 122) = 15;
*((_DWORD *)this + 131) = 16;
*((_DWORD *)this + 140) = 17;
*((_DWORD *)this + 142) = 19;
*((_DWORD *)this + 143) = 19;
*((_DWORD *)this + 144) = 18;
*((_DWORD *)this + 149) = 13;
*((_DWORD *)this + 153) = 21;
*((_DWORD *)this + 154) = 21;
*((_DWORD *)this + 155) = 19;
*((_DWORD *)this + 164) = 20;
*((_DWORD *)this + 166) = 21;
*((_DWORD *)this + 173) = 22;
*((_DWORD *)this + 175) = 21;
*((_DWORD *)this + 176) = 21;
*((_DWORD *)this + 177) = 17;
*((_DWORD *)this + 178) = 21;
*((_DWORD *)this + 179) = 22;
*((_DWORD *)this + 188) = 23;
*((_DWORD *)this + 190) = 25;
*((_DWORD *)this + 191) = 24;
*((_DWORD *)this + 197) = 25;
*((_DWORD *)this + 200) = 25;
*((_DWORD *)this + 209) = 26;
*((_DWORD *)this + 218) = 28;
*((_DWORD *)this + 219) = 27;
*((_DWORD *)this + 228) = 28;
*((_DWORD *)this + 230) = 29;
*((_DWORD *)this + 237) = 28;
*((_DWORD *)this + 239) = 30;
*((_DWORD *)this + 248) = 26;
*((_DWORD *)this + 249) = 30;
*((_DWORD *)this + 250) = 26;
*((_DWORD *)this + 251) = 31;
*((_DWORD *)this + 260) = 32;
*((_DWORD *)this + 262) = 33;
*((_DWORD *)this + 269) = 32;
*((_DWORD *)this + 271) = 35;
*((_DWORD *)this + 272) = 34;
*((_DWORD *)this + 281) = 36;
*((_DWORD *)this + 282) = 37;
*((_DWORD *)this + 283) = 35;
*((_DWORD *)this + 292) = 36;
*((_DWORD *)this + 294) = 37;
*((_DWORD *)this + 301) = 36;
*((_DWORD *)this + 303) = 38;
*((_DWORD *)this + 312) = 39;
*((_DWORD *)this + 321) = 40;
*((_DWORD *)this + 330) = 41;
*((_DWORD *)this + 339) = 41;
*((_DWORD *)this + 340) = 42;
*((_DWORD *)this + 342) = 43;
*((_DWORD *)this + 349) = 45;
*((_DWORD *)this + 351) = 45;
*((_DWORD *)this + 352) = 43;
*((_DWORD *)this + 353) = 46;
*((_DWORD *)this + 354) = 43;
*((_DWORD *)this + 355) = 44;
*((_DWORD *)this + 364) = 45;
*((_DWORD *)this + 366) = 47;
*((_DWORD *)this + 367) = 41;
*((_DWORD *)this + 368) = 46;
*((_DWORD *)this + 373) = 41;
*((_DWORD *)this + 377) = 47;
*((_DWORD *)this + 386) = 49;
*((_DWORD *)this + 387) = 48;
*((_DWORD *)this + 396) = 49;
*((_DWORD *)this + 398) = 50;
*((_DWORD *)this + 405) = 52;
*((_DWORD *)this + 407) = 51;
*((_DWORD *)this + 416) = 52;
*((_DWORD *)this + 425) = 53;
*((_DWORD *)this + 434) = 55;
*((_DWORD *)this + 435) = 54;
*((_DWORD *)this + 444) = 55;
*((_DWORD *)this + 446) = 58;
*((_DWORD *)this + 447) = 56;
*((_DWORD *)this + 453) = 57;
*((_DWORD *)this + 456) = 57;
*((_DWORD *)this + 465) = 58;
*((_DWORD *)this + 474) = 60;
*((_DWORD *)this + 475) = 59;
*((_DWORD *)this + 484) = 60;
*((_DWORD *)this + 486) = 61;
*((_DWORD *)this + 493) = 60;
*((_DWORD *)this + 495) = 62;
*((_DWORD *)this + 504) = 63;
*((_DWORD *)this + 513) = 66;
*((_DWORD *)this + 514) = 66;
*((_DWORD *)this + 515) = 64;
*((_DWORD *)this + 524) = 65;
*((_DWORD *)this + 526) = 66;
*((_DWORD *)this + 533) = 68;
*((_DWORD *)this + 535) = 67;
*((_DWORD *)this + 544) = 68;
*((_DWORD *)this + 553) = 69;
*((_DWORD *)this + 562) = 70;
*((_DWORD *)this + 571) = 66;
*((_DWORD *)this + 572) = 71;
*((_DWORD *)this + 574) = 73;
*((_DWORD *)this + 575) = 74;
*((_DWORD *)this + 576) = 72;
*((_DWORD *)this + 581) = 67;
*((_DWORD *)this + 585) = 74;
*((_DWORD *)this + 586) = 73;
*((_DWORD *)this + 595) = 74;
*((_DWORD *)this + 604) = 75;
*((_DWORD *)this + 606) = 76;
*((_DWORD *)this + 613) = 75;
*((_DWORD *)this + 615) = 77;
*((_DWORD *)this + 624) = 78;
*((_DWORD *)this + 633) = 79;
*((_DWORD *)this + 642) = 82;
*((_DWORD *)this + 643) = 80;
*((_DWORD *)this + 652) = 81;
*((_DWORD *)this + 654) = 82;
*((_DWORD *)this + 661) = 81;
*((_DWORD *)this + 663) = 83;
*((_DWORD *)this + 672) = 84;
*((_DWORD *)this + 681) = 85;
*((_DWORD *)this + 690) = 86;
*((_DWORD *)this + 699) = 88;
*((_DWORD *)this + 700) = 87;
*((_DWORD *)this + 702) = 89;
*((_DWORD *)this + 703) = 87;
*((_DWORD *)this + 704) = 88;
*((_DWORD *)this + 709) = 83;
*((_DWORD *)this + 713) = 91;
*((_DWORD *)this + 714) = 89;
*((_DWORD *)this + 723) = 90;
*((_DWORD *)this + 732) = 91;
*((_DWORD *)this + 734) = 94;
*((_DWORD *)this + 735) = 87;
*((_DWORD *)this + 736) = 87;
*((_DWORD *)this + 737) = 92;
*((_DWORD *)this + 741) = 93;
*((_DWORD *)this + 746) = 88;
*((_DWORD *)this + 747) = 93;
*((_DWORD *)this + 756) = 94;
*((_DWORD *)this + 758) = 95;
*((_DWORD *)this + 765) = 97;
*((_DWORD *)this + 767) = 97;
*((_DWORD *)this + 768) = 96;
*((_DWORD *)this + 777) = 99;
*((_DWORD *)this + 778) = 99;
*((_DWORD *)this + 779) = 97;
*((_DWORD *)this + 788) = 98;
*((_DWORD *)this + 790) = 99;
*((_DWORD *)this + 797) = 100;
*((_DWORD *)this + 799) = 100;
*((_DWORD *)this + 808) = 101;
*((_DWORD *)this + 817) = 102;
*((_DWORD *)this + 826) = 103;
*((_DWORD *)this + 835) = 99;
*((_DWORD *)this + 836) = 104;
*((_DWORD *)this + 838) = 106;
*((_DWORD *)this + 839) = 105;
*((_DWORD *)this + 845) = 100;
*((_DWORD *)this + 848) = 108;
*((_DWORD *)this + 849) = 108;
*((_DWORD *)this + 850) = 101;
*((_DWORD *)this + 851) = 106;
*((_DWORD *)this + 860) = 107;
*((_DWORD *)this + 862) = 108;
*((_DWORD *)this + 869) = 107;
*((_DWORD *)this + 871) = 109;
*((_DWORD *)this + 880) = 109;
*((_DWORD *)this + 881) = 109;
*((_DWORD *)this + 882) = 110;
*((_DWORD *)this + 891) = 111;
*((_DWORD *)this + 894) = 107;
*((_DWORD *)this + 895) = 111;
*((_DWORD *)this + 896) = 112;
*((_DWORD *)this + 900) = 114;
*((_DWORD *)this + 901) = 114;
*((_DWORD *)this + 905) = 108;
*((_DWORD *)this + 906) = 113;
*((_DWORD *)this + 915) = 114;
*((_DWORD *)this + 918) = 116;
*((_DWORD *)this + 919) = 114;
*((_DWORD *)this + 920) = 115;
*((_DWORD *)this + 924) = 110;
*((_DWORD *)this + 925) = 110;
*((_DWORD *)this + 929) = 115;
*((_DWORD *)this + 930) = 116;
*((_DWORD *)this + 939) = 117;
*((_DWORD *)this + 948) = 118;
*((_DWORD *)this + 950) = 119;
*((_DWORD *)this + 957) = 120;
*((_DWORD *)this + 959) = 120;
*((_DWORD *)this + 968) = 121;
*((_DWORD *)this + 977) = 122;
*((_DWORD *)this + 986) = 123;
*((_DWORD *)this + 995) = 125;
*((_DWORD *)this + 996) = 124;
*((_DWORD *)this + 998) = 125;
*((_DWORD *)this + 1005) = 124;
*((_DWORD *)this + 1007) = 126;
*((_DWORD *)this + 1016) = 127;
*((_DWORD *)this + 1025) = 128;
*((_DWORD *)this + 1034) = 130;
*((_DWORD *)this + 1035) = 129;
*((_DWORD *)this + 1044) = 130;
*((_DWORD *)this + 1046) = 133;
*((_DWORD *)this + 1047) = 131;
*((_DWORD *)this + 1053) = 126;
*((_DWORD *)this + 1056) = 132;
*((_DWORD *)this + 1065) = 128;
*((_DWORD *)this + 1066) = 132;
*((_DWORD *)this + 1067) = 133;
*((_DWORD *)this + 1076) = 134;
*((_DWORD *)this + 1078) = 135;
*((_DWORD *)this + 1085) = 136;
*((_DWORD *)this + 1087) = 136;
*((_DWORD *)this + 1096) = 137;
*((_DWORD *)this + 1105) = 138;
*((_DWORD *)this + 1114) = 139;
*((_DWORD *)this + 1123) = 142;
*((_DWORD *)this + 1124) = 140;
*((_DWORD *)this + 1126) = 142;
*((_DWORD *)this + 1127) = 143;
*((_DWORD *)this + 1128) = 141;
*((_DWORD *)this + 1133) = 140;
*((_DWORD *)this + 1137) = 144;
*((_DWORD *)this + 1138) = 142;
*((_DWORD *)this + 1147) = 143;
*((_DWORD *)this + 1156) = 144;
*((_DWORD *)this + 1158) = 147;
*((_DWORD *)this + 1159) = 144;
*((_DWORD *)this + 1160) = 146;
*((_DWORD *)this + 1161) = 145;
*((_DWORD *)this + 1165) = 146;
*((_DWORD *)this + 1170) = 141;
*((_DWORD *)this + 1171) = 146;
*((_DWORD *)this + 1180) = 147;
*((_DWORD *)this + 1182) = 148;
*((_DWORD *)this + 1189) = 147;
*((_DWORD *)this + 1191) = 148;
*((_DWORD *)this + 1192) = 149;
*((_DWORD *)this + 1201) = 151;
*((_DWORD *)this + 1202) = 151;
*((_DWORD *)this + 1203) = 150;
*((_DWORD *)this + 1212) = 151;
*((_DWORD *)this + 1214) = 152;
*((_DWORD *)this + 1221) = 154;
*((_DWORD *)this + 1223) = 153;
*((_DWORD *)this + 1232) = 154;
*((_DWORD *)this + 1241) = 155;
*((_DWORD *)this + 1250) = 156;
*((_DWORD *)this + 1259) = 156;
*((_DWORD *)this + 1260) = 157;
*((_DWORD *)this + 1262) = 157;
*((_DWORD *)this + 1263) = 158;
*((_DWORD *)this + 1269) = 160;
*((_DWORD *)this + 1272) = 159;
*((_DWORD *)this + 1281) = 159;
*((_DWORD *)this + 1282) = 155;
*((_DWORD *)this + 1283) = 160;
*((_DWORD *)this + 1292) = 161;
*((_DWORD *)this + 1294) = 164;
*((_DWORD *)this + 1295) = 164;
*((_DWORD *)this + 1296) = 162;
*((_DWORD *)this + 1301) = 161;
*((_DWORD *)this + 1305) = 163;
*((_DWORD *)this + 1314) = 159;
*((_DWORD *)this + 1315) = 164;
*((_DWORD *)this + 1324) = 165;
*((_DWORD *)this + 1326) = 167;
*((_DWORD *)this + 1327) = 161;
*((_DWORD *)this + 1328) = 166;
*((_DWORD *)this + 1333) = 168;
*((_DWORD *)this + 1337) = 169;
*((_DWORD *)this + 1338) = 167;
*((_DWORD *)this + 1347) = 168;
*((_DWORD *)this + 1350) = 169;
*((_DWORD *)this + 1356) = 171;
*((_DWORD *)this + 1357) = 170;
*((_DWORD *)this + 1359) = 170;
*((_DWORD *)this + 1368) = 171;
*((_DWORD *)this + 1377) = 167;
*((_DWORD *)this + 1378) = 173;
*((_DWORD *)this + 1379) = 172;
*((_DWORD *)this + 1388) = 173;
*((_DWORD *)this + 1390) = 174;
*((_DWORD *)this + 1397) = 169;
*((_DWORD *)this + 1399) = 170;
*((_DWORD *)this + 1400) = 174;
*((_DWORD *)this + 1401) = 177;
*((_DWORD *)this + 1402) = 177;
*((_DWORD *)this + 1403) = 175;
*((_DWORD *)this + 1406) = 176;
*((_DWORD *)this + 1412) = 175;
*((_DWORD *)this + 1413) = 177;
*((_DWORD *)this + 1415) = 176;
*((_DWORD *)this + 1416) = 177;
*((_DWORD *)this + 1425) = 178;
*((_DWORD *)this + 1434) = 179;
*((_DWORD *)this + 1443) = 180;
*((_DWORD *)this + 1452) = 181;
*((_DWORD *)this + 1461) = 184;
*((_DWORD *)this + 1478) = 0;
*((_DWORD *)this + 1479) = 23;
*((_DWORD *)this + 1480) = 21;
*((_DWORD *)this + 1481) = 32;
*((_DWORD *)this + 1482) = 19;
*((_DWORD *)this + 1483) = 17;
*((_DWORD *)this + 1484) = 31;
*((_DWORD *)this + 1485) = 38;
*((_DWORD *)this + 1486) = 0;
*(_QWORD *)((char *)this + 5948) = 0;
*(_QWORD *)((char *)this + 5956) = 0;
*(_QWORD *)((char *)this + 5964) = 0;
*(_QWORD *)((char *)this + 5972) = 0;
}通过观察状态机的逻辑,我们发现这是一个 Flag 校验机。这意味着正确的输入会让状态机按部就班地晋级:State 0→State 1→State 2⋯→State 181
因此这是一条单向链表
所以exp为
import re
# ==========================================
# 题目数据 (完整构造函数代码)
# ==========================================
code_raw = """
std::vector<Step>::vector(this, a2);
v2 = (_DWORD *)((char *)this + 24);
memset(v2, 0, 0x1700u);
*v2 = 1;
*((_DWORD *)this + 15) = 1;
*((_DWORD *)this + 16) = 2;
*((_DWORD *)this + 25) = 2;
*((_DWORD *)this + 26) = 4;
*((_DWORD *)this + 27) = 5;
*((_DWORD *)this + 28) = 3;
*((_DWORD *)this + 30) = 6;
*((_DWORD *)this + 31) = 3;
*((_DWORD *)this + 32) = 3;
*((_DWORD *)this + 34) = 4;
*((_DWORD *)this + 37) = 6;
*((_DWORD *)this + 43) = 4;
*((_DWORD *)this + 44) = 5;
*((_DWORD *)this + 46) = 5;
*((_DWORD *)this + 47) = 6;
*((_DWORD *)this + 53) = 5;
*((_DWORD *)this + 56) = 7;
*((_DWORD *)this + 65) = 10;
*((_DWORD *)this + 66) = 10;
*((_DWORD *)this + 67) = 7;
*((_DWORD *)this + 68) = 8;
*((_DWORD *)this + 70) = 4;
*((_DWORD *)this + 71) = 11;
*((_DWORD *)this + 72) = 9;
*((_DWORD *)this + 77) = 11;
*((_DWORD *)this + 81) = 10;
*((_DWORD *)this + 90) = 6;
*((_DWORD *)this + 91) = 12;
*((_DWORD *)this + 92) = 11;
*((_DWORD *)this + 94) = 12;
*((_DWORD *)this + 101) = 11;
*((_DWORD *)this + 103) = 13;
*((_DWORD *)this + 112) = 16;
*((_DWORD *)this + 113) = 14;
*((_DWORD *)this + 122) = 15;
*((_DWORD *)this + 131) = 16;
*((_DWORD *)this + 140) = 17;
*((_DWORD *)this + 142) = 19;
*((_DWORD *)this + 143) = 19;
*((_DWORD *)this + 144) = 18;
*((_DWORD *)this + 149) = 13;
*((_DWORD *)this + 153) = 21;
*((_DWORD *)this + 154) = 21;
*((_DWORD *)this + 155) = 19;
*((_DWORD *)this + 164) = 20;
*((_DWORD *)this + 166) = 21;
*((_DWORD *)this + 173) = 22;
*((_DWORD *)this + 175) = 21;
*((_DWORD *)this + 176) = 21;
*((_DWORD *)this + 177) = 17;
*((_DWORD *)this + 178) = 21;
*((_DWORD *)this + 179) = 22;
*((_DWORD *)this + 188) = 23;
*((_DWORD *)this + 190) = 25;
*((_DWORD *)this + 191) = 24;
*((_DWORD *)this + 197) = 25;
*((_DWORD *)this + 200) = 25;
*((_DWORD *)this + 209) = 26;
*((_DWORD *)this + 218) = 28;
*((_DWORD *)this + 219) = 27;
*((_DWORD *)this + 228) = 28;
*((_DWORD *)this + 230) = 29;
*((_DWORD *)this + 237) = 28;
*((_DWORD *)this + 239) = 30;
*((_DWORD *)this + 248) = 26;
*((_DWORD *)this + 249) = 30;
*((_DWORD *)this + 250) = 26;
*((_DWORD *)this + 251) = 31;
*((_DWORD *)this + 260) = 32;
*((_DWORD *)this + 262) = 33;
*((_DWORD *)this + 269) = 32;
*((_DWORD *)this + 271) = 35;
*((_DWORD *)this + 272) = 34;
*((_DWORD *)this + 281) = 36;
*((_DWORD *)this + 282) = 37;
*((_DWORD *)this + 283) = 35;
*((_DWORD *)this + 292) = 36;
*((_DWORD *)this + 294) = 37;
*((_DWORD *)this + 301) = 36;
*((_DWORD *)this + 303) = 38;
*((_DWORD *)this + 312) = 39;
*((_DWORD *)this + 321) = 40;
*((_DWORD *)this + 330) = 41;
*((_DWORD *)this + 339) = 41;
*((_DWORD *)this + 340) = 42;
*((_DWORD *)this + 342) = 43;
*((_DWORD *)this + 349) = 45;
*((_DWORD *)this + 351) = 45;
*((_DWORD *)this + 352) = 43;
*((_DWORD *)this + 353) = 46;
*((_DWORD *)this + 354) = 43;
*((_DWORD *)this + 355) = 44;
*((_DWORD *)this + 364) = 45;
*((_DWORD *)this + 366) = 47;
*((_DWORD *)this + 367) = 41;
*((_DWORD *)this + 368) = 46;
*((_DWORD *)this + 373) = 41;
*((_DWORD *)this + 377) = 47;
*((_DWORD *)this + 386) = 49;
*((_DWORD *)this + 387) = 48;
*((_DWORD *)this + 396) = 49;
*((_DWORD *)this + 398) = 50;
*((_DWORD *)this + 405) = 52;
*((_DWORD *)this + 407) = 51;
*((_DWORD *)this + 416) = 52;
*((_DWORD *)this + 425) = 53;
*((_DWORD *)this + 434) = 55;
*((_DWORD *)this + 435) = 54;
*((_DWORD *)this + 444) = 55;
*((_DWORD *)this + 446) = 58;
*((_DWORD *)this + 447) = 56;
*((_DWORD *)this + 453) = 57;
*((_DWORD *)this + 456) = 57;
*((_DWORD *)this + 465) = 58;
*((_DWORD *)this + 474) = 60;
*((_DWORD *)this + 475) = 59;
*((_DWORD *)this + 484) = 60;
*((_DWORD *)this + 486) = 61;
*((_DWORD *)this + 493) = 60;
*((_DWORD *)this + 495) = 62;
*((_DWORD *)this + 504) = 63;
*((_DWORD *)this + 513) = 66;
*((_DWORD *)this + 514) = 66;
*((_DWORD *)this + 515) = 64;
*((_DWORD *)this + 524) = 65;
*((_DWORD *)this + 526) = 66;
*((_DWORD *)this + 533) = 68;
*((_DWORD *)this + 535) = 67;
*((_DWORD *)this + 544) = 68;
*((_DWORD *)this + 553) = 69;
*((_DWORD *)this + 562) = 70;
*((_DWORD *)this + 571) = 66;
*((_DWORD *)this + 572) = 71;
*((_DWORD *)this + 574) = 73;
*((_DWORD *)this + 575) = 74;
*((_DWORD *)this + 576) = 72;
*((_DWORD *)this + 581) = 67;
*((_DWORD *)this + 585) = 74;
*((_DWORD *)this + 586) = 73;
*((_DWORD *)this + 595) = 74;
*((_DWORD *)this + 604) = 75;
*((_DWORD *)this + 606) = 76;
*((_DWORD *)this + 613) = 75;
*((_DWORD *)this + 615) = 77;
*((_DWORD *)this + 624) = 78;
*((_DWORD *)this + 633) = 79;
*((_DWORD *)this + 642) = 82;
*((_DWORD *)this + 643) = 80;
*((_DWORD *)this + 652) = 81;
*((_DWORD *)this + 654) = 82;
*((_DWORD *)this + 661) = 81;
*((_DWORD *)this + 663) = 83;
*((_DWORD *)this + 672) = 84;
*((_DWORD *)this + 681) = 85;
*((_DWORD *)this + 690) = 86;
*((_DWORD *)this + 699) = 88;
*((_DWORD *)this + 700) = 87;
*((_DWORD *)this + 702) = 89;
*((_DWORD *)this + 703) = 87;
*((_DWORD *)this + 704) = 88;
*((_DWORD *)this + 709) = 83;
*((_DWORD *)this + 713) = 91;
*((_DWORD *)this + 714) = 89;
*((_DWORD *)this + 723) = 90;
*((_DWORD *)this + 732) = 91;
*((_DWORD *)this + 734) = 94;
*((_DWORD *)this + 735) = 87;
*((_DWORD *)this + 736) = 87;
*((_DWORD *)this + 737) = 92;
*((_DWORD *)this + 741) = 93;
*((_DWORD *)this + 746) = 88;
*((_DWORD *)this + 747) = 93;
*((_DWORD *)this + 756) = 94;
*((_DWORD *)this + 758) = 95;
*((_DWORD *)this + 765) = 97;
*((_DWORD *)this + 767) = 97;
*((_DWORD *)this + 768) = 96;
*((_DWORD *)this + 777) = 99;
*((_DWORD *)this + 778) = 99;
*((_DWORD *)this + 779) = 97;
*((_DWORD *)this + 788) = 98;
*((_DWORD *)this + 790) = 99;
*((_DWORD *)this + 797) = 100;
*((_DWORD *)this + 799) = 100;
*((_DWORD *)this + 808) = 101;
*((_DWORD *)this + 817) = 102;
*((_DWORD *)this + 826) = 103;
*((_DWORD *)this + 835) = 99;
*((_DWORD *)this + 836) = 104;
*((_DWORD *)this + 838) = 106;
*((_DWORD *)this + 839) = 105;
*((_DWORD *)this + 845) = 100;
*((_DWORD *)this + 848) = 108;
*((_DWORD *)this + 849) = 108;
*((_DWORD *)this + 850) = 101;
*((_DWORD *)this + 851) = 106;
*((_DWORD *)this + 860) = 107;
*((_DWORD *)this + 862) = 108;
*((_DWORD *)this + 869) = 107;
*((_DWORD *)this + 871) = 109;
*((_DWORD *)this + 880) = 109;
*((_DWORD *)this + 881) = 109;
*((_DWORD *)this + 882) = 110;
*((_DWORD *)this + 891) = 111;
*((_DWORD *)this + 894) = 107;
*((_DWORD *)this + 895) = 111;
*((_DWORD *)this + 896) = 112;
*((_DWORD *)this + 900) = 114;
*((_DWORD *)this + 901) = 114;
*((_DWORD *)this + 905) = 108;
*((_DWORD *)this + 906) = 113;
*((_DWORD *)this + 915) = 114;
*((_DWORD *)this + 918) = 116;
*((_DWORD *)this + 919) = 114;
*((_DWORD *)this + 920) = 115;
*((_DWORD *)this + 924) = 110;
*((_DWORD *)this + 925) = 110;
*((_DWORD *)this + 929) = 115;
*((_DWORD *)this + 930) = 116;
*((_DWORD *)this + 939) = 117;
*((_DWORD *)this + 948) = 118;
*((_DWORD *)this + 950) = 119;
*((_DWORD *)this + 957) = 120;
*((_DWORD *)this + 959) = 120;
*((_DWORD *)this + 968) = 121;
*((_DWORD *)this + 977) = 122;
*((_DWORD *)this + 986) = 123;
*((_DWORD *)this + 995) = 125;
*((_DWORD *)this + 996) = 124;
*((_DWORD *)this + 998) = 125;
*((_DWORD *)this + 1005) = 124;
*((_DWORD *)this + 1007) = 126;
*((_DWORD *)this + 1016) = 127;
*((_DWORD *)this + 1025) = 128;
*((_DWORD *)this + 1034) = 130;
*((_DWORD *)this + 1035) = 129;
*((_DWORD *)this + 1044) = 130;
*((_DWORD *)this + 1046) = 133;
*((_DWORD *)this + 1047) = 131;
*((_DWORD *)this + 1053) = 126;
*((_DWORD *)this + 1056) = 132;
*((_DWORD *)this + 1065) = 128;
*((_DWORD *)this + 1066) = 132;
*((_DWORD *)this + 1067) = 133;
*((_DWORD *)this + 1076) = 134;
*((_DWORD *)this + 1078) = 135;
*((_DWORD *)this + 1085) = 136;
*((_DWORD *)this + 1087) = 136;
*((_DWORD *)this + 1096) = 137;
*((_DWORD *)this + 1105) = 138;
*((_DWORD *)this + 1114) = 139;
*((_DWORD *)this + 1123) = 142;
*((_DWORD *)this + 1124) = 140;
*((_DWORD *)this + 1126) = 142;
*((_DWORD *)this + 1127) = 143;
*((_DWORD *)this + 1128) = 141;
*((_DWORD *)this + 1133) = 140;
*((_DWORD *)this + 1137) = 144;
*((_DWORD *)this + 1138) = 142;
*((_DWORD *)this + 1147) = 143;
*((_DWORD *)this + 1156) = 144;
*((_DWORD *)this + 1158) = 147;
*((_DWORD *)this + 1159) = 144;
*((_DWORD *)this + 1160) = 146;
*((_DWORD *)this + 1161) = 145;
*((_DWORD *)this + 1165) = 146;
*((_DWORD *)this + 1170) = 141;
*((_DWORD *)this + 1171) = 146;
*((_DWORD *)this + 1180) = 147;
*((_DWORD *)this + 1182) = 148;
*((_DWORD *)this + 1189) = 147;
*((_DWORD *)this + 1191) = 148;
*((_DWORD *)this + 1192) = 149;
*((_DWORD *)this + 1201) = 151;
*((_DWORD *)this + 1202) = 151;
*((_DWORD *)this + 1203) = 150;
*((_DWORD *)this + 1212) = 151;
*((_DWORD *)this + 1214) = 152;
*((_DWORD *)this + 1221) = 154;
*((_DWORD *)this + 1223) = 153;
*((_DWORD *)this + 1232) = 154;
*((_DWORD *)this + 1241) = 155;
*((_DWORD *)this + 1250) = 156;
*((_DWORD *)this + 1259) = 156;
*((_DWORD *)this + 1260) = 157;
*((_DWORD *)this + 1262) = 157;
*((_DWORD *)this + 1263) = 158;
*((_DWORD *)this + 1269) = 160;
*((_DWORD *)this + 1272) = 159;
*((_DWORD *)this + 1281) = 159;
*((_DWORD *)this + 1282) = 155;
*((_DWORD *)this + 1283) = 160;
*((_DWORD *)this + 1292) = 161;
*((_DWORD *)this + 1294) = 164;
*((_DWORD *)this + 1295) = 164;
*((_DWORD *)this + 1296) = 162;
*((_DWORD *)this + 1301) = 161;
*((_DWORD *)this + 1305) = 163;
*((_DWORD *)this + 1314) = 159;
*((_DWORD *)this + 1315) = 164;
*((_DWORD *)this + 1324) = 165;
*((_DWORD *)this + 1326) = 167;
*((_DWORD *)this + 1327) = 161;
*((_DWORD *)this + 1328) = 166;
*((_DWORD *)this + 1333) = 168;
*((_DWORD *)this + 1337) = 169;
*((_DWORD *)this + 1338) = 167;
*((_DWORD *)this + 1347) = 168;
*((_DWORD *)this + 1350) = 169;
*((_DWORD *)this + 1356) = 171;
*((_DWORD *)this + 1357) = 170;
*((_DWORD *)this + 1359) = 170;
*((_DWORD *)this + 1368) = 171;
*((_DWORD *)this + 1377) = 167;
*((_DWORD *)this + 1378) = 173;
*((_DWORD *)this + 1379) = 172;
*((_DWORD *)this + 1388) = 173;
*((_DWORD *)this + 1390) = 174;
*((_DWORD *)this + 1397) = 169;
*((_DWORD *)this + 1399) = 170;
*((_DWORD *)this + 1400) = 174;
*((_DWORD *)this + 1401) = 177;
*((_DWORD *)this + 1402) = 177;
*((_DWORD *)this + 1403) = 175;
*((_DWORD *)this + 1406) = 176;
*((_DWORD *)this + 1412) = 175;
*((_DWORD *)this + 1413) = 177;
*((_DWORD *)this + 1415) = 176;
*((_DWORD *)this + 1416) = 177;
*((_DWORD *)this + 1425) = 178;
*((_DWORD *)this + 1434) = 179;
*((_DWORD *)this + 1443) = 180;
*((_DWORD *)this + 1452) = 181;
*((_DWORD *)this + 1461) = 184;
*((_DWORD *)this + 1478) = 0;
*((_DWORD *)this + 1479) = 23;
*((_DWORD *)this + 1480) = 21;
*((_DWORD *)this + 1481) = 32;
*((_DWORD *)this + 1482) = 19;
*((_DWORD *)this + 1483) = 17;
*((_DWORD *)this + 1484) = 31;
*((_DWORD *)this + 1485) = 38;
*((_DWORD *)this + 1486) = 0;
*(_QWORD *)((char *)this + 5948) = 0;
*(_QWORD *)((char *)this + 5956) = 0;
*(_QWORD *)((char *)this + 5964) = 0;
*(_QWORD *)((char *)this + 5972) = 0;
"""
def solve_linear_chain():
print("[*] Parsing code...")
# 构建状态转移表:transitions[state][hit] = next_state
graph = {}
# 正则匹配形如 + 15) = 1 的代码
pattern = re.compile(r'\+\s*(\d+)\)\s*=\s*(\d+);')
matches = pattern.findall(code_raw)
# 1. 自动解析代码
for offset_str, value_str in matches:
offset = int(offset_str)
value = int(value_str)
# 过滤非转移表数据
if offset >= 1478: continue
rel_offset = offset - 6
if rel_offset < 0: continue
state = rel_offset // 8
hit = rel_offset % 8
if state not in graph: graph[state] = {}
graph[state][hit] = value
# 2. 手动补充 *v2 = 1 这一行 (State 0, Hit 0 -> State 1)
# 因为 v2 是 (char*)this + 24,即 offset 6。
if 0 not in graph: graph[0] = {}
graph[0][0] = 1
print(f"[*] Graph parsed with {len(graph)} source states.")
print("[*] Tracing the linear chain (0 -> 1 -> ... -> 181)...")
# 3. 线性追踪
# 我们假设 Flag 的路径就是:State i -> Hit -> State i+1
path = []
for current_state in range(181):
found_hit = -1
if current_state in graph:
# 在所有可能的 Hit 中寻找能跳到 current_state + 1 的那个
for hit, next_state in graph[current_state].items():
if next_state == current_state + 1:
found_hit = hit
break
if found_hit != -1:
path.append(found_hit)
else:
print(f"[-] Broken chain at State {current_state}! Could not find transition to {current_state + 1}")
break
print(f"[*] Chain complete. Length: {len(path)}")
# 4. 解码 Flag
flag = ""
val = 0
last_hit = -1
for hit in path:
# 如果当前 Hit <= 上一个 Hit,说明是新字符的开始
if hit <= last_hit:
flag += chr(val)
val = 0
last_hit = -1
val |= (1 << hit)
last_hit = hit
flag += chr(val) # 加上最后一个字符
print("\n" + "=" * 40)
print(f"FLAG: {flag}")
print("=" * 40)
if __name__ == "__main__":
solve_linear_chain()