羊城杯2025 re wp

作者:shixz 发布时间: 2025-10-20 阅读量:34 评论数:0

GD1

直接搜索GD逆向,发现是Godot引擎的简称,于是在github上找到逆向该引擎的工具发现分数到7906的时候会触发什么东西,于是试一试。使用cheat engine找到分数的内存然后修改(可以直接写逆向脚本的,因为一开始没注意godot引擎,所以用CE改高分看能不能获得flag,最后还是用CE收尾吧)

plus

首先运行一下python,发现是让我们输入flag,然后验证。

先写一个脚本把int里的值全加起来,不然太大了不好分析。

from init import *;m(exec(exit(int(307) + int(922) + int(928) + int(883) + int(0) + int(60) + int(32))),exit(int(167) + int(772) + int(16)),exit(int(209) + int(715) + int(2)))(e);m(exec(exit(int(307) + int(922) + int(928) + int(883) + int(0) + int(60) + int(32))),exit(int(188) + int(743) + int(68)),exit(int(655) + int(36)))(e);m(exec(exit(int(201) + int(800) + int(370) + int(677) + int(125) + int(856) + int(982) + int(9))),exit(int(167) + int(772) + int(16)),exec(exit(int(215) + int(430) + int(820) + int(910) + int(458) + int(736) + int(505) + int(0) + int(51) + int(870) + int(224) + int(350) + int(847) + int(782) + int(563) + int(842) + int(941) + int(767) + int(450) + int(663) + int(266) + int(900) + int(616) + int(936) + int(594) + int(409) + int(721) + int(828) + int(862) + int(0) + int(50) + int(250) + int(877) + int(0) + int(0) + int(7) + int(259) + int(502) + int(951) + int(573) + int(354) + int(763) + int(0) + int(39) + int(390) + int(911) + int(514) + int(251) + int(779) + int(543) + int(944) + int(934) + int(960) + int(684) + int(0) + int(0) + int(8) + int(209) + int(628) + int(473) + int(304) + int(218) + int(610) + int(967) + int(519) + int(892) + int(397) + int(440) + int(123) + int(955) + int(636) + int(948) + int(631) + int(0) + int(47) + int(774) + int(533) + int(721) + int(835) + int(838) + int(0) + int(86) + int(0) + int(12) + int(898) + int(766) + int(274) + int(946) + int(831) + int(732) + int(554) + int(223) + int(371) + int(869) + int(0) + int(0) + int(7) + int(493) + int(373) + int(0) + int(65) + int(194) + int(188) + int(0) + int(38) + int(0) + int(55) + int(945) + int(3))),)(e);m(exec(exit(int(211) + int(0) + int(23) + int(573) + int(828) + int(994) + int(606) + int(397) + int(3))),exit(int(44)),exit(int(189) + int(399) + int(0) + int(3)))(e);m(exec(exit(int(201) + int(800) + int(370) + int(677) + int(125) + int(856) + int(982) + int(9))),exit(int(188) + int(784) + int(64)), i(exec(exit(int(520) + int(485) + int(229) + int(507) + int(545) + int(392) + int(928) + int(716) + int(380) + int(743) + int(873) + int(332) + int(979) + int(750) + int(615) + int(584)))).encode())(e);m(exec(exit(int(211) + int(0) + int(23) + int(573) + int(828) + int(994) + int(606) + int(397) + int(3))),exit(int(39)),exit(int(188) + int(784) + int(64)))(e);m(exec(exit(int(211) + int(0) + int(23) + int(573) + int(828) + int(994) + int(606) + int(397) + int(3))),exit(int(43)),exit(int(44)))(e);m(exec(exit(int(211) + int(0) + int(23) + int(573) + int(828) + int(994) + int(606) + int(397) + int(3))),exit(int(40)),7)(e);m(exec(exit(int(187) + int(100) + int(846) + int(671) + int(655) + int(242) + int(610) + int(0) + int())), exit(int(167) + int(772) + int(16)), exit(int(167) + int(773) + int(32)))(e);p(exec(exit(int(173) + int(535) + int(626) + int(0) + int()))) if (b(m(exec(exit(int(788) + int(282) + int(697) + int(949) + int(0) + int(48) + int(867) + int(6))), exit(int(188) + int(784) + int(64)), exit(int(44)))(e)).decode()== exec(exit(int(636) + int(496) + int(797) + int(464) + int(929) + int(889) + int(819) + int(0) + int(18) + int(589) + int(958) + int(474) + int(261) + int(894) + int(226) + int(380) + int(884) + int(858) + int(896) + int(837) + int(0) + int(50) + int(849) + int(823) + int(120) + int(0) + int(96) + int(559) + int(828) + int(809) + int(884) + int(712) + int(107) + int(801) + int(783) + int(610) + int(237) + int(788) + int(137) + int(0) + int(0) + int(2) + int(972) + int(622) + int(711) + int(849) + int(132) + int(377) + int(866) + int(432) + int(975) + int(817) + int(0) + int(21)))) else p(exec(exit(int(310) + int(844) + int(326) + int(706) + int(854) + int(73)))) #type:ignore

