查看原文
其他

看雪.京东 2018 CTF 第十三题 NeuralCrackme 点评与解析

看雪CTF 看雪学院 2019-09-17

你知道第十三题过去,就剩两题了。

万里长征就剩最后800米了,

小伙伴们冲啊!

第十三题作者    xk以被17人攻破的成绩,位列第八名。

第12题过后,攻击方

jackandkx在本题表现突出,上升4位,从第9名升至第5名。


距离比赛还有三题!

所剩机会不多了,

谁能最终亲临颁奖典礼现场?

真的是非常期待啊!



看雪版主&评委 netwind 点评


本题设计了一个3层BP网络算法,输入值有2个,输出值有1个,神经元18个,输入样本820组,对样本训练252次。通过把注册码输入神经网络来进行算法验证。此题可以通过分析BP网络算法来求解,也可以在不了解BP网络算法的情况下暴力枚举求解。题目构思巧妙,设计新颖。


看雪.京东 2018 CTF 第十三题 作者简介



lelfei

bbs.pediy.com/user-30

第十三题出题者简介:


lelfei,业余Crack爱好者,学生时代对电脑产生了浓厚的兴趣,经历了很长时间的游戏沉迷后,开始慢慢转向学习技术,工作后自学了ASM,VB,VC,HTML,ASP,Python等语言的入门。工作原因上网较少,对单机的逆向分析、算法比较感兴趣,但是由于缺少系统的学习,水平处于“入行较早层次较低知识较杂”的阶段。


看雪.京东 2018 CTF 第十三题 设计思路


说明:纯算法题,无壳无花无ANTI,希望你不要被绕晕。


注册码:F13FE02140。注册成功后显示“Congratulation”


设计思路


最近对人工智能比较感兴趣,收集了一堆神经网络BP算法。当前比较火的是G家的TensorFlow库,在Python下封装了大部分常见神经网络算法,比如卷积神经CNN(通常用作图像识别),循环神经RNN(通常用作自然语言识别)等等,实现起来非常简单,还有可视化的图表动态显示训练过程,很好玩。但是封装好的东西就学不到原理了,又找了一些入门级的BP源码,C语言实现起来居然非常简单,这个Crackme就是基于3层神经网络实现的加法,训练过程非常快,误差到0.003以内只需要252次训练,对于10以内的浮点数加法计算结果还是比较准确的。下面具体说一下这个Crackme的实现过程。


1、设计一个3层BP网络,输入值有2个,输出值有1个,神经元18个。(为什么用18个神经元?因为对同一样本测试了不同神经元数量,发现18个时收敛的最快。)

2、输入样本820组,样本数据见源码。(为什么是820组?因为网上的源码就是这么定的。为什么要用这个样本?因为对于随机样本,一般需要1000到2000次训练才能达到误差0.003以内,大约需要2秒多时间,但是这个样本居然只要252次不到0.3秒就到了,可能是样本的随机性比较好吧。)

3、对样本训练252次。训练过程为:

  1>initBPNework()对样本进行归一化处理,即把输入数和输出数映射到(0,1)之间。

  2>trainNetwork()样本输入BP算法第一层开始学习,前向传播算法forwardTransfer()用的是sigmod()函数。统计每轮学习后的误差。

  3>训练到指定次数或误差小于指定值时,训练结束。

4、Crackme算法的实现过程:

  1>注册码输入10位16进制字符,转化为5字节16进制数据。后面会要求注册码第6位必须为“0”。

  2>分别用前2字节和后3字节初始化2个浮点数,要求浮点值为1到10之间。

  3>2个浮点数作为输入值,给训练好的神经网络进行计算(即计算加法结果),得到1个输出值。

  4>输出的浮点值转化为字符,检测第2位必须为“.”。

  5>把第1,3,4位作为三角形的三条边,计算三角形的面积,面积大于15.5;且输入值相加的结果与输出值的误差小于0.003时,即为正确注册码。

