【楚慧杯 2023】Level_One
收获
安卓逆向如何获取数据资源
更换码表的 Base64 加解密
(2023年3月30日)【楚慧杯 2023】Level_One
思路
下载解压得到一个 chall.apk 文件
界面是常规的输入 flag 然后进行校验,没什么特别的
在 jadx-gui 中打开,找到 MainActivity
:
定位到关键判断语句:
获取用户输入,并转化为 utf-8 编码,以字节形式调用 Encrypt()
函数进行加密,然后与 C0535R.string.enc
进行比较,如果相同则获得 flag
Encrypt()
函数的内容在下方给出:
形式看起来比较复杂,但是可以注意到代码后面一段:
这里对 bArr
的长度做了一个余 3 的操作,余数为 length2
并且,还对余数的两种情况分别进行了讨论:
当 length2 = 1
时,会在最后添加 "%&"
当 length2 = 2
时,会在最后添加 "*"
同时,java 中 charAt()
方法用于返回字符串中指定索引位置的字符,这里利用 append()
向 sb
插入索引处的值
这样的特点非常类似 Base64 加密算法,但是不同的是:这里根据余数为 1
或 2
添加的不是 "=="
和 "="
继续观察,可以发现 append()
插入用到的索引表 string
来自 C0535R.string.table
在资源中定位这个文件:
可以得到表中的内容:n4/Xv90ElB32S6MmyK+iVIgkjU17CDGaLYxeuzsc8TqNOtrJWPfRp=HFhAwdb5oZQ
包含大写字母、小写字母、数字 0-9、'+'
、'/'
,很符合 Base64 码表的形式,只是原始码表的顺序被打乱了
但是根据这里码表的变动,且多出一个 '='
,导致 Encrypt()
函数中最后根据余数个数添加的不是 '=='
和 '='
,也就基本可以确定 Encrypt()
函数是更换过码表的 Base64 加密了
在资源里找到 if 条件中对比的数据 C0535R.string.enc
:
得到 enc
的内容:UsPYUFORMXnHSXIe6xpFSilR2iKs6RvtjeVFSfpHSX9zM0Sp6XVFjeK5
利用更换码表的 Base64 解密即可
脚本
# coding:utf-8
s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" # 原始码表
def my_base64_encode(inputs):
# 将字符串转化为2进制
bin_str = []
for i in inputs:
x = str(bin(ord(i))).replace('0b', '')
bin_str.append('{:0>8}'.format(x))
# print(bin_str)
# 输出的字符串
outputs = ""
# 不够三倍数,需补齐的次数
nums = 0
while bin_str:
# 每次取三个字符的二进制
temp_list = bin_str[:3]
if len(temp_list) != 3:
nums = 3 - len(temp_list)
while len(temp_list) < 3:
temp_list += ['0' * 8]
temp_str = "".join(temp_list)
# print(temp_str)
# 将三个8字节的二进制转换为4个十进制
temp_str_list = []
for i in range(0, 4):
temp_str_list.append(int(temp_str[i * 6:(i + 1) * 6], 2))
# print(temp_str_list)
if nums:
temp_str_list = temp_str_list[0:4 - nums]
for i in temp_str_list:
outputs += s[i]
bin_str = bin_str[3:]
outputs += nums * '='
print("加密完成:\n%s " % outputs)
def my_base64_decode(inputs):
# 将字符串转化为2进制
bin_str = []
for i in inputs:
if i != '=':
x = str(bin(s.index(i))).replace('0b', '')
bin_str.append('{:0>6}'.format(x))
# print(bin_str)
# 输出的字符串
outputs = ""
nums = inputs.count('=')
while bin_str:
temp_list = bin_str[:4]
temp_str = "".join(temp_list)
# print(temp_str)
# 补足8位字节
if (len(temp_str) % 8 != 0):
temp_str = temp_str[0:-1 * nums * 2]
# 将四个6字节的二进制转换为三个字符
for i in range(0, int(len(temp_str) / 8)):
outputs += chr(int(temp_str[i * 8:(i + 1) * 8], 2))
bin_str = bin_str[4:]
print("解密完成:\n%s " % outputs)
print(" 可更换码表的 Base64 加解密系统 ")
print("*************************************")
select = input("是否更换加密的码表? (y or n) 你的选择: ")
if select == "y" or select == "yes":
s = input("在这里输入码表: ")
print(".....done, 已更改!")
else:
print("码表未做更改!")
num = input("选择要进行的操作? (1:加密 or 2:解密) 你的选择: ")
if num == "1" or num == "1:加密":
input_str = input("输入明文以加密: ")
my_base64_encode(input_str)
elif num == "2" or num == "2:解密":
input_str = input("输入密文以解密: ")
my_base64_decode(input_str)
else:
print(".....bye")
结果
flag{380605c6-7123-4f71-b573-601e8c4457b4}