本文最后更新于:2021年7月22日 下午
第一个Android程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String username_hash = md5(edt_username.getText().toString()); String password = edt_password.getText().toString(); if(!edt_username.getText().toString().isEmpty() && !password.isEmpty() && password.equals(username_hash)){ Toast.makeText(MainActivity.this, "登陆成功!", Toast.LENGTH_SHORT).show(); }else { Toast.makeText(MainActivity.this, "登陆失败!", Toast.LENGTH_SHORT).show(); } } }); }
|
编译并且在模拟器中运行,
接下来我们破解这个程序,使用Android_killer打开程序,
在如图中所示的文件中,找到了判断登录是否成功的代码点,发现cond_0是登录失败,所以我们通过修改smali代码使得无论输入什么都显示密码正确。
将eqz改为nez,进行重编译,并且安装apk。会发现账号密码随便输入,显示登陆成功。
Dalvik可执行格式与字节码规范
Dalvik虚拟机的特点:
Dalvik虚拟机与java虚拟机的区别:
编译java源文件:
使用d8命令生成dex文件。dx已经被弃用,使用d8代替。
d8 --output dex Hello.class
使用dexdump查看dalvik字节码:
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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| s1lenc3@chenmodeMacBook-Pro:base/dex $ dexdump -d classes.dex Processing 'classes.dex'... Opened 'classes.dex', DEX version '035' Class Class descriptor : 'LHello;' Access flags : 0x0001 (PUBLIC) Superclass : 'Ljava/lang/Object;' Interfaces - Static fields - Instance fields - Direct methods - name : '<init>' type : '()V' access : 0x10001 (PUBLIC CONSTRUCTOR) code - registers : 1 ins : 1 outs : 1 insns size : 4 16-bit code units 00016c: |[00016c] Hello.<init>:()V 00017c: 7010 0400 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0004 000182: 0e00 |0003: return-void catches : (none) positions : 0x0000 line=1 locals : 0x0000 - 0x0004 reg=0 this LHello;
name : 'main' type : '([Ljava/lang/String;)V' access : 0x0009 (PUBLIC STATIC) code - registers : 4 ins : 1 outs : 3 insns size : 17 16-bit code units 000184: |[000184] Hello.main:([Ljava/lang/String;)V 000194: 2203 0100 |0000: new-instance v3, LHello; // type@0001 000198: 7010 0000 0300 |0002: invoke-direct {v3}, LHello;.<init>:()V // method@0000 00019e: 6200 0000 |0005: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000 0001a2: 1251 |0007: const/4 v1, 0001a4: 1232 |0008: const/4 v2, 0001a6: 6e30 0100 1302 |0009: invoke-virtual {v3, v1, v2}, LHello;.foo:(II)I // method@0001 0001ac: 0a03 |000c: move-result v3 0001ae: 6e20 0300 3000 |000d: invoke-virtual {v0, v3}, Ljava/io/PrintStream;.println:(I)V // method@0003 0001b4: 0e00 |0010: return-void catches : (none) positions : 0x0000 line=7 0x0005 line=8 0x0010 line=9 locals : 0x0000 - 0x0011 reg=3 (null) [Ljava/lang/String;
Virtual methods - name : 'foo' type : '(II)I' access : 0x0001 (PUBLIC) code - registers : 4 ins : 3 outs : 0 insns size : 6 16-bit code units 000150: |[000150] Hello.foo:(II)I 000160: 9000 0203 |0000: add-int v0, v2, v3 000164: b132 |0002: sub-int/2addr v2, v3 000166: 9200 0002 |0003: mul-int v0, v0, v2 00016a: 0f00 |0005: return v0 catches : (none) positions : 0x0000 line=3 locals : 0x0000 - 0x0006 reg=1 this LHello; 0x0000 - 0x0006 reg=2 (null) I 0x0000 - 0x0006 reg=3 (null) I
source_file_idx : 12 (hello.java)
|
虚拟机的执行流程
Android系统由Linux内核、函数库、Android运行时、应用程序框架及应用程序组成。Dalvik虚拟机属于Android运行时环境。
1 2
| Android系统启动并加载内核后,立即执行init进程,完成初始化后再读区init.rc文件并启动重要外部程序Zygote。 Zygote是Android系统中所有进程的孵化器进程。
|
1 2 3 4
| 即时编译(JIT)又称动态编译,一种在运行时将字节码翻译为机器码的技术。 有两种编译方式: method方式:以函数或方法为单位进行编译。 trace方式:以trace为单位进行编译,优先编译“热路径”。
|
Dalvik语言基础
1 2 3 4 5
| dexdump反汇编工具寄存器采用“v”命名法,baksmali反汇编工具寄存器采用“p”和“v”命名法。如果是共同使用的话,v寄存器一般作为局部变量寄存器。 Dalvik寄存器都是32位的,对于64位类型,可以使用两个相邻的寄存器来表示。 寄存器范围是v0-v65535 Dalvik虚拟机为每个进程维护一个调用栈,调用栈的作用之一就是“虚拟”寄存器。 Dalvik字节码只有基本类型和引用类型,对象和数组属于引用类型。字节码描述符如下表。
|
1 2 3 4 5 6 7 8 9 10
| Ljava/lang/String;相当于java.lang.String [[I相当于int[][] [Ljava/lang/String表示jva中的字符串数组 方法: Lpackage/name/ObjectName;->MethodName(III)Z method(I[[IILjava/lang/String;[Ljava/lang/Object;)Ljava/lang/String; java形式为:String method(int, int[][],int, String, Object[]) 字段: Lpackage/name/ObjectName;->FieldName:Ljava/lang/String; 类型;->字段名:字段类型
|
Dalvik指令集
获取字段时,普通字段指令前缀为i,静态字段的指令前缀为s。
1 2 3
| 使用adb运行dex文件。 java -jar smali.jar ass ../base/HelloWorld.smali adb shell dalvikvm -cp /data/local/tmp/out.dex HelloWorld
|
常见Android文件格式
1 2
| jar包可以使用jd-gui进行反编译 aar包包含资源文件
|
apk是一个压缩文件,后缀改为zip可以解压出来。
AndroidMainfest.xml
一些配置清单,名字、版本、权限、引用的库文件等等。找一个apk看看这个文件,就什么都懂了。
META-INF目录
存放签名信息,android程序生成release版本的apk需要签名,加密信息应该就保存的这里了。
res目录
存放各种资源文件。最终被映射到Android工程中的R文件中,对各种资源会生成对应ID。逆向android主要是找strings.xml,便于定位关键字符串。一般在values文件夹中,layout文件夹是存放的应用界面布局。
lib目录
存放依赖的native库文件,.so文件是C/C++写的。可以根据特定目录确定是什么架构的。.so文件在逆向中应用挺多。
assets目录
这个文件夹中的文件会原封不动的打包到apk中。
resources.arsc
编译后的二进制资源文件的索引。应该就是程序运行,然后从这个文件根据对应ID找到对应的res资源文件。
classes.dex文件
java字节码文件,这个文件很重要,可以用jeb等转换成java源码。说到这就不得不提一下几个文件的转换了。
baksmali.jar和smali.jar有时候java自带的老出问题,我是从网上下载的。https://bitbucket.org/JesusFreke/smali/downloads/
当然敲命令有点费事,可以去吾爱的爱盘下载android逆向工具包。
apk打包流程
将编译模块和依赖项打包成dex文件、将apk packager打包成apk和签名。
apk安装流程
1 2 3
| 请求安装apk时会启动packageinstaller.apk,并且接收通过Intent传递过来的APK信息。 进入PackageInstallActivity中的onCreate方法,主要PackageUtil中的getPackageInfo方法和initiateInstall方法最为重要。 getPackageInfo->initiateInstall->startInstallConfirm->onClick->instalPackage->installPackageWithVerification->processPendingInstall->installlPackageLI->scanPackageLI->mInstaller.install()->
|
Activity 的主要方法
onClick
CrackMe类型的题目,重点关注这个方法。
init和onCreate
Activity创建时的初始化函数
BuildConfig和R
编译时自动添加的资源类,不常用
Native层与so
Android原生代码,Linux中的执行代码,C/C++写的,底层是ARM汇编,很重要。
JNI_ONLOAD
这个函数是Dalvik虚拟机加载库时的初始化函数。
动态调试Smali代码演示
使用adb install xxxxxx.apk
安装apk。
拖入apk到jeb中,Ctrl+B下断点。
使用adb shell am start -D -n com.droider.crackme0201/.MainActivity 让程序处于等待调试状态
然后点 调试器->开始
附加。断点设在点击事件后。
然后正常执行app,就会在指定断点处断下来。
可以查看局部变量。
在VM 终端中可以执行指令:
还有很多用法,我也不会用。
判断跳转:
用APKIDE修改 if-nez 为 if-eqz。
APK破解成功。