5、采用gcc+m64编译,64位程序先把OD踢出局,不过在IDA里接近于源码显示了。


破解思路


说实话我自己都觉得算法太绕了,验证条件拆分成了很多小步,想要靠分析算法得到答案真的太难,建议还是穷举吧。


先说说逆向分析怎么得到答案:

1、根据算法第5条,三角形三边长度都在0到9之间,必须为9、9、9时才能面积大于15.5,结合第4条,得到输出值为9.99。

2、分析出BP算法其实就是加法,如果懂神经网络的话应该一眼就能看出输入值与输出值之间的规律。

3、可得知输入码分成二段作为浮点数,相加结果为9.99。

4、结合算法第2条浮点值为1到10之间,浮点数1是F03F,浮点数9是2240,输入值范围已经大大缩小。

5、测试输入不同的值观察输出值,输入0040002040(即2和8),输出为10.017779,输入0840001C40(即3和7)输出为10.030374,输入1440001440输出10.034630,发现算法与第1个输入值成正比,第1个值越大则和值越大。且第1个输入值为2时结果已经超过了10,不能得到9.99。

6、可得知第1个值要尽量小,2个字节能表示出的最接近1的值是F13F,即1.0625。第2个值要小于9,分别从3字节能表示出的最接近9的值开始试,F02140即8.96875时输出10.029751,E02140即8.9375时输出9.998681,D02140即8.90625时输出9.967608,C02140即8.875时输出9.936532。只有E02140时符合条件。

此时误差为1.0625+8.9375-9.998681=0.001319,符合条件5。得到最终注册码为:F13FE02140。


穷举的话,不需要了解BP算法,只需要识别出二项条件:

一是2个浮点数值为1到10之间,即前2字节取值范围为3FF0-4024,后3字节取值范围为3FF000-4024F0(最后一位必须为0),范围其实相当小;

二是计算结果为9.99到10之间。难点在于修改程序直接调用BP算法的验证过程,需要手输大量ASM代码并跳过算法陷阱,或者IDA还原出BP算法然后自己写一个程序,这二种方法实现起来都比较麻烦。构造好穷举程序后,穷举过程不到0.1秒。

源码中main - kg.c文件是穷举过程,总共只有38416种可能,调用BP算法得到输出值为9.99的只有24种,误差在0.003以内的只有1种。

源码中main - my.c文件是网上源码经过我的简单改写,训练完成后可以输入2个10以内值来查看输出值。

源码中main.c是crackme源码。

PS:这是去年秋季赛时做的,由于当时题库已经满了,没提交。这次本来设计了一个新算法,一不小心翻出去年的这题,就拿这题冲锋陷阵吧。



看雪.京东 2018 CTF 第十三题解析



本解析来自看雪论坛 oooAooo


IDA载入后发现有2个TlsCallback函数,初步看这两个函数,没发现特别的东西,暂时不管,先分析下main的代码。


一、main函数

1、获取输入sn,并判断sn长度如果不是10,则输出key error错误提示。(条件一)

2、调用401CE0函数,将sn字符串转换成一个64位16进制数称为keyValue,同时要求输入的字符必须在"0123456789ABCDEF"范围内。(条件二)      输入"123456789A",将转换成为0x9A78453412。

3、将64位16进制数,拆分成2个double数称为 keyValue_x 和 keyValue_y,分别为低4字节和高6字节,为:0x3412000000000000,0x9A78560000000000。

4、调用4015E0函数将上面的2个double数经过了一系列的浮点运算获得一个双精度值称之为:newKeyValue。同时判断上面的两个双精度数必须在      1.0 和10.0之间(条件三)

5、将 newKeyValue格式化成字符串 newKeyString,并要求 newKeyString [1]必须是小数点。(条件四)

6、将newKeyString[0]、 newKeyString[2]、 newKeyString[3]字符减去0x30,并将它们的平方和作为输入调用403360函数,在其内部进行了一系列的浮点运算,并将运算结果赋给 newKeyValue2。

