1、Petition
总的来说还是比较喜欢这类题目的,因为它的flag是一位一位校验的,能爆破当然是一件很爽的事情。
回到正文:IDA载入文件:
程序从start开始执行,说是说"%36s",实际上要输入整整42位,骗子。
往下的start后面的一堆函数,去翻看的话会发现每个都长得差不多,然后就猜测flag会不会是逐位校验,一位flag对应一个函数。
调试什么的还是比较累的(我不会告诉你其实我根本看不懂flag是怎么校验的),为了偷懒,这里是直接使用了Unicorn,将start函数里面调用printf、scanf 的地方给patch掉,再对scanf 做一个hook以保证flag能输入到内存。这样就能把程序的输入和校验功能给运行起来了,下面是我对这个程序写的Unidbg类:
from unicorn import *
from unicorn.x86_const import *
from capstone import *
import binascii
Petition_base = 0x0 # 程序加载的地址
Petition_stack_base = 0x10000
with open("Petition", "rb") as f:
code = f.read()
xxx = [b'\x00', b'\x01', b'\x02', b'\x03', b'\x04', b'\x05', b'\x06', b'\x07', b'\x08', b'\x09', b'\x0a', b'\x0b',
b'\x0c', b'\x0d', b'\x0e', b'\x0f', b'\x10', b'\x11', b'\x12', b'\x13', b'\x14', b'\x15', b'\x16', b'\x17',
b'\x18', b'\x19', b'\x1a', b'\x1b', b'\x1c', b'\x1d', b'\x1e', b'\x1f', b'\x20', b'\x21', b'\x22', b'\x23',
b'\x24', b'\x25', b'\x26', b'\x27', b'\x28', b'\x29', b'\x2a', b'\x2b', b'\x2c', b'\x2d', b'\x2e', b'\x2f',
b'\x30', b'\x31', b'\x32', b'\x33', b'\x34', b'\x35', b'\x36', b'\x37', b'\x38', b'\x39', b'\x3a', b'\x3b',
b'\x3c', b'\x3d', b'\x3e', b'\x3f', b'\x40', b'\x41', b'\x42', b'\x43', b'\x44', b'\x45', b'\x46', b'\x47',
b'\x48', b'\x49', b'\x4a', b'\x4b', b'\x4c', b'\x4d', b'\x4e', b'\x4f', b'\x50', b'\x51', b'\x52', b'\x53',
b'\x54', b'\x55', b'\x56', b'\x57', b'\x58', b'\x59', b'\x5a', b'\x5b', b'\x5c', b'\x5d', b'\x5e', b'\x5f',
b'\x60', b'\x61', b'\x62', b'\x63', b'\x64', b'\x65', b'\x66', b'\x67', b'\x68', b'\x69', b'\x6a', b'\x6b',
b'\x6c', b'\x6d', b'\x6e', b'\x6f', b'\x70', b'\x71', b'\x72', b'\x73', b'\x74', b'\x75', b'\x76', b'\x77',
b'\x78', b'\x79', b'\x7a', b'\x7b', b'\x7c', b'\x7d']
class Unidbg:
def __init__(self, flag, except_hit):
self.except_hit = except_hit
self.hit = 0
self.flag = flag
self.fff = 0
mu = Uc(UC_ARCH_X86, UC_MODE_32)
# 程序基址为 0x1000,分配 128 KB内存
mu.mem_map(Petition_base, 0x10000 * 2)
mu.mem_write(Petition_base, code)
# 设置寄存器的值
mu.reg_write(UC_X86_REG_EAX, 0)
mu.reg_write(UC_X86_REG_EBX, 0)
mu.reg_write(UC_X86_REG_ECX, 0)
mu.reg_write(UC_X86_REG_EDX, 0)
mu.reg_write(UC_X86_REG_ESI, 0)
mu.reg_write(UC_X86_REG_EDI, 0)
mu.reg_write(UC_X86_REG_EBP, 0)
mu.reg_write(UC_X86_REG_ESP, Petition_stack_base)
mu.reg_write(UC_X86_REG_EIP, 0x1040)
# patch printf()
mu.mem_write(0x10C6, b'\x90\x90\x90\x90\x90')
# patch scanf()
mu.mem_write(0x110B, b'\x90\x90\x90\x90\x90')
mu.hook_add(UC_HOOK_CODE, self.hook_function_scanf, begin=0x110A, end=0x110C)
mu.hook_add(UC_HOOK_CODE, self.hook_code, begin=0x119C, end=0x28A0)
if self.except_hit < 127:
mu.hook_add(UC_HOOK_CODE, self.trace)
self.mu = mu
self.md = Cs(CS_ARCH_X86, CS_MODE_32)
def solve(self):
try:
self.mu.emu_start(0x1040, 0x29C4)
except:
pass
return self.hit
def trace(self, mu, address, size, data):
disasm = self.md.disasm(mu.mem_read(address, size), address)
for i in disasm:
print(i)
def hook_function_scanf(self, mu, address, size, data):
if address == 0x110C:
# self.show_reg()
edx = mu.reg_read(UC_X86_REG_EDI)
mu.mem_write(edx, self.flag)
# print(self.flag, binascii.b2a_hex(mu.mem_read(edx, 36)).decode(), hex(edx))
pass
def show_reg(self ):
print("EAX", hex(self.mu.reg_read(UC_X86_REG_EAX))[2:].upper().zfill(8))
print("EBX", hex(self.mu.reg_read(UC_X86_REG_EBX))[2:].upper().zfill(8))
print("ECX", hex(self.mu.reg_read(UC_X86_REG_ECX))[2:].upper().zfill(8))
print("EDX", hex(self.mu.reg_read(UC_X86_REG_EDX))[2:].upper().zfill(8))
print("ESP", hex(self.mu.reg_read(UC_X86_REG_ESP))[2:].upper().zfill(8))
print("EBP", hex(self.mu.reg_read(UC_X86_REG_EBP))[2:].upper().zfill(8))
print("ESI", hex(self.mu.reg_read(UC_X86_REG_ESI))[2:].upper().zfill(8))
print("EDI", hex(self.mu.reg_read(UC_X86_REG_EDI))[2:].upper().zfill(8))
print("EIP", hex(self.mu.reg_read(UC_X86_REG_EIP))[2:].upper().zfill(8))
def hook_code(self, mu, address, size, data):
Temp = False
if address == 0x119E: # 取每个函数中第二条指令的首地址
Temp = True
if address == 0x122C:
Temp = True
if address == 0x12BA:
Temp = True
if address == 0x1346:
Temp = True
if address == 0x13D2:
Temp = True
if address == 0x145E:
Temp = True
if address == 0x14EA:
Temp = True
if address == 0x1576:
Temp = True
if address == 0x1604:
Temp = True
if address == 0x1690:
Temp = True
if address == 0x171E:
Temp = True
if address == 0x17A8:
Temp = True
if address == 0x1836:
Temp = True
if address == 0x18C4:
Temp = True
if address == 0x1950:
Temp = True
if address == 0x19DC:
Temp = True
if address == 0x1A66:
Temp = True
if address == 0x1AF0:
Temp = True
if address == 0x1B7C:
Temp = True
if address == 0x1C08:
Temp = True
if address == 0x1C96:
Temp = True
if address == 0x1D22:
Temp = True
if address == 0x1DAC:
Temp = True
if address == 0x1E36:
Temp = True
if address == 0x1EC4:
Temp = True
if address == 0x1F50:
Temp = True
if address == 0x1FDC:
Temp = True
if address == 0x2066:
Temp = True
if address == 0x20F2:
Temp = True
if address == 0x217C:
Temp = True
if address == 0x220A:
Temp = True
if address == 0x2294:
Temp = True
if address == 0x2320:
Temp = True
if address == 0x23AE:
Temp = True
if address == 0x243C:
Temp = True
if address == 0x24C6:
Temp = True
if address == 0x2554:
Temp = True
if address == 0x25E0:
Temp = True
if address == 0x266E:
Temp = True
if address == 0x26FC:
Temp = True
if address == 0x2786:
Temp = True
if address == 0x2812:
Temp = True
if address == 0x289E:
Temp = True
if Temp:
print(hex(address))
self.hit += 1
先随便输入几个flag,验证一下之前的猜想:
好吧,到这里已经可以确认,我之前的猜想是成立的,这就很nice。
下面就是爆破了:
flag = b''
for j in range(42):
for i in b'0123456789abcdef{}-_#!?ghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ':
tem = bytearray(flag + b'x' * (42 - len(flag)))
tem[j] = i
if Unidbg(bytes(tem), 127).solve() == j +1:
flag += xxx[i]
print(flag.decode())
效果示意:
FLAG : flag{96c69646-8184-4363-8de9-73f7398066c1}