突然得到一道安卓逆向题,这就逝逝

题目要求

1.解出正确的用户名密码(10pts)
2.根据逻辑使用c语言恢复正向加密逻辑(15pts)
3.写出c语言逆向解密逻辑(15pts)
4.成功使用logcat捕获log信息(10pts)
5.借助log出来的信息完成对用户名密码的分析(25pts)
6.通过修改文件逻辑让其自输出正确的密码(15pts)//出题人自己做不出来()
6,尝试绕过登录页面进入程序
7.wp能够清晰,详细的体现分析过程(10pts)
为了避免ai一把梭,从第二点开始。

test1,2,3

1,打开jadx,加载apk,我们发现LoginActivity,进一步寻找,发现lib文件ndk01。


2,将apk更改后缀名后解压,得到libndk01.so,放进ida,注意到左侧的login,跳转翻看伪代码。



3,通过图示分析,得到账户:CTFer,并且发现最后检验为s与v29


4,获取byte_6F0,分析变种rc4加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
加密
#include <bits/stdc++.h>
using namespace std;

int main()
{
unsigned char v32[256];
unsigned char v33[256];
string ezAndroid ="ezAndroid";
for(int n=0;n < 256;n=n+2)
{
v32[n]=n& 0xCC;
v32[n+1]=n& 0xCC;
}
for(int n=0;n != 256;n=n+2)
{
string ezAndroid ="ezAndroid";
v33[n]=ezAndroid[(n ^ 0x20uLL) % 9];
v33[n+1]=ezAndroid[(n ^ 0x21uLL) % 9];
}
int v11=0,v12,v14;
for(int n256_1=0; n256_1 < 256; ++n256_1)
{
v12 = v32[n256_1];
v14 = v33[n256_1];
v11 = (v11 + v12 + v14) % 256;
unsigned char temp = v32[n256_1];
v32[n256_1] = v32[v11];
v32[v11] = temp;
}
char s[41]= "0aecbfedb9dd6b968dabe69b18693d6b1bd6146";
int v18=0, v20=0;
for(int v19=0; v19<40; v19++)
{
v18 = (v18 + 1) % 256;
unsigned char v22 = v32[v18];
v20 = (v20 + v22) % 256;
unsigned char temp = v32[v18];
v32[v18] = v32[v20];
v32[v20] = temp;
s[v19] += v32[(v22 + v32[v18])%256];
}
char ss[41];
for(int i = 0; i < 40; ++i)
{
ss[i]= s[i]^0xCC;
}
for(int i = 0; i < 40; ++i)
{

cout << hex << setfill('0') << setw(2) << (int)(unsigned char)ss[i];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
解密
int main()
{
//v32,v33初始化
unsigned char v32[256];
for(int n=0;n < 256;n=n+2)
{
v32[n]=n& 0xCC;
v32[n+1]=n& 0xCC;
}
unsigned char v33[256];
for(int n=0;n != 256;n=n+2)
{
string ezAndroid ="ezAndroid";
v33[n]=ezAndroid[(n ^ 0x20uLL) % 9];
v33[n+1]=ezAndroid[(n ^ 0x21uLL) % 9];
}
//获取新的v32,v33
int v11=0,v12,v13,v14,v15,v16,n256_1=0;
do
{
v12 = v32[n256_1];
v13 = v12 + v11;
v14 = v33[n256_1];
v15 = v14 + v13 + 255;
v16 = v14 + v13;
if ( v16 >= 0 )
v15 = v16;
v11 = v16 - (v15 & 0xFFFFFF00);

v32[n256_1] = v32[v11];
v32[v11] = v12;
++n256_1;
}
while ( n256_1 != 256 );
//获取密文
unsigned char miwen[]={0xB0, 0xE9, 0x7D, 0xAF, 0x6E, 0xE2, 0xE9, 0x60, 0x6A, 0xCD,
0x7C, 0xFC, 0x72, 0xE2, 0x4D, 0x36, 0xF4, 0xBC, 0x2D, 0x2E,
0x29, 0x0E, 0x89, 0x62, 0xBD, 0x08, 0xB6, 0xF5, 0x73, 0x7C, 0xF6,
0x2A, 0x71, 0x26, 0x7C, 0x0E, 0xBD, 0xB0, 0x3A, 0xCC};
unsigned char v29[]={};
v29[0]=124;
for(int i=1;i<40;i++)
{

v29[i]= miwen[i]^0xCC;

}
//解密
int v18=0,v22=0,v20=0,v24=0;
char s[41];
for(int v19=0;v19<41;v19++)
{

v18=(v18+1)%256;
v22 = v32[v18];
v24=v22+v20;
v20=v24%256;
v32[v18]=v32[v20];
v32[v20]=v22;
s[v19]=v29[v19]-v32[(v22+v32[v18])%256];
;
}
s[40] = '\0';
cout<<s;
}

得到最后的密码

test4,5

根据问题,打开AS,输入以下指令监听

1
adb logcat -s JSnow


接下来我们分析so层的核心加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
do
{
v21 = v18 + 256;
if ( v18 + 1 >= 0 )
v21 = v18 + 1;
v18 = v18 - (v21 & 0xFFFFFF00) + 1;
v22 = *(v31 + v18);
v23 = v20 + v22 + 255;
v24 = v22 + v20;
if ( v24 >= 0 )
v23 = v24;
v20 = v24 - (v23 & 0xFFFFFF00);
*(v31 + v18) = *(v31 + v20);
*(v31 + v20) = v22;
s[v19++] += *(v31 + (v22 + *(v31 + v18)));
}
while ( v17 != v19 );

我们可以发现一件事,即对每一次循环而言,直接有关加密字符的其实就最后一步
那我们结合logcat,前面账号输错了我就说为什么没结果

1
2
3
4
5
6
s[v19++] += *(v31 + (v22 + *(v31 + v18)));
可以简化成s[v19]=s[v19]+P,且有P在每个循环中变动
我们会发现logcat返回的其实是输入密码[记作A]的加密后结果[记作B],以及应该得到的加密后正确值[记作C]
那么我们可以得到
正确密码=C-(B-A)
这样,我们可以通过得到一个密文便计算一个明文的方式得到最后的密码

test6

我们也可以修改ida的汇编指令
注意到程序的log输出循环中的这一判定条件,对应的汇编指令如图所示,我们可以将jz修改为jmp(恒成立)


修改后的伪代码如图

这样当我们if条件成立时,我们可以直接得到”login success”的反馈
根据前面,我们得知,即输入的密码首字母加密后应该为7c,即密码明文首字母为0,这样就成功绕过登录判断得到结果了