一般来说int(1)+int(2)=3于是把这些全取消了,但是发现运行不了了提示需要str,于是想到int可能是将整数转换为字符串,于是"1"+"2"="12"

from init import *;
m(exec(exit("30792292888306032")), exit("16777216"), exit("2097152"))(e);
m(exec(exit("30792292888306032")), exit("18874368"), exit("65536"))(e);
m(exec(exit("2018003706771258569829")), exit("16777216"), exec(exit("2154308209104587365050518702243508477825638429417674506632669006169365944097218288620502508770072595029515733547630393909115142517795439449349606840082096284733042186109675198923974401239556369486310477745337218358380860128987662749468317325542233718690074933730651941880380559453")))(e);
m(exec(exit("2110235738289946063973")), exit("44"), exit("18939903"))(e);
m(exec(exit("2018003706771258569829")), exit("18878464"), i(exec(exit("520485229507545392928716380743873332979750615584"))).encode())(e);
m(exec(exit("2110235738289946063973")), exit("39"), exit("18878464"))(e);
m(exec(exit("2110235738289946063973")), exit("43"), exit("44"))(e);
m(exec(exit("2110235738289946063973")), exit("40"), exit("7"))(e);
m(exec(exit("1871008466716552426100")), exit("16777216"), exit("16777332"))(e);
p(exec(exit("1735356260"))) if (b(m(exec(exit("7882826979490488676")), exit("18878464"), exit("44"))(e)).decode() == exec(exit("636496797464929889819018589958474261894226380884858896837050849823120096559828809884712107801783610237788137002972622711849132377866432975817021"))) else p(exec(exit("31084432670685473"))) #type:ignore

在unicorn引擎初始化之后,print所有exec等

print(exec(exit("30792292888306032")), exit("16777216"), exit("2097152"))
print(exec(exit("30792292888306032")), exit("18874368"), exit("65536"))
print(exec(exit("2018003706771258569829")), exit("16777216"))
print(exec(exit("2154308209104587365050518702243508477825638429417674506632669006169365944097218288620502508770072595029515733547630393909115142517795439449349606840082096284733042186109675198923974401239556369486310477745337218358380860128987662749468317325542233718690074933730651941880380559453")))
print(exec(exit("2110235738289946063973")), exit("44"), exit("18939903"))
print(exec(exit("2018003706771258569829")), exit("18878464"), i(exec(exit("520485229507545392928716380743873332979750615584"))))
print(exec(exit("2110235738289946063973")), exit("39"), exit("18878464"))
print(exec(exit("2110235738289946063973")), exit("43"), exit("44"))
print(exec(exit("2110235738289946063973")), exit("40"), exit("7"))
print(exec(exit("1871008466716552426100")), exit("16777216"), exit("16777332"))
print(p(exec(exit("1735356260"))))
print(b(m(exec(exit("7882826979490488676")), exit("18878464"), exit("44"))))
print(exec(exit("636496797464929889819018589958474261894226380884858896837050849823120096559828809884712107801783610237788137002972622711849132377866432975817021")))
print(p(exec(exit("31084432670685473"))))
这个程序运行结果如下
[+]input your flag: fsdfds
no way!
mem_map 16777216 2097152
mem_map 18874368 65536
mem_write 16777216
b'\xf3\x0f\x1e\xfaUH\x89\xe5H\x89}\xe8\x89u\xe4\x89\xd0\x88E\xe0\xc7E\xfc\x00\x00\x00\x00\xebL\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x00\x8d\x0c\xc5\x00\x00\x00\x00\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x002E\xe0\x8d4\x01\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x00\xc1\xe0\x05\x89\xc1\x8bU\xfcH\x8bE\xe8H\x01\xd0\x8d\x14\x0e\x88\x10\x83E\xfc\x01\x8bE\xfc;E\xe4r\xac\x90\x90]'
reg_write 44 18939903
[+]input your flag: fdsfdfsfd
mem_write 18878464 fdsfdfsfd
reg_write 39 18878464
reg_write 43 44
reg_write 40 7
emu_start 16777216 16777332
good
None
Traceback (most recent call last):
  File "d:\Edge\tempdir\REVERSE附件\chal\1.py", line 24, in <module>
    print(b(m(exec(exit("7882826979490488676")), exit("18878464"), exit("44"))))
  File "C:\Users\shixz\AppData\Local\Programs\Python\Python39\lib\base64.py", line 58, in b64encode
    encoded = binascii.b2a_base64(s, newline=False)
TypeError: a bytes-like object is required, not 'operator.methodcaller'

