首页
论坛
课程
招聘
[原创]京东AI CTF大挑战Writeup
2018-7-2 23:16 3440

[原创]京东AI CTF大挑战Writeup

2018-7-2 23:16
3440
这是一道很有意思的题目,成功的激起了我的兴趣。做的不一定对,也不一定好。跟大家分享。
一、审题
download附件,解压文件,审题。
1、题目
题目提供了二进制序列的十进制数列,共200个数。
使用机器学习的模型对这两百个数进行判断,是函数入口就打个1,
问题是现在学习出来的全是0,少了唯一的一个1。
2、h5文件
这个文件是Keras的model文件,使用load_model可以加载
二、环境
1、windows
2、x32dbg
3、anaconda3
4、keras(windows下pip安装keras主体没问题,但是可视化的部分有坑,弃之)
5、vscode
三、手工分析数据
题目已经说明,两百个样本点是“二进制代码”,但是数据看起来被加工过,数据在[1,256]之间
特别是多次出现神奇的256,这不在正常的0x00--0xff的区间,所以推测手工分析数据应该全减一
处理一下数据,输出成能复制粘贴的形式。
strstr="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"
strs=strstr.split(' ')
res=""
for i in strs:
    j=hex(int(i)-1)
    if len(j)==3:
        j=j[:2]+'0'+j[-1]
    if len(j)==5:                 #这里是因为最开始未-1,无脑转,发现有0x100的情况,很尴尬
        j="0x00"
    res=res+j[2:]+" "
print(res)
结果

31 c0 83 fa 0f 0f 94 c0 8d 04 c5 08 00 00 00 c3 31 c0 83 fa 0f 0f 94 c0 8d 04 c5 09 00 00 00 c3 90 8d b4 26 00 00 00 00 83 ec 0c 89 5c 24 04 31 db 89 74

24 08 89 c6 0f b6 04 1e e8 18 ff ff ff 83 f8 14 76 0b e8 7e 3a ff ff 8d b6 00 00 00 00 ff 24 85 a8 91 0f 08 90 b8 03 00 00 00 8b 74 24 08 01 d8 8b 5c 24

04 83 c4 0c c3 90 8d 74 26 00 b8 02 00 00 00 8b 74 24 08 01 d8 8b 5c 24 04 83 c4 0c c3 90 8d 74 26 00 83 c3 01 eb aa 8d 76 00 b8 01 00 00 00 8b 74 24 08

01 d8 8b 5c 24 04 83 c4 0c c3 90 8d 74 26 00 b8 05 00 00 00 8b 74 24 08 01 d8 8b 5c 24 04 83 c4 0c c3 8d b6 00 00 00 00 8d bc 27 00 00 00 00

复制到x32dbg中查看

发现代码确实处于可阅读的状态了,而结果表中1的位置在40(从0算起),对应到sub esp,C的位置
确实符合题目函数入口的要求,
而对比其余ret片段,前面几段短的未涉及esp(0x83)的代码不算在题目要求之列,而后面几段只有add esp,未做平衡的代码也明显不是正常函数。
推测:题目所要求“函数入口”需局部变量的堆栈平衡操作,特征以0x83为主
四、模型分析
from keras.models import load_model
import numpy as np
model = load_model('gcc_O2.h5')
model.summary()

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_1 (Embedding)      (None, 200, 16)           4112      
_________________________________________________________________
bidirectional_1 (Bidirection (None, 200, 16)           400       
_________________________________________________________________
time_distributed_1 (TimeDist (None, 200, 2)            34        
=================================================================
Total params: 4,546
Trainable params: 4,546
Non-trainable params: 0
_________________________________________________________________
可以看到,模型分三层网络,最后输出200*2的结果,直接把题目样本给进去,看结果
input_str="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"
input_strs=input_str.split(" ")
k=0
for i in input_strs:
    input_strs[k]=int(input_strs[k])
    k=k+1
input_n=np.array(input_strs).reshape(1,200)
print(input_n)
out = model.predict(input_n)
t=0
print(out)
for i in out[0] :
    if(i[0]<0.999999):
        print(t,hex(input_n[0][t]-1),i[0],i[1])
    t=t+1
其中0.999999是我先大概看了一下输出之后选的阀值,因为大多数值都很接近1了

0 0x31 0.999413 0.000587229
40 0x83 0.740849 0.259151
136 0x83 0.999995 4.99645e-06
144 0xb8 0.998111 0.00188946
168 0xb8 0.999863 0.00013703
可以看到,只有40的概率相对高(很多),但是也未突破0.5。另外注意到 0x31对应到xor,0xb8对应到mov
四、目标推测
可以看到这个模型的问题在于,1的置信度不够,也就是说,原样本的0太多了,需要多一点1的样例来让机器确定这就是“函数入口”。
那么基于原样本改造出训练样本的思路也就出来了,
在训练集多加符合题意的1,
那么我就加了一个函数入口,在168的位置
------>
转换回数据之后训练
strstr="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 145 132 237 13 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"
train_str=strstr.split(" ")
k=0
for i in train_str:
    train_str[k]=int(train_str[k])
    k=k+1
train_str=np.array(train_str).reshape(1,200)
print(train_str)
train_res=[1,0]*200
train_res=np.array(train_res).reshape(1,200,2)
train_res[0][40][0]=0
train_res[0][40][1]=1
train_res[0][168][0]=0
train_res[0][168][1]=1
print(train_res)
model.fit(train_str,train_res)
这次的原样本预测结果:
input_str="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"
input_strs=input_str.split(" ")
k=0
for i in input_strs:
    input_strs[k]=int(input_strs[k])
    k=k+1
input_n=np.array(input_strs).reshape(1,200)
print(input_n)
out = new_model.predict(input_n)
t=0
print(out)
for i in out[0] :
    if(i[0]<0.9):
        print(t,hex(input_n[0][t]-1),i[0],i[1])
    t=t+1

40 0x83 0.366599 0.633401
结果应该是满足要求了,只有一个位置的概率大于50%,并且是正确的位置。
至此,题目按我的理解应该是pass了。
五、优化
1、构造的数据集可以有更丰富的组合来避免掉一些误判,如add和sub的顺序调换,降低mov的命中概率等等
2、optimizer完全是默认的,还没有观察和改造。
这部分就不深入研究啦。(我这个随便的态度应该拿不了高分哈哈)。

感谢:
梦里茶对Keras的精致讲解,他的主页
https://ahangchen.github.io/
以及今天食堂美味的小炒鸡柳。

[注意] 欢迎加入看雪团队!base上海,招聘CTF安全工程师,将兴趣和工作融合在一起!看雪20年安全圈的口碑,助你快速成长!

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (1)
雪    币: 32
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
spidercrow 活跃值 2018-8-3 13:32
2
0
你们是科学家吗
游客
登录 | 注册 方可回帖
返回