静态分析综合题目¶
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 了。