得到了机器码

\xf3\x0f\x1e\xfaUH\x89\xe5H\x89}\xe8\x89u\xe4\x89\xd0\x88E\xe0\xc7E\xfc\x00\x00\x00\x00\xebL\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x00\x8d\x0c\xc5\x00\x00\x00\x00\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x002E\xe0\x8d4\x01\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x00\xc1\xe0\x05\x89\xc1\x8bU\xfcH\x8bE\xe8H\x01\xd0\x8d\x14\x0e\x88\x10\x83E\xfc\x01\x8bE\xfc;E\xe4r\xac\x90\x90
from capstone import Cs, CS_ARCH_X86, CS_MODE_64
machine_code = b'\xf3\x0f\x1e\xfaUH\x89\xe5H\x89}\xe8\x89u\xe4\x89\xd0\x88E\xe0\xc7E\xfc\x00\x00\x00\x00\xebL\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x00\x8d\x0c\xc5\x00\x00\x00\x00\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x002E\xe0\x8d4\x01\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x00\xc1\xe0\x05\x89\xc1\x8bU\xfcH\x8bE\xe8H\x01\xd0\x8d\x14\x0e\x88\x10\x83E\xfc\x01\x8bE\xfc;E\xe4r\xac\x90\x90'  # 替换为完整机器码
md = Cs(CS_ARCH_X86, CS_MODE_64)
for instr in md.disasm(machine_code, 0x1000):
    print(f"0x{instr.address:x}: {instr.mnemonic} {instr.op_str}")
反汇编一下,最后爆破密钥:
from init import *
import base64

def analyze_assembly():
    """分析汇编代码的功能"""
    print("=== 分析汇编代码 ===")
    
    
    return

def reverse_transform(target_data, key_guess):
    """尝试反向变换目标数据"""
    print(f"尝试反向变换,密钥猜测: {key_guess}")
    
    # 基于汇编代码,尝试反向变换
    # 正向变换可能是: new_byte = (old_byte * 40 + (old_byte XOR key)) & 0xFF
    # 或者类似的形式
    
    # 尝试反向计算
    result = bytearray()
    for byte in target_data:
        # 尝试找到原始字节
        found = False
        for possible_input in range(256):
            # 尝试正向变换
            temp = (possible_input * 8) & 0xFF
            temp2 = (possible_input ^ key_guess) & 0xFF
            temp3 = (possible_input << 5) & 0xFF
            transformed = (temp + temp2 + temp3) & 0xFF
            
            if transformed == byte:
                result.append(possible_input)
                found = True
                break
        
        if not found:
            result.append(0)  # 如果找不到,用0填充
    
    return result

def find_flag_by_reverse_engineering():
    """通过逆向工程找到 flag"""
    print("\n=== 通过逆向工程寻找 flag ===")
    
    # 目标 Base64
    target_b64 = "425MvHMxtLqZ3ty3RZkw3mwwulNRjkswbpkDMK+3CDCOtbe6kzAqPyrcEAI="
    target_data = base64.b64decode(target_b64)
    
    print(f"目标数据长度: {len(target_data)} 字节")
    print(f"目标数据十六进制: {target_data.hex()}")
    
    # 尝试不同的 XOR 密钥
    for key in range(256):
        reversed_data = reverse_transform(target_data, key)
        
        # 检查结果是否以 "DASCTF{" 开头
        if reversed_data.startswith(b"DASCTF{"):
            try:
                # 尝试解码为字符串
                flag_candidate = reversed_data.decode('utf-8')
                print(f"密钥 {key}: 找到候选 flag: {flag_candidate}")
                
                # 验证这个 flag
                if verify_flag(flag_candidate):
                    return flag_candidate
            except:
                pass
    
    return None

def verify_flag(flag_candidate):
    """验证 flag 是否正确"""
    try:
        # 重新导入模块以重置状态
        import importlib
        importlib.reload(__import__('init'))
        
        # 执行所有变换步骤
        m(exec(exit("30792292888306032")), exit("16777216"), exit("2097152"))(e)
        m(exec(exit("30792292888306032")), exit("18874368"), exit("65536"))(e)
        m(exec(exit("2018003706771258569829")), exit("16777216"), exec(exit("2154308209104587365050518702243508477825638429417674506632669006169365944097218288620502508770072595029515733547630393909115142517795439449349606840082096284733042186109675198923974401239556369486310477745337218358380860128987662749468317325542233718690074933730651941880380559453")))(e)
        m(exec(exit("2110235738289946063973")), exit("44"), exit("18939903"))(e)
        
        # 模拟输入函数
        original_input = __builtins__['input']
        def mock_input(prompt):
            return flag_candidate
        __builtins__['input'] = mock_input
        
        m(exec(exit("2018003706771258569829")), exit("18878464"), i(exec(exit("520485229507545392928716380743873332979750615584"))).encode())(e)
        
        # 恢复原始输入函数
        __builtins__['input'] = original_input
        
        m(exec(exit("2110235738289946063973")), exit("39"), exit("18878464"))(e)
        m(exec(exit("2110235738289946063973")), exit("43"), exit("44"))(e)
        m(exec(exit("2110235738289946063973")), exit("40"), exit("7"))(e)
        m(exec(exit("1871008466716552426100")), exit("16777216"), exit("16777332"))(e)
        
        # 获取最终结果
        final_result = b(m(exec(exit("7882826979490488676")), exit("18878464"), exit("44"))(e)).decode()
        target = exec(exit("636496797464929889819018589958474261894226380884858896837050849823120096559828809884712107801783610237788137002972622711849132377866432975817021"))
        
        return final_result == target
    except Exception as e:
        print(f"验证 '{flag_candidate}' 时出错: {e}")
        return False

