This app holds a secret inside. Can you find it?
adb -d install UnCrackable-Level1.apk
装好之后先大致看一下逻辑,一打开就有个 Root detected!
弹窗,点 OK 就强制退出了。
public void onCreate(Bundle bundle) {
if (C0002c.m2a() || C0002c.m3b() || C0002c.m4c()) {
m5a("Root detected!");
}
if (C0001b.m1a(getApplicationContext())) {
m5a("App is debuggable!");
}
super.onCreate(bundle);
setContentView(R.layout.activity_main);
}
跟进可以发现,这一部分是判断是否有 root 的逻辑。
public class C0002c {
/* renamed from: a */
public static boolean m2a() {
for (String file : System.getenv("PATH").split(":")) {
if (new File(file, "su").exists()) {
return true;
}
}
return false;
}
/* renamed from: b */
public static boolean m3b() {
String str = Build.TAGS;
return str != null && str.contains("test-keys");
}
/* renamed from: c */
public static boolean m4c() {
for (String file : new String[]{"/system/app/Superuser.apk", "/system/xbin/daemonsu", "/system/etc/init.d/99SuperSUDaemon", "/system/bin/.ext/.su", "/system/etc/.has_su_daemon", "/system/etc/.installed_su_daemon", "/dev/com.koushikdutta.superuser.daemon/"}) {
if (new File(file).exists()) {
return true;
}
}
return false;
}
}
退出的逻辑:
private void m5a(String str) {
AlertDialog create = new AlertDialog.Builder(this).create();
create.setTitle(str);
create.setMessage("This is unacceptable. The app is now going to exit.");
create.setButton(-3, "OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int i) {
System.exit(0);
}
});
create.setCancelable(false);
create.show();
}
为了不退出,先尝试 hook 住 System.exit(0)
。
setImmediate(function () {
console.log("[*] hook start")
Java.perform(function () {
var System = Java.use("java.lang.System");
System.exit.implementation = function (v) {
console.log("[*] exit called");
}
console.log("[*] exit handler modified");
})
})
hook 成功,下一步就是获取到 secret string。
public void verify(View view) {
String str;
String obj = ((EditText) findViewById(R.id.edit_text)).getText().toString();
AlertDialog create = new AlertDialog.Builder(this).create();
if (C0005a.m6a(obj)) {
create.setTitle("Success!");
str = "This is the correct secret.";
} else {
create.setTitle("Nope...");
str = "That's not it. Try again.";
}
create.setMessage(str);
create.setButton(-3, "OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
});
create.show();
}
按照这里的逻辑,先使用简单粗暴的方法,直接 hook 住 m6a
的返回值就可以了。
setImmediate(function () {
console.log("[*] hook start")
Java.perform(function () {
var System = Java.use("java.lang.System");
System.exit.implementation = function (v) {
console.log("[*] exit called");
}
console.log("[*] exit handler modified");
/* 这种一直没有成功,以后再深究下原因。 */
// var String = Java.use("java.lang.String");
// String.equals.overload("java.lang.Object").implementation = function (v) {
// console.log("[*] return true");
// // return true;
// }
// console.log("[*] equals handler modified");
var String = Java.use("sg.vantagepoint.uncrackable1.a");
String.a.implementation = function (v) {
console.log("[*] return true");
return true;
}
console.log("[*] a handler modified");
})
})
在运行时拿到 secret string。
a.a(b("8d127684cbc37c17616d806cf50473cc"), Base64.decode("5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=", 0));
Java.use("sg.vantagepoint.a.a").a.implementation = function (arg1, arg2) {
const retVal = this.a(arg1, arg2); // 获取到返回值
var decrypt = "";
for (var i = 0; i < retVal.length; i++) { // frida 里不支持 let 写法……
decrypt += String.fromCharCode(retVal[i]);
}
console.log(decrypt);
return retVal;
}
This app holds a secret inside. May include traces of native code.
这题开始涉及 lib 的逆向了,未完待续。
The crackme from hell!
The Radare2 community always dreamed with its decentralized and free currency to allow r2 fans to make payments in places and transfer money between r2 users. A debug version has been developed and it will be supported very soon in many stores and websites. Can you verify that this is cryptographically unbreakable?
Hint: Run the APK in a non-tampered device to play a bit with the app.
r2con{PIN_NUMERIC:SALT_LOWERCASE}
r2con{ascii(key)}