描述

package: com.tlamb96.spetsnazmessenger

目标

找到 Flag

APK 下载

点击下载

题解

首先打开 APP 看一下,检测到只能运行在俄罗斯版的设备上,需要先过设备检测。

CrackMe

Jadx-GUI 打开找到设备检测的位置,发现用 System.getProperty("user.home") 来获取设备类型,同时发现还用到 System.getenv("USER") 增加了用户白名单检测,白名单用户是 R.string.User

CrackMe

想找用户字符串很简单,Android 的字符串资源都存储在 /res/values/strings.xml 中,可以使用 apktool 工具解压 apk 文件查看 apktool d base.apk。或者直接在 Jadx-GUI 的路径 /资源文件/resources.arsc/res/values/strings.xml 中查看。 打开 xml 文件后可以看到名为 User 的字符串值:RkxBR3s1N0VSTDFOR180UkNIM1J9Cg== 即为白名单用户。

CrackMe

hook 以上两个函数就可以过检测了。

function passCheck() {
    Java.perform(function () {
        var System = Java.use('java.lang.System');
        System.getProperty.overload('java.lang.String').implementation = function (name) {
            if (name == 'user.home') return 'Russia';
            return this.getProperty(name);
        };
        System.getenv.overload('java.lang.String').implementation = function (name) {
            if (name == 'USER') return 'RkxBR3s1N0VSTDFOR180UkNIM1J9Cg==';
            return this.getProperty(name);
        };
    });
}

function main() {
    passCheck();
}

setImmediate(main);

打开 APP 没有提示了,显示需要输入用户名密码进入 APP。

CrackMe

用 Jadx-GUI 继续看 APP,找到登陆页,发现用户名依旧是从资源里面取 username,拿到是 codenameduchess。密码是在 j() 方法中对比。

CrackMe

打开 j() 方法,发现是 md5 对比,md5 字符串是从资源里面取 password,拿到是 84e343a0486ff05530df6c705c8bb4。这是一个非标准 md5,需要进行补 0,直接去百度搜 md5 原文,或者 hook j() 方法,搜索得到明文是 guest

CrackMe

输入用户名 codenameduchess 和密码 guest 进入 APP,显示是个聊天页,需要发送正确消息。

CrackMe

用 Jadx-GUI 继续看 APP,找到发送消息页,发现是在 i() 方法中获得 flag,但是 i() 方法需要用到 this.qthis.s

CrackMe

继续看可以找到 this.qthis.s 是在 onSendMessage() 方法中生成的,而要生成 this.sthis.q 又需要用到 a() 方法和 b() 方法。

CrackMe

a() 方法比较简单,直接用本地进行还原。

from z3 import *
from binascii import b2a_hex, a2b_hex
 
def question1():
    char_array = list("V@]EAASB\u0012WZF\u0012e,a$7(&am2(3.\u0003")
    length = len(char_array)
    for i in range(length // 2):
        c = char_array[length - i - 1]
        char_array[length - i - 1] = chr(ord(char_array[i]) ^ ord('2'))
        char_array[i] = chr(ord(c) ^ ord('A'))
    print(''.join(char_array))


def main():
    question1()
 
 
if __name__ == "__main__":
    main()

# output: Boris, give me the password
CrackMe

b() 方法较 a() 方法难度提升,同时有不可见字符,直接逆向推导函数会有不可见字符,使用 Z3-Solver 进行推导。

CrackMe
from z3 import *
from binascii import b2a_hex, a2b_hex
 
def question2():
 
    r = "\u0000dslp}oQ\u0000 dks$|M\u0000h +AYQg\u0000P*!M$gQ\u0000"
    r_result = bytearray(a2b_hex(''.join(f'{ord(c):02x}' for c in r)))
    for i in range(int(len(r_result) / 2)):
        c = r_result[i]
        r_result[i] = r_result[len(r_result) - i - 1]
        r_result[len(r_result) - i - 1] = c
 
    s = Solver()
    x = [BitVec("x%s" % i, 32) for i in range(len(r_result))]
    for i in range(len(r_result)):
        c = r_result[i]
        s.add(((x[i] >> (i % 8)) ^ x[i]) == r_result[i])        #z3
        s.add(x[i] >= 0, x[i] <= 255)
    if (s.check() == sat):
        model = (s.model())
        flag = ""
        for i in range(len(r_result)):
            if (model[x[i]] != None):
                value = model[x[i]].as_long().real
                flag += " " if value == 0 else chr(value)
            else:
                flag += " "
        print('"' + flag + '"')


def main():
    question2()
 
 
if __name__ == "__main__":
    main()

# output: " ay I *P EASE* h ve the  assword "

将密码输入 APP,密码正确,密码为 ay I *P EASE* h ve the assword。(由于加密的字符中有许多 ascii 为 0 的字符无法正确推导,所以也可以对 output 中的空格进行替换,得到:May I *PLEASE* hive the password?)

CrackMe