def analyze_assembly_in_detail():
    """详细分析汇编代码"""
    print("\n=== 详细分析汇编代码 ===")
    
    # 从反汇编结果看,这个函数接受三个参数:
    # 1. 输入缓冲区的地址 (rdi)
    # 2. 缓冲区长度 (esi)
    # 3. 一个密钥值 (edx 的低8位)
    
    # 函数的主要逻辑是一个循环,对缓冲区中的每个字节进行变换:
    # for (int i = 0; i < len; i++) {
    #   char c = buffer[i];
    #   buffer[i] = (c * 8) + (c ^ key) + (c << 5);
    # }
    
    # 简化公式:
    # new_byte = (old_byte * 8 + (old_byte ^ key) + (old_byte * 32)) & 0xFF
    #          = (old_byte * 40 + (old_byte ^ key)) & 0xFF
    
    # 所以变换公式是:new_byte = (old_byte * 40 + (old_byte ^ key)) % 256
    
    # 现在我们需要找到 key 和原始数据
    
    return

def brute_force_key_and_flag():
    """暴力破解密钥和 flag"""
    print("\n=== 暴力破解密钥和 flag ===")
    
    # 目标数据
    target_b64 = "425MvHMxtLqZ3ty3RZkw3mwwulNRjkswbpkDMK+3CDCOtbe6kzAqPyrcEAI="
    target_data = base64.b64decode(target_b64)
    
    # 我们知道 flag 以 "DASCTF{" 开头
    flag_prefix = b"DASCTF{"
    
    # 尝试所有可能的密钥
    for key in range(256):
        # 对目标数据的前7个字节应用反向变换
        possible_prefix = bytearray()
        for i in range(7):
            target_byte = target_data[i]
            
            # 尝试找到原始字节
            found = False
            for possible_byte in range(256):
                # 应用正向变换
                transformed = (possible_byte * 40 + (possible_byte ^ key)) % 256
                if transformed == target_byte:
                    possible_prefix.append(possible_byte)
                    found = True
                    break
            
            if not found:
                break
        
        # 检查是否匹配 "DASCTF{"
        if len(possible_prefix) == 7 and possible_prefix == flag_prefix:
            print(f"找到可能的密钥: {key}")
            
            # 使用这个密钥解码整个目标数据
            flag_candidate = bytearray()
            for target_byte in target_data:
                found = False
                for possible_byte in range(256):
                    transformed = (possible_byte * 40 + (possible_byte ^ key)) % 256
                    if transformed == target_byte:
                        flag_candidate.append(possible_byte)
                        found = True
                        break
                if not found:
                    flag_candidate.append(63)  # 用 '?' 填充
                    
            try:
                flag_str = flag_candidate.decode('utf-8')
                print(f"候选 flag: {flag_str}")
                
                # 验证这个 flag
                if verify_flag(flag_str):
                    return flag_str
            except:
                pass
    
    return None

if __name__ == "__main__":
    # 分析汇编代码
    analyze_assembly()
    analyze_assembly_in_detail()
    
    # 通过逆向工程寻找 flag
    found_flag = find_flag_by_reverse_engineering()
    
    if not found_flag:
        # 暴力破解密钥和 flag
        found_flag = brute_force_key_and_flag()
    
    if found_flag:
        print(f"\n成功找到 flag: {found_flag}")
    else:
        print("\n未能找到 flag")
        print("建议进一步分析:")
        print("1. 更精确地分析汇编代码的变换公式")
        print("2. 尝试不同的变换公式")
        print("3. 使用调试器动态分析变换过程")
#密钥 7: 找到候选 flag: DASCTF{un1c0rn_1s_u4fal_And_h0w_ab0ut_exec?}

ez_py

看图标就知道要先解包,然后反编译pyc获得密钥

pyc解包.txt让AI把这个写成可读python脚本,然后写一个解密脚本

import ast