7、判断 newKeyValue2 不小于15.5。(条件五)

8、要求 keyValue的第五位必须为0。(条件六)

9、 (keyValue_x+ keyValue_y -  newKeyValue) <0.003。(条件七)满足上面的7个条件则输出“Congratulation”表示成功。

// local variable allocation has failed, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
 double v3; // xmm1_8
 __int64 v4; // rdx
 __int64 v5; // rax
 __int64 v6; // rdx
 __int64 v7; // rax
 __int64 v8; // rdx
 __int64 v10; // rax
 __int64 v11; // rax
 double v12; // xmm1_8
 __int64 v13; // rax
 int newKey1; // [rsp+20h] [rbp-198h]
 char v15; // [rsp+24h] [rbp-194h]
 char Format[4]; // [rsp+30h] [rbp-188h]
 char v17; // [rsp+34h] [rbp-184h]
 double v18; // [rsp+40h] [rbp-178h]
 double low4Byte_newKey; // [rsp+50h] [rbp-168h]
 double high5_newKey1; // [rsp+58h] [rbp-160h]
 double low4Byte_newKey_1; // [rsp+60h] [rbp-158h]
 double high5_newKey1_1; // [rsp+68h] [rbp-150h]
 __int64 v23; // [rsp+70h] [rbp-148h]
 __int64 v24; // [rsp+78h] [rbp-140h]
 __int64 v25; // [rsp+80h] [rbp-138h]
 __int64 v26; // [rsp+88h] [rbp-130h]
 char inputKey[256]; // [rsp+90h] [rbp-128h]
 int v28; // [rsp+190h] [rbp-28h]

 sub_401F00(*(__int64 *)&argc, (__int64 *)argv);
 v15 = 0;
 memset(inputKey, 0, sizeof(inputKey));
 v17 = 0;
 low4Byte_newKey = 0.0;
 high5_newKey1 = 0.0;
 low4Byte_newKey_1 = 0.0;
 high5_newKey1_1 = 0.0;
 v18 = 0.0;
 v28 = 0;
 newKey1 = 0;
 *(_DWORD *)Format = 0;
 v23 = 0LL;
 v24 = 0LL;
 v25 = 0LL;
 v26 = 0LL;
 sub_401790();
 sub_401C50((__int64)&v28, (__int64)inputKey, v4, 252);
 v5 = 0LL;
 do
 {
   v6 = (byte_408D00[v5] ^ 0x19) - (unsigned int)v5;
   *((_BYTE *)&v23 + v5) = (byte_408D00[v5] ^ 0x19) - v5;
   ++v5;
 }
 while ( v5 != 16 );
 printf(Format, inputKey, v6, &v23);
 Format[0] = byte_408D70 ^ 0x25;
 Format[1] = (byte_408D71 ^ 0x25) - 1;
 scanf(Format, inputKey, inputKey, Format);
 if ( strlen(inputKey) != 10 )                 // 条件1:长度为10
 {
   v7 = 0LL;
   do
   {
     v8 = (byte_408D10[v7] ^ 0xF) - (unsigned int)v7;
     *((_BYTE *)&v23 + v7) = (byte_408D10[v7] ^ 0xF) - v7;
     ++v7;
   }
   while ( v7 != 16 );
   goto LABEL_6;
 }
 if ( (unsigned int)charToHex((__int64)Format, (__int64)inputKey, (__int64)&newKey1, (__int64)inputKey, 10) != 5 )// 将输入转换成整数
 {
   v10 = 0LL;
   do
   {
     v8 = (byte_408D20[v10] ^ 0x21) - (unsigned int)v10;
     *((_BYTE *)&v23 + v10) = (byte_408D20[v10] ^ 0x21) - v10;
     ++v10;
   }
   while ( v10 != 16 );
   goto LABEL_6;
 }
 HIWORD(low4Byte_newKey) = newKey1;            // "1234" --> 3412000000000000
 low4Byte_newKey_1 = low4Byte_newKey;
 *(_WORD *)((char *)&high5_newKey1 + 5) = HIWORD(newKey1);
 HIBYTE(high5_newKey1) = v15;                  // 123456789A --->9A78560000000000
 high5_newKey1_1 = high5_newKey1;
 sub_4015E0((__int64)Format, (__int64)inputKey, &v18, &low4Byte_newKey);// if ( v17 <= 1.0 || v17 >= 10.0 || (v18 = v8[3], v18 <= 1.0) || v18 >= 10.0 )
 Format[0] = byte_408D72 ^ 0x12;
 Format[1] = (byte_408D73 ^ 0x12) - 1;
 Format[2] = (byte_408D74 ^ 0x12) - 2;
 sprintf(Format, inputKey, Format, inputKey, *(_QWORD *)&v18, v18, v3, v18);
 if ( inputKey[1] != '.' )
 {
   v11 = 0LL;                                  // Ohhh.Try again
   do
   {
     v8 = (byte_408D40[v11] ^ 0x3F) - (unsigned int)v11;
     *((_BYTE *)&v23 + v11) = (byte_408D40[v11] ^ 0x3F) - v11;
     ++v11;
   }
   while ( v11 != 16 );
   goto LABEL_6;
 }
 v12 = (double)(inputKey[3] - 0x30) * (double)(inputKey[3] - 0x30);
 if ( sub_403360(
        (__int64)Format,
        (__int64)inputKey,
        (double)(inputKey[0] - 0x30) * (double)(inputKey[0] - 0x30)
      + (double)(inputKey[2] - 0x30) * (double)(inputKey[2] - 0x30)
      + v12,
        v12) <= 15.5
   || newKey1 & 0xF0000 )
 {
   v13 = 0LL;
 }
 else
 {
   v13 = 0LL;
   if ( COERCE_DOUBLE(COERCE_UNSIGNED_INT64(low4Byte_newKey_1 + high5_newKey1_1 - v18) & xmmword_409080) < 0.003 )
   {
     do                                        // Congratulation
     {
       v8 = (byte_408D50[v13] ^ 0x47) - (unsigned int)v13;
       *((_BYTE *)&v23 + v13) = (byte_408D50[v13] ^ 0x47) - v13;
       ++v13;
     }
     while ( v13 != 16 );
     goto LABEL_6;
   }
 }
 do
 {
   v8 = (byte_408D60[v13] ^ 0x37) - (unsigned int)v13;// comeon go on
   *((_BYTE *)&v23 + v13) = (byte_408D60[v13] ^ 0x37) - v13;
   ++v13;
 }
 while ( v13 != 16 );
