app3 解题报告
下载得到一个
.ab
文件这个一个备份数据文件,解析工具可以使用
abe
下载链接
解析
1 | java -jar .\abe.jar unpack .\app3.ab app3.tar |
得到压缩文件,解压缩之后
打开apk没发现什么有用的东西
JEB分析:
MainActivity
onCreate
方法和onClick方法
可以看到这里面有输入name和password之后出现的界面,只是单纯的启动anotherActivity
如下:
(略智障)那么应该不在这里,结合onCreate
方法里面的信息,以及调用a方法,以及附带的2个db
文件,合理猜测使用了加密数据库,信息存储在数据库里,接下来分析a方法:
1 | private void a() { |
getWritableDatabase
获取读写对象时候附带密码 ,可以知道加密密钥即是
之后,分析加密算法:
1 | package com.example.yaphetshan.tencentwelcome.a; |
可以看到一个是md5算法,一个是sha1算法,知道算法之后,写exp如下:
1 | v2 = "Stra1234" |
得到结果ae56f99
使用密钥解密,得到VGN0ZntIM2xsMF9Eb19ZMHVfTG92M19UZW5jM250IX0=
base64解密得到flagTctf{H3ll0_Do_Y0u_Lov3_Tenc3nt!}
总结
这个题目主要是应对Android的备份问题,在AndroidManifest.xml
中allowBackup
属性设置为true
之后,用户可以通过adb backup
和adb restore
对应用数据进行备份和恢复。
而对于一些聊天类app,聊天记录等信息都是在本地存放,所以加解密算法也应该保存在本地,通过这种方式,可以分析出密码,从而解出聊天记录数据。也就是在这道题目中,我们所做的事。包括:
- 解压缩ab
- 获取关键信息
- 分析加密算法写出对应密钥
- 利用密钥打开数据库获取信息
补充
0.1 MD5算法
一种被广泛使用的密码hash算法,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。
MD5是输入不定长度信息,输出固定长度128-bits的算法。经过程序流程,生成四个32位数据,最后联合起来成为一个128-bits的hash值
一个MD5运算— 由类似的64次循环构成,分成4组16次。F 一个非线性函数;一个函数运算一次。Mi 表示一个 32-bits 的输入数据,Ki 表示一个 32-bits 常数,用来完成每次不同的计算。
0.1.1 算法原理
0.1.1.1 数据填充
填充待加密的消息,使得长度length满足 $(length = 448 )mod 512$ $(byte = 56)mod 64$
因此消息长度扩展为$N * 512 + 448$位,也即$N * 64 + 56$字节
填充方法是在数据后面添加一个1
其余位填0
0.1.1.1 添加长度
在第一步得到的结果后,添加64位的数据表示填充前的信息长度length,如果数据长度大于$2^{64}$,取64,最终信息长度为 $(N+1) * 512$位
0.1.1.1 初始化变量
1 | A=0x01234567; |
0.1.1.2 数据处理
以512位为1组,处理函数如下,输入为3个32位的数据,输出为1个32位的数据
1 | F(x, y, z) = (x & y) | ((~x) & z); |
对进入循环的512位消息分组的16个32位数据分别进行如下操作:
使用临时变量a,b,c,d中的三个经F,G,H,I变换后的结果与第4个相加,再加上32位字和一个32位字的加法常数,并将所得值循环左移若干位,最后将所得结果加上a,b,c,d之一,并返回A,B,C,D,由此完成一次循环。
0.1.1.3 算法特征
数据长度初始填充到448位,填充方法为补
10*
会增加一个64位代表长度
会使用初始化的4个常量数据
输出是128位
输入的64个常量
1
{ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 };
0.1.2 算法逆向特征
0.1.2.1 MD5Init()
这一步作用是初始化ABCD
0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476
0.1.2.2 MD5Update()
FF(a ,b ,c ,d ,Mj ,s ,ti ) 操作为 a = b + ( (a + F(b,c,d) + Mj + ti) << s) //循环位移
GG(a ,b ,c ,d ,Mj ,s ,ti ) 操作为 a = b + ( (a + G(b,c,d) + Mj + ti) << s)
HH(a ,b ,c ,d ,Mj ,s ,ti) 操作为 a = b + ( (a + H(b,c,d) + Mj + ti) << s)
II(a ,b ,c ,d ,Mj ,s ,ti) 操作为 a = b + ( (a + I(b,c,d) + Mj + ti) << s)
四轮64步
0.1.2.3 MD5Final()
所有这些完成之后,将a、b、c、d分别在原来基础上再加上A、B、C、D。 即a = a + A,b = b + B,c = c + C,d = d + D
0.1.2.4 代码
1 | /* 伪c代码逆向主特征 */ |
1 | # python 代码 |
0.2 SHA1
0.2.1 算法原理
由美国国家安全局设计,主要适用于数字签名标准里面定义的数字签名算法。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。
0.2.1.1 分组
对于任意长度的明文,SHA1的明文分组过程与MD5相类似,首先需要对明文添加位数,使明文总长度为448(mod512)位。在明文后添加位的方法是第一个添加位是l,其余都是0。然后将真正明文的长度(没有添加位以前的明文长度)以64位表示,附加于前面已添加过位的明文后,此时的明文长度正好是512位的倍数。与MD5不同的是SHA1的原始报文长度不能超过2的64次方,另外SHA1的明文长度从低位开始填充。
0.2.1.2 分组
经过添加位数处理的明文,其长度正好为512位的整数倍,然后按512位的长度进行分组(block),可以划分成L份明文分组,我们用Y0,Y1,……YL-1表示这些明文分组。对于每一个明文分组,都要重复反复的处理,这些与MD5是相同的。
对于512位的明文分组,SHA1将其再分成16份子明文分组(sub-block),每份子明文分组为32位,我们使用M[k](k= 0, 1,……15)来表示这16份子明文分组。
0.2.1.3 扩充
将这16份子明文分组扩充到80份子明文分组,我们记为W[k](k= 0, 1,……79),扩充的方法如下。
1
2
3 > W t = M t , 当0≤t≤15
> W t = ( W t-3 ⊕ W t-8⊕ W t-14⊕ W t-16 ) <<< 1, 当16≤t≤79
>
0.2.1.3 运算
![SHA-1 - Wikipedia](xctf-writeup-app3/SHA-1 - Wikipedia.png)
SHA1有4轮运算,每一轮包括20个步骤,一共80步,当第1轮运算中的第1步骤开始处理时,A、B、C、D、E五个链接变量中的值先赋值到另外5个记录单元A′,B′,C′,D′,E′中。这5个值将保留,用于在第4轮的最后一个步骤完成之后与链接变量A,B,C,D,E进行求和操作。
4轮运算,共80个步骤使用同一个操作程序
1
2 > A,B,C,D,E←[(A<<<5)+ ft(B,C,D)+E+Wt+Kt],A,(B<<<30),C,D
>
其中 ft(B,C,D)为逻辑函数,Wt为子明文分组W[t],Kt为固定常数。这个操作程序的意义为:
a、将[(A<<<5)+ ft(B,C,D)+E+Wt+Kt]的结果赋值给链接变量A;
b、将链接变量A初始值赋值给链接变量B;
c、将链接变量B初始值循环左移30位赋值给链接变量C;
d、将链接变量C初始值赋值给链接变量D;
e、将链接变量D初始值赋值给链接变量E。
0.2.1 算法特征
消息填充和
MD5
一样,512位为一组,然后16组x32位
,然后扩充位80组x32位
,进行4*20
次运算使用该函数进行计算
A,B,C,D,E←[(A<<5)+ ft(B,C,D)+E+Wt+Kt],A,(B<<30),C,D
,f
函数伪代码在下方1
2
3
4常数
1
2
3
4初始化寄存器hash值
1
2
3
4
5
0.3 Base64编码
是一种基于64个可打印字符来表示二进制数据的表示方法。转换的时候,将3字节的数据,先后放入一个24位的缓冲区中,先来的字节占高位。数据不足3字节的话,于缓冲器中剩下的比特用0补足。每次取出6比特(因为${2^{6}=64}$),按照其值选择
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
中的字符作为编码后的输出,直到全部输入数据转换完成。
特征
- 会有索引表
- 每次取出3字节数据放入24位缓冲区,然后每次取6位编码