blowfish例题 RCTF-DontEatMe

本题的主要考点是Blowfish算法。附带反调试、迷宫
alt text
程序的开始阶段除了输入之外,还固定了一个随机数种子,并通过动态获取Ntdll地址的方式去调用ZwSetInformationThread来反调试。这里可以把该函数直接nop。而为了考虑栈平衡,需要计算有多少个push、call需要nop或者直接nop一片。
我将Ntdll相关的几个函数全nop后汇编头尾如下:
alt text
alt text
之后程序开始获取随机数:
alt text
需要注意的是:
①我动调扒取rand()、解码后的数组时,这个序列的最后是0xcd,而靠这个序列我解不出来最后的答案,最后是调试到最后,回头看了下这个全局数组发现最后一位不是0xcd,是0x20,将改为0x20后成功解出。这里可能有我手法的问题。
②原因未知,我使用ida 对上述的反调试nop后,在动调时被nop的代码还会出新的代码,此时可能需要重复patch。
获取完随机数序列后,该序列还会做一个简单的decode from ‘fishFISH’。之后进入sub_C41090函数,此为blowfish的box交换函数。
算法的分析有两点,第一点是寻找box特征:
alt text
可在该函数开头看到:
alt text
第二点是分析算法特征,需要有算法实现源码。最直接的对比是算法参数:循环长度、循环步长。需要分析的是算法结构:指令流(循环第一个指令、第二个指令、第三个指令分别是什么,是否匹配)、每个指令的输入、输出。初次分析可判断到是什么算法即可,当解不出来时考虑是否魔改,进而进一步分析算法细节。
①blowfish的pbox异或key,且异或长度为18,循环步长为+2
alt text
alt text
同理对比
alt text
alt text
BF_FN:
alt text
alt text
因此,基本确认这里是blowfish的初始化函数,通过key生成新的box,和输入无关。
接下来有几个重点:
①输入从str被vsscanf转为了hex如:’12’->b’\x12’
②在blowfish算法后有一个迷宫操作,迷宫的地图是通过计算生成的。
③一个小trick来了。
正常来说,我们默认题里的算法是做加密操作后进行密文匹配,而本题,输入的字符串转hex后,经过blowfish计算的结果要作为迷宫的前进方向,计算的结果是在’wasd’之内。
alt text
初始
alt text
最终check
alt text
也就是说,本题是:输入一串hex,经过blowfish处理变成wasd明文。
于是这里的blowfish处理就不能是加密的:明文-》hex密文,而得是解密的:hex密文-》明文。
那么我们这里就清晰了:迷宫的路径是明文,同时是blowfish解密的结果,而要获得最初输入,动调获取blowfish 的key并将明文加密即可。
exp:

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
# -*- coding: utf-8 -*-
import codecs
from Crypto.Cipher import Blowfish


def print_maze(maze: list, weight: int):
for i in range(len(maze)):
if i % weight == 0:
print()
print(maze[i], end="")


# 随机种子作key
a = b"\x90\xbbK\xee\xde\xfa\xf2\xcbh\xf8\x83\xd3\x96\xf8z\xc8\xd8\xfb\xc3\xd1V\xc5\xba\x8fh\xbc\x8b\x91\x9e\xba\xb2\x8b\x00"
a = list(a)
key = b"DontEatM"
for i in range(len(a)):
a[i] ^= key[i % len(key)]

maze = []
for i in range(len(a)):
ptr = 0xf
while ptr > -1:
tmp = (((1 << ptr) & a[i]) >> ptr)
ptr -= 1
maze.append(tmp)
maze = [0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001,
0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001]
maze[0xa*0x10+5] = '#'
maze[0x4*0x10+9] = 'x'
print_maze(maze, 0x10)
# ddddwwwaaawwwddd
key_array = list(b'fishFISH')

# key_array[1] = 0xf
# key_array[2] = (0x73 ^ key_array[1])
# key_array[3] = (0x68 ^ key_array[1])
# key_array[4] = key_array[2] ^ key_array[4]
key_array = [0x00, 0x0F, 0x1A, 0x01, 0x35, 0x3A, 0x3B, 0xCD]

# -*- coding: utf-8 -*-


class blowfish():
def __init__(self):
pass

def Encrypt(self, code, key):
key = key
l = len(code)
if l % 8 != 0:
# Blowfish底层决定了字符串长度必须8的整数倍,所补位空格也可以根据自己需要补位其他字符
code = code + b' ' * (8 - (l % 8))
code = code
cl = Blowfish.new(key, Blowfish.MODE_ECB)
encode = cl.encrypt(code)
# hex_encode = codecs.encode(encode, 'hex_codec') # 可以根据自己需要更改hex_codec
return encode

def Decrypt(self, string, key):
key = key
string = string
cl = Blowfish.new(key, Blowfish.MODE_ECB)
# codecs.decode(string, 'hex_codec') # 可以根据自己需要更改hex_codec
ciphertext = string
code = cl.decrypt(ciphertext)
return "%s" % (code)


if __name__ == '__main__':
# encode = b'8749C71106E48B51'
code = b'ddddwwwaaawwwddd'
key_array = [0x00, 0x0F, 0x1A, 0x01, 0x35, 0x3A, 0x3B, 0x20]
key = bytes(key_array)
gw = blowfish()
print("明文密码:%s,经过key:%s加密之后的加密密码是:%s" % (code, key, gw.Encrypt(code, key)))
encode = gw.Encrypt(code, key)
print(encode.hex())
print("加密密码:%s,经过key:%s解密之后的明文密码是:%s" %
(encode, key, gw.Decrypt(encode, key)))