计算机系统应用教程网站

网站首页 > 技术文章 正文

用Frida来hack 安卓应用III(一)

btikc 2024-09-02 16:41:57 技术文章 12 ℃ 0 评论

原文首发看雪论坛

在我发完第二篇关于Frida的博文之后,

@muellerberndt

立即就决定公布另一个OWASP Android crackme。我想试试看我是否依然可以用Frida来解决这个问题。如果你也想跟着我一起,那么你需要:

OWASP Uncrackable Level2 APK

Android SDK 和 模拟器 (我用的是Android 7.1 x64镜像)

Frida 的安装包(加上 frid服务器 )

字节码查看器

radare2 (或者你自己选择用其他的反编译器)

apktool

如果你需要Frida的安装教程,请查看Frida的官方文档. 至于Frida的使用,请查看这个教程的 第一部分 。现在我就当你已经准备好所有东西,在继续往前走之前,你还需要稍微熟悉Frida的使用,还有,确保Frida可以连接你的设备/模拟器。(比如,通过使用frida-ps -U 命令)。

说在前面的话:这不仅仅是一篇解决那个crackme的攻略。相反的,我打算向你展示几种不同的方法来解决这个具体的问题。如果你只是想看解决方法,可以直接翻到本教程的最后面,那里有Frida脚本。

注意:如果你在使用Frida时收到下面的错误

1

Erro: access violation accessing 0xebad8082

或者其他类似的错误,它可能会清除模拟器上所有的用户数据,所以你要重启然后重新安装apk。

做好得多试好几次的思想准备,程序会崩溃,模拟器会需要重启,一切都可能变得很乱七八糟的,但最终,我们会成功。

第一次运行

和在UnCrackable 1做的一样,我们第一步是运行那个app。也和它UnCrackable 1 一样,当我们在模拟器上运行这个app时,它会被检测到说是在已经root过的设备上运行的。

移除Uncrackable root

我们会像在UnCrackable 1那里一样,钩住 OnClickListener函数。但在这之前,我们得看看我们是否已经连上Frida以便我们修改。

michael@sixtyseven:~

/Development

$ frida -U sg.vantagepoint.uncrackable2

____

/ _ | Frida 9.1.27 - A world-class dynamic instrumentation framework

