本文最后更新于:2021年7月22日 下午
                
              
            
            
              第一个Android程序

| 12
 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字节码:
| 12
 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运行时环境。

| 12
 
 | Android系统启动并加载内核后,立即执行init进程,完成初始化后再读区init.rc文件并启动重要外部程序Zygote。Zygote是Android系统中所有进程的孵化器进程。
 
 | 

| 12
 3
 4
 
 | 即时编译(JIT)又称动态编译,一种在运行时将字节码翻译为机器码的技术。有两种编译方式:
 method方式:以函数或方法为单位进行编译。
 trace方式:以trace为单位进行编译,优先编译“热路径”。
 
 | 
Dalvik语言基础
| 12
 3
 4
 5
 
 | dexdump反汇编工具寄存器采用“v”命名法,baksmali反汇编工具寄存器采用“p”和“v”命名法。如果是共同使用的话,v寄存器一般作为局部变量寄存器。Dalvik寄存器都是32位的,对于64位类型,可以使用两个相邻的寄存器来表示。
 寄存器范围是v0-v65535
 Dalvik虚拟机为每个进程维护一个调用栈,调用栈的作用之一就是“虚拟”寄存器。
 Dalvik字节码只有基本类型和引用类型,对象和数组属于引用类型。字节码描述符如下表。
 
 | 

| 12
 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。
| 12
 3
 
 | 使用adb运行dex文件。java -jar smali.jar ass ../base/HelloWorld.smali
 adb shell dalvikvm -cp /data/local/tmp/out.dex HelloWorld
 
 | 
常见Android文件格式
| 12
 
 | 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安装流程
| 12
 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 终端中可以执行指令:


还有很多用法,我也不会用。
判断跳转:
](/2020/03/10/Android%E9%80%86%E5%90%91%E5%9F%BA%E7%A1%80/clipboard.png)
用APKIDE修改 if-nez 为 if-eqz。
APK破解成功。