LABEL_6:
 printf(Format, inputKey, v8, &v23);
 return 0;
}



二、获取SN


一共有7个条件,不想分析浮点运算了。利用下面4个条件爆破sn,具体如下:


1、长度为10

2、必须是"0123456789ABCDEF";

3、转换的2个双精度数值必须大于1.0小于10.0,其中1.0对应的为 0x3FF0000000000000,10.0对应为0x4024000000000000。因此要求输入的      keyValue_x取值范围为0x3FF1和0x4024之间, keyValue_y取值范围为0x3FF000和402400之间。

4、 keyValue的第5位为0,也就说明上面的递增步长是0x10。


脚本如下:

import subprocess
import sys
pname ="NNCrackme.exe"
def getSn():
   a=['0','0','0','0','0','0','0','0','0','0',0]
   i =0x3FF1
   while i<0x4024:
       print('i='+ str(hex(i)))  
       temp = i&0x000f
       a[1]= (i&0x000f)
       a[0]= ((i&0x00f0)>>4)
       a[3]= ((i&0x0f00)>>8)
       a[2]= ((i&0xf000)>>12)
       j = 0x3FF010
       while j<0x402400:
           a[5]= (j&0x00000f)
           a[4]= ((j&0x0000f0)>>4)
           a[7]= ((j&0x000f00)>>8)
           a[6]= ((j&0x00f000)>>12)
           a[9]= ((j&0x0f0000)>>16)
           a[8]= ((j&0xf00000)>>20)
           k =0
           keystr = ''
           while k <10:
               keystr=keystr+str(hex(a[k]))[2:].upper()
               k=k+1      
           print(keystr)
           arg =keystr
           io = subprocess.Popen(pname,stdin=subprocess.PIPE,stdout=subprocess.PIPE)
           ret=io.communicate(input=arg)
           res = ret[0].decode()[0:]
           print(res)
           if(res.find('Congratulation')>=0):
               print("the flag is:%s"%keystr)
               return
           else:
               j=j+0x10
       i=i+1
   return
