本文最后更新于 46 天前,其中的信息可能已经有所发展或是发生改变。
首先下载附件round.zip
解压并用jadx-gui反编译apk
用搜索寻找D0g3xGC(比赛的flag格式是D0g3xGC{XXX})
发现逻辑是分别检测用户输入的用户名和密码
如果都正确就将用户名和密码拼接在一起作为flag输出
所以我们分别研究程序是如何检测用户名和密码的
用户名
阅读源代码,是将用户名base64编码后和字符串比较
但是直接解码字符串发现是乱码
查看base64的实现方式,发现在将字节数组转换成字符的时候
调换了顺序变成0,2,1,3
于是自己写一个base64的解码
python脚本如下
BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
def decode_base64(encoded_str):
# 转换 Base64 字符为字节
bytes_array = bytearray()
for i in range(0, len(encoded_str), 4):
# 获取 4 个 Base64 字符及其相应的索引
b0 = BASE64_CHARS.index(encoded_str[i])
b1 = BASE64_CHARS.index(encoded_str[i + 1])
b2 = BASE64_CHARS.index(encoded_str[i + 2])
b3 = BASE64_CHARS.index(encoded_str[i + 3])
# 组合成 3 个字节
byte1 = (b0 << 2) | (b2 >> 4)
bytes_array.append(byte1&0xFF)
byte2 = (b2 << 4) | (b1 >> 2)
bytes_array.append(byte2&0xFF)
byte3 = (b1 << 6) | b3
bytes_array.append(byte3&0xFF)
return bytes(bytes_array).decode('utf-8', errors='replace') # 转换为字符串,忽略解码错误
得到用户名为round_and
密码
密码用了类似哈希或者随机数的方式来检查
给出的数组可以看成是密码的特征值
所以写一个脚本,模拟检查过程,对每一位单独爆破
以下为脚本
bbli="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_" # 只有大小写字母和下划线合法
class Result:
def __init__(self, num, rip):
self.num = num
self.rip = rip
def get_num(self):
return self.num
def get_rip(self):
return self.rip
class Round:
def round(self, iArr):
length = 12
ansarr = [352, 646, 752, 882, 65, 0, 122, 0, 0, 7, 350, 360] # 特征值
firsti = [33,795, 887, 962, 638, 243, 468, 992, 747, 1023, 999, 988] # 爆破之后得到的种子(最开始不知道)
seedlist=[] # 存放爆破过程中种子的列表
for i2 in range(length): # 遍历密码字符串的每一位
for i3 in bbli: # 遍历每个字符
i = firsti[i2] # 设为hash或者种子的初始值
charAt = ord(i3) # 取出当前字符的ASCII码
for _ in range(32): # 进行运算
i4 = (((iArr[i] ^ charAt) % 5) + 5) % 5
if i4 == 0:
add = self.add(iArr, charAt, i)
elif i4 == 1:
add = self.sub(iArr, charAt, i)
elif i4 == 2:
add = self.xor(iArr, charAt, i)
elif i4 == 3:
add = self.shl(charAt, i)
elif i4 == 4:
add = self.shr(charAt, i)
else:
add = Result(charAt, i)
charAt = add.get_num()
i = add.get_rip()
if charAt == ansarr[i2]: # 判断和特征值是否相同
print(i3,i,end=" ") # 这次的最后一个i会作为下次的种子
seedlist.append(i) # 将种子存入列表中
# 不要break,有可能有多个符合特征值的字符,但是他们的种子可能不同,所以要继续尝试
print()
print(seedlist) # 打印出本次的种子列表
seedlist=[] # 清空种子列表
def add(self, iArr, i, i2):
i3 = (((i + iArr[i2]) % 1024) + 1024) % 1024
return Result(i3, (i2 + i3) % 1024)
def sub(self, iArr, i, i2):
i3 = (((i - iArr[i2]) % 1024) + 1024) % 1024
return Result(i3, (i2 + i3) % 1024)
def xor(self, iArr, i, i2):
i3 = (iArr[i2] ^ i) % 1024
return Result(i3, (i2 + i3) % 1024)
def shl(self, i, i2):
i3 = (i >> 3) % 1024
return Result(i3, (i2 + i3) % 1024)
def shr(self, i, i2):
i3 = (i << 3) % 1024
return Result(i3, (i2 + i3) % 1024)
def makebox(s):
iArr = [0] * 1024 # 创建一个大小为1024的整数列表
# 将整数0到1023逆序填充到数组中
for i in range(1024):
iArr[1023 - i] = i
# 对数组中的每个元素进行异或运算
for i2 in range(1024):
iArr[i2] = iArr[i2] ^ ord(s[i2 % len(s)]) # 使用ord()将字符转换为对应的ASCII码
return iArr # 返回最终的整数列表
if __name__ == "__main__":
round_instance = Round()
round_instance.round(makebox("c9m1bRmfY5Wk")) # 模拟过程
得到密码为_rounD_we_go
合起来得到D0g3xGC{round_and_rounD_we_go}