出题战队:天外星系
设计思路
运行环境:win10 win11
输出提示:key正确则输出提示ok!
初始状态为:
{0, 1, 3},
{5, 2, 6},
{4, 7, 8}
{1, 2, 3},
{4, 5, 6},
{7, 8, 0}
011110202122
赛题解析
逐步断点,调用了以下函数:
ntdll.dll
NtAddBootEntry
TpAllocWait
TpSetWait
之后创建2个线程,线程A执行shellcode, 线程B等待答案并输出结果ok!或no!
这时候我们是不能F5的,因为作者做了混淆。
混淆分析
第一部分将部分字节0x00换成了0x2A,导致IDA静态分析挂掉所以不能F5。
第二部分增加了一些无关的跳转和无效指令。
?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? FD EB 1F 3E 1C EB EB
?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? FF EB 15 3E 1D EB FB
?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? FE EB 18 3E 1C EB EB
去混淆脚本
# -*- coding: gbk -*-
def replace_bytes_and_preceding(file_path, search_bytes, replace_byte, preceding_length):
# 读取二进制文件内容
with open(file_path, 'rb') as file:
data = file.read()
# 将要查找的字节和替换的字节转换为字节类型
search_bytes = bytes.fromhex(search_bytes)
replace_byte = bytes.fromhex(replace_byte)
replace_length = len(search_bytes) + preceding_length # 替换的总长度
# 创建一个可变字节数组来进行操作
modified_data = bytearray(data)
# 初始化搜索开始位置
start = 0
while start < len(modified_data):
# 查找字节序列的位置
index = modified_data.find(search_bytes, start)
if index == -1:
break
# 计算需要替换的起始位置
replace_start = max(0, index - preceding_length)
# 将替换的范围全部设置为 `replace_byte`
modified_data[replace_start:index + len(search_bytes)] = replace_byte * replace_length
# 更新搜索开始位置,跳过当前替换的位置
start = index + len(search_bytes)
# 将修改后的数据写回到文件中
with open(file_path, 'wb') as file:
file.write(modified_data)
print(f"Replaced all occurrences of {search_bytes.hex()} and preceding {preceding_length} bytes with {replace_byte.hex()} in {file_path}.")
preceding_length = 11 # 替换之前的字节数
replace_bytes_and_preceding('test3.exe', 'FD EB 1F 3E 1C EB EB', '90', preceding_length)
replace_bytes_and_preceding('test3.exe', 'FF EB 15 3E 1D EB FB', '90', preceding_length)
replace_bytes_and_preceding('test3.exe', 'FE EB 18 3E 1C EB EB', '90', preceding_length)
# -*- coding: gbk -*-
def replace_specific_byte_in_range(file_path, search_bytes, search_byte, replace_byte, offset_start, offset_end):
# 读取二进制文件内容
with open(file_path, 'rb') as file:
data = file.read()
# 将要查找的字节和替换的字节转换为字节类型
search_bytes = bytes.fromhex(search_bytes)
search_byte = bytes.fromhex(search_byte)
replace_byte = bytes.fromhex(replace_byte)
# 创建一个可变字节数组来进行操作
modified_data = bytearray(data)
# 初始化搜索开始位置
start = 0
while start < len(modified_data):
# 查找字节序列的位置
index = modified_data.find(search_bytes, start)
if index == -1:
break
# 计算替换范围的起始和结束位置
range_start = index + offset_start
range_end = min(index + offset_end, len(modified_data))
# 替换范围内的所有指定字节
for i in range(range_start, range_end):
if modified_data[i] == search_byte[0]:
modified_data[i] = replace_byte[0]
# 更新搜索开始位置,跳过当前查找的位置
start = index + len(search_bytes)
# 将修改后的数据写回到文件中
with open(file_path, 'wb') as file:
file.write(modified_data)
print(f"Replaced all occurrences of {search_byte.hex()} with {replace_byte.hex()} in range [{offset_start:#X}, {offset_end:#X}] after each occurrence of {search_bytes.hex()} in {file_path}.")
replace_specific_byte_in_range('test3.exe', '57 50 51 56 E8 FF FF FF FF C0', '2A', '00', 0x2C, 0xEA7 + 0x2C)
用脚本去除混淆后的代码,可以发现无效指令都被换成nop了。
然后就可以愉快的F5了。
然后分析主校验函数:
分析后看起来是一个8-puzzle问题,要求从起始棋盘状态走到最终状态的最少步数。
0 1 3
5 2 6
4 7 8
1 2 3
4 5 6
7 8 0
这个棋盘挺简单的,可以手动解出。
解棋盘步骤
0 1 3
5 2 6
4 7 8
1 0 3
5 2 6
4 7 8
1 2 3
5 0 6
4 7 8
1 2 3
0 5 6
4 7 8
1 2 3
4 5 6
0 7 8
1 2 3
4 5 6
7 0 8
1 2 3
4 5 6
7 8 0
得到走法的路径为1,4,3,6,7,8
换成三进制01 11 10 20 21 22
得到flag 011110202122
球分享
球点赞
球在看
点击阅读原文查看更多
原文始发于微信公众号(看雪学苑):2024 KCTF 大赛 | 第十题《试探》设计思路及解析