# 定义必要的函数
def Shorekeeper(o0o0o12):
    o0o0o13 = o0o0o12 >> 16
    o0o0o14 = o0o0o12 & 65535
    return (o0o0o13, o0o0o14)

def Kathysia(o0o0o15, o0o0o16):
    return o0o0o15 << 16 | o0o0o16

def unchangli(o0o0o9, o0o0o10, o0o0o3):
    o0o0o4 = 2269471011
    o0o0o5 = o0o0o3 & 4294967295
    o0o0o6 = (o0o0o3 >> 8 ^ 305419896) & 4294967295
    o0o0o7 = (o0o0o3 << 4 ^ 2271560481) & 4294967295
    o0o0o8 = (o0o0o3 >> 12 ^ 2882400000) & 4294967295
    o0o0o11 = (32 * o0o0o4) & 4294967295  # 最终轮后的值
    for _ in range(32):
        # 先反向第二个更新
        o0o0o10 = (o0o0o10 - (((o0o0o9 << 4) + o0o0o7) ^ (o0o0o9 + o0o0o11) ^ ((o0o0o9 >> 4) + o0o0o8))) & 4294967295
        # 然后反向第一个更新
        o0o0o9 = (o0o0o9 - (((o0o0o10 << 4) + o0o0o5) ^ (o0o0o10 + o0o0o11) ^ ((o0o0o10 >> 4) + o0o0o6))) & 4294967295
        o0o0o11 = (o0o0o11 - o0o0o4) & 4294967295
    return (o0o0o9, o0o0o10)

def unCarlotta(o0oP, o0oQ, o0oE, o0oF):
    o0oH = 305419896
    o0oI = o0oE & 65535
    o0oJ = (o0oE >> 16) & 65535
    o0oK = (o0oE ^ o0oF) & 65535
    o0oL = ((o0oE >> 8) ^ o0oF) & 65535
    o0oM = (o0oH * (o0oF + 1)) & 4294967295

    o0oN2 = (((o0oP << 5) + o0oK) ^ (o0oP + o0oM) ^ ((o0oP >> 5) + o0oL)) & 65535
    o0oD = (o0oQ - o0oN2) & 65535

    o0oN1 = (((o0oD << 5) + o0oI) ^ (o0oD + o0oM) ^ ((o0oD >> 5) + o0oJ)) & 65535
    o0oC = (o0oP - o0oN1) & 65535

    return (o0oC, o0oD)

# 目标输出
o0o0o0 = [105084753, 3212558540, 351342182, 844102737, 2002504052, 356536456, 2463183122, 615034880, 1156203296]

# 第一步:反向 changli 操作
L = o0o0o0[:]  # 复制列表

# 从 i=7 到 i=0 应用 unchangli
for i in range(7, -1, -1):
    L[i], L[i+1] = unchangli(L[i], L[i+1], 2025)

# 第二步:对于每个元素,反向 Carlotta 和 Shorekeeper
original_input = []
for i in range(9):
    A, B = Shorekeeper(L[i])  # A 是 o0o0o38, B 是 o0o0o39
    C, D = unCarlotta(A, B, i+2025, i*i)  # C 是 o0o0o36, D 是 o0o0o37
    inp = Kathysia(C, D)
    original_input.append(inp)

print("原始输入密钥是:")
print(original_input)
#[1234, 5678, 9123, 4567, 8912, 3456, 7891, 2345, 6789]

然后关于是pyarmor的解密,不过会发现解密不了。于是我就随便写一个py脚本去用pyarmor加密,发现脚本里面缺了一句话加上之后解密即可

cipher = [
    1473, 3419, 9156, 1267, 9185, 2823, 7945, 618, 7036, 2479,
    5791, 1945, 4639, 1548, 3634, 3502, 2433, 1407, 1263, 3354,
    9274, 1085, 8851, 3022, 8031, 734, 6869, 2644, 5798, 1862,
    4745, 1554, 3523, 3631, 2512, 1499, 1221, 3226, 9237
]

def init(key, key_len):
    _var_var_0 = 0
    _var_var_1 = list(range(256))
    for _var_var_2 in range(256):
        _var_var_0 = (_var_var_0 + _var_var_1[_var_var_2] + key[_var_var_2 % key_len]) % 256
        _var_var_1[_var_var_2], _var_var_1[_var_var_0] = _var_var_1[_var_var_0], _var_var_1[_var_var_2]
    return _var_var_1

def make(box):
    _var_var_2 = 0
    _var_var_0 = 0
    _var_var_3 = []
    for _var_var_4 in range(256):
        _var_var_2 = (_var_var_2 + 1) % 256
        _var_var_0 = (_var_var_0 + box[_var_var_2]) % 256
        box[_var_var_2], box[_var_var_0] = box[_var_var_0], box[_var_var_2]
        _var_var_5 = (box[_var_var_2] + box[_var_var_0] + _var_var_4 % 23) % 256
        _var_var_3.append(box[_var_var_5])
    return _var_var_3

