TPCTF2025 wp re部分

作者:shixz 发布时间: 2025-11-01 阅读量:8 评论数:0

chase

使用FCEUX打开nes文件,然后使用其自带的cheat工具去搜索每关需要吃的豆子数量的内存

将83地址修改为0,即可跳过五关获得part1的flag。

然后在FPU Viewer可以找到part3

我们可以轻松比对我们已经获得的两个flag都有的特征,FLAG PT.X

IS所以我们直接在fpu里查看这些所对应的tile,然后可以知道字符tile就是其所对应的ascii码-20,例T的ascii为0x54,其tile则为0x34.

于是直接在hex中搜索26 2c 21对应FLA的tile

这是第一个

这是第二个,可以看到第一个从26 2c 21开始知道D1才不一样,我们可以通过FPU知道D1代表1,所以D2应该就是part2了,并且我们知道了数字的表示不能从上面的tile对应,应该是

这里的tile对应即D0~D9,然后写一个脚本转换tile

tile_hex = "262C21270030340ED200262F3200392F35002933000112A4000118302C2139D12ED93DD6202DD3333D00019ABDADAD6D000103B9ADAD6E000103B9ADAD6E0001030D01030001100001000F0F0F0F0F"

tile_bytes = bytes.fromhex(tile_hex)

result = []
for tile in tile_bytes:
    if tile == 0x3D:#总所周知flag的连接一般都是使用_,但是以前版本是],所以直接指定tile转换为_
        result.append('_')
    elif 0xD0 <= tile <= 0xD9:
        num = tile - 0xD0
        result.append(str(num))
    elif 0x10 <= tile <= 0x19:#排除1~9
        continue
    elif tile==0x01:#排除!,因为以前的版本运行之后发现在已知前缀中有各种!和数字,所以两个都排除了
        continue
    else:
        ascii_val = tile + 0x20
        if 0 <= ascii_val <= 0x7F:
            result.append(chr(ascii_val))

final_str = ''.join(result)
print("转换结果:")
print(final_str)

#转换结果:
#FLAG PT.2 FOR YOU IS  PLAY1N9_6@M3S_  # # #-#   /////

protable

使用binwalk检测,发现有exe和elf文件及其偏移,写一个脚本获取

f = open("portable_ef3c5f3a7155c5a4c6f0b08606dea575","rb").read()
exe_data=  f[0x9C000:]
open("dump","wb").write(exe_data)


exe_data=  f[0x10000:]
open("dump.exe","wb").write(exe_data)

使用IDA查看其逻辑发现只是一个简单的xor

data =[     0x34, 0x2A, 0x42, 0x0E, 0x00, 0x1D, 0x5C, 0x33, 0x5E, 0x44,
    0x3E, 0x1A, 0x0B, 0x5C, 0x2C, 0x3A, 0x5F, 0x22, 0x03, 0x28,
    0x36, 0x1B, 0x07, 0x31, 0x8D, 0xDE, 0x10, 0xA2, 0xEB,
    0xB2, 0xDA, 0xA2, 0xD8, 0x18, 0x0D, 0x17, 0x1C, 0x1F,
    0xBD, 0xD9, 0x1D, 0xBF, 0xEB, 0xA2, 0xD8, 0x16, 0x0D,
    0xA0, 0xF6, 0x30, 0xBD, 0xD8, 0x17, 0xBE, 0xDA, 0x0F,
    0xAB, 0xC1, 0xAE, 0xEA, 0x8D, 0xDE, 0x11, 0x01, 0xA1,
    0xC5
]

flag = []

key = b"Cosmopolitan"
for i in range(66):
    flag.append(data[i]^key[i%12])
flag = b"TPCTF{"+bytes(flag)+b"}"



print(flag.decode())

不过由于flag中含有希腊字母,所以需要uft-8解码。

magicfile

可以阅读原文档去了解magic文件的结构

  • offset 位于 12-15 字节

  • .mgc魔数是1c 04 1e f1

  • 需要比较的值位于 32 字节

  • 输出位于 160 字节

  • 操作符位于第 4 字节

  • 从 = 操作符重现的频率可以知道 magic 结构体在文件中的长度是 0x178

从程序提取出mgc文件 第二个参数是地址 第三个参数是长度

