170623 逆向-暗号

1625-5 王子昂 总结《2017年6月22日》 【连续第263天总结】

A. 全国大学生信息安全竞赛2016 Reverse GeekerDoll和暗号

B. GeekerDoll用记事本打开以后发现文件头显示ELF,这是Linux的可执行文件,可以使用IDA调试、反编译

静态调试时由于一些函数的jmp地址储存在寄存器中,因此无法进行

于是连接上Linux虚拟机进行动态调试,选择Remote Linux Debugger后,设置路径即可

成功运行后却在一个似乎是call计时器的地方出异常了:got sigvtalrm ,然后就再也无法进行

无奈放置,百度上好像说要换模拟器OTZ

暗号:

下载下来是一个APK,第一次接触安卓的逆向,不过之前大概看过也准备过工具。

首先把后缀名改为zip,找到其中的dex文件,拖出来使用dex2jar工具转成jar

此时打开就是字节码的samil文件了

不过ARM指令集毫无了解……所以用jd-gui再进行一次反编译,得到java源代码

在包中找到了界面按钮和最外层的函数:两个输入检测,为空时弹窗

但是在输入错误的时候提示"what a pity!"的字符串却没有在源码中发现

源码很简单,只有几个函数。但是最关键的check函数只有声明,没有函数体

查询得知核心函数在so文件中,跟ELF格式相同,使用IDA可打开

于是拖到IDA里,shift+F12查找字符串,终于找到了what a pity

跟过去找到调用,空格查看流程,发现很明显,有两个关键函数:NowYouSeeMe()和WeAreTheChangPingPeople(),名字都挺好玩儿的,昌平人吗233

分别对两个函数进行反编译,发现过程:

WeAreTheChangPingPeople()创建了一个服务器和客户端,客户端接收输入的字符串后对其进行处理,然后发送给服务器,服务器与“hhxptgdlffojwztpewc”进行比对,如果相同就返回客户端“hehehhehe”,客户端再把返回字符串与“hehehhehe”比对,相同则成功

很明显,flag处理的结果就是“hhxptgdlffojwztpewc”了

那么关键就是NowYouSeeMe()对输入字符串进行了怎样的处理了,反编译后浏览,核心部分只有这一块儿:

for ( i = 0; ; i = 0 )                      // 死循环,直到break跳出
    {
      while ( i < v28 )
      {
        v15 = 3 * i;
        v16 = 100 * (unsigned __int8)s1[3 * i] + 1000 * v13;// 第3i个字符的ASCII乘100+ 上一次剩的余数*1000
        v17 = 3 * i++;
        v18 = &s1[v17];                         // 5328为48*100+48*10+48,即之前多的ASCII
        v19 = v16 + 10 * (unsigned __int8)s1[v17 + 1] + (unsigned __int8)s1[v17 + 2] - 5328;// 第3i个字符的ASCII乘100+第3i+1个字符的ASCII乘10+第3i+2个字符的ASCII-5328
        v13 = v19 % 26;                         // 求除以26的余
        v19 /= 26;                              // 除以26的商
        s1[v15] = v19 / 100 + 48;               // 第3i个字符为第三位以前的值,转换为数字字符
        v18[1] = v19 % 100 / 10 + 48;           // 第3i+1个字符为商的倒数第二位,转换成数字字符
        v18[2] = v19 % 10 + 48;                 // 第3i+2个字符为最后一位,转换为数字字符
      }
      sprintf(&src, "%c", v13 + 97);            // 把余数转成字母输出
      puts(&src);
      strcat(&v32, &src);
      if ( !memcmp(s1, &s2, 3 * v28) )          // 比较两个字符串的3*v28个字节,如果不等则跳;
                                                // 即当修改的变量超过字符串长度时break
        break;
      v13 = 0;
    }


其中v28看起来是len/3,其他部分进行一遍分析就行了

把输入的字符串先转换成整型,然后每三个转换成整型,除以26,商保留,余数放入下一个循环进行计算,直到整个字符串循环一遍以后把余数输出,然后再回头重新进行,直到整个字符串全部处理完成

双层嵌套循环,用python梳理了一遍:

原始算法
str1="Thi51siT!"
str2=""
i=0
key=""
s=0
y=0
for i in str1:
    str2=str2+'{:0>3}'.format(str(ord(i)))
print(str2)
str1=str2
str2=""
while(1):
    while(3*i+2        s=ord(str1[3*i])*100+ord(str1[3*i+1])*10+ord(str1[3*i+2])-5328+y*1000
        i=i+1
        y=s%26
        s=s//26
        str2=str2+'{:0>3}'.format(str(s))
    key=key+chr(y+97)
    str1=str2
    str2=""
    i=0
    y=0
    if(int(str1,10)==0):
        break
print(key)

仔细分析一下就能发现,实际上就是将整个数当成千进制数(三位一段),然后转换成26进制,再换成ASCII的过程

事实上本身十进制数直接转换成26进制也是可以的,不断求余就行,但是要注意顺序。源程序从左往右求余的顺序和十进制从右往左求余的顺序正好相反。

于是根据源代码写出逆算法:

str1="hhxptgdlffojwztpewc"
flag=""
s=0
for i in str1[::-1]:
   s=s*26+ord(i)-97
print(s)
s="084104105053049115105084033"
#中间数
i=0
for i in range(len(s)//3):
    flag=flag+chr(int(s[i*3:3*i+3],10))
print(flag)
注意中间字符串需要补0,因为作为和最前方的0被舍去了,而长度不是3的倍数,因此需要在前方补0

 于是得到flag,提交成功

正式的CTF就是厉害……用了不少工具,算法说起来容易,分析起来却很乱……不过只要理解透了就很简单了

C. 明日计划

继续练习比赛题,加强难度

你可能感兴趣的:(CTF)