首先,阅读题目,发现模型是使用深度学习来检测一段二进制代码中是否存在函数入口,存在的话将入口点标为1,否则为0。而题目需要我们对模型机进行微调,使得模型能够识别出一段不能识别的二进制代码的入口点。说白就是我们需要造一些样本,重新训练模型使得模型能够识别给定样本的函数入口点并保证不是入口点也能识别正确。题目提供了模型的文件和样本点2个数据。
知道了模型使用的框架,那么之后重新训练模型就非常方便了,再来看看样本点。观察发现,一段正常二进制代码应该会存在很多个0,而样本点存在很多个1且没有0,同时单字节存在几个256(单字节最大应该只有255),所以这里的二进制代码是经过加1运算后的代码,我们通过减1再进行反汇编,看看函数的入口特征:
题目提示说这一段代码的函数入口点是在下标为40的点,也就是上面所示的 0048E028 地址的 sub esp,0xC 这一句函数的入口点 模型无法识别,这个函数反编译后的结果其实是一个switch结构,类似如下:
首先,我们把样本点输入到模型中,看模型预测的结果,发现每个点输出一个包含2个元素的向量,第1个表示不是入口点的概率,第2个表示是入口点的概率。从输出的结果可以看出,除去第40个点的概率是 [0.722,0.278],其他基本都是 [1,0],说明模型差一点就能识别出函数的入口点,而其它的点也没有识别错误。
那么,最简单的办法就是将样本点随便改改,然后输入到模型重新训练,应该就能识别。(这里题目没有要求模型需要保证在原始的样本中保持某一个准确度)
我这里将样本点的第0个点 50 改成 49,也就是将第一句的 xor eax,eax 改成 xor al,al ,作为模型的一个训练样本,使用SGD作为优化方法,参数学习率 lr=0.0001, 动量momentum=0.9,使用的迭代次数为10次,并且冻结除RNN的其他层,再重新训练,结果如下:
from keras.optimizers import SGD
from keras.models import load_model
import numpy as np
def setup_to_finetune(model):
for layer in model.layers:
layer.trainable = False
model.layers[1].trainable = True # RNN层 不冻结
model.compile(optimizer=SGD(lr=0.0001, momentum=0.9), loss='categorical_crossentropy', metrics=['accuracy'])
a = [49,193,132,251,16,16,149,193,142,5,198,9,1,1,1,196,50,193,132,251,16,16,149,193,142,5,198,10,1,1,1,196,145,142,181,39,1,1,1,1,132,237,13,138,93,37,5,50,220,138,117,37,9,138,199,16,183,5,31,233,25,256,256,256,132,249,21,119,12,233,127,59,256,256,142,183,1,1,1,1,256,37,134,169,146,16,9,145,185,4,1,1,1,140,117,37,9,2,217,140,93,37,5,132,197,13,196,145,142,117,39,1,185,3,1,1,1,140,117,37,9,2,217,140,93,37,5,132,197,13,196,145,142,117,39,1,132,196,2,236,171,142,119,1,185,2,1,1,1,140,117,37,9,2,217,140,93,37,5,132,197,13,196,145,142,117,39,1,185,6,1,1,1,140,117,37,9,2,217,140,93,37,5,132,197,13,196,142,183,1,1,1,1,142,189,40,1,1,1,1]
x_train = np.array([a])
b = [[1,0] for i in range(200)]
b[40] = [0,1] # 设置第40位为函数入口点
y_train = np.array([b])
model = load_model("gcc_O2.h5")
setup_to_finetune(model)
model.fit(x_train,y_train,batch_size=1,epochs=10) # 重新训练
a = [50,193,132,251,16,16,149,193,142,5,198,9,1,1,1,196,50,193,132,251,16,16,149,193,142,5,198,10,1,1,1,196,145,142,181,39,1,1,1,1,132,237,13,138,93,37,5,50,220,138,117,37,9,138,199,16,183,5,31,233,25,256,256,256,132,249,21,119,12,233,127,59,256,256,142,183,1,1,1,1,256,37,134,169,146,16,9,145,185,4,1,1,1,140,117,37,9,2,217,140,93,37,5,132,197,13,196,145,142,117,39,1,185,3,1,1,1,140,117,37,9,2,217,140,93,37,5,132,197,13,196,145,142,117,39,1,132,196,2,236,171,142,119,1,185,2,1,1,1,140,117,37,9,2,217,140,93,37,5,132,197,13,196,145,142,117,39,1,185,6,1,1,1,140,117,37,9,2,217,140,93,37,5,132,197,13,196,142,183,1,1,1,1,142,189,40,1,1,1,1]
x_test = np.array([a])
print(np.rint(model.predict(x_test))) # 预测
model.save("gcc_O2_new.h5") # 保存模型
感觉这道题的逆向要求不是很高,然后深度学习算法的要求也不是很高,但是两者结合起来感觉挺有意思的。不过,没有其他限制的话,模型是可以过拟合的(只识别一个提供的样本点),为了避免模型过拟合,主办方其实可以提供一些样本,要求新的模型在这些样本下也能够保持准确度。