@TOC

起因

题目长这样,祥云杯类似题
在这里插入图片描述
我第一反应 拿angr去跑一遍,然后准备喝茶,后来southwood大哥,也写了angr脚本,我就两个一起跑,直到pizza出了后,我明白了一个道理
angr=等死

angr做法

赛后,也是socular给我发了一个angr脚本,还真是一跑就出来了,就趁着此时,复习了一波angr,把之前的angr知识过了一遍,重新做题
这题用到的就是blank对象,条件约束,.bss地址固定写入

1
2
3
4
5
6
7
8
9
p.factory.blank_state
.blank_state()#构造了一个“空白石板”空白状态,其大部分数据未初始化。

u = claripy.BVS("u", 8)
state.memory.store(0x804a021, u)#给bss数据赋值

tate.add_constraints(input.get_byte(i) != 0)#添加条件

add_options={angr.options.LAZY_SOLVES} #加速用的

作者:acdwas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
p = angr.Project("./runofthemill", load_options={"auto_load_libs": False})

input = claripy.BVS("input", 8*34)

state = p.factory.blank_state(addr=0x40104d, add_options={angr.options.LAZY_SOLVES})

state.memory.store(0x412000, input)
state.regs.rdi = 0x412000

for i in range(34):
state.add_constraints(input.get_byte(i) != 0)
state.add_constraints(input.get_byte(i) >= 0x20)
state.add_constraints(input.get_byte(i) <= 0x7f)

state.add_constraints(state.memory.load(0x412000, 6) == int(binascii.hexlify(b"DrgnS{"), 16))

sm = p.factory.simulation_manager(state)

sm.explore(find=0x411833, avoid=(0x411822))
found = sm.found[0]

flag_str = found.solver.eval(input, cast_to=bytes)
print('Password: ' + flag_str.decode(errors='ignore'))

pizza做法&notpizza做法

利用capstone库
脚本提取汇编~~
看的人头皮发麻
先看pizza的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172

from capstone import *
from capstone.x86 import *
from capstone.x86_const import *

buf = open('runofthemill', 'rb').read()
code = buf[0x1054:0x117F7]

cs = Cs(CS_ARCH_X86, CS_MODE_64)
cs.imm_unsigned = True
cs.detail = True



def all_insns():
it = cs.disasm(code, 0x401054)
return it


def is_buffer_addr(addr):
return 0x412000 <= addr < 0x412040

def buffer_off(op):
return op.mem.disp - 0x412000

def is_buffer_op(op0):
return (op0.type == X86_OP_MEM \
and is_buffer_addr(op0.mem.disp) \
and op0.mem.scale == 1 \
and op0.mem.base == X86_REG_INVALID \
and op0.mem.index == X86_REG_INVALID \
)

def is_temp_op(op0):
return (op0.type == X86_OP_MEM \
and op0.mem.disp == 0x412090 \
and op0.mem.scale == 1 \
and op0.mem.base == X86_REG_INVALID \
and op0.mem.index == X86_REG_INVALID \
)


SIMPLE_ENC = {
X86_INS_ADD: X86_INS_SUB,
X86_INS_SUB: X86_INS_ADD,
X86_INS_XOR: X86_INS_XOR,
X86_INS_ROR: X86_INS_ROL,
X86_INS_ROL: X86_INS_ROR,
}

class Operator:
def __init__(self, op, off, size, param):
self.op = op
self.off = off
self.size = size
self.param = param


@classmethod
def is_simple(cls, insn:CsInsn):
if insn.id not in SIMPLE_ENC:
return False

if len(insn.operands) != 2:
return False

op0 = insn.operands[0] # type:X86Op
op1 = insn.operands[1] # type:X86Op
if not is_buffer_op(op0):
return False

if not (op1.type == X86_OP_IMM):
return False

return True

@classmethod
def simple_op(cls, insn:CsInsn):

op0 = insn.operands[0] # type:X86Op
op1 = insn.operands[1] # type:X86Op

return cls(insn.id, buffer_off(op0), op0.size, op1.imm)


ins_types = {}
for insn in all_insns():
key = insn.mnemonic
if key not in ins_types:
ins_types[key] = 1
else:
ins_types[key] += 1


ops = []
regs = {}
for insn in all_insns():

if Operator.is_simple(insn):
op = Operator.simple_op(insn)
assert op is not None, hex(insn.address) +' ' + insn.mnemonic + ' ' + insn.op_str
ops.append(op)

