android UnCrackable练习

OWASP的crackme练习里面有一些app安全的保护,破解者需要掌握一些逆向的知识才能获取正确的值。下面是android的两个题目,能帮助掌握基本的jadx逆向java代码、frida和ida逆向so的使用。

样本地址:
Github:https://github.com/OWASP/owasp-mstg/tree/master/Crackmes
百度网盘地址:
链接: https://pan.baidu.com/s/1YCiUU2Xy2xBSUQNxric8mQ 密码: 81kn

我用到的环境和工具

  • pixel xl arm64-v8a
  • python 3.8.0
  • frida 12.8.0
  • java 11.0.8
  • jadx 1.1.0
  • IDA 7.0
  • Ghidra 9.1.2

Android Level 1

UnCrackable1下载

考察的知识点:
java逆向、root检测绕过
工具:
jadxfrida

安装应用

1
2
3
owasp-mstg/Crackmes/Android/Level_01(master*) » adb install UnCrackable-Level1.apk 
Performing Streamed Install
Success

打开应用会提示root

要绕root
用jadx打开apk 搜索root文本

发现c.a\c.b\c.c方法是判断root然后进入到方法里查看(选中方法右键跳到声明)

1
2
3
4
5
6
7
8
9
10
11
12
···
public void onCreate(Bundle bundle) {
if (c.a() || c.b() || c.c()) {
a("Root detected!");
}
if (b.a(getApplicationContext())) {
a("App is debuggable!");
}
super.onCreate(bundle);
setContentView(R.layout.activity_main);
}
···

跳到方法发现这个类是检测root的类,使用frida进行hook掉检测类

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
package sg.vantagepoint.a;

import android.os.Build;
import java.io.File;

public class c {
public static boolean a() {
for (String file : System.getenv("PATH").split(":")) {
if (new File(file, "su").exists()) {
return true;
}
}
return false;
}

public static boolean b() {
String str = Build.TAGS;
return str != null && str.contains("test-keys");
}

public static boolean c() {
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;
}
}

首先通过逆向分析发现sg.vantagepoint.a.c的类是检测root的类
c类下的a、b、c方法是检测的方法hook方法让他返回值为false
frida代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Java.perform(function () {
send("hook start");
var c=Java.use("sg.vantagepoint.a.c");
//返回值改成false
c.a.overload().implementation = function(){
return false;
}
c.b.overload().implementation = function(){
return false;
}
c.c.overload().implementation = function(){
return false;
}
send("hook end");
});

执行frida代码frida -U -f owasp.mstg.uncrackable1 --no-pause -l uncrackable1.js
启动后已经hook成功了,没有弹框了,然后是随便输入个值在点击VERIFY,提示That’s not it.Try again.

然后我们在jadx反编译后的代码里搜索下Try again

发现跳转verify方法,用于验证的是a.a(obj)跳转到a方法

1可以继续用frida hook该函数进行返回值输出

1
2
3
4
5
6
7
8
9
10
11
public static boolean a(String str) {
byte[] bArr;
byte[] bArr2 = new byte[0];
try {
bArr = sg.vantagepoint.a.a.a(b("8d127684cbc37c17616d806cf50473cc"), Base64.decode("5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=", 0));
} catch (Exception e) {
Log.d("CodeCheck", "AES error:" + e.getMessage());
bArr = bArr2;
}
return str.equals(new String(bArr));
}

frida代码:

1
2
3
4
5
6
7
8
var a =Java.use("sg.vantagepoint.a.a");
a.a.overload('[B', '[B').implementation=function(arg1,arg2){
//执行函数
var ret = this.a(arg1,arg2);
//输出返回值
console.log(jhexdump(ret));
return ret;
}

uncrackable1.js
frida完整代码:

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
// owasp.mstg.uncrackable1 
// hookroot检测
function hookrootuncrackable1(){
Java.perform(function () {
send("hook start");
var c=Java.use("sg.vantagepoint.a.c");
//返回值改成false
c.a.overload().implementation = function(){
return false;
}
var a =Java.use("sg.vantagepoint.a.a");
/**
*重载报错 根据报错把overload添加上就可以了
* Error: a(): argument count of 0 does not match any of:
.overload('[B', '[B')
at throwOverloadError (frida/node_modules/frida-java-bridge/lib/class-factory.js:1020)
at frida/node_modules/frida-java-bridge/lib/class-factory.js:686
at /uncrackable1.js:13
at frida/node_modules/frida-java-bridge/lib/vm.js:11
at E (frida/node_modules/frida-java-bridge/index.js:346)
at frida/node_modules/frida-java-bridge/index.js:332
at input:1
*/
a.a.overload('[B', '[B').implementation=function(arg1,arg2){
//执行函数
var ret = this.a(arg1,arg2);
console.log(jhexdump(ret));
// console.log(byte2string(ret));
/***
* retval = this.a(arg1, arg2);
password = ''
for(i = 0; i < retval.length; i++) {
password += String.fromCharCode(retval[i]);
}
console.log("[*] Decrypted: " + password);
*/
return ret;
}


send("hook end");
});
}
function jhexdump(array,off,len) {
var ptr = Memory.alloc(array.length);
for(var i = 0; i < array.length; ++i)
Memory.writeS8(ptr.add(i), array[i]);
//console.log(hexdump(ptr, { offset: off, length: len, header: false, ansi: false }));
console.log(hexdump(ptr, { offset: 0, length: array.length, header: false, ansi: false }));
}