with open("D:\Edge\dump.mgc", "wb") as fp:
    b = ida_bytes.get_bytes(0x21004, 0x116f9f8)
    fp.write(b)

使用IDA提取,然后再从mgc文件中提取。

并且可以看到

有许多无意义Try again的填充应该也是magic结构体,不过是干扰提取的,所以需要稍加绕过

import struct
import string

with open('D:\\Edge\\dump.mgc', 'rb') as f_in:
    with open('D:\\Edge\\dump1.txt', 'w', encoding='utf-8') as f_out:
        b = f_in.read(0x178)
        indexes = []
        table = ''
        while True:
            b = f_in.read(0x178)
            if len(b) != 0x178:
                break
            line = b[:0x30]
            _type = line[6]
            text = b[0xa0:].decode()
            if "Try again" in text:
                text = "Try again"
            else:
                text = ""
            off = struct.unpack_from('<I', line, 0x0c)[0]
            s = f'type: {_type:02X}, off: {off:02X}, text = ' + text.strip()
            if _type == 5:
                s += ', str: ' + line[0x20:].decode()
                f_out.write(s + '\n') 
            elif _type == 1:
                n1, n2 = line[0x18], line[0x20]
                v = n1 ^ n2
                s += f', byte: {n1:02X} {n2:02X} {v:02X} {chr(v)}'
                table += chr(v)
                if "Try again" in text:
                    print(s)
            elif _type == 3:
                if "Try again" not in text:
                    f_out.write(s + '\n')
                s += ', default '
            f_out.write(s + '\n')
输出后直接搜索}
type: 01, off: 07, text = , byte: 00 6F 6F o
type: 01, off: 08, text = , byte: 00 55 55 U
type: 01, off: 09, text = , byte: 00 5F 5F _
type: 01, off: 0A, text = , byte: 00 41 41 A
type: 01, off: 0B, text = , byte: 00 52 52 R
type: 01, off: 0C, text = , byte: 00 33 33 3
type: 01, off: 0D, text = , byte: 00 5F 5F _
type: 01, off: 0E, text = , byte: 00 53 53 S
type: 01, off: 0F, text = , byte: 00 4F 4F O
type: 01, off: 10, text = , byte: 00 5F 5F _
type: 01, off: 11, text = , byte: 00 35 35 5
type: 01, off: 12, text = , byte: 00 6D 6D m
type: 01, off: 13, text = , byte: 00 40 40 @
type: 01, off: 14, text = , byte: 00 52 52 R
type: 01, off: 15, text = , byte: 00 37 37 7
type: 01, off: 16, text = , byte: 00 5F 5F _
type: 01, off: 17, text = , byte: 00 54 54 T
type: 01, off: 18, text = , byte: 00 4F 4F O
type: 01, off: 19, text = , byte: 00 5F 5F _
type: 01, off: 1A, text = , byte: 00 63 63 c
type: 01, off: 1B, text = , byte: 00 52 52 R
type: 01, off: 1C, text = , byte: 00 41 41 A
type: 01, off: 1D, text = , byte: 00 43 43 C
type: 01, off: 1E, text = , byte: 00 6B 6B k
type: 01, off: 1F, text = , byte: 00 5F 5F _
type: 01, off: 20, text = , byte: 00 54 54 T
type: 01, off: 21, text = , byte: 00 68 68 h
type: 01, off: 22, text = , byte: 00 31 31 1
type: 01, off: 23, text = , byte: 00 24 24 $
type: 01, off: 24, text = , byte: 00 5F 5F _
type: 01, off: 25, text = , byte: 00 6D 6D m
type: 01, off: 26, text = , byte: 00 40 40 @
type: 01, off: 27, text = , byte: 00 39 39 9
type: 01, off: 28, text = , byte: 00 69 69 i
type: 01, off: 29, text = , byte: 00 43 43 C
type: 01, off: 2A, text = , byte: 00 5F 5F _
type: 01, off: 2B, text = , byte: 00 66 66 f
type: 01, off: 2C, text = , byte: 00 31 31 1
type: 01, off: 2D, text = , byte: 00 6C 6C l
type: 01, off: 2E, text = , byte: 00 65 65 e
type: 01, off: 2F, text = , byte: 00 7D 7D }


差一位,判断为Y
TPCTF{YoU_AR3_SO_5m@R7_TO_cRACk_Th1$_m@9iC_f1le}

评论