七夕特别节目~
前两天在强网杯的逆向safe_m2m中无意中发现了这么一段代码:
关键代码是:
1 | v5 ^= ((v5 ^ (unsigned int)(32 * v5)) >> 17) ^ (32 * v5) ^ ((((v5 ^ (unsigned int)(32 * v5)) >> 17) ^ v5 ^ (32 * v5)) << 13); |
整理一下:
1 | c = p ^ (p << 5) ^ ((p ^ (p << 5)) >> 17) ^ ((((p ^ (p << 5)) >> 17) ^ p ^ (p << 5)) << 13); |
已知c,求p。且由于运算量过大(循环100016次),不可爆破。
乍一看无从下手,这怎么逆向啊?
观察了一会,发现可以进行变量代换。
设x = p ^ (p << 5)
,有:
1 | c = x ^ (x >> 17) ^ (((x >> 17) ^ x) << 13); |
继续设y = x ^ (x >> 17)
,有:
1 | c = y ^ (y << 13); |
综上,有如下的关系:
1 | x = p ^ (p << 5) |
这样,算式变得简单多了。
但是,对于这种类型的等式,假设已知x(int),应该如何求p(int)呢?
其实很简单,如果这个变量不是一个int,而是一个数组,我感觉很容易想到解法,但是这样的 形式不太容易想。
答案是以bit为一个单位进行运算。
对于低5位bit:
由于p << 5
的低5位bit均为0,所以 p ^ (p << 5)
的低5位bit的值就是p低5位bit的值。可以写作p[i] = x[i]
对于x的高27位(32-5)bit:
有x[i] = p[i] ^ p[i - 5]
,即p[i] = x[i] ^ p[i - 5]
。x是已知的,同时由于上一步知道了p[0]~p[4]的值,所以p[i - 5]
也是可以逐步求出的。
以上便是左移形式的算式的求解,右移形式的求解与之类似,不在赘述。
可能大佬们觉得很简单,但这是我第一次接触这种以bit为基本单位进行依次求解的算式,会有种比较新奇的感觉。
顺便今天七夕闲得没事干,于是写了点相关的加解密代码。
前面的示例中嵌套了3次,实际上可以嵌套无数次(效率另说),以增强逆向的难度。
我感觉嵌套了3次的还勉强能看出可以进行变量代换,但是如果嵌套了七八次,甚至更多次数,就很难进行分析了。下面的代码是嵌套了8次的:
1 | unsigned int encrypt1(unsigned int p){ |
上面的这个代码扔进ida里,如果没有事先了解这个形式的算式,要是很容易逆出来,那您绝对是大佬~
当然我知道古典密码加解密毫无安全性可言,同时这个算法的效率也不高(也或许是我的代码优化得不够到位??),根本毫无用武之地。不过,当个简单一点的CTF题或许不错?
菜鸟七夕闲得慌,就随手一写,希望大佬们可以轻喷~~
下面是代码,共三个文件。前两个是算法,最后一个是函数生成器,如果你希望加大难度,可以使用第三个脚本生成相应的encrypt1()和decrypt(),不过嵌套次数也别太大了,我测试过嵌套次数为15的,可以正常运行,比这个数字更大的没测试,不过问题应该不大,顶多效率很差就是了。
QiXi-algorithm.c
首先是核心算法,对一个unsigned int加密后解密。
1 | /************************************************************************** |
QiXi-file.c
然后是对文件进行加解密的代码:
1 | /************************************************************************** |
QiXi-builder.py
最后是一个生成加解密函数的python脚本,可以控制嵌套次数:
1 | ''' |
写的有些赶(想赶在七夕结束之前),没有过多检查。如果本文有谬误,劳请大佬们斧正~~