多读书多实践,勤思考善领悟

Android安卓逆向入门教程

本文于2019天之前发表,文中内容可能已经过时。

一、初识 APK、Dalvik字节码以及Smali

1. apk是什么?

apk实质上是一个zip压缩包,将apk后缀修改为zip,解压之后可以看到其内部结构:

2. apk 的组成

assets: 资源目录1,assets 和 res 都是资源目录但有所区别:

res 目录下的资源文件在编译时会自动生成索引文件(R.java),在Java代码中勇R.xxx.yyy来引用;而asset 目录下的资源文件不需要生成索引,在Java 代码中需要用AssetManager来访问;

一般来说,除了音频和视频资源(需要放在raw或asset下),使用Java开发的Android工程使用到的资源文件都会放在res下;使用C++游戏引擎(或使用 Lua Unity3D等)的资源文件均需要放在 assets 下。

lib: so 库存放位置,一般由NDK编译得到,常见于使用游戏引擎或 JNI native调用的工程中

META-INF: 存放工程一些属性文件,例如 Manifest.MF

res: 资源目录2,

AndroidManifest.xml: Android工程的基础配置属性文件

classes.dex: Java代码编译得到的 Dalvik VM 能直接执行的文件

resources.arsc: 对res 目录下的资源的一个索引文件,保存了原工程中 strings.xml等文件内容

其他文件夹等

3. Dalvik字节码(学习破解的基础)

• Dalvik 是 google 专门为 Android 操作系统设计的一个虚拟机,经过深度优化。虽然 Android 上的程序是使用 java 来开发的,但是 Dalvik 和标准的 java 虚拟机 JVM 还是两回事。Dalvik VM 是基于寄存器的,而 JVM 是基于栈的;Dalvik有专属的文件执行格式 dex (dalvik executable),而 JVM 则执行的是 java 字节码。Dalvik VM 比 JVM 速度更快,占用空间更少。

• 通过 Dalvik 的字节码我们不能直接看到原来的逻辑代码,这时需要借助如 Apktool 或 dex2jar+jd-gui 工具来帮助查看。但是,我们最终修改 APK 需要操作的文件是 .smali 文件,而不是导出来的 Java 文件重新编译。

4. Smali(破解的重中之重)

• Smali,Baksmali 分别是指安卓系统里的 Java 虚拟机(Dalvik)所使用的一种 dex 格式文件的汇编器,反汇编器。其语法是一种宽松式的 Jasmin/dedexer 语法,而且它实现了 .dex 格式所有功能(注解,调试信息,线路信息等)

• 当我们对 APK 文件进行反编译后,便会生成此类文件。在Davlik字节码中,寄存器都是32位的,能够支持任何类型,64位类型(Long/Double)用2个寄存器表示;Dalvik字节码有两种类型:原始类型;引用类型(包括对象和数组)

• 原始类型:

B—byte

C—char

D—double

F—float

I—int

J—long

S—short

V—void

Z—boolean

