安卓做题记录
安卓的好题极少,故而放在一起.
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的定义,还是看起来比较方便的.

浙公网安备 33010602011771号