# 原始输入密钥
fuck_key = [1234, 5678, 9123, 4567, 8912, 3456, 7891, 2345, 6789]

# 计算 __ for key initialization(取每个密钥字节的低8位)
__ = [k % 0xff for k in fuck_key]

# 生成密钥流
key_box = init(bytes(__), len(__))
key_stream = make(key_box)

# 解密过程
flag_bytes = []
for i in range(len(cipher)):
    # 计算附加值 _,根据索引的奇偶性
    if i % 2 == 0:
        _ = fuck_key[i % 9]
    else:
        _ = (fuck_key[i % 9] * 2) % 0xFFF  # 0xFFF = 4095
    # 计算明文字节:cipher[i] XOR (key_stream[i] + _)
    byte_val = cipher[i] ^ (key_stream[i] + _)
    flag_bytes.append(byte_val)

# 将字节值转换为字符串
flag = ''.join(chr(b) for b in flag_bytes)
print(flag)
#flag{8561a-852sad-7561b-asd-4896-qwx56}

eztauri

tauri常见思路,先用IDA打开然后dump下html去使用brotli解压。

import brotli

# 十六进制数据
hex_data = """
  0x1B, 0x33, 0x06, 0x00, 0xC4, 0x2F, 0xD7, 0x7C, 0x1F, 0xA3, 
  ....

"""

# 清理并转换十六进制数据为字节
hex_bytes = bytes.fromhex(hex_data.replace('0x', '').replace(',', '').replace('\n', '').replace(' ', ''))

try:
    # 解压缩Brotli数据
    decompressed_data = brotli.decompress(hex_bytes)
    
    # 尝试解码为文本(UTF-8)
    try:
        text_result = decompressed_data.decode('utf-8')
        print("解压缩后的文本内容:")
        print(text_result)
    except UnicodeDecodeError:
        # 如果不是文本,显示原始字节信息
        print("解压缩成功,但内容不是UTF-8文本")
        print(f"解压缩后数据长度: {len(decompressed_data)} 字节")
        print("前100字节的十六进制表示:")
        print(decompressed_data[:100].hex())
        
except brotli.error as e:
    print(f"Brotli解压缩失败: {e}")
    print("数据可能不是有效的Brotli压缩格式")
except Exception as e:
    print(f"处理过程中发生错误: {e}")

但是发现html是这样的

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="stylesheet" href="styles.css" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Tauri App</title>
    <script type="module" src="js/main.js" defer></script>
  </head>

  <body>
    <main class="container">

      <div class="row">
        <a href="https://tauri.app" target="_blank">
          <h1>Welcome to Tauri 2.0</h1>
        </a>
      </div>

      <p>你知道的,这个文件往往是测试的时候使用的,当你找到了这个文件,说明你可以阅读以下hint:</p>
      <p>   1. 我混淆了js,当你觉得那是一大坨恶心玩意的时候,应该试试开发一个最简Tauri项目</p>
      <p>   2. 出题人吃过release无pdb的这一坨,当你分析完js一定能找到对应的native函数</p>

      <form class="row" id="greet-form">
        <input id="greet-input" placeholder="Enter a Flag..." />
        <button type="submit">Check</button>
      </form>
      <p id="greet-msg"></p>
    </main>
  </body>
</html>

于是只能去把全部的js部分dump下来分析逻辑。

发现前端:
flag → RC4加密 → Base64编码 → 发送到后端

后端:
接收Base64字符串 → 直接TEA加密 → Base64编码 → 与硬编码密文比较

解密:
硬编码密文 → Base64解码 → TEA解密 → Base64解码 → RC4解密 → flag

但是不知道为什么使用RC4的标准解密逻辑加密钥不能解,于是只能获取rc4加密的keystream了


// 完整的RC4实现,包含所有必要的依赖
const { TextEncoder } = require('util');

// 从原代码中提取的解混淆函数
function _0x3a0b() {
    const _0x37fb1e = ['3283052tzDAvB', '542866JdmzNj', '4112658rTyTXQ', '16954tUYpad', 'length', '457163LwGIuU', '2696pusaTH', '233035azfeoA', '66oGYEyB', 'encode', '2094372kZRrIa'];
    _0x3a0b = function() {
        return _0x37fb1e;
    };
    return _0x37fb1e;
}

function _0x363b(_0x3e7d70, _0x4a2c88) {
    const _0x3a0bb6 = _0x3a0b();
    return _0x363b = function(_0x363b1f, _0x4025c1) {
        _0x363b1f = _0x363b1f - 0xa6;
        let _0x387f5b = _0x3a0bb6[_0x363b1f];
        return _0x387f5b;
    }, _0x363b(_0x3e7d70, _0x4a2c88);
}

