来源:https://buuoj.cn/
内容:无
附件: https://pan.baidu.com/s/1qq_64SNIRnnTTCNqNIKOiw?pwd=1iz9 提取码:1iz9
答案:KDEEIFGKIJ@AFGEJAEF@FDKADFGIJFA@FDE@JG@J
判断和输入相关的函数,然后对这些函数使用deflat.py对程序扁平化处理。
反向编写脚本得到答案
general_inspection
以及trace
等自定义方法的,程序发现控制流特别大,可能是进行了混淆使用deflat.py(见附件),在ida中查看main方法的地址为0x4006F0
,分别deflat掉对输入有操作的函数,check1
、check3
、check2
check3
函数中套了一个check2
,该函数中是判断用户的输入值是否与全局变量相同,双击点开sodoku
和D0g3
变量,shift+e按如图设置得到地图。其中D0g3
变量会接收变更后的input值,即使得用户输入与其sodoku
变量相同即可
其中check1
函数的实现是转换v_input:
0
-len/2-1
与 len/2
-len
互换通过使用z3创建一个 0-9
的映射,即将每个数字都得到原来的输入
from typing import List
import z3
import struct
def convert(raw: str) -> List[int]:
raw = bytearray.fromhex(raw)
raw = [raw[x*4:(x+1)*4] for x in range(int(len(raw)/4))]
raw = [struct.unpack(', x)[0] for x in raw]
return raw
v_sudo = '010000000400000005000000030000000200000007000000060000000900000008000000080000000300000009000000060000000500000004000000010000000200000007000000060000000700000002000000080000000100000009000000050000000400000003000000040000000900000006000000010000000800000005000000030000000700000002000000020000000100000008000000040000000700000003000000090000000500000006000000070000000500000003000000020000000900000006000000040000000800000001000000030000000600000007000000050000000400000002000000080000000100000009000000090000000800000004000000070000000600000001000000020000000300000005000000050000000200000001000000090000000300000008000000070000000600000004000000'
v_d0g3 = '010000000000000005000000030000000200000007000000000000000000000008000000080000000000000009000000000000000500000000000000000000000200000000000000000000000700000000000000000000000100000000000000050000000000000003000000040000000900000000000000010000000000000000000000030000000000000000000000000000000100000000000000000000000700000000000000090000000000000006000000070000000000000003000000020000000900000000000000040000000800000000000000000000000600000000000000050000000400000000000000080000000000000009000000000000000000000004000000000000000000000001000000000000000300000000000000000000000200000001000000000000000300000000000000070000000000000004000000'
v_sudo = convert(v_sudo)
v_d0g3 = convert(v_d0g3)
print(f'v_sudo:{v_sudo}')
print(f'v_d0g3:{v_d0g3}')
s = z3.Solver()
v_result = [index + 48 for index, x in enumerate([0] * 10)] # 初始化数字的ascii
v_input = [0] * len(v_result)
v_input = [z3.BitVec(str(index), 16) for index, x in enumerate(v_input)]
def exp(index: int):
x = v_input[index]
r = v_result[index]
return (x & 0xf3 | ~x & 0xc) - 20 == r
def get_result(index: int) -> int:
r = None
s.add(exp(index))
result = s.check()
if result == z3.sat:
rs = s.model()
r = rs[0] # 获取最后结果key
r = rs[r].as_long() # 转换为value
else:
print(f'fail on {index}')
s.reset()
return r
key_mapper = [get_result(index) for index, x in enumerate(v_result)]
print(f'key_mapper:{key_mapper}')
得到 key_mapper:[72, 73, 74, 75, 68, 69, 70, 71, 64, 65]
发现在最后的input判断中是只判断当前数组中为0的项的,即输入的值是v_sudo和v_d0g3不一致的值
# 只填入d0g3中为0的位置,使得v_sudo == v_d0g3
is_invalid = -1
v_encoded = [v_sudo[index] if x == 0 else is_invalid for index,
x in enumerate(v_d0g3)] # 转换为输入
v_encoded = list(filter(lambda x: x != is_invalid, v_encoded))
v_encoded = [key_mapper[x] for x in v_encoded]
v_encoded = [chr(x) for x in v_encoded]
print(f'v_encoded:{v_encoded}')
得到转换后的v_input v_encoded:['D', 'F', 'A', 'K', 'F', 'D', 'I', 'G', 'F', 'J', '@', 'A', 'D', 'F', '@', 'E', 'G', 'J', 'J', '@', 'D', 'K', 'E', 'E', 'F', 'I', 'K', 'G', 'J', 'I', 'A', '@', 'G', 'F', 'J', 'E', 'E', 'A', '@', 'F']
最后将其通过key_mapper后,按:
0
-len/2-1
与 len/2
-len
互换最终的exp为
from typing import List
import z3
import struct
def convert(raw: str) -> List[int]:
raw = bytearray.fromhex(raw)
raw = [raw[x*4:(x+1)*4] for x in range(int(len(raw)/4))]
raw = [struct.unpack(', x)[0] for x in raw]
return raw
v_sudo = '010000000400000005000000030000000200000007000000060000000900000008000000080000000300000009000000060000000500000004000000010000000200000007000000060000000700000002000000080000000100000009000000050000000400000003000000040000000900000006000000010000000800000005000000030000000700000002000000020000000100000008000000040000000700000003000000090000000500000006000000070000000500000003000000020000000900000006000000040000000800000001000000030000000600000007000000050000000400000002000000080000000100000009000000090000000800000004000000070000000600000001000000020000000300000005000000050000000200000001000000090000000300000008000000070000000600000004000000'
v_d0g3 = '010000000000000005000000030000000200000007000000000000000000000008000000080000000000000009000000000000000500000000000000000000000200000000000000000000000700000000000000000000000100000000000000050000000000000003000000040000000900000000000000010000000000000000000000030000000000000000000000000000000100000000000000000000000700000000000000090000000000000006000000070000000000000003000000020000000900000000000000040000000800000000000000000000000600000000000000050000000400000000000000080000000000000009000000000000000000000004000000000000000000000001000000000000000300000000000000000000000200000001000000000000000300000000000000070000000000000004000000'
v_sudo = convert(v_sudo)
v_d0g3 = convert(v_d0g3)
print(f'v_sudo:{v_sudo}')
print(f'v_d0g3:{v_d0g3}')
s = z3.Solver()
v_result = [index + 48 for index, x in enumerate([0] * 10)] # 初始化数字的ascii
v_input = [0] * len(v_result)
v_input = [z3.BitVec(str(index), 16) for index, x in enumerate(v_input)]
def exp(index: int):
x = v_input[index]
r = v_result[index]
return (x & 0xf3 | ~x & 0xc) - 20 == r
def get_result(index: int) -> int:
r = None
s.add(exp(index))
result = s.check()
if result == z3.sat:
rs = s.model()
r = rs[0] # 获取最后结果key
r = rs[r].as_long() # 转换为value
else:
print(f'fail on {index}')
s.reset()
return r
key_mapper = [get_result(index) for index, x in enumerate(v_result)]
print(f'key_mapper:{key_mapper}')
# 只填入d0g3中为0的位置,使得v_sudo == v_d0g3
is_invalid = -1
v_encoded = [v_sudo[index] if x == 0 else is_invalid for index,
x in enumerate(v_d0g3)] # 转换为输入
v_encoded = list(filter(lambda x: x != is_invalid, v_encoded))
v_encoded = [key_mapper[x] for x in v_encoded]
v_encoded = [chr(x) for x in v_encoded]
print(f'v_encoded:{v_encoded}')
result = v_encoded
vel = len(result) # result_len
vehl = int(vel / 2) # result_half_len
for i in range(vehl):
(result[i], result[i+vehl]) = (result[i+vehl], result[i])
for i in range(0, vel-1, 2):
(result[i], result[i+1]) = (result[i+1], result[i])
print(''.join(result))
得到答案 KDEEIFGKIJ@AFGEJAEF@FDKADFGIJFA@FDE@JG@J
CTF逆向-常用的逆向工具 提取码:pnbt
常见用法
B站教程中国某省队CTF集训(逆向工程部分)
XXTEA
、Base64
换表Z3
方程式、不定式等的 约束求解
X
键跟踪,寻找所有Ty
为w
的引用(即类型是写入的),通常就是关键位置find crypt
插件ctrl+alt+f
int 3
,需要跳过去基本知识
更多CTF逆向题通用性做法和常用工具下载参考该博文内容:CTF逆向Reverse题的玩法
Python
远程调试汇编
流程控制
逆向思维
安卓
虚拟机
反调试和SMC
加密
花指令
流程混淆的扁平化处理
c/cpp基础