getSn()


爆破结果如下:

F13F802140
Input your KEY:Come on, go on
F13F902140
Input your KEY:Come on, go on
F13FA02140
Input your KEY:Come on, go on
F13FB02140
Input your KEY:Come on, go on
F13FC02140
Input your KEY:Come on, go on
F13FD02140
Input your KEY:Come on, go on
F13FE02140
Input your KEY:Congratulation
the flag is:F13FE02140

flag = F13FE02140




合作伙伴



京东集团是中国收入最大的互联网企业之一,于2014年5月在美国纳斯达克证券交易所正式挂牌上市,业务涉及电商、金融和物流三大板块。

 

京东是一家技术驱动成长的公司,并发布了“第四次零售革命”下的京东技术发展战略。信息安全作为保障业务发展顺利进行的基石发挥着举足轻重的作用。为此,京东信息安全部从成立伊始就投入大量技术和资源,支撑京东全业务线安全发展,为用户、供应商和京东打造强大的安全防护盾。

 

随着京东全面走向技术化,大力发展人工智能、大数据、机器自动化等技术,将过去十余年积累的技术与运营优势全面升级。面向AI安全、IoT安全、云安全的机遇及挑战,京东安全积极布局全球化背景下的安全人才,开展前瞻性技术研究,成立了硅谷研发中心、安全攻防实验室等,并且与全球AI安全领域知名的高校、研究机构建立了深度合作。

 

京东不仅积极践行企业安全责任,同时希望以中立、开放、共赢的态度,与友商、行业、高校、政府等共同建设互联网安全生态,促进整个互联网的安全发展。


CTF 旗帜已经升起,等你来战!

扫描二维码,立即参战!


看雪.京东 2018 CTF 

看雪.京东 2018 CTF 第一题点评与解析

看雪.京东 2018 CTF 第二题点评与解析

看雪.京东 2018 CTF 第三题点评与解析

看雪.京东 2018 CTF 第四题点评与解析

看雪.京东 2018 CTF 第五题点评与解析

看雪.京东 2018 CTF 第六题点评与解析

看雪.京东 2018 CTF 第七题 密室逃脱 点评与解析

看雪.京东 2018 CTF 第八题 薛定谔之猫 点评与解析

看雪.京东 2018 CTF 第九题 PWN-羞耻player 点评与解析

看雪.京东 2018 CTF 第十题 暗风吹雨 点评与解析

看雪.京东 2018 CTF 第十一题 PWN-3pigs 点评与解析

看雪.京东 2018 CTF 第十二题 破解之道 点评与解析



看雪2018安全开发者峰会

2018年7月21日,拥有18年悠久历史的老牌安全技术社区——看雪学院联手国内最大开发者社区CSDN,倾力打造一场技术干货的饕餮盛宴——2018 安全开发者峰会,将在国家会议中心隆重举行。会议面向开发者、安全人员及高端技术从业人员,是国内开发者与安全人才的年度盛事。此外峰会将展现当前最新、最前沿技术成果,汇聚年度最强实践案例,为中国软件开发者们呈献了一份年度技术实战解析全景图。



戳下图↓,立即购票,享5折优惠!








戳原文,立刻加入战斗!


Modified on

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存