function main(){
hookrootuncrackable1();
}
setImmediate(main)
// owasp.mstg.uncrackable1
//frida -U -f owasp.mstg.uncrackable1 --no-pause -l uncrackable1.js

执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
~ » frida -U -f  owasp.mstg.uncrackable1 --no-pause -l uncrackable1.js                   
____
/ _ | Frida 12.8.0 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://www.frida.re/docs/home/
Spawned `owasp.mstg.uncrackable1`. Resuming main thread!
[Google Pixel XL::owasp.mstg.uncrackable1]-> message: {'type': 'send', 'payload': 'hook start'} data: None
message: {'type': 'send', 'payload': 'hook end'} data: None
7062ac63b0 49 20 77 61 6e 74 20 74 6f 20 62 65 6c 69 65 76 I want to believ
7062ac63c0 65 e

I want to believe 是最后的值

2.分析代码使用aes算法进行输出

1
2
 » echo 5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc= | openssl enc -aes-128-ecb -base64 -d -nopad -K 8d127684cbc37c17616d806cf50473cc
I want to believe%

解题参考链接:
https://www.codemetrix.io/hacking-android-apps-with-frida-2/
翻译【技术分享】利用FRIDA攻击Android应用程序(二)

Android Level 2

UnCrackable2下载
考察的知识点:
java逆向、root检测绕过、so逆向
工具:jadxfrida、IDA、Ghidra

安装应用:adb install UnCrackable-Level2.apk
发现仍然有root检测

用jadx打开UnCrackable-Level2.apk分析下代码吧~ jadx-gui UnCrackable-Level2.apk
依旧搜索Root detected!
发现跟第一道题是一样的,就是类名换掉了,用frida在hook下,就可以解决了

frida代码:

1
2
3
4
5
6
7
8
9
10
11
12
Java.perform(function(){
var b=Java.use("sg.vantagepoint.a.b");
b.a.overload().implementation = function(){
return false;
}
b.b.overload().implementation = function(){
return false;
}
b.c.overload().implementation = function(){
return false;
}
});

正常启动后再搜索Try again

1
2
3
4
5
6
7
8
9
10
verify()
···
if (this.m.a(obj)) {
create.setTitle("Success!");
str = "This is the correct secret.";
} else {
create.setTitle("Nope...");
str = "That's not it. Try again.";
}
···

1
2
3
4
5
private native boolean bar(byte[] bArr);

public boolean a(String str) {
return bar(str.getBytes());
}

最后分析到a方法最后到了bar方法是native函数里,是写在so里的,需要用IDA进行分析
解压UnCrackable-Level2.apk
到Level_02/UnCrackable-Level2/lib/armeabi-v7a文件夹中有个libfoo.so文件用IDA打开进行分析
搜索bar函数


按F5查看伪c代码

发现有个Thanks for all the fish字符串尝试输入,发现已经Success了

除了IDA还可以用Ghidra打开尝试
搜索bar函数 打开


分析代码发现有几个值进行对比
然后拼接后转字符串查看 小端序所以是倒着的
6873696620656874206c6c6120726f6620736b6e616854
使用十六进制转换工具进行查看
十六进制转换工具:https://zixuephp.net/tool-str-hex.html
选择16进制转字符串就可以了
工具:https://gchq.github.io/CyberChef

得到的结果hsif eht lla rof sknahT
最后的结果Thanks for all the fish

解题参考链接:
https://tereresecurity.wordpress.com/2021/03/23/write-up-uncrackable-level-2/

https://enovella.github.io/android/reverse/2017/05/20/android-owasp-crackmes-level-2.html

https://www.codemetrix.io/hacking-android-apps-with-frida-3/
翻译【技术分享】利用FRIDA攻击Android应用程序(三)

Android Level 3

解题参考链接:
https://enovella.github.io/android/reverse/2017/05/20/android-owasp-crackmes-level-3.html

http://sh3llc0d3r.com/owasp-uncrackable-android-level3/
翻译【技术分享】利用FRIDA攻击Android应用程序(四)

Android Level 4

https://www.romainthomas.fr/post/20-09-r2con-obfuscated-whitebox-part1/
https://www.romainthomas.fr/post/20-09-r2con-obfuscated-whitebox-part2/

[原创]UnCrackable App 三部曲学习记录分享
OWASP Android Uncrackable1~3练习