安卓做题记录

安卓的好题极少,故而放在一起.

X1Login

下载后发现打不开,考虑有检测,一看果然有.

private final void checkSecutity() {
        if (Secure.checkDebug()) {
            Toast.makeText(this, "Debugger Detected!", 0).show();
            exit();
        }
        if (Secure.checkRoot()) {
            Toast.makeText(this, "Root Detected!", 0).show();
            exit();
        }
    }

这里直接用mt管理器去掉exit.

接下来可以看到一个方法是用一个本地函数解密,可以动调获取解密后的脚本,也可以用frida主动调用.

package com.nctf.simplelogin;

/* loaded from: classes.dex */
public class DecStr {
    public static native String get(String str);

    static {
        System.loadLibrary("simple");
    }
}

可以发现有加载了一个dex文件(但是名字是so,这里迷惑了我,导致没看懂),然后调用了一个check函数.

去看那个so库会发现那个是没有check函数,只有一个docheck函数.

去assest看看,找到了一个so库,但是他是读取0x40开始的文件,用010打开看看发现有有dex特征修改后用jadx打开

package com.nctf.simplelogin;

import android.content.Context;
import android.widget.Toast;
import java.security.MessageDigest;

/* loaded from: D:\Backup\Desktop\题目\libsimple.so */
public class Check {
    Context context;
    String password;
    String username;

    public Check(Context context, String username, String password) {
        this.username = username;
        this.password = password;
        this.context = context;
    }