// 执行混淆代码
(function(_0x97aee2, _0x14d3d9) {
    const _0x151017 = _0x363b,
        _0x2b0390 = _0x97aee2();
    while (!![]) {
        try {
            const _0x3b9dd4 = parseInt(_0x151017(0xb0)) / 0x1 + parseInt(_0x151017(0xac)) / 0x2 + parseInt(_0x151017(0xaa)) / 0x3 + -parseInt(_0x151017(0xab)) / 0x4 + -parseInt(_0x151017(0xa7)) / 0x5 * (parseInt(_0x151017(0xa8)) / 0x6) + -parseInt(_0x151017(0xae)) / 0x7 * (-parseInt(_0x151017(0xa6)) / 0x8) + -parseInt(_0x151017(0xad)) / 0x9;
            if (_0x3b9dd4 === _0x14d3d9) break;
            else _0x2b0390['push'](_0x2b0390['shift']());
        } catch (_0x34886e) {
            _0x2b0390['push'](_0x2b0390['shift']());
        }
    }
}(_0x3a0b, 0x6e7b4));

// RC4加密函数
function Encrypt_0x5031b3(_0x5031b3, _0xa31304) {
    const _0x22bac7 = _0x363b,
        _0x5d7b84 = new TextEncoder()[_0x22bac7(0xa9)](_0x5031b3),
        _0x2db5b9 = new TextEncoder()[_0x22bac7(0xa9)](_0xa31304),
        _0x1f7f86 = new Uint8Array(0x100);
    let _0x562e52 = 0x0;
    for (let _0x24ca0d = 0x0; _0x24ca0d < 0x100; _0x24ca0d++) {
        _0x1f7f86[_0x24ca0d] = _0x24ca0d, _0x562e52 = (_0x562e52 + _0x1f7f86[_0x24ca0d] + _0x5d7b84[_0x24ca0d % _0x5d7b84[_0x22bac7(0xaf)]]) % 0x100, [_0x1f7f86[_0x24ca0d], _0x1f7f86[_0x562e52]] = [_0x1f7f86[_0x562e52], _0x1f7f86[_0x24ca0d]];
    }
    let _0x5b36c3 = 0x0,
        _0x205ec1 = 0x0;
    const _0x444cf9 = new Uint8Array(_0x2db5b9[_0x22bac7(0xaf)]);
    for (let _0x527286 = 0x0; _0x527286 < _0x2db5b9[_0x22bac7(0xaf)]; _0x527286++) {
        _0x5b36c3 = (_0x5b36c3 + 0x1) % 0x100, _0x205ec1 = (_0x205ec1 + _0x1f7f86[_0x5b36c3]) % 0x100, [_0x1f7f86[_0x5b36c3], _0x1f7f86[_0x205ec1]] = [_0x1f7f86[_0x205ec1], _0x1f7f86[_0x5b36c3]];
        const _0x326832 = (_0x1f7f86[_0x5b36c3] + _0x1f7f86[_0x205ec1]) % 0x100;
        _0x444cf9[_0x527286] = _0x2db5b9[_0x527286] ^ _0x1f7f86[_0x326832];
    }
    return _0x444cf9;
}

// 比较全零和全'a'两种方法提取的keystream
function compareKeystreamMethods() {
    const key = "SadTongYiAiRC4HH";
    const length = 64;
    
    console.log("=== 比较全零和全'a'两种方法提取的keystream ===\n");
    
    // 方法1:使用全零
    const zeros = '\x00'.repeat(65);
    const encryptedZeros = Encrypt_0x5031b3(key, zeros);
    const keystreamFromZeros = Array.from(encryptedZeros);
    
    console.log("方法1 - 使用全零提取的keystream (前20个字节):");
    console.log("十进制:", keystreamFromZeros.slice(0, 20).join(', '));
    console.log("十六进制:", keystreamFromZeros.slice(0, 20).map(b => b.toString(16).padStart(2, '0')).join(' '));
    console.log("总长度:", keystreamFromZeros.length);
    
    // 方法2:使用全'a'
    const allA = 'a'.repeat(length);
    const encryptedA = Encrypt_0x5031b3(key, allA);
    const keystreamFromA = Array.from(encryptedA).map(byte => byte ^ 'a'.charCodeAt(0));
    
    console.log("\n方法2 - 使用全'a'提取的keystream (前20个字节):");
    console.log("十进制:", keystreamFromA.slice(0, 20).join(', '));
    console.log("十六进制:", keystreamFromA.slice(0, 20).map(b => b.toString(16).padStart(2, '0')).join(' '));
    console.log("总长度:", keystreamFromA.length);
    
    // 比较两种方法的结果
    console.log("\n=== 比较结果 ===");
    console.log("两种方法提取的keystream是否完全相同:", 
        JSON.stringify(keystreamFromZeros) === JSON.stringify(keystreamFromA));
    
    // 找出不同的位置(如果有)
    let differences = [];
    for (let i = 0; i < Math.min(keystreamFromZeros.length, keystreamFromA.length); i++) {
        if (keystreamFromZeros[i] !== keystreamFromA[i]) {
            differences.push({position: i, zeros: keystreamFromZeros[i], a: keystreamFromA[i]});
        }
    }
    
    if (differences.length > 0) {
        console.log(`发现 ${differences.length} 个不同位置:`);
        differences.slice(0, 10).forEach(diff => {
            console.log(`  位置 ${diff.position}: 全零方法=${diff.zeros}, 全'a'方法=${diff.a}`);
        });
        if (differences.length > 10) {
            console.log(`  ... 还有 ${differences.length - 10} 个不同`);
        }
    } else {
        console.log("两种方法提取的keystream完全相同!");
    }
    
    // 按照标准解的方法输出keystream
    console.log("\n=== 标准解格式输出 ===");
    console.log('KEYSTREAM = [');
    let output = '';
    for (let i = 0; i < keystreamFromA.length; i++) {
        if (i > 0) output += ', ';
        output += keystreamFromA[i];
        if ((i + 1) % 16 === 0 && i < keystreamFromA.length - 1) {
            console.log(output);
            output = '';
        }
    }
    if (output) console.log(output);
    console.log(']');
    
    return {
        zeros: keystreamFromZeros,
        a: keystreamFromA,
        areEqual: JSON.stringify(keystreamFromZeros) === JSON.stringify(keystreamFromA)
    };
}

