下载apk,直接拖到jadx里面看源代码,根据代码的逻辑可以分析出是要判断用户名和密码正确,就会继续执行下一步。在判断用户名的时候,没有什么特别的处理,直接就是判断字符串是否相等。

但是判断密码的时候就有了特殊的处理。处理函数如下:
rivate String getEncodeStr(String str) {
byte[] bArr = null;
try {
bArr = MessageDigest.getInstance(getString(C0818R.string.encode)).digest(str.getBytes(StandardCharsets.UTF_8));
for (int i = 0; i < bArr.length; i++) {
bArr[i] = (byte) (bArr[i] - 1);
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return new BigInteger(1, bArr).toString(16);
}
一般这些C0818R.xxxx的内容都存在resources.arsc.res.values.string中
函数的做法是将输入的密码取md5,然后每一位都减去1,这里操作的是byte,8位。所以在写逆向函数的时候,字符串是16进制的,要先转换成byte的形式。
- 第一种想法:
c232666f1410b3f5010dc51cec341f58是十六进制的表示,举个例子,前两位c2先转换成二进制11000010,这里是有符号数补码表示,十进制就是10111110 = -62,后面的字符也是同样的处理。 - 第二种想法:每次取出两个字符,是十六进制表示,然后按照十六进制计算方式加一,然后转成字符串。
计组没学好!!!!!!!
pwd = 'c232666f1410b3f5010dc51cec341f58'
a = ''
for i in range(0,len(pwd),2): #每次取出两个字符
hex = pwd[i]+pwd[i+1]
f = int(hex,16) + 1
print("%02x"%f,end='')
这样计算出来了md5值,然后去查一下密码是多少

验证了账号密码正确之后,就会调用getKeyAndRedirect函数,这个函数主要干的事就是拿着密码去请求服务器,得到一个东西


得到了这样一串字符串,这具体是干啥用的,还得继续分析
请求完了服务器,函数就会判断key的长度是否是0,如果不是0,就会调用CheckFlagActivity函数,再来看CheckFlagActivity函数的实现

看到了很亲切的东西,这里最重要的就是checkFlag这个函数
private boolean checkFlag(String flag) {
return new String(EncodeUtils.encode(flag.getBytes(StandardCharsets.UTF_8), false, this.key.getBytes(StandardCharsets.UTF_8))).equals(getString(C0818R.string.encodeFlag));
}
调用了一个EncodeUtils.encode方法,然后判断两个字符串是否相等,如果相等,那就提示正确,否则就失败。这里还传入了我们前面获取到的key。
首先找到它比对的字符串是啥,在和passwd同样的位置。3lkHi9iZNK87qw0p6U391t92qlC5rwn5iFqyMFDl1t92qUnL6FQjqln76l

然后,再来看EncodeUtils.encode方法具体的实现

根据大佬们的wp叙述,这个就是一个指定码表的base方法,其中指定的码表就是请求服务器获得的那一串字符串。
根据以下方法即可以还原flag
enc = '3lkHi9iZNK87qw0p6U391t92qlC5rwn5iFqyMFDl1t92qUnL6FQjqln76l-P'
table = 'TGtUnkaJD0frq61uCQYw3-FxMiRvNOB/EWjgVcpKSzbs8yHZ257X9LldIeh4APom'
bin_tmp = ''
flag = ''
for i in enc:
bin_tmp += '{:06b}'.format(table.index(i))
for i in range(0, len(bin_tmp), 8):
flag += chr(int('0b' + bin_tmp[i:i+8],2))
print(flag)

