Android UnCrackable L1
Using APK Lab, an extension of VS Code to reverse this app.
There are 2 condition statements to prevent debug and login as root. I deleted smali lines to by this check.
I saw verify function to check password:
a.a(obj)
is a compare function to return true if obj
equal secret key.
Step into function a.a()
As you can see, the secret key is new String(bArr)
string object so I set a breakpoint at the smali line refer to this line.
I use JADX_GUI and Genymotion to debug this app.
Boommm
I got the key
v1 is the register save value of new String(bArr)
Secret key: I want to believe
Script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Java.perform(function () {
Java.use("sg.vantagepoint.uncrackable1.MainActivity").a.overload(
"java.lang.String"
).implementation = function (string) {
console.log("Bypass root check: " + string);
};
Java.use("java.lang.String").equals.overload(
"java.lang.Object"
).implementation = function (arg) {
if (this == "flag") console.log("Password: " + arg);
return this.equals(arg);
};
Java.use(
"sg.vantagepoint.uncrackable1.MainActivity"
).onResume.implementation = function () {
this.onResume();
Java.use("sg.vantagepoint.uncrackable1.a").a(
Java.use("java.lang.String").$new("flag")
);
};
});
Result:
Android UnCrackable L2
Patch onCreate()
to bypass root and debugger check.
.method protected onCreate(Landroid/os/Bundle;)V
.locals 4
invoke-direct {p0}, Lsg/vantagepoint/uncrackable2/MainActivity;->init()V
goto :goto_0
invoke-static {}, Lsg/vantagepoint/a/b;->a()Z
move-result v0
if-nez v0, :cond_0
invoke-static {}, Lsg/vantagepoint/a/b;->b()Z
move-result v0
if-nez v0, :cond_0
invoke-static {}, Lsg/vantagepoint/a/b;->c()Z
move-result v0
if-eqz v0, :cond_1
:cond_0
const-string v0, "Root detected!"
invoke-direct {p0, v0}, Lsg/vantagepoint/uncrackable2/MainActivity;->a(Ljava/lang/String;)V
:cond_1
invoke-virtual {p0}, Lsg/vantagepoint/uncrackable2/MainActivity;->getApplicationContext()Landroid/content/Context;
move-result-object v0
invoke-static {v0}, Lsg/vantagepoint/a/a;->a(Landroid/content/Context;)Z
move-result v0
if-eqz v0, :cond_2
const-string v0, "App is debuggable!"
invoke-direct {p0, v0}, Lsg/vantagepoint/uncrackable2/MainActivity;->a(Ljava/lang/String;)V
:cond_2
new-instance v0, Lsg/vantagepoint/uncrackable2/MainActivity$2;
invoke-direct {v0, p0}, Lsg/vantagepoint/uncrackable2/MainActivity$2;-><init>(Lsg/vantagepoint/uncrackable2/MainActivity;)V
const/4 v1, 0x3
new-array v1, v1, [Ljava/lang/Void;
const/4 v2, 0x0
const/4 v3, 0x0
aput-object v3, v1, v2
const/4 v2, 0x1
aput-object v3, v1, v2
const/4 v2, 0x2
aput-object v3, v1, v2
invoke-virtual {v0, v1}, Lsg/vantagepoint/uncrackable2/MainActivity$2;->execute([Ljava/lang/Object;)Landroid/os/AsyncTask;
:goto_0
new-instance v0, Lsg/vantagepoint/uncrackable2/CodeCheck;
invoke-direct {v0}, Lsg/vantagepoint/uncrackable2/CodeCheck;-><init>()V
iput-object v0, p0, Lsg/vantagepoint/uncrackable2/MainActivity;->m:Lsg/vantagepoint/uncrackable2/CodeCheck;
invoke-super {p0, p1}, Landroid/support/v7/app/c;->onCreate(Landroid/os/Bundle;)V
const p1, 0x7f09001b
invoke-virtual {p0, p1}, Lsg/vantagepoint/uncrackable2/MainActivity;->setContentView(I)V
return-void
.end method
Let’s dive into main code:
This app using m4a()
from CodeCheck
to check for the correct key.
Jump into class CodeCheck
I saw native funtion bar
so I found the lib file.
Lib foo
had been loaded in MainActivity
class.
I reversed [libfoo.so](http://libfoo.so)
using ghidra
and I found function bar
:
This function compare input string with an string save in the memory. But secret string has been splited into some memory addresses located side by side shown in above picture.
I concatenate these variables to:
0x6873696620656874206c6c6120726f6620736b6e616854
This is the hex present of the secret string after flipped
Decode it to string and reverse:
The secret string is Thanks for all the fish
Script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Java.perform(function () {
Java.use("sg.vantagepoint.uncrackable2.MainActivity").a.overload(
"java.lang.String"
).implementation = function (string) {
console.log("Bypass root and degguber check: " + string);
};
Java.use(
"sg.vantagepoint.uncrackable2.MainActivity"
).onResume.implementation = function () {
this.onResume();
Java.use("sg.vantagepoint.uncrackable2.CodeCheck")
.$new()
.a(Java.use("java.lang.String").$new("Jvc9fqeJVkbNAqpCESxr5it"));
Interceptor.detachAll();
};
});
Interceptor.attach(Module.findExportByName("libc.so", "strncmp"), {
onEnter: function (args) {
let inputStr = "Jvc9fqeJVkbNAqpCESxr5it";
if (args[2].toInt32() != inputStr.length) return;
try {
let str1 = args[0].readCString(inputStr.length);
let str2 = args[1].readCString(inputStr.length);
if (str1.includes(inputStr) || str2.includes(inputStr)) {
console.log(`Flag in: ${str1} || ${str2}`);
}
} catch (e) {
console.log(e);
}
},
});
PoC:
Android UnCrackable L3
Using JEB to decompile APK and its native library:
onCreate()
In order to bypass onCreate()
function we just need to change showDialog()
1
2
3
4
5
6
7
8
9
10
11
12
13
Java.perform(function () {
Java.use("sg.vantagepoint.uncrackable3.MainActivity").showDialog.overload(
"java.lang.String"
).implementation = function (string) {
console.log("Bypassed showDialog, message: ", string);
return;
};
Java.use("sg.vantagepoint.uncrackable3.MainActivity").$init.implementation =
function () {
this.$init();
printSecretKey();
};
});
But when run frida we meet an error:
According to this error, goodbye()
in native library is called in order to exit program.
Go deep into natve lib, I saw a function called goodbye()
, when strstr()
is call with an argument named as “frida” or “xposed”, the program will exit.
strstr()
is a function of libc.so
, I change strstr function in order to run frida.
code_check()
using bar()
from native lib to check password:
Go to bar()
:
It use xor to check password. password = xor__key ^ secret_key
secret_key
is generated by generate_secret()
Use this code to find secret_key
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function printSecretKey() {
Interceptor.attach(Module.getBaseAddress("libfoo.so").add(0x0fa0), {
onEnter: function (args) {
console.log("Secret key before generate at address: " + args[0]);
this.answerAdress = args[0];
console.log(
hexdump(args[0], {
offset: 0,
length: 24,
header: true,
ansi: true,
})
);
},
onLeave: function (retVal) {
console.log("Secret key after generate: ");
console.log(
hexdump(this.answerAdress, {
offset: 0,
length: 24,
header: true,
ansi: true,
})
);
},
});
Java.use(
"sg.vantagepoint.uncrackable3.MainActivity"
).onResume.implementation = function () {
this.onResume();
Java.choose("sg.vantagepoint.uncrackable3.CodeCheck", {
onMatch: function (instance) {
instance.check_code(Java.use("java.lang.String").$new("123"));
return "stop";
},
onComplete: function () {},
});
};
}
Combine above scripts:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
Java.perform(function () {
Java.use("sg.vantagepoint.uncrackable3.MainActivity").showDialog.overload(
"java.lang.String"
).implementation = function (string) {
console.log("Bypassed showDialog, message: ", string);
return;
};
Java.use("sg.vantagepoint.uncrackable3.MainActivity").$init.implementation =
function () {
this.$init();
printSecretKey();
};
});
Interceptor.attach(Module.findExportByName("libc.so", "strstr"), {
onEnter: function (args) {
this.fridaDetected = 0;
if (args[1].readUtf8String().indexOf("frida") != -1) {
this.fridaDetected = 1;
}
},
onLeave: function (retVal) {
if (this.fridaDetected == 1) retVal.replace(0);
},
});
function printSecretKey() {
Interceptor.attach(Module.getBaseAddress("libfoo.so").add(0x0fa0), {
onEnter: function (args) {
console.log("Secret key before generate at address: " + args[0]);
this.answerAdress = args[0];
console.log(
hexdump(args[0], {
offset: 0,
length: 24,
header: true,
ansi: true,
})
);
},
onLeave: function (retVal) {
console.log("Secret key after generate: ");
console.log(
hexdump(this.answerAdress, {
offset: 0,
length: 24,
header: true,
ansi: true,
})
);
},
});
Java.use(
"sg.vantagepoint.uncrackable3.MainActivity"
).onResume.implementation = function () {
this.onResume();
Java.choose("sg.vantagepoint.uncrackable3.CodeCheck", {
onMatch: function (instance) {
instance.check_code(Java.use("java.lang.String").$new("123"));
return "stop";
},
onComplete: function () {},
});
};
}
Secret key as bytes array:
Find password using cyberchef: