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+2s=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. 明日计划
继续练习比赛题,加强难度