题目Flag
Flag:kxuectf{D3crypted1sV3rylntere5tin91}
题目设置时需要说明flag格式:kxuectf{},否则后面涉及到暴力破解,可能会出现题目无解。
安全策略
1. DEX 修改和反调试检测
(1)对DEX文件的SHA-1值进行检测:从/data/dalvik-cache/目录中读出应用安装存储的DEX文件,获取里面DEX文件的SHA-1值,并与应用原始的SHA-1值比较,不相等则退出,隐藏在动态注册中;
(2)为了防止解题者直接nop掉exit()函数,在进入so中的check函数后首先再进行一次DEX是否修改和反调试检测,如果检测到任意一个就将DES算法密钥的第5个和第6个值改掉。
2. 隐藏MainActivity,使得程序不执行配置文件中的MainActivity 。
安全策略
1.采用符号隐藏策略,将函数名隐藏
2.动态注册
使用动态注册将java层的check函数动态注册为ab函数
Java层算法
1. 在配置文件的MainActivity 即Main类中添加了多余的迷惑解题者的签名验证,实际并没有执行。
2. Java层算法一共三个,ab.a()、ba.a()、ca.a(),但是真正有用的是ab.a(),一个简单的移位加密。
Main类中的调用顺序是:
str=ca.a(input)+ba.a(input)+ab.a(input)
uv类中的调用顺序是:
str= ab.a(input)+ba.a(input)+ca.a(input)
ab.a()、ba.a()函数密文和输入长度一样,ca.a()函数密文与输入长度有关,所以算法需要对输入位数的判断,来确定输入为多少位。
3. 真正用于so中验证的只取前36位,也就是真正有用的加密函数只有ab.a()。
Native 层算法
1. Native层算法思路简单,使用两层加密算法,分别是RC6和DES。
2. 算法基本步骤如下:
3. 首先将输入利用DES进行加密,其中DES密钥硬编码在程序中,需要进行5轮。
4. 将加密后的数据分成两部分,前4轮组成0x20个字节作为第一部分,而后面一轮的前4个字节作为另外一部分。
5. 将步骤2中得到的最后一轮前4个字节替换掉硬编码的0x10个字节后作为下一步骤进行加密的RC6密钥。
6. 利用步骤3中得到的RC6密钥对步骤1中加密后得到的前半部分(0x20)进行加密后和硬编码的数据进行比较。
7. 最后根据比较结果得到True或者False并返回。
8. Native层的加密算法整个流程见PPT第二页。
相关思路
1. 在步骤1中使用的DES加密算法为标准的加密算法,使用的Paddting填充模式,填充数据为(8-(加密长度%8))
2. 在RC6算法中,对两个固定参数中的一个稍微进行了修改,参数Q由0x9E3779B9变成了0x61c88647。
3. 为了增加一点难度,这里没有直接将输入经过两次加密后直接和硬编码数据进行比较,而是将第一次加密后的部分作为第二次密钥的一部分。这里不会造成无解。因为在程序中已经进行了提示flag开头为kxuectf{,因此前8个字节固定,经过java层相关变换以及到了native层第一次DES加密时,由于DES也是8字节分组加密,因此DES加密后输出8字节也是固定的,为“\x4c\xd9\xa3\xe6\xed\xfe\xd1\x05”。
这样在进行解题中,由于知道最后比较结果,知道RC6密钥的前12个字节,以及知道RC6加密前的明文的前8个字节格式,因此这里一般思路是可以暴力枚举4个字节,将比较结果进行解密判断前8个字节是否等于上面的结果,从而得到RC6的密钥,进而逆推还原。
4. 但其实只需要破解3个字节就ok,通过分析程序可知,flag为36个字符,最后一个为},经过java层加密后得到固定字符‘{’,在进行DES加密时,采用padding模式,因此最后一组肯定是”xxx{\x04\x04\x04\x04”。
从这个地方推过去,由于最后一组进行DES加密后会去前4个字节来替换掉RC6的密钥,因此枚举最后一组前的3个字符即可,这样将最后一组进行枚举并一次进行DES加密然后将结果中的前四个字节取出来替换到硬编码的RC6密钥,然后利用RC6密钥对最后比较的0x20个字节进行解密,如果接的得到的前8个字节为“\x4c\xd9\xa3\xe6\xed\xfe\xd1\x05”则表示最后flag的最后四个字符已经确定,并且RC6的密钥也确定,这样在再对硬编码数据进行解密并依次逆推后即可顺利得到最后的flag:kxuectf{D3crypted1sV3rylntere5tin91}。