    public void check() {
        try {
            if (check_username()) {
                MessageDigest digest = MessageDigest.getInstance(DecStr.get("tMC2"));
                digest.update(this.username.getBytes());
                byte[] output = digest.digest();
                boolean result = check_password(output);
                if (result) {
                    Toast.makeText(this.context, "Login Successful! Now submit your flag!", 0).show();
                    return;
                }
            }
            Toast.makeText(this.context, "Login Failed!", 0).show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean check_username() {
        return this.username.equals(DecStr.get("uZPOs29goMu6l38="));
    }

    private boolean check_password(byte[] key) {
        return Secure.doCheck(this.password, key);
    }
}

tMC2是md5,故而我们能获得user名字

so库里面是3des加密(为什么大家都能一眼丁真啊(꒦_꒦) ),解密即可.

kotlindroid

程序很乱直接定位到加密地方,看加密逻辑

public static final Unit Button$lambda$7$lambda$6(String text, Context context) {
        Intrinsics.checkNotNullParameter(text, "$text"); // 对两个静态变量各自异或后相加作为key
        Intrinsics.checkNotNullParameter(context, "$context");
        byte[] key1 = {118, 99, 101, 126, 124, 114, 110, 100};
        byte[] key2 = Config.INSTANCE.getBYTE_ARRAY();
        Collection destination$iv$iv = new ArrayList(key1.length);
        for (byte item$iv$iv : key1) {
            byte it = (byte) (item$iv$iv ^ 23);
            destination$iv$iv.add(Byte.valueOf(it));
        }
        byte[] modifiedKey1 = CollectionsKt.toByteArray((List) destination$iv$iv);
        Collection destination$iv$iv2 = new ArrayList(key2.length);
        for (byte item$iv$iv2 : key2) {
            byte it2 = (byte) (item$iv$iv2 ^ 8);
            destination$iv$iv2.add(Byte.valueOf(it2));
        }
        byte[] modifiedKey2 = CollectionsKt.toByteArray((List) destination$iv$iv2);
        byte[] key = ArraysKt.plus(modifiedKey1, modifiedKey2);
        check(text, context, key);
        return Unit.INSTANCE;
    }
private static final void check(String text, Context context, byte[] key) {
        SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
        sec(context, secretKey, text, new Function1() { // from class: com.atri.ezcompose.SearchActivityKt$$ExternalSyntheticLambda4
            public final /* synthetic */ Context f$0;

            public /* synthetic */ SearchActivityKt$$ExternalSyntheticLambda4(Context context2) {
                context = context2;
            }

            @Override // kotlin.jvm.functions.Function1
            public final Object invoke(Object obj) {
                Unit check$lambda$14;
                check$lambda$14 = SearchActivityKt.check$lambda$14(context, (String) obj);
                return check$lambda$14;
            }
        });
    }
public static final Unit check$lambda$14(Context context, String flag) {
        Intrinsics.checkNotNullParameter(context, "$context");
        Intrinsics.checkNotNullParameter(flag, "flag");
        if (Intrinsics.areEqual(flag, "MTE0NTE0HMuJKLOW1BqCAi2MxpHYjGjpPq82XXQ/jgx5WYrZ2MV53a9xjQVbRaVdRiXFrSn6EcQPzA==")) {
            Toast.makeText(context, "Congratulations! :)", 0).show();
        } else {
            Toast.makeText(context, "Wrong :(", 0).show();
        }
        return Unit.INSTANCE;
    }

    /* JADX INFO: Access modifiers changed from: private */
    public static final Cipher createCipher() {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        Intrinsics.checkNotNullExpressionValue(cipher, "getInstance(...)");
        return cipher;
    }

    /* JADX INFO: Access modifiers changed from: private */
    public static final byte[] generateIV() {
        byte[] bytes = "114514".getBytes(Charsets.UTF_8);
        Intrinsics.checkNotNullExpressionValue(bytes, "getBytes(...)");
        return bytes;
    }

    /* JADX INFO: Access modifiers changed from: private */
    public static final GCMParameterSpec getGCMParameterSpec(byte[] iv) {
        return new GCMParameterSpec(128, iv);

private static final void sec(Context context, SecretKeySpec secretKey, String text, Function1<? super String, Unit> function1) {
        BuildersKt__Builders_commonKt.launch$default(CoroutineScopeKt.CoroutineScope(Dispatchers.getIO()), null, null, new SearchActivityKt$sec$1(secretKey, text, function1, null), 3, null);
    }

final class SearchActivityKt$sec$1 extends SuspendLambda implements Function2<CoroutineScope, Continuation<? super Unit>, Object> {
    final /* synthetic */ Function1<String, Unit> $onResult;
    final /* synthetic */ SecretKeySpec $secretKey;
    final /* synthetic */ String $text;
    int label;

    /* JADX WARN: 'super' call moved to the top of the method (can break code semantics) */
    /* JADX WARN: Multi-variable type inference failed */
    SearchActivityKt$sec$1(SecretKeySpec secretKeySpec, String str, Function1<? super String, Unit> function1, Continuation<? super SearchActivityKt$sec$1> continuation) {
        super(2, continuation);
        this.$secretKey = secretKeySpec;
        this.$text = str;
        this.$onResult = function1;
    }

    @Override // kotlin.coroutines.jvm.internal.BaseContinuationImpl
    public final Continuation<Unit> create(Object obj, Continuation<?> continuation) {
        return new SearchActivityKt$sec$1(this.$secretKey, this.$text, this.$onResult, continuation);
    }

    @Override // kotlin.jvm.functions.Function2
    public final Object invoke(CoroutineScope coroutineScope, Continuation<? super Unit> continuation) {
        return ((SearchActivityKt$sec$1) create(coroutineScope, continuation)).invokeSuspend(Unit.INSTANCE);
    }

    /* JADX WARN: Multi-variable type inference failed */
    /* JADX WARN: Type inference failed for: r3v0, types: [int] */
    /* JADX WARN: Type inference failed for: r3v1 */
    /* JADX WARN: Type inference failed for: r3v10 */
    /* JADX WARN: Type inference failed for: r3v8 */
    /* JADX WARN: Type inference failed for: r3v9 */
    /* JADX WARN: Type inference failed for: r4v0 */
    /* JADX WARN: Type inference failed for: r4v1, types: [kotlin.coroutines.Continuation] */
    /* JADX WARN: Type inference failed for: r4v5 */
    /* JADX WARN: Type inference failed for: r4v6 */
    /* JADX WARN: Type inference failed for: r4v7 */
    @Override // kotlin.coroutines.jvm.internal.BaseContinuationImpl
    public final Object invokeSuspend(Object obj) {
        SearchActivityKt$sec$1 searchActivityKt$sec$1;
        Object obj2;
        Object obj3;
        Cipher createCipher;
        byte[] generateIV;
        GCMParameterSpec gCMParameterSpec;
        Object coroutine_suspended = IntrinsicsKt.getCOROUTINE_SUSPENDED();
        SearchActivityKt$sec$1 searchActivityKt$sec$12 = this.label;
        ?? r4 = 0;
        r4 = 0;
        try {
        } catch (Exception e) {
            searchActivityKt$sec$1 = searchActivityKt$sec$12;
            Log.e("sec", "Error occurred", e);
            searchActivityKt$sec$1.label = 2;
            if (BuildersKt.withContext(Dispatchers.getMain(), new AnonymousClass2(searchActivityKt$sec$1.$onResult, r4), searchActivityKt$sec$1) == coroutine_suspended) {
                return coroutine_suspended;
            }
            obj2 = obj3;
        }
        switch (searchActivityKt$sec$12) {
            case 0:
                ResultKt.throwOnFailure(obj);
                SearchActivityKt$sec$1 searchActivityKt$sec$13 = this;
                obj3 = obj;
                createCipher = SearchActivityKt.createCipher();
                generateIV = SearchActivityKt.generateIV();
                gCMParameterSpec = SearchActivityKt.getGCMParameterSpec(generateIV);
                createCipher.init(1, searchActivityKt$sec$13.$secretKey, gCMParameterSpec);
                byte[] bytes = JNI.INSTANCE.getAt().getBytes(Charsets.UTF_8);
                Intrinsics.checkNotNullExpressionValue(bytes, "getBytes(...)");
                createCipher.updateAAD(bytes);
                String str = searchActivityKt$sec$13.$text;
                Charset UTF_8 = StandardCharsets.UTF_8;
                Intrinsics.checkNotNullExpressionValue(UTF_8, "UTF_8");
                byte[] bytes2 = str.getBytes(UTF_8);
                Intrinsics.checkNotNullExpressionValue(bytes2, "getBytes(...)");
                byte[] doFinal = createCipher.doFinal(bytes2);
                Intrinsics.checkNotNull(doFinal);
                String encode$default = Base64.encode$default(Base64.INSTANCE, ArraysKt.plus(generateIV, doFinal), 0, 0, 6, null);
                searchActivityKt$sec$13.label = 1;
                Object withContext = BuildersKt.withContext(Dispatchers.getMain(), new AnonymousClass1(searchActivityKt$sec$13.$onResult, encode$default, null), searchActivityKt$sec$13);
                searchActivityKt$sec$12 = searchActivityKt$sec$13;
                r4 = withContext;
                if (withContext == coroutine_suspended) {
                    return coroutine_suspended;
                }
                return Unit.INSTANCE;
            case 1:
                searchActivityKt$sec$12 = this;
                obj3 = obj;
                ResultKt.throwOnFailure(obj3);
                return Unit.INSTANCE;
            case 2:
                searchActivityKt$sec$1 = this;
                obj2 = obj;
                ResultKt.throwOnFailure(obj2);
                return Unit.INSTANCE;
            default:
                throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
        }
    }

可以看到是这样的情况,里面有个ADD方法,在本地库,由hook可知,结果不受影响,可以直接frida获得,然后去解密.

so库使用kotlin写的,给了相关api的定义,还是看起来比较方便的.

posted @ 2025-04-16 19:15  T0fV404  阅读(22)  评论(0)    收藏  举报