elif insn.id in (X86_INS_MOVABS, X86_INS_MOV, X86_INS_MOVQ):
op0 = insn.operands[0] # type:X86Op
op1 = insn.operands[1] # type:X86Op
opty = (op0.type, op1.type)
assert opty in {(X86_OP_REG, X86_OP_IMM), (X86_OP_REG, X86_OP_MEM), (X86_OP_MEM, X86_OP_REG)}
if opty == (X86_OP_REG, X86_OP_IMM):
regs[op0.reg] = op1
elif opty == (X86_OP_REG, X86_OP_MEM):
assert is_buffer_op(op1) or is_temp_op(op1)
regs[op0.reg] = op1
elif opty == (X86_OP_MEM, X86_OP_REG):
assert is_buffer_op(op0) or is_temp_op(op0)
if is_temp_op(op0):
regs[X86_REG_ENDING] = regs[op1.reg]
else:
assert op1.reg == X86_REG_MM0
o = regs[op1.reg]
assert isinstance(o, Operator)
assert buffer_off(op0) == o.off
ops.append(o)
else:
raise AssertionError

elif insn.id == X86_INS_PXOR:
op0 = insn.operands[0] # type:X86Op
op1 = insn.operands[1] # type:X86Op
assert op0.type == X86_OP_REG and op1.type == X86_OP_MEM
assert op0.reg == X86_REG_MM0 and is_buffer_op(op1)
op00 = regs[op0.reg]
assert op00.type == X86_OP_IMM or is_temp_op(op00)
if is_temp_op(op00):
op00 = regs[X86_REG_ENDING]
assert op00.type == X86_OP_IMM

regs[op0.reg] = Operator(X86_INS_XOR, buffer_off(op1), op1.size, op00.imm)


elif insn.id in SIMPLE_ENC:
# assert len(insn.operands) == 2
# op0 = insn.operands[0] # type:X86Op
# op1 = insn.operands[1] # type:X86Op
# assert op0.type == X86_OP_REG and is_buffer_op(op1)
# op0v = regs[op0.reg]
# assert op0v.type == X86_OP_IMM
# regs[op0.reg] = Operator(insn.id, buffer_off(op1.mem.disp), op1.size, op0v.imm)
pass

else:
raise AssertionError(hex(insn.address) +' ' + insn.mnemonic + ' ' + insn.op_str)



# print(ins_types)
# print(len(ops))

for i in reversed(ops):
mnem = cs.insn_name(SIMPLE_ENC[i.op])
sz = {8:'qword', 4:'dword', 2:'word', 1:'byte'}[i.size]
param = i.param
if param < 0:
param += 1 << (i.size * 8)

if i.size != 8:
print(f'{mnem} {sz} [buffer + 0x{i.off:x}], 0x{param:x}')
else:
print(f'mov rax, 0x{param:x}')
print(f'{mnem} {sz} [buffer + 0x{i.off:x}], rax')


notpizza的(好理解)
作者:RootMeUpBeforeYouGoGo
我添加了部分注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
from pwn import *
from capstone import *

def process_operands(ods):
Os = ods.split(", ")
nOs = []
for o in Os:
if "ptr" in o:
address = int(o[o.index('[')+1:o.index("]")],16)#取出数据转化为10进制
newo = o.replace("ptr","")#去掉ptr
if address != 0x412090:
off = address - 0x412000
newo = newo.replace(hex(address),"var + "+str(off))
nOs.append(newo)
else:
nOs.append(o)
return ", ".join(nOs)

# get .text and .data
BIN = ELF("./runofthemill")
text = BIN.get_section_by_name(".text").data()
data = BIN.get_section_by_name(".data").data()
data = data[0x50:0x90]#比较数据

# disassemble .text
md = Cs(CS_ARCH_X86, CS_MODE_64)
asm = []
for i in md.disasm(text, 0x401000):
if i.address > 0x401051 and i.address < 0x4117f7:
asm.append((i.mnemonic,i.op_str))#操作码 操作数 ('sub', 'byte ptr [0x412026], 0x7d'), ('xor', 'dword ptr [0x41200f], 0x888b13aa')

# reverse all ops
asm = asm[::-1]#逆序
out = []
i = 0
while i < len(asm):
oc, ods = asm[i]#sub byte ptr [0x412026], 0x7d
ods = process_operands(ods)#byte [var + 38], 0x7d
#print(oc)
if oc == "sub":
out.append("add "+ods)
elif oc == "add":
out.append("sub "+ods)
elif oc == "ror":
out.append("rol "+ods)
elif oc == "rol":
out.append("ror "+ods)
elif oc == "movq": #处理 _m_pxor
const = asm[i+4][1].split(", ")[1]
ods = ods.replace("mm0","rbx")
out.append("mov rbx, "+const)
out.append("xor "+ods)
i += 4 #4行
elif oc == "movabs":
out.append("mov "+ods)
else:
out.append(oc+" "+ods)
i += 1

newasm = """global _start
section .text

_start:
%s

mov rax,1
mov rdi,1
mov rsi, var
mov rdx, 0x41
syscall
mov rax, 0x3c
mov rdi, 0
syscall

section .data
var db """ % "\n ".join(out)

for b in data:
newasm += str(int(b))+", "
newasm += "10, 0\n"

f = open("reverse.asm","w")
f.write(newasm)
f.close()

命令

1
2
nasm -felf64 reverse.asm -o reverse.o
ld -o reverse reverse.o -m elf_x86_64

easy nft

easy nft 看不懂~~