[XXX—array

Lxxx/yyy—object

这里解析下最后两项,数组的表示方式是:在基本类型前加上前中括号“[”,例如 int 数组和 float 数组分别表示为:[I、[F;对象的表示则以 L 作为开头,格式是 LpackageName/objectName;(注意必须有个分号跟在最后),例如 String 对象在 smali 中为:Ljava/lang/String;,其中 java/lang 对应 java.lang 包,String 就是定义在该包中的一个对象。

或许有人问,既然类是用 LpackageName/objectName; 来表示,那类里面的内部类又如何在 smali 中引用呢?答案是:LpackageName/objectNamesubObjectName;。也就是在内部类前加”符号,关于“$”符号更多的规则将在后面谈到.

方法的定义

Func-Name (Para-Type1Para-Type2Para-Type3…)Return-Type

注意参数与参数之间没有任何分隔符,举例如下:

hello ()V

没错,这就是void hello()。

hello (III)Z

这个则是boolean hello(int, int, int)。

hello (Z[I[ILjava/lang/String;J)Ljava/lang/String;

看出来这是String hello (boolean, int[], int[], String, long) 了吗?

Smali基本语法

.field private isFlag:z  定义变量

.method  方法

.parameter  方法参数

.prologue  方法开始

.line 123  此方法位于第123行

invoke-super  调用父函数

const/high16 v0, 0x7fo3  把0x7fo3赋值给v0

invoke-direct  调用函数

return-void  函数返回void

.end method  函数结束

new-instance  创建实例

iput-object  对象赋值

iget-object  调用对象

invoke-static  调用静态函数

条件跳转分支

二、深入 Smali 文件

1. Smali中的包信息

.class public Lcom/aaaaa; (它是com.aaaaa这个package下的一个类)

.super Lcom/bbbbb; (继承自com.bbbbb这个类)

.source “ccccc.java” (一个由ccccc.java编译得到的smali文件)

2. Smali中的声明

一般来说,在Smali文件中声明如下:

3. 关于寄存器的知识补充

• 寄存器是什么意思呢?在 smali 里的所有操作都必须经过寄存器来进行:本地寄存器用 v 开头,数字结尾的符号来表示,如v0、v1、v2、…参数寄存器则使用 p 开头,数字结尾的符号来表示,如p0、p1、p2、…特别注意的是,p0 不一定是函数中的第一个参数,在非 static 函数中,p0 代指“this”,p1 表示函数的第一个参数,p2 代表函数中的第二个参数…而在 static 函数中 p0 才对应第一个参数(因为 Java 的 static 方法中没有 this 方法。

4. 寄存器简单实例分析

• 我们来分析一下上面的两句 smali 代码,首先它使用了 v0 本地寄存器,并把值 0x1 存到 v0 中,然后第二句用 iput-boolean 这个指令把 v0 中的值存放到 com.aaa.IsRegistered 这个成员变量中。

• 即相当于:this.IsRegistered= true;(上面说过,在非static函数中p0代表的是“this”,在这里就是 com.aaa 实例)。

5. Smali中的成员变量

• 成员变量格式是:.field public/private [static] [final] varName:。

• 对于不同的成员变量也有不同的指令。

• 一般来说,获取的指令有:iget、sget、iget-boolean、sget-boolean、iget-object、sget-object等。

• 操作的指令有:iput、sput、iput-boolean、sput-boolean、iput-object、sput-object等。

• 没有“-object”后缀的表示操作的成员变量对象是基本数据类型,带“-object”表示操作的成员变量是对象类型,特别地,boolean 类型则使用带“-boolean”的指令操作。

6. Smali成员变量指令简析

简析一

• sget-object就是用来获取变量值并保存到紧接着的参数的寄存器中,本例中,它获取ID这个String类型的成员变量并放到v0这个寄存器中。

• 注意:前面需要该变量所属的类的类型,后面需要加一个冒号和该成员变量的类型,中间是“->”表示所属关系。

简析二

• 可以看到iget-object指令比sget-object多了一个参数,就是该变量所在类的实例,在这里就是p0即“this”。

• 获取array的话我们用aget和aget-object,指令使用和上述一致

简析三(put指令的使用和get指令是统一的)

• 相当于:this.timer= null;

• 注意,这里因为是赋值object 所以是null,若是boolean的话,大家想应该相当于什么呢?

简析四

• 相当于:args.what = 18;(args 是 Message 的实例)

三、Smali函数分析

1. Smali中函数的调用

• smali中的函数和成员变量一样也分为两种类型,分别为direct和virtual之分。那么direct method和virtual method有什么区别呢?

• 简单来说,direct method 就是 private 函数,其余的 public 和 protected 函数都属于 virtual method。所以在调用函数时,有invoke-direct,invoke-virtual,另外还有invoke-static、invoke-super以及invoke-interface等几种不同的指令。当然其实还有invoke-XXX/range 指令的,这是参数多于4个的时候调用的指令,比较少见,了解下即可。

• (1).invoke-static:用于调用static函数,例如:

这里注意到 invoke-static 后面有一对大括号“{}”,其实是调用该方法的实例+参数列表,由于这个方法既不需参数也是static的,所以{}内为空,再看一个:

这个是调用 static void System.loadLibrary(String) 来加载 NDK 编译的 so 库用的方法,同样也是这里 v0 就是参数”NDKLIB”了。

• (2).invoke-super:调用父类方法用的指令,一般用于调用onCreate、onDestroy等方法。

• (3).invoke-direct:调用private函数:

​ 这里init()就是定义在TabActivity中的一个private函数

• (4).invoke-virtual:用于调用 protected 或 public 函数,同样注意修改smali时不要错用 invoke-direct 或 invoke-static:

​ 这里相信大家都已经很清楚了:

v0是bbb:Lcom/ccc

v1是传递给Messages方法的Ljava/lang/Object参数。

• (5).invoke-xxxxx/range:当方法的参数多于5个时(含5个),不能直接使用以上的指令,而是在后面加上“/range”,range表示范围,使用方法也有所不同:

需要传递v0到v5一共6个参数,这时候大括号内的参数采用省略形式,且需要连续。

2. Smali中函数返回结果操作

• 在Java代码中调用函数和返回函数结果可以用一条语句完成,而在Smali里则需要分开来完成,在使用上述指令后,如果调用的函数返回非void,那么还需要用到move-result(返回基本数据类型)和move-result-object(返回对象)指令:

v2保存的就是调用t方法返回的String字符串。

3. Smali中函数实体分析–if函数分析

.method private ifRegistered()Z

.locals 2 //在这个函数中本地寄存器的个数

.prologue

const/4 v0, 0x1 // v0赋值为1

.local v0, tempFlag:Z

if-eqz v0, :cond_0 // 判断v0是否等于0,等于0则跳到cond_0执行

const/4 v1, 0x1 // 符合条件分支

:goto_0 //标签

return v1 //返回v1的值

:cond_0 //标签

const/4 v1, 0x0 // cond_0分支

goto :goto_0 //跳到goto_0执行 即返回v1的值 这里可以改成return v1 也是一样的

.end method

四、实战

软件:计算管家 v3.4 目标:破解授权会员
目标软件验证方法:软件获取设备号号后调用支付宝进行付费,付费成功后会返回对应参数,点击授权按钮后即完成付费用户授权。
实战目标:点击授权按钮直接授权成功,小雨之前也做过3.3的分析,我的手段比较简单,适合新手

1. 脱壳

本软件v3.3使用的爱加密壳,v3.4使用的360壳,不过对于那些大鸟们来说,什么壳都没用。

2. 运行软件找关键点

我们首先来到付费页面,如下图:
img
开头中我已经讲过这个软件的验证流程,既然这样,我们直接点击授权按钮观察有什么关键点
img
软件提示我们并没有获取授权,那么这类文字通常会以文本形式写在String.xml,对应的布局文件或者以unicode形式写在smali中,对于游戏来说,这类可能是以图片形式放在图片文件,或者自定义格式的文件,甚至是通过网络获取的,我们了解之后,先尝试第一种,直接搜索
Androidkiller载入apk
img
嗯,直接搜索到了,减少了我们很多的工作量,那么,文本都有对应的id,我们再次搜索这个id,等等,我们好像发现了更重要的东西
img
我们看到了授权成功的文字,那么我们换思路,直接搜索授权成功的id
img
我们这里看到了这个神奇的数字,新手相信都不知道这个玩意是怎么来的,那么这里我稍微普及下,apk在编写时,开发工具都会对源代码自动编译,生成对应的标示符,通过这些标示符,来引用对应的资源,详细一点的可以阅读这篇文章
http://blog.sina.com.cn/s/blog_7b83134b01016043.html

3. 尝试破解

我们接着搜索0x7f080100,发现两处可疑处
img
分别点进去看看,顺便我们也记下授权失败的标示符,0x7f080102,这里我们可以方便我们判断
img
这里我们看到的就是授权失败时弹出的对话框,我们找到它的标签是:cond_1,接着我们向上翻,看到哪些跳转会跳到:cond_1,我们来到方法的开始
img
这是个接收信息的方法,通常是由handler实现的,我们大体可以知道,在付费成功后,会想这个函数传递一个message,这个方法就是判断传入的message的值,通过这个值来判断是否付费成功,这里我们看到一个cond_3,我们找到:cond_3,
img
这里我们看到,获取res资源中的0x7f08011a,并转换为string类型,再传入到Alertdialog的message中,我们看看它到底调用了什么资源
img
看到是这个,看来跟我们破解无关,跳过,我们就直接针对这两个跳转即可
img
如果v1<=0或者v1>v2,则跳转到:cond_1,我们不让它跳,在pc破解中,我们有两种方法,一种是设置为相反的跳转,另一种就是nop掉,lez对应的就是ge,不过这种方法不保险,我推荐第二种,不走跳转,直接向下走if函数中的代码,我们删除这两条即可,另一个smali修改方法类似,稍有出入
img
接收传入的message,判断v0是否等于0x1b207,不等的话,跳转到:cond_2,我们观察到,如果跳转的话,就会越过成功提示的方法,我们不能让它跳,nop掉,下面一个跳转到:cond_1的判断也一并删除,保存,打包测试。
img
嗯,提示成功,那么我们测试下付费用户的功能是否能够正常使用
img
ok,付费主题可以使用,说明我们破解成功了,那是不是就结束了呢?当我们退出程序再进入的时候发现主题丢了,什么鬼?来到付费页面,发现授权按钮又可以点击了,之前我们注意到,授权成功后,按钮就会呈现灰色不可点击的状态,那说明了什么,这个程序有重启验证!

4. 突破重启验证

之前我们在尝试破解时,在成功函数中看到了这段信息,
img
如果是付费状态,“isDefaultTheme”的值是v1,在方法首可以看到v1定义的是1,这里是个putBoolean,即通过Sharepreferences.editor写入了true,那么我们通过分析发现,我们是在重启后丢失授权信息的,那验证方法肯定在第一个显示的页面被调用,我们找到程序的主页面所在的类,来到HelloWorldActivity.smali下,我们尝试搜索“isDefaultTheme”,结果搜到好几个
img

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
.method static synthetic d(Llongbin/helloworld/HelloWorldActivity;)V
.locals 6

.prologue
.line 3763
iget-object v0, p0, Llongbin/helloworld/HelloWorldActivity;->cc:Landroid/content/SharedPreferences;

const-string v1, "alipay_appid"

const-string v2, "89d43e35dd25de5d2ce9334f2624d6a61345893a"

invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;

move-result-object v1

const-string v0, ""

:try_start_0
iget-object v2, p0, Llongbin/helloworld/HelloWorldActivity;->cb:Landroid/content/SharedPreferences;

const-string v3, "WWxoT2JnPT0="

const-string v4, ""

invoke-interface {v2, v3, v4}, Landroid/content/SharedPreferences;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;

move-result-object v2

invoke-static {v2}, Llongbin/helloworld/hm;->b(Ljava/lang/String;)Ljava/lang/String;
:try_end_0
.catch Ljava/io/UnsupportedEncodingException; {:try_start_0 .. :try_end_0} :catch_0

move-result-object v0

:goto_0
iget-object v2, p0, Llongbin/helloworld/HelloWorldActivity;->cb:Landroid/content/SharedPreferences;

const-string v3, "font_size"

const-string v4, "28"

invoke-interface {v2, v3, v4}, Landroid/content/SharedPreferences;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;

move-result-object v2

iget-object v3, p0, Llongbin/helloworld/HelloWorldActivity;->cb:Landroid/content/SharedPreferences;

const-string v4, "isDefaultTheme"

const/4 v5, 0x0

invoke-interface {v3, v4, v5}, Landroid/content/SharedPreferences;->getBoolean(Ljava/lang/String;Z)Z

move-result v3

invoke-static {v0}, Llongbin/helloworld/m;->f(Ljava/lang/String;)Ljava/lang/String;

move-result-object v0

invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

move-result v0

if-eqz v0, :cond_0

const-string v0, "24"

invoke-virtual {v2, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

move-result v0

if-eqz v0, :cond_0

if-eqz v3, :cond_0

new-instance v1, Llongbin/helloworld/gb;

const v0, 0x7f090027

sget-object v2, Llongbin/helloworld/n;->h:[I

invoke-direct {v1, p0, v0, v2}, Llongbin/helloworld/gb;-><init>(Llongbin/helloworld/HelloWorldActivity;I[I)V

sget-object v0, Llongbin/helloworld/n;->h:[I

:goto_1
new-instance v2, Landroid/app/AlertDialog$Builder;

invoke-direct {v2, p0}, Landroid/app/AlertDialog$Builder;-><init>(Landroid/content/Context;)V

new-instance v3, Llongbin/helloworld/cg;

invoke-direct {v3, p0, v0}, Llongbin/helloworld/cg;-><init>(Llongbin/helloworld/HelloWorldActivity;[I)V

invoke-virtual {v2, v1, v3}, Landroid/app/AlertDialog$Builder;->setAdapter(Landroid/widget/ListAdapter;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder;

new-instance v0, Llongbin/helloworld/ch;

invoke-direct {v0, p0}, Llongbin/helloworld/ch;-><init>(Llongbin/helloworld/HelloWorldActivity;)V

invoke-virtual {v2, v0}, Landroid/app/AlertDialog$Builder;->setOnCancelListener(Landroid/content/DialogInterface$OnCancelListener;)Landroid/app/AlertDialog$Builder;

invoke-virtual {v2}, Landroid/app/AlertDialog$Builder;->show()Landroid/app/AlertDialog;

return-void

:cond_0
new-instance v1, Llongbin/helloworld/gb;

const v0, 0x7f090026

sget-object v2, Llongbin/helloworld/n;->g:[I

invoke-direct {v1, p0, v0, v2}, Llongbin/helloworld/gb;-><init>(Llongbin/helloworld/HelloWorldActivity;I[I)V

sget-object v0, Llongbin/helloworld/n;->g:[I

goto :goto_1

:catch_0
move-exception v2

goto :goto_0
.end method

比如这样的,通过观察,我们发现这些方法都只是调用了isDefaultTheme的布尔值,也就是说,这些并不会改变我们的授权状态,我们需要找到的是会更改布尔值的函数,即putBoolean,我们逐个排查,经过筛选后,我们找到了唯一一个
img
那其中的v2就是它经过判断后重新赋予的值,我们找到这个值是什么,向上翻
img
我们看到此处定义的是0,即false,那找到病因了,我们就该下药了

A.方法1:
img
在传值之前更改v2的值,考虑到不影响整个程序的运行,不建议修改方法开始时的v2值,我们仅在此处修改

B.方法2:
不让程序进行赋值,即跳过这个判断流程,我们找到这个函数体的标签,即:cond_3,根据尝试破解中的手段,跳过这个标签即可。
img
我们只找到一个,nop掉,为保证程序一定会能跳过此处,建议是nop掉if-nez v3的同时,将下方的if-eqz v0,改成goto :cond_4,这样程序就一定不会走:cond_3这个分支,isDefaultTheme的布尔值就一定会真,好,打包测试
img
我们退出后重开程序,依然是灰色状态,说明我们真正授权成功拉!

五、JAVA反编译工具

首先,我们需要了解下,安卓应用是拿什么语言开发的,毫无疑问,JAVA,那么安卓=java么,显然这不是一个等式。

总的来说Java程序和Android程序的区別在于Android程序是基于组件,基于配置的.Android 虽然使用Java语言 作为开发语言,但是在实际开发中发现,还是与Java SDK 有 一些不同的地方。Android SDK引用了大部分的Java SDK,少数部分被Android SDK抛弃,比如说界面部分,java.awt package除了java.awt.font被引用外,其他都被抛弃,在Android平台开发中不能使用。

上面废话了一大堆,目的就是说,安卓是用java开发的,不过部分api进行了添加修改和舍弃。那么为什么我们逆向要接触java呢,常规的我们使用apktool进行反编译之后的classes.dex文件都被转换成了smali文件,大家刚接触安卓逆向的可能都知道,smali语言晦涩难懂,比如楼主,到现在smali还是一知半解的,那么有没有什么更好的办法来帮助我们学习逆向呢,当然有,常规的逆向工具已经为我们集成了dex2jar组件,这个组件就是用来将dex文件转换成jar文件,配合神器jd-gui就可以查看java源码,直接打开jd-gui,使用内置的打开功能或者拖拽将jar包放入jd-gui中即可查看。

首先,我们需要了解下,安卓应用是拿什么语言开发的,毫无疑问,JAVA,那么安卓=java么,显然这不是一个等式。
总的来说Java程序和Android程序的区別在于Android程序是基于组件,基于配置的.Android 虽然使用Java语言 作为开发语言,但是在实际开发中发现,还是与Java SDK 有 一些不同的地方。Android SDK引用了大部分的Java SDK,少数部分被Android SDK抛弃,比如说界面部分,java.awt package除了java.awt.font被引用外,其他都被抛弃,在Android平台开发中不能使用。
上面废话了一大堆,目的就是说,安卓是用java开发的,不过部分api进行了添加修改和舍弃。那么为什么我们逆向要接触java呢,常规的我们使用apktool进行反编译之后的classes.dex文件都被转换成了smali文件,大家刚接触安卓逆向的可能都知道,smali语言晦涩难懂,比如楼主,到现在smali还是一知半解的,那么有没有什么更好的办法来帮助我们学习逆向呢,当然有,常规的逆向工具已经为我们集成了dex2jar组件,这个组件就是用来将dex文件转换成jar文件,配合神器jd-gui就可以查看java源码,直接打开jd-gui,使用内置的打开功能或者拖拽将jar包放入jd-gui中即可查看。
img

配合搜索功能,我们可以很轻易的到达我们需要查看的类或者搜索方法甚至是常量,在search for中我们可以设置。
那我们可不可以直接保存出这个java源码放入开发工具直接使用呢,我相信50%是可以的,不过jd-gui的还原能力当然也是有限的,大部分情况下,我们看到的源码虽说功能没有错误,但语法是有错误的,比如下面这张:
img

我们就讨论下那面那个return,大家玩pc逆向的可能也都清楚,程序遇到return即会跳出判断,循环等等,不会再执行下面的语句,这很明显是反编译问题,那有没有什么更加准确的反编译工具呢,当然有,JEB,绝对神器,目前我们能够免费使用的是14年版的jeb 1.5,官方已经更新到了2.0.1,有条件的可以自己去买个正版,一年1080刀,差不多近7000块钱。可以看下图对比一下反编译准确性
img

有没有发现这基本就像程序猿写出来的标准代码?JEB的强势之处还远不及如此,一些经过混淆的类,在dex2jar+jd-gui无法显示的时候,JEB可以做到,JEB可以解决大部分这类情况,但也有少数混淆加密过于强大,使得神器也失效。
不过需要注意的是,目前还没有任何一款工具是能够直接修改java源码的,懂得这些工具也是帮助大家分析smali语言,说到底,懂得smali才是王道,毕竟只要能反编译成功就一定能看到smali语言,而二次反编译成java是很有可能失败的。
本课只是对大家进行了解相关工具,具体实践还是得靠大家。
JEB使用入门教程:http://blog.csdn.net/u011069813/article/details/9373465
jd-gui更新地址:https://github.com/java-decompiler/jd-gui/releases
dex2jar更新地址:https://github.com/pxb1988/dex2jar/releases

六、JEB和IDA的使用

1. jeb是什么

JEB是Android应用静态分析的de facto standard,除去准确的反编译结果、高容错性之外,JEB提供的API也方便了我们编写插件对源文件进行处理,实施反混淆甚至一些更高级的应用分析来方便后续的人工分析.(摘自乌云)

jeb凭借其牛X的保护措施和高昂的售价,使得诸多普通逆向爱好者望而却步,当然ida也是,目前网络上仅流传着jeb 1.5和ida 6.6的破解版,虽是旧版,面对很多情况依然是绰绰有余

2. 使用jeb

第一次使用jeb一般是需要配置一下java环境的

img

如上图配置,JAVA_HOME根据自己的jre环境设定,之后就可以运行jeb_wincon.bat了,打开jeb后,将apk或者dex文件拖入到jeb窗体中,经过耐心的等待后,就可以看到jeb已经反编译完成了,针对一些加密的apk,apktool反编译不了的,往往jeb能够成功,但不代表所有都能成功,这也是jeb的牛逼之处,安卓加密商针对的更多的是apktool自身的漏洞,反编译完成后,我们就看到程序的smali代码了,

img

可以点击string查看dex中的字符串,ctrl+f就可以进行搜索了

img

img

搜索到需要的字符串后双击就可以来到smali代码处,如果看不懂smali,按下tab就可以看到java代码了,是不是很神奇?

img

当我们来到一个java文件后,想快速定位到一个命令调用的方法时,可以直接在这条命令上双击即可

img

img

还是很方便的吧,点击上方的后退按钮可以快速回到上一级,即返回这条命令的地方

img

那jeb的使用呢,差不多就这些,其它的自己琢磨吧,切记jeb是无法修改代码的哦(小提示,在代码中右键点击comment可以快速添加备注,点击rename item可以修改函数名哦,在代码被混淆时,这个非常有用)

img

3. ida是什么

交互式反汇编器专业版(Interactive Disassembler Professional),人们常称其为IDA Pro,或简称为IDA,是总部位于比利时列日市(Liège)的Hex-Rayd公司的一款产品。开发IDA的是一位编程天才,名叫Ilfak Guilfanov。

ida的强势之处在于其强大的静态分析能力,不管什么格式的文件,不管此文件是否能够正常打开,都不会影响它的静态分析,配合更加牛逼的f5插件(目前只有ida 6.5版本的插件),可以轻松的看到c的伪代码,对于看不懂汇编的人,这无疑很大程度上帮助他理解代码

PS:当然我们都是盗版用户拉,目前正版版本6.8

在安卓上面,我们使用ida主要是分析so,lua,dll此类文件,dex什么的交给

Android killer

这类软件即可,本次主要讲了解静态分析,不涉及动态

样本依然是鬼哥的apk文件,我们提取出其中的so文件,用ida打开,默认选项ok等待其分析完成即可

img

在exports选项卡中可以看到所有调用的函数,我们定位到需要分析的方法,首先我们需要知道怎么知道方法名,在编写jni的时候,每调用一个nativie方法,都会使用这类语句进行调用

public static native 类型 方法名(参数类型)

样本中如下图

img

那我们可以在ida中快速搜索ggPrintHello即可,回到ida,alt+t可以进行搜索方法名(如果需要搜索字符串,只需要先shift+f12切换到string选项卡,再次alt+t即可),输入后回车就可以定位到该方法,双击进入后

映入我们眼帘的肯定就是汇编代码了

img

那看不懂怎么办,只需要f5一下即可

img

是不是相当于汇编来说直观多了,当然ida也支持重命名方法名和参数类型等等

不过ida对中文的支持很不好,除了添加备注外,其余情况下基本不能添加或者修改为中文,那ida分析so的时候也是只能看不能改,怎么才能改呢,这就要借助到16进制编辑器了,定位到我们需要修改的代码

img

然后切换到HEX VIEW-A就可以看到16进制了,记下当前的地址,

img

使用如010Eiditor此类软件打开,ctrl+G来到00000BBA地址,修改16进制后保存即可,具体的进制转换自己查资料吧