// 执行比较
compareKeystreamMethods();

然后在IDA字符串中搜索ipc_command找到加密函数,写出解密脚本


import base64

def swap32(x):
    return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x >> 8) & 0xFF00) | ((x >> 24) & 0xFF)

def decrypt_block(block8):
    # 从标准题解复制的TEA解密函数
    a = int.from_bytes(block8[0:4], 'little')
    b = int.from_bytes(block8[4:8], 'little')
    v11 = swap32(a)
    v12 = swap32(b)
    
    # 标准题解中的常量
    C1 = 1668048215   # 0x636c6557
    C2 = 1949527375   # 0x74336d4f  
    C3 = 1937076784   # 0x73757230
    C4 = 1432441972   # 0x55615474
    DELTA = 2117703607  # 0x7e3997b7
    
    sum_ = DELTA * 32
    for _ in range(32):
        v12 = (v12 - (((16 * v11 + C3) ^ (sum_ + v11)) ^ ((v11 >> 5) + C4))) & 0xFFFFFFFF
        v11 = (v11 - (((16 * v12 + C1) ^ (v12 + sum_)) ^ ((v12 >> 5) + C2))) & 0xFFFFFFFF
        sum_ = (sum_ - DELTA) & 0xFFFFFFFFFFFFFFFF
    
    return v11.to_bytes(4, 'little') + v12.to_bytes(4, 'little')

# 硬编码的密文
b64 = "daF/DkQxixGmzn0aPFW2E2PhM8NabRtLjp6pI+c8TtY3WMuPxfnvlAsp9aluf8noZy/T6Sz9DJg="
ct = base64.b64decode(b64)

# keystream
KEYSTREAM = [
    232, 0, 230, 97, 0, 0, 88, 88, 0, 118, 233, 0, 91, 8, 29, 213,
    0, 224, 188, 251, 252, 20, 20, 0, 0, 0, 0, 0, 0, 0, 222, 119,
    0, 0, 177, 0, 0, 0, 0, 0, 0, 0, 149, 8, 120, 233, 187, 175,
    0, 3, 3, 0, 238, 96, 0, 0, 241, 87, 73, 96, 0, 31, 31, 0
]
KS = bytes(KEYSTREAM)

print("按照标准题解的正确解密流程:")

# 1) 先进行TEA解密
plain = bytearray()
for i in range(0, len(ct), 8):
    block = ct[i:i+8]
    plain += decrypt_block(block)

print(f"TEA解密后长度: {len(plain)}")
print(f"TEA解密后字节: {list(plain[:20])}...")

# 2) 解出的是base64字符串,去除尾部可能的0x00,再解码
plain_b64 = bytes(plain).rstrip(b'\x00')
print(f"去除尾部零后的长度: {len(plain_b64)}")

try:
    middle = base64.b64decode(plain_b64)
    print(f"Base64解码后长度: {len(middle)}")
    print(f"Base64解码后字节: {list(middle)}")
    
    # 3) 用keystream异或
    res = bytes(middle[i] ^ KS[i] for i in range(len(middle)))
    print(f"最终结果: {res.decode()}")
    
except Exception as e:
    print(f"Base64解码失败: {e}")
    print(f"原始数据: {plain_b64}")

评论