| (_| |

> _ | Commands:

/_/

|_| help -> Displays the help system

. . . . object? -> Display information about

'object'

. . . .

exit

/quit

-> Exit

. . . .

. . . . More info at http:

//www

.frida.re

/docs/home/

Failed to attach: ambiguous name; it matches: sg.vantagepoint.uncrackable2 (pid: 5184), sg.vantagepoint.uncrackable2 (pid: 5201)

这是啥?有两个同名进程。我们可以

frida-ps -U

来验证:

5184 s.vantagepoin.uncrackable2

5201 s.vantagepoin.uncrackable2

好奇怪,让我们把Frida注入到父进程:

michael@sixtyseven:~

/Development

$ frida -U 5184

____

/ _ | Frida9..2 - A world class dynamic instrumentation framewor

| (_| |

> _ | Commands:

/_

|_| help -> Displays the help system

. . . . object? -> Display information about

'object'

. . . .

exit

/quit

-> Exit

. . . .

. . . . More info at http:

//

www.frida.r

/docs/homeFailed

to attach: unable to access process with pid 518due to system restrictions;

try'

sudo

sysctl kernel.yama.ptrace_scope=`,or run Frida as root

没用。因为我们是在以root运行Frida时得到这样的结果的,所以这个方案没什么效果。这是怎么了?我们得好好研究这个app。解压apk,用字节码查看器(例如CFR-Decompiler)反编译classes.dex

package

sg.vantagepoint.uncrackable2;

import

android.app.AlertDialog;

import

android.content.Context;

import

android.content.DialogInterface;

import

android.os.AsyncTask;

import

android.os.Bundle;

import

android.support.v7.app.c;

import

android.text.Editable;

import

android.view.View;

import

android.widget.EditText;

import

sg.vantagepoint.a.a;

import

sg.vantagepoint.a.b;

import

sg.vantagepoint.uncrackable2.CodeCheck;

import

sg.vantagepoint.uncrackable2.MainActivity;

public

class

MainActivity

extends

c {

private

CodeCheck m;

static

{

System.loadLibrary(

"foo"

);

//[1]

}

private

void

a(String string) {

AlertDialog alertDialog =

new

AlertDialog.Builder((Context)

this

).create();

alertDialog.setTitle((CharSequence)string);

alertDialog.setMessage((CharSequence)

"This in unacceptable. The app is now going to exit."

);

alertDialog.setButton(-

3

, (CharSequence)

"OK"

, (DialogInterface.OnClickListener)

new

/* Unavailable Anonymous Inner Class!! */);

alertDialog.setCancelable(false);

alertDialog.show();

}

static /* synthetic */ void a(MainActivity mainActivity, String string) {

mainActivity.a(string);

}

private native void init(); //[2]

protected void onCreate(Bundle bundle) {

this.init(); //[3]

if (b.a() || b.b() || b.c()) {

this.a("Root detected!");

}

if (a.a((Context)this.getApplicationContext())) {

this.a("App is debuggable!");

}

new /* Unavailable Anonymous Inner Class!! */.execute((Object[])new Void[]{null, null, null});

this.m = new CodeCheck();

super.onCreate(bundle);

this.setContentView(2130968603);

}

public void verify(View view) {

String string = ((EditText)this.findViewById(2131427422)).getText().toString();

AlertDialog alertDialog = new AlertDialog.Builder((Context)this).create();

if (this.m.a(string)) {

alertDialog.setTitle((CharSequence)"Success!");

alertDialog.setMessage((CharSequence)"This is the correct secret.");

} else {

alertDialog.setTitle((CharSequence)"Nope...");

alertDialog.setMessage((CharSequence)"That's not it. Try again.");

}

alertDialog.setButton(-3, (CharSequence)"OK", (DialogInterface.OnClickListener)new /* Unavailable Anonymous Inner Class!! */

);

alertDialog.show();

}

}

我们注意到static块里调用了

System.load

来加载foo库(参看【1】)。这个app还在OnCreate函数里的第一行就调用了this.init(),而这个函数被声明为native函数(参看【2】),所以它应该是foo的一部分。

让我们来看看这个foo库。在radare2中打开这个库(你会在lib文件夹里看到几个不同的架构,我这里用的是lib/x86_64 ),分析它并列出它的输出。

michael@sixtyseven:~

/Development/UnCrackable2/lib/x86_64

$ r2 libfoo.so

-- Don

't look at the code. Don'

t

look

.

[0x000007a0]> aaa

[x] Analyze all flags starting with sym. and entry0 (aa)

[x] Analyze len bytes of instructions

for

references (aar)

[x] Analyze

function

calls (aac)

[ ] [*] Use -AA or aaaa to perform additional experimental analysis.

[x] Constructing a

function

name

for

fcn.* and sym.func.* functions (aan))

[0x000007a0]> iE

[Exports]

vaddr=0x00001060 paddr=0x00001060 ord=004 fwd=NONE sz=183 bind=GLOBAL

type

=FUNC name=Java_sg_vantagepoint_uncrackable2_CodeCheck_bar

vaddr=0x00001050 paddr=0x00001050 ord=006 fwd=NONE sz=15 bind=GLOBAL

type

=FUNC name=Java_sg_vantagepoint_uncrackable2_MainActivity_init

vaddr=0x00004008 paddr=0x00003008 ord=014 fwd=NONE sz=0 bind=GLOBAL

type

=NOTYPE name=__bss_start

vaddr=0x00004008 paddr=0x00003008 ord=015 fwd=NONE sz=0 bind=GLOBAL

type

=NOTYPE name=__bss_start

vaddr=0x0000400d paddr=0x0000400d ord=016 fwd=NONE sz=0 bind=GLOBAL

type

=NOTYPE name=_end

5 exports

[0x000007a0]>

我们看到这个库导出了两个很有意思的函数:

Java_sg_vantagepoint_uncrackable2_MainActivity_init

Java_sg_vantagepoint_uncrackable2_CodeCheck_bar

(关于这些函数的命名,请查看

Java nativ interface JNI

),我们要看的是:

Java_sg_vantagepoint_uncrackable2_MainActivity_init

[0x000007a0]> s 0x00001050

[0x00001050]> V

这是一个蛮短的函数:

[0x00001050 29% 848 libfoo.so]> pd $r @ sym.Java_sg_vantagepoint_uncrackable2_MainActivity_init

/ (fcn) sym.Java_sg_vantagepoint_uncrackable2_MainActivity_init 15

| sym.Java_sg_vantagepoint_uncrackable2_MainActivity_init ();

| 0x00001050 50 push rax

| 0x00001051 e8caf7ffff call sub.fork_820 ;[1]

| 0x00001056 c605af2f0000. mov byte [0x0000400c], 1 ; [0x400c:1]=58 ;

": (GNU) 4.9.x 20150123 (prerelease)"

| 0x0000105d 58 pop rax

\ 0x0000105e c3 ret

0x0000105f 90 nop

它调用了另一个函数

sub.fork_820

,这个要做的事就比较多了:

[0x00000820 14% 265 libfoo.so]> pd $r @ sub.fork_820

/ (fcn) sub.fork_820 242

| sub.fork_820 ();

| ; var int local_8h @ rsp+0x8

| ; var int local_10h @ rsp+0x10

| ; CALL XREF from 0x00001051 (sym.Java_sg_vantagepoint_uncrackable2_MainActivity_init)

| 0x00000820 4156 push r14

| 0x00000822 53 push rbx

| 0x00000823 4883ec18 sub rsp, 0x18

| 0x00000827 64488b042528. mov rax, qword fs:[0x28] ; [0x28:8]=0x3180 ;

'('

| 0x00000830 4889442410 mov qword [local_10h], rax

| 0x00000835 e806ffffff call sym.imp.fork ;[1]

| 0x0000083a 8905c8370000 mov dword loc.__bss_start, eax ; [0x4008:4]=0x43434700 ; loc.__bss_start

| 0x00000840 85c0

test

eax, eax

| ,=< 0x00000842 741a je 0x85e ;[2]

| | 0x00000844 488d15a5ffff. lea rdx, 0x000007f0 ; 0x7f0

| | 0x0000084b 488d7c2408 lea rdi, [local_8h] ; 0x8

| | 0x00000850 31f6 xor esi, esi

| | 0x00000852 31c9 xor ecx, ecx

| | 0x00000854 e8f7feffff call sym.imp.pthread_create ;[3]; ssize_t

read

(int fildes, void *buf, size_t nbyte)

| ,==< 0x00000859 e990000000 jmp 0x8ee ;[4]

| || ; JMP XREF from 0x00000842 (sub.fork_820)

| |`-> 0x0000085e e8fdfeffff call sym.imp.getppid ;[5]

| | 0x00000863 89c3 mov ebx, eax

| | 0x00000865 bf10000000 mov edi, 0x10

| | 0x0000086a 31d2 xor edx, edx

| | 0x0000086c 31c9 xor ecx, ecx

| | 0x0000086e 31c0 xor eax, eax

| | 0x00000870 89de mov esi, ebx

| | 0x00000872 e8f9feffff call sym.imp.ptrace ;[6]

| | 0x00000877 4885c0

test

rax, rax

| |,=< 0x0000087a 7572 jne 0x8ee ;[4]

| || 0x0000087c 4c8d742408 lea r14, [local_8h] ; 0x8

| || 0x00000881 31d2 xor edx, edx

| || 0x00000883 89df mov edi, ebx

| || 0x00000885 4c89f6 mov rsi, r14

| || 0x00000888 e883feffff call sym.imp.waitpid ;[7]

我们看到调用了fork

pthread_create

,

getppid

,

ptrace

waitpid

.

无需花太多时间来反编译我们就可以猜到,当调试器用

ptrace

的时候,主进程会

fork

一个子进程来关联它。这是很简单的反调试技术,你可以从

这里

了解到更多细节。

因为Fridaptrace来初始化注入,所以这就解释了为什么我们不能连接到父进程:因为已经连接了一个进程来作为调试器,再来一个进程关联调试将被阻塞。

反反调试方案1:Frida

Frida救援。相比于注入Frida到一个正在运行的进程,我们可以让它自己spawn出一个进程来给我们。用-f选项,我们告诉Frida注入Zygote然后启动该应用程序。在我们启动Frida后关掉这个应用程序,看看发生什么:

frida -U -f sg.vantagepoint.uncrackable2

我们得到:

michael@sixtyseven:~

/Development/UnCrackable2/lib/x86_64

$ frida -U -f sg.vantagepoint.uncrackable2 --no-pause

____

/ _ | Frida 9.1.27 - A world-class dynamic instrumentation framework

| (_| |

> _ | Commands:

/_/

|_| help -> Displays the help system

. . . . object? -> Display information about

'object'

. . . .

exit

/quit

-> Exit

. . . .

. . . . More info at http:

//www

.frida.re

/docs/home/

Spawned `sg.vantagepoint.uncrackable2`. Resuming main thread!

[USB::Android Emulator 5554::[

'sg.vantagepoint.uncrackable2'

]]->

呼啦啦!Frida注入到Zygote了,spawn 我们的进程并等待输入。(我承认,有很多教程都告诉你们要在Frida加-f选项,但你也被警告过……)

我们现在已经做好准备了。但是在继续往下走之前,我们再来看另一种针对这个crackme的反反调试方案。

反反调试方案2:修改

除去让Frida来spawn,我们也可以通过修改这个app来解决这个问题。这意味着,我们要反编译这个app,重新打包和签名修改过的apk。然而,在这个crackme中,这样做会在后面给我们带来麻烦。就算这样,我还是决定告诉你怎样做,后面的问题后面解决。

我们可以用apktool来修改:

michael@sixtyseven:~

/Disassembly

$

/opt/apktool/apktool

.sh -r d UnCrackable-Level2.apk

I: Using Apktool 2.2.0 on UnCrackable-Level2.apk

I: Copying raw resources...

I: Baksmaling classes.dex...

I: Copying assets and libs...

I: Copying unknown files...

I: Copying original files...

(我用-r来跳过了提取资源,因为这会在重新编译apk时出错。而且,在这里我们也不需要这些资源。)

来看看在

smali/sg/vantagepoint/uncrackable2/MainActivity.smali

的smali代码。你可以看到调用inti的操作是在82行附近,你可以把它注释掉。

# virtual methods

.method protected onCreate(Landroid

/os/Bundle

;)V

.locals 4

const

/4

v3, 0x0

# invoke-direct {p0}, Lsg/vantagepoint/uncrackable2/MainActivity;->init()V

invoke-static {}, Lsg

/vantagepoint/a/b

;->a()Z

重新打包(忽略叼那个fatal error……):

michael@sixtyseven:~

/Disassembly/UnCrackable-Level2

$

/opt/apktool/apktool

.sh b

I: Using Apktool 2.2.0

I: Checking whether sources has changed...

I: Smaling smali folder into classes.dex...

[Fatal Error] AndroidManifest.xml:1:1: Content ist nicht zul?ssig

in

Prolog.

I: Checking whether resources has changed...

I: Copying raw resources...

I: Copying libs... (

/lib

)

I: Building apk

file

...

I: Copying unknown files

/dir

...

align(优化):

michael@sixtyseven:~

/Disassembly/UnCrackable-Level2

$ zipalign -

v

4 dist

/UnCrackable-Level2

.apk UnCrackable2.recompiled.aligned.apk

Verifying alignment of UnCrackable2.recompiled.aligned.apk (4)...

49 AndroidManifest.xml (OK - compressed)

914 classes.dex (OK - compressed)

269899 lib

/arm64-v8a/libfoo

.so (OK - compressed)

273297 lib

/armeabi-v7a/libfoo

.so (OK - compressed)

279346 lib

/armeabi/libfoo

.so (OK - compressed)

签名(注意:在这一步你要有一个key和keystore,你可以在 OWASP 手机安全测试指南 看到更多介绍。):

michael@sixtyseven:~

/Disassembly/UnCrackable-Level2

$ jarsigner -verbose -keystore ~/.android

/debug

.keystore UnCrackable2.recompiled.aligned.apk signkey

Enter Passphrase

for

keystore:

adding: META-INF

/MANIFEST

.MF

adding: META-INF

/SIGNKEY

.SF

adding: META-INF

/SIGNKEY

.RSA

signing: AndroidManifest.xml

signing: classes.dex

signing: lib

/arm64-v8a/libfoo

.so

signing: lib

/armeabi-v7a/libfoo

.so

signing: lib

/armeabi/libfoo

.so

signing: lib

/mips/libfoo

.so

[...]

卸载原来的apk,安装这个修改过的apk:

1

2

adb uninstall sg.vantagepoint.uncrackable2

adb

install

UnCrackable2.recompiled.aligned.apk

启动这个app,运行frida-ps我们会看到只有一个进程了:

29996 sg.vantagepoint.uncrackable2

然后连接Frida也没有问题:

michael@sixtyseven:~

/Disassembly/UnCrackable-Level2

$ frida -U sg.vantagepoint.uncrackable2

____

/ _ | Frida 9.1.27 - A world-class dynamic instrumentation framework

| (_| |

> _ | Commands:

/_/

|_| help -> Displays the help system

. . . . object? -> Display information about

'object'

. . . .

exit

/quit

-> Exit

. . . .

. . . . More info at http:

//www

.frida.re

/docs/home/

[USB::Android Emulator 5554::sg.vantagepoint.uncrackable2]->

相比于只是在Frida里加-r选项是比较麻烦啦,但这也更普遍。

就像前面提过的,如果我们使用这个修改过的版本,那后面要提取那个Secret String就不会那么容易(尽管如此,我还是会告诉你怎样解决的,所以不要放弃哦)。但是现在我们要用的是原来的版本来进行后面的操作。确保你下面安装的是原来的版本。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表