静态分析综合题目¶
2017 ISCC Crackone¶
利用 jadx 进行反编译,可以得到程序的基本逻辑如下
- 对用户输入的内容进行 base64 编码,然后在指定长度位置处插入
\r\n
,这个似乎并没有什么乱用。 - 之后程序将编码后的内容传递给 so 中的 check 函数。这个函数的逻辑如下
env = a1; len = plen; str = pstr; v7 = malloc(plen); ((*env)->GetByteArrayRegion)(env, str, 0, len, v7); v8 = malloc(len + 1); memset(v8, 0, len + 1); memcpy(v8, v7, len); v9 = 0; for ( i = 0; ; ++i ) { --v9; if ( i >= len / 2 ) break; v11 = v8[i] - 5; v8[i] = v8[len + v9]; v8[len + v9] = v11; } v8[len] = 0; v12 = strcmp(v8, "=0HWYl1SE5UQWFfN?I+PEo.UcshU"); free(v8); free(v7); return v12 <= 0;
不难看出,程序就是直接将 base64 之后的字符串的两半分别进行适当的操作,这里我们很容易写出 python 对应的恢复代码,如下
import base64 def solve(): ans = '=0HWYl1SE5UQWFfN?I+PEo.UcshU' length = len(ans) flag = [0] * length beg = 0 end = length while beg < length / 2: end -= 1 flag[beg] = chr(ord(ans[end]) + 5) flag[end] = ans[beg] beg += 1 flag = ''.join(flag) print base64.b64decode(flag) if __name__ == "__main__": solve()
对应的结果如下
➜ 2017ISCC python exp.py flag{ISCCJAVANDKYXX}
2017 NJCTF easycrack¶
通过简单逆向,可以发现程序的基本逻辑如下
- 监控界面文本框,如果文本框内容改变则调用 native
parseText
函数。 parseText
的主要功能如下- 首先调用 java 层的函数 messageMe 获取一个字符串 mestr。这个函数的逻辑基本是
- 依次将 packagename 的最后一个
.
后面的字符串的每一个与 51进行异或,将结果拼接起来。
- 依次将 packagename 的最后一个
- 然后以 mestr 长度为周期,将两者进行异或,核心逻辑
str[i + j] = mestr[j] ^ iinput[i + j];
- 继而下面以
I_am_the_key
为密钥,使用 RC4 加密对该部分进行加密,然后将结果与最后的compare
比较。这里猜测的依据如下- 在 init 函数中有 256 这个关键字,而且基本就是 RC4 密钥的初始化过程。
- crypt 函数显然就是一个 RC4 加密函数,明显就是 RC4 的加密逻辑。
- 首先调用 java 层的函数 messageMe 获取一个字符串 mestr。这个函数的逻辑基本是
解密脚本如下
from Crypto.Cipher import ARC4 def messageme(): name = 'easycrack' init = 51 ans = "" for c in name: init = ord(c) ^ init ans += chr(init) return ans def decrypt(cipher,key): plain ="" for i in range(0,len(cipher),len(key)): tmp = cipher[i:i+len(key)] plain +=''.join(chr(ord(tmp[i])^ord(key[i])) for i in range(len(tmp))) return plain def main(): rc4 = ARC4.new('I_am_the_key') cipher = 'C8E4EF0E4DCCA683088134F8635E970EEAD9E277F314869F7EF5198A2AA4' cipher = ''.join(chr(int(cipher[i:i+2], 16)) for i in range(0, len(cipher), 2)) middleplain = rc4.decrypt(cipher) mestr = messageme() print decrypt(middleplain,mestr) if __name__ == '__main__': main()
结果如下
➜ 2017NJCTF-easycrack python exp.py It_s_a_easyCrack_for_beginners ➜ 2017NJCTF-easycrack
2018 强网杯 picture lock¶
简单分析之后发现这是一个图片加密程序:java 层为 native 层传入 image/ 下的第一个文件名,以及希望加密后的图片文件名,包括对应的 apk 的签名的 md5。
下面我们就可以分析 native 层代码,由于程序很明显说是一个加密程序,我们可以使用IDA 的 findcrypto 插件来进行识别,结果却是发现了 S 盒,而且基本上就是符合 AES 的加密流程的,所以可以基本确定程序的主体是一个 AES 加密,经过细致分析可以发现 native 层程序的基本流程如下
- 将传入的签名的 md5 字符串分为两半,生成两组密钥。
- 每次读入md5sig[i%32]大小的内容
- 根据读入的大小决定使用哪一组密钥
- 奇数使用第二组密钥
- 偶数使用第一组密钥
- 如果读入的大小不够 16 的话,就将后面填充为不够的大小(比如大小为12时,填充 4 个0x4)
- 这时修改后的内容必然够16个字节,对前16个字节进行 AES 加密。对于后面的字节,将其与 md5sig[i%32]依次进行异或。
既然知道加密算法后,那就很容易逆了,我们首先可以获取签名的 md5,如下
➜ picturelock keytool -list -printcert -jarfile picturelock.apk 签名者 #1: 签名: 所有者: CN=a, OU=b, O=c, L=d, ST=e, C=ff 发布者: CN=a, OU=b, O=c, L=d, ST=e, C=ff 序列号: 5f4e6be1 有效期为 Fri Sep 09 14:32:36 CST 2016 至 Tue Sep 03 14:32:36 CST 2041 证书指纹: MD5: F8:C4:90:56:E4:CC:F9:A1:1E:09:0E:AF:47:1F:41:8D SHA1: 48:E7:04:5E:E6:0D:9D:8A:25:7C:52:75:E3:65:06:09:A5:CC:A1:3E SHA256: BA:12:C1:3F:D6:0E:0D:EF:17:AE:3A:EE:4E:6A:81:67:82:D0:36:7F:F0:2E:37:CC:AD:5D:6E:86:87:0C:8E:38 签名算法名称: SHA256withRSA 主体公共密钥算法: 2048 位 RSA 密钥 版本: 3 扩展: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 71 A3 2A FB D3 F4 A9 A9 2A 74 3F 29 8E 67 8A EA q.*.....*t?).g.. 0010: 3B DD 30 E3 ;.0. ] ] ➜ picturelock md5value=F8:C4:90:56:E4:CC:F9:A1:1E:09:0E:AF:47:1F:41:8D ➜ picturelock echo $md5value | sed 's/://g' | tr '[:upper:]' '[:lower:]' f8c49056e4ccf9a11e090eaf471f418d
继而,我们可以直接使用已有的 AES 库来进行解密
#!/usr/bin/env python import itertools sig = 'f8c49056e4ccf9a11e090eaf471f418d' from Crypto.Cipher import AES def decode_sig(payload): ans = "" for i in range(len(payload)): ans +=chr(ord(payload[i]) ^ ord(sig[(16+i)%32])) return ans def dec_aes(): data = open('flag.jpg.lock', 'rb').read() jpg_data = '' f = open('flag.jpg', 'wb') idx = 0 i = 0 cipher1 = AES.new(sig[:0x10]) cipher2 = AES.new(sig[0x10:]) while idx < len(data): read_len = ord(sig[i % 32]) payload = data[idx:idx+read_len] #print('[+] Read %d bytes' % read_len) print('[+] Totally %d / %d bytes, sig index : %d' % (idx, len(data), i)) if read_len % 2 == 0: f.write(cipher1.decrypt(payload[:0x10])) else: f.write(cipher2.decrypt(payload[:0x10])) f.write(decode_sig(payload[16:])) f.flush() idx += read_len i += 1 print('[+] Decoding done ...') f.close() dec_aes()
最后可以得到一个图片解密后的结果,其中就包含 flag 了。
本页面的全部内容在 CC BY-NC-SA 4.0 协议之条款下提供,附加条款亦可能应用。