前言
这题应该是原本在攻防世界的,但由于近期改版,在平台未找到原题,我也是从别的帖子上下载的题目。
考察apk
0、首先夜神模拟器跑看看,虽然无卵用,知道大概就是输入个flag,验证是否正确。
APK逆向
1、下载apk,拖入jadx-gui-1.4.4
里面有个函数叫a,重命名为localCheck,另外一个类叫a,重命名为aClass,发现调用了aClass的encode方法为用户输入编码,然后调用ncheck来检查,ncheck是native函数,在so文件可以找到。
2、考察aClass的encode方法
看到了这个charSets数组,数一下数量,发现有64个字符,猜测是base64编码
新编码表:
{'i', '5', 'j', 'L', 'W', '7', 'S', '0', 'G', 'X', '6', 'u', 'f', '1', 'c', 'v', '3', 'n', 'y', '4', 'q', '8', 'e', 's', '2', 'Q', '+', 'b', 'd', 'k', 'Y', 'g', 'K', 'O', 'I', 'T', '/', 't', 'A', 'x', 'U', 'r', 'F', 'l', 'V', 'P', 'z', 'h', 'm', 'o', 'w', '9', 'B', 'H', 'C', 'M', 'D', 'p', 'E', 'a', 'J', 'R', 'Z', 'N'}
正常的base64编码表如下:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
Base64编码详解
既然猜测是base64编码,就不得不提到base64的编码过程。
暂时无法在飞书文档外展示此内容
简而言之,base64编码过程就是把用8bit表示的ASCII“数字”,转成6bit表示的“数字”。
举例来说,假如有字符串abcd转成YWJjZA==。
Base64编码过程
0、待编码字符串按照3个一组分组
1、字符转ascii码值
2、ascii码值转换为8bit二进制表示
3、按照6bit一组重新组合
4、6bit数转10进制
5、查表
Base64 编码图解
(建议读者在纸上手写一遍)
根据替换的base64编码表还原
如果base64编码表被替换,那么在第5步查表的过程,将发现查出来的字符串会不一样,
但无论如何,查表所得的字符串所代表的(0-63)10进制数字是不变的。
假如有个字符串:QAoOQMPFks1BsB7cbM3TQsXg30i9g3==
按照上述的新编码表,替换为正常的base64字符串为:
ZmxhZ3tqdXN0X0FOb3QjZXJfQHAzfQ==
替换过程可以利用Excel完成,或者写代码完成
注意要用LOOKUP函数,不能用Vlookup,不然Excel会不区分大小写
关键代码:
1
2
|
=
LOOKUP(
1
,
0
/
EXACT(改写后的编码表!B:B,A2),改写后的编码表!C:C)
=
LOOKUP(
1
,
0
/
EXACT(Base64编码表!A:A,B2),Base64编码表!B:B)
|
小贴士:字符串行转列
利用零宽断言字符串行转列,拖入sublime
(?<=.) 替换为\n
python替换脚本
(抄来的,py3才能用)
1
2
3
4
5
6
7
8
9
10
|
import
base64
import
string
str1
=
"QAoOQMPFks1BsB7cbM3TQsXg30i9g3=="
string1
=
'i'
+
'5'
+
'j'
+
'L'
+
'W'
+
'7'
+
'S'
+
'0'
+
'G'
+
'X'
+
'6'
+
'u'
+
'f'
+
'1'
+
'c'
+
'v'
+
'3'
+
'n'
+
'y'
+
'4'
+
'q'
+
'8'
+
'e'
+
's'
+
'2'
+
'Q'
+
'+'
+
'b'
+
'd'
+
'k'
+
'Y'
+
'g'
+
'K'
+
'O'
+
'I'
+
'T'
+
'/'
+
't'
+
'A'
+
'x'
+
'U'
+
'r'
+
'F'
+
'l'
+
'V'
+
'P'
+
'z'
+
'h'
+
'm'
+
'o'
+
'w'
+
'9'
+
'B'
+
'H'
+
'C'
+
'M'
+
'D'
+
'p'
+
'E'
+
'a'
+
'J'
+
'R'
+
'Z'
+
'N'
string2
=
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
print
(str1)
print
(string1)
print
(string2)
print
(str1.translate(
str
.maketrans(string1,string2)))
print
(base64.b64decode(str1.translate(
str
.maketrans(string1,string2))))
|
那么你一定会好奇,QAoOQMPFks1BsB7cbM3TQsXg30i9g3== 这个串哪来的?
这时候就要研究so文件了。
考察so
把easyjni.apk当作zip解压,在lib\armeabi-v7a中发现libnative.so,拖入ida
ida逆向so
然后万能的F5,看到关键代码
只能连蒙带猜,从代码看出v6的长度,一定要为32个字符。
结合底下的MbT3sQgX039i3g==AQOoMQFPskB1Bsc7,长度恰好为32,猜测用户输入的字符串经过某种变换,和这个字符串比较刚好相等。
为了好读,就改下变量名。
第一轮变换
1
2
3
4
5
6
7
8
|
do
{
v8
=
&s1[i];
s1[i]
=
t_str[i
+
16
];
v9
=
t_str[i
+
+
];
v8[
16
]
=
v9;
}
while
( i !
=
16
);
|
先考察这段代码,恕我眼拙,光看,真的看不懂,幸好我会VC6
随便给两个字符串赋值一下
char s1[33]="s1:abcdefghijklmnopqrstuvwxyz123";
char t_str[33]="t_str:ABCDEFGHIJKLMNOPQRSTUVWXYZ";
原样拖入vc6,竟然能跑
跑一跑可以发现,t_str没变,s1被从中间被切开,左右交换了。
接下来这一句啥意思真的不知道,所以只能跳过,
第二轮变换
下一个逻辑
1
2
3
4
5
6
7
8
9
10
|
do
{
v12
=
__OFSUB__(v10,
30
);
v11
=
v10
-
30
<
0
;
v16
=
s1[v10];
s1[v10]
=
s1[v10
+
1
];
s1[v10
+
1
]
=
v16;
v10
+
=
2
;
}
while
( v11 ^ v12 );
|
说实话,OFSUB(v10, 30) 和 v10 - 30 < 0; 到底啥意思,真不知道,而且v11和v12还控制着整个循环。
但我们可以猜,因为前面的循环代码,是 do while ( i != 16 );
我们可以照抄,或者把16改成32,两种都试试,幸运的是,16改成32就是题目表示的逻辑
再跑跑看
发现字符串分成2个为一组,左右交换了。
按照这个逻辑,逆回来,我们的最终字符串是
MbT3sQgX039i3g==AQOoMQFPskB1Bsc7
那么2个为一组,左右交换一下,是
bM3TQsXg30i9g3==QAoOQMPFks1BsB7c
再从中间切开,左右交换,就是
QAoOQMPFks1BsB7cbM3TQsXg30i9g3==
解码
这个字符串,就是被替换过编码表的Base64编码,按照上一节所述,大概就是
QAoOQMPFks1BsB7cbM3TQsXg30i9g3== 变成
ZmxhZ3tqdXN0X0FOb3QjZXJfQHAzfQ== 变成
flag{justANot#er@p3}
思考题
1、假如字符串长度是n,那么转成base64后,长度为多少?
2、之前保存的ida工程,再按f5代码居然会不一样
3、
1
2
3
4
5
6
7
8
9
10
|
do
{
v12
=
__OFSUB__(v10,
30
);
v11
=
v10
-
30
<
0
;
v16
=
s1[v10];
s1[v10]
=
s1[v10
+
1
];
s1[v10
+
1
]
=
v16;
v10
+
=
2
;
}
while
( v11 ^ v12 );
|
这些黄底的到底啥意思? OFSUB到底是啥?
附件
c++代码
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
67
68
69
|
int
main()
{
int
i
=
0
;
int
j
=
0
;
char
*
v8;
char v9;
char s1[
33
]
=
"s1:abcdefghijklmnopqrstuvwxyz123"
;
char t_str[
33
]
=
"t_str:ABCDEFGHIJKLMNOPQRSTUVWXYZ"
;
do
{
v8
=
&s1[i];
s1[i]
=
t_str[i
+
16
];
v9
=
t_str[i
+
+
];
v8[
16
]
=
v9;
}
while
( i !
=
16
);
for
(j
=
0
;j<
33
;j
+
+
)
printf(
"%c"
,t_str[j]);
printf(
"\n"
);
printf(
"\n"
);
printf(
"\n"
);
for
(j
=
0
;j<
33
;j
+
+
)
printf(
"%c"
,s1[j]);
printf(
"\n"
);
printf(
"\n"
);
printf(
"\n"
);
printf(
"\n"
);
printf(
"\n"
);
printf(
"\n"
);
printf(
"\n"
);
i
=
0
;
/
*
do
{
v12
=
__OFSUB__(i,
30
);
v11
=
(i
-
30
) <
0
;
v16
=
s1[i];
s1[i]
=
s1[i
+
1
];
s1[i
+
1
]
=
v16;
i
+
=
2
;
}
while
( v11 ^ v12 );
*
/
do
{
v9
=
s1[i];
s1[i]
=
s1[i
+
1
];
s1[i
+
1
]
=
v9;
i
+
=
2
;
}
while
( i !
=
32
);
for
(j
=
0
;j<
33
;j
+
+
)
printf(
"%c"
,s1[j]);
return
0
;
}
|
心灵鸡汤
1、教是最好的学,输出是最好的输入。
2、要多培养自己写文档分享的习惯。
3、基础不牢,地动山摇,base64解码的流程又是什么呢?
(1、查表 2、转6bit 3、合并8bit 4、8bit转ascii码)
4、题目说是easyjni,但感觉不是很easy。
参考
1、base64编码_动画演示 Base 64 编码
https://blog.csdn.net/weixin_39519741/article/details/110582856
2、一篇文章彻底弄懂Base64编码原理
https://blog.csdn.net/wo541075754/article/details/81734770
3、攻防世界--MOBILE区“easyjni.apk”逆向过程,附apk文件和python脚本
https://www.52pojie.cn//thread-1628993-1-1.html
鸣谢
从这个帖子找的题目附件
https://www.52pojie.cn//thread-1628993-1-1.html