这道题也是让我学到了re的新姿势啊
实例题
PS D:\ForCode\pythoncode\.idea> nc hnctf.yuanshen.life 33255
I am about to send you 128 base64-encoded ELF files, which load a value onto the stack, then do an XOR operation.
But I forgot to write the value after the XOR back onto the stack. Can you send back the value after XOR as a hex string for me?
You must analyze them all in under 120 seconds
Let's start with a warmup
ELF: f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeIAECAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAFAAAAAAAAAAAAAAAAgAQIAAAAAACABAgAAAAAJAQAAAAAAAAkBAAAAAAAAAAQAAAAAAAASIPsGEiNNekBAABIiee5GAAAAPOkSDHJigQMNL1Ig8EBigQMNLBIg8EBigQMNP1Ig8EBigQMNKFIg8EBigQMNG5Ig8EBigQMNCJIg8EBigQMNPZIg8EBigQMNCZIg8EBigQMNP5Ig8EBigQMNGBIg8EBigQMNHZIg8EBigQMNBlIg8EBigQMNCFIg8EBigQMNJhIg8EBigQMNAJIg8EBigQMNEFIg8EBigQMNGJIg8EBigQMNCtIg8EBigQMNKZIg8EBigQMNCJIg8EBigQMNKNIg8EBigQMNKFIg8EBigQMNCxIg8EBigQMNAFIg8EBuDwAAAAPBZukuo0Dd2M3ddipTN1oVQ+JEouBBIQ9vZH+PYJK7hzxrtaVISiZPOtgXvYySV00G2jbBDcEnTMG3R913XeWjZxVSPHLItAjRjl5I0MT5bEayW2V7Mgb3RUVMMnD5hAYqjIIEch6AgQUEIkC6rw8EvhcQ02Yq1dPlrdrPuUCG6v2470ZaY5uySCfuCFIaQhcgZmoW6qUhF1xbltno3zUs2gk5wqIHoTzFH64S9M6qQ1EL7Rd1+v/5DQNWBD5QdKIZVp5QLn6gqPmkWncQ8OnGk6eWyUi7W9lu+t94lO/TJ3HQET4oIuszyoi/oRJY563cs4Et91e92sOKPoW7FbRsG3agk5Vyy7H03EXY4AaIiebsCOPUaKOtIWpTwZIPDaTilhhnoS0dLOR0Salo3IyQJ52iZRku7mX
XoCFnY4KVpHRdIZhk7uPI4sO3L0qrpqWUJAa4YLtdxn1RffnwWsgs9vOhD9acq1htzHZ+WfZu3GriuexCcTqo/OY4ejMK26ZGuoUPISlUbjD6w1aPkvya2ISQ9HAQkmORTftpMuPCG0RUId9NAdaVD0xURwZ4e6SUhEqm9aeDvFX0qxr5QfYao
kgJxJZTllq2OFoYU9mNY2rIeWs6HmrXXm/r1LrKf/AgyKjcdhpj4mycF3el+xGxM1r/ksPe4F5yYS41lagQrP4rdHrJEnIoTQ35P5254QBhTdirzwlWBn2Fs8PLnLca91v+wsBFY6hFD7NbBEmpgSU1rg9xG4/MMHbTqsKAGBTh4NRS8qbrbbKBEKSwSYiDV1rEXbPzbILLL2L4vF280A9uqhuEeNTX6iHNY2DBuQNgOnCNWiJxuWub+Xe1OzUbwR3RjHjZyfENDCD9QVnGTMfJH7uCHrZC2sBjj3lhlzOo60UWKs3+QGrM2hUU3P2USBOjbIUKQ==
Expected bytes: 6c00907bec6ca3edd0a7a56836fb825b400c3d92802e7da3
Bytes?
就是xor操作,然后给你的elf文件base64解码
cyber解码然后保存
一个正常elf文件
就是这里,一共24个
然后这里就是原文
import idc
ea = 0x08048093 # 起始地址
enc = 0x0008048274
v = [get_wide_byte(enc+i)for i in range(24)]
def next_aim(n):# 获取下一条目的地址的指令
global ea
for i in range(n):
ea = idc.next_head(ea)
key = []
for i in range(24):# 获取几个字符
alp = idc.get_operand_value(ea,1) # 获取字符操作
key.append(alp)
next_aim(3)
flag = ''
for i in range(len(key)):
flag += ''.join(hex(v[i] ^ key[i])[2:].zfill(2))
print(flag)
这里就是一次得到的密文,你输入后
服务器会又给你这样的elf文件,一共有120多个,我的天
然后就是编写自动化脚本
我不会》。直接抄
from qiling import *
from qiling.const import QL_VERBOSE
from pwn import *
from Crypto.Util.number import *
xor_key = []
def hook_code(ql, address, size, user_data):
buf = ql.mem.read(address, size)
asm = next(user_data.disasm(buf, address))
x = f"{asm.mnemonic} {asm.op_str}"
if 'xor' in x and 'al' in x:
xor_key.append(int(asm.op_str[(asm.op_str.index(',')) + 2:], 16))
context(os='linux', arch='amd64', log_level='debug')
io = remote('hnctf.imxbt.cn', 33534)
io.recvuntil(b'Expected bytes: ')
a = io.recv(48)
io.sendlineafter(b'Bytes? ', a)
for i in range(128):
io.recvuntil(b'ELF: ')
encrypted_data = io.recvuntil(b'\n').strip()
decoded_data = base64.b64decode(encrypted_data)
with open("./111", "wb") as file:
file.write(decoded_data)
ql = Qiling(["./111"], verbose=QL_VERBOSE.OFF)
ql.arch.disassembler.detail = True
ql.mem.map(0x10000, 0x10000)
ql.hook_code(hook_code, user_data=ql.arch.disassembler)
ql.run()
ecx_value = ql.arch.regs.rsi
memory_address = ql.mem.read(ecx_value - 25, 25)
enc = hex(bytes_to_long(bytes([bytes(memory_address[1:25])[j] ^ xor_key[j] for j in range(0x18)])))
enc = enc[2:].rjust(48, '0').encode()
io.sendlineafter(b'Bytes? ', enc)
print(i)
xor_key = []
io.interactive()
from qiling import *
from qiling.const import QL_VERBOSE
from pwn import *
from Crypto.Util.number import *
xor_key = []
def hook_code(ql, address, size, user_data):
buf = ql.mem.read(address, size)
asm = next(user_data.disasm(buf, address))
x = f"{asm.mnemonic} {asm.op_str}"
if 'xor' in x and 'al' in x:
xor_key.append(int(asm.op_str[(asm.op_str.index(',')) + 2:], 16))
context(os='linux', arch='amd64', log_level='debug')
io = remote('hnctf.imxbt.cn', 33534)
io.recvuntil(b'Expected bytes: ')
a = io.recv(48)
io.sendlineafter(b'Bytes? ', a)
for i in range(128):
io.recvuntil(b'ELF: ')
encrypted_data = io.recvuntil(b'\n').strip()
decoded_data = base64.b64decode(encrypted_data)
with open("./111", "wb") as file:
file.write(decoded_data)
ql = Qiling(["./111"], verbose=QL_VERBOSE.OFF)
ql.arch.disassembler.detail = True
ql.mem.map(0x10000, 0x10000)
ql.hook_code(hook_code, user_data=ql.arch.disassembler)
ql.run()
ecx_value = ql.arch.regs.rsi
memory_address = ql.mem.read(ecx_value - 25, 25)
enc = hex(bytes_to_long(bytes([bytes(memory_address[1:25])[j] ^ xor_key[j] for j in range(0x18)])))
enc = enc[2:].rjust(48, '0').encode()
io.sendlineafter(b'Bytes? ', enc)
print(i)
xor_key = []
io.interactive()
这段代码你要pip install pwntools (qiling)这两个工具
这段代码结合了
qiling
框架(用于动态二进制分析)、pwntools
库(针对渗透测试和逆向工程的瑞士军刀)以及Crypto.Util.number
(处理大数运算的模块),目的是解密远程服务器上提供的加密数据并通过一系列交互完成某个挑战或任务。以下是其工作流程的详细解析:初始化
- 环境配置:设置上下文环境为Linux amd64架构,并开启详细的日志记录。
- 远程连接:使用
pwntools
的remote
函数连接到指定的远程服务器和端口。- 接收与发送:首先接收预期的字节长度信息,然后回传接收到的48字节数据作为初始响应。
分析与解密循环
- 主循环:对于128轮循环,每轮执行以下步骤:
- 接收加密数据:接收服务器发来的Base64编码的加密数据。
- 保存并加载二进制:将接收到的数据保存到本地文件
111
,然后使用Qiling
框架加载此文件准备动态分析。- 代码钩子设置:定义一个
hook_code
函数,用于监听执行过程中的每条指令。当遇到包含xor
和寄存器al
的指令时(这通常涉及到解密操作),提取并记录异或键(xor_key
)。- 运行并分析:运行二进制文件,在执行过程中,每当遇到符合条件的
xor
指令就会触发钩子,逐步构建出完整的解密密钥。- 解密过程:
- 读取寄存器
ECX
的值作为关键地址。- 从该地址读取25字节数据,这部分数据预计与之前收集的
xor_key
有关。- 应用异或密钥解密数据:对读取的内存区域中的每个字节与对应位置的
xor_key
元素进行异或操作。- 将解密后的数据转换为大端表示的16进制字符串,并确保填充至48位长度,然后将其发送回服务器作为下一轮的输入。
结束交互
- 循环结束后,使用
io.interactive()
进入交互模式,允许手动进一步与远程服务进行通信或查看最终输出。总之,这段代码通过动态分析技术(利用
qiling
模拟执行)和简单的加密解密逻辑(基于XOR操作),实现了与远程服务的自动交互,目的是逐步解密并响应服务器发送的加密数据,直至完成所有预设的交互轮次。
这里改成对应端口就OK
127个我的仙人