前言
通往结果的路途是最美好的!珍惜在这过程中你所遇到的人和经历。不负遇见!
分析
这道题我感觉只是套了个apk的外壳,一点都没有做Android题目的感觉。本质上是一个aes和一个异或的解密。难受。
j()用于获取apk签名的MD5值
关键是这个enc()是一个native方法。动态调。
动态调试可以参考这篇文章
http://blog.csdn.net/qq_33438733/article/details/79516372
主要就是这个sub_5fa37a48这个函数
进去之后就是一大堆加密算法。看了下特征,发现一个类似S盒的数据块,猜测是aes加密。
fread在读取数据时会按着指定序列读取,这个序列也就是签名的MD5值。这个值可以通过动态调smali的方法拿到。刚开始我用smali插桩的方法,导致我在尝试aes解密的时候,密匙出错,所以卡了很久。因为如果修改了smali,重新打包后签名会变化,所以得到的密匙也就不一样。
这是获取二次打包的密钥的截图。正确的密钥其实可以字节从内存中获取。
比较关键地方,v32是用于AES的密匙,在这里他做了判断
if ( !(v28 & 1) )
也就是每次的密匙都会发生变化,查看&dword_5FA3C008;和&dword_5FA3C004;的内容可以看到他这里对原本32位的MD5值分别取了前半段和后半段。
这一大段就是AES,不用管。(估计应该是用标准的AES进行加密的,如果不是那就GG)
这里就是进行异或加密了,这里只对数据的前16个字节进行了aes加密,而后对剩余的字节进行异或加密。根据加密的过程写出解密代码。
# -*- coding: UTF-8 -*-
from Crypto.Cipher import AES
from binascii import b2a_hex
from binascii import a2b_hex
from aes_util import *
def dec_xor_data(cipher):
plain=[]
for i in range(16,len(cipher)):
temp=chr(ord(cipher[i]) ^ ord(akey[i%32]))
plain.append(temp)
return "".join(plain)
akey="f8c49056e4ccf9a11e090eaf471f418d"
dword_5FA3C00C=[0x66,0x38,0x63,0x34,0x39,0x30,0x35,0x36,0x65,0x34,0x63,0x63,0x66,0x39,0x61,0x31,0x31,0x65,0x30,0x39,0x30,0x65,0x61,0x66,034,0x37,0x31,0x66,0x34,0x31,0x38,0x64]
def main():
fp_enc=open("flag.jpg.lock","rb")
fp_dec=open("de.jpg","wb")
i=0
while 1:
v24 = ord(akey[i&0x1f])
cipher=fp_enc.read(v24)
cipher_aes=b2a_hex(cipher[:16])
if (v24&0x1)==0:
hkey=akey[:16]
plain_aes=decrypt_ECB(cipher_aes,hkey)
else:
hkey=akey[16:]
plain_aes=decrypt_ECB(cipher_aes,hkey)
if len(plain_aes) < 16:
plain_aes+=chr(0)*(16-len(plain_aes))
fp_dec.write(plain_aes)
plain_xor=dec_xor_data(cipher)
fp_dec.write(plain_xor)
i=i+1
if len(cipher)<1:
break
fp_dec.close()
if __name__=="__main__":
main()
总结
这题最难的地方是解aes加密,刚开始也不确定,所以就自己加密了一张图片,然后测试前16个字节进行比对。拿github上的aes尝试了一下,不对,然后找了一个python的解密脚本,成了!
最后解密图片,拿到flag