您的位置:首页 > 移动开发 > Android开发

【Android SDK程序逆向分析与破解系列】之五:Android APK的静态分析

2015-05-28 17:21 531 查看
作者:郭嘉

邮箱:allenwells@163.com

博客:http://blog.csdn.net/allenwells

github:https://github.com/AllenWells

【Android SDK程序逆向分析与破解系列】章节索引

【Android SDK程序逆向分析与破解系列】之一:Android安装程序APK分析

【Android SDK程序逆向分析与破解系列】之二:Android可执行程序DEX分析(一)

【Android SDK程序逆向分析与破解系列】之三:Android可执行程序DEX分析(二)

【Android SDK程序逆向分析与破解系列】之四:Android可执行程序ODEX分析

【Android SDK程序逆向分析与破解系列】之五:Android APK的静态分析

什么是静态分析?

静态分析(Static Analysis)是指在不运行代码的情况下,采用词法分析、语法分析等技术手段对程序文件进行扫描而生成程序的反汇编代码,然会阅读反汇编代码来掌握程序功能的一种技术。

一 如何快速定位Android程序的关键代码

APK程序反编译后生成的文件中包含一个AndroidManifest.xml文件,该文件包含了软件的包名,运行系统的版本,使用的组件是破解的关键点。

关注MainActivity

每个APK只有一个主Activity,它的定义如下图所示:



android:label:Activity的标题

android:name:Activity的包名+类名

intent-filter:Activity的启动意图

找到主Activity后,查看该类的onCreate()函数的反汇编代码,这就是程序的入口,从这里一直往下看,追踪软件的执行流程。

关注Application

Application类主要负责在程序的组件之间传递全局变量,或者在Activity启动之前做一些初始化工作,比如商业软件的授权等等Application类在application标签中用包名+类名来注册。

APK反编译的后代码量一般非常庞大,那么定位关键代码就需要有技巧,有方法,常见的定位关键代码的方法如下所示:

1.1 信息反馈法

信息反馈法是先运行目标程序,然后根据程序运行的反馈信息定位关键代码。比如商业软件需要注册,在输入错误的饿注册码时,程序会反馈“无效的注册码”等信息,类似这样的字符串一般会存储在string.xml文件中或硬编码到程序代码中,对于前者会以id的形式访问,直接在反汇编的代码中搜索字符串的id即可,对于后者,在反汇编代码中直接搜索字符串即可。

1.2 特征函数法

特征函数法与信息反馈法类似,消息的提示一般最终都是由Android的API来完成,比如Toast、AlertDialog等。

1.3 顺序查看法

顺序查看法是从软件的启动代码开始,逐行的向下分析,掌握软件的执行流程。这种方法在病毒分析经常用到,

1.4 代码注入法

代码注入法属于动态调试方法,它的原理是手动修改APK的反汇编代码,加入Log输出,配合LogCat查看程序执行到定点的状态数据,这种方法在解密程序时经常使用。

1.5 栈跟踪法

栈跟踪法属于动态调试法,然后查看栈上函数调用序列来理解方法的执行流程。

1.6 方法剖析

方法剖析(Method Profiling)属于动态调试方法,它主要用于热点分析和性能优化,该功能可以记录每个函数占用的CPU时间,还能追踪所有的函数调用关系,并提供比栈跟踪法更详细的函数调用序列报告。

二 Smail语法

2.1 类信息

Smail文件的头三行描述了当前类的信息

.class <访问权限>[修饰关键字]<类名>
.super <父类名>
.source <源文件名>


注意:经过混淆的DEX文件,反编译出来的Smail代码可能没有源文件信息,即.source可能为空。

2.2 主体部分

一个类由多个字段或方法组成。

2.2.1 字段

字段的声明使用.field指令。

静态字段:field<访问权限>staic[修饰关键字]<字段名>:<字段类型>

实例字段:field<访问权限>[修饰关键字]<字段名>:<字段类型>

2.2.2 方法

方法的声明使用.method指令。

直接方法:.method<访问权限>[静态关键字]<方法原型>,起始处会注释# direct methods

虚方法:.method<访问权限>[静态关键字]<方法原型>,起始处会注释#virtual methods

2.2.3 类

内部类

Android的内部类分为成员内部类、静态嵌套类、方法内部类和匿名内部类。

class Outer
{
    class Inner()
    {

    }
}


内部类会被反编译成两个文件Outer.smail和Inner.smail

监听器

监听器的本质是接口

btn.setOnClickListener(new OnClickListener()
{
    @Override
    public void onClick(View v)
    {

    }
});


.implements<接口名>,起始处会注释#interfaces

注释类

.annotation[注释属性]<注释类名> [注释字段 = 值]

.end annotation,起始处会注释#annotations

自动生成的类

R类

R类主要描述APK的资源信息,其中包含内部类,R.smail、Rattr.smail、Rattr.smail、Rdimen.smail、Rdrawable.smail、Rdrawable.smail、Rid.smail、Rstyle.smail、Rstyle.smail、Rstring.smail、R$menu.smail等。

BuildConfg类

BuildConfg类中只有一个boolean类型的DEBUG字段,用来标识程序发布的类型,true表示已调试版本发布,false表示以非调试版本发布。

注释类

如果在代码中使用了SuppresLint或TargetApi等注释,程序将会自动生成对应的类,反编译后会在smail\android\annotation目录下生成相应的Smail文件。

2.2.4 循环语句

循环语句主要有两种:

for循环

for(<对象><对象名>; <对象名范围>; <对象递增/递减步长>)
{
    [处理单个对象的代码体]
}


for循环对应的Smail代码如下所示:

.local v1, i:I #初始化v1为0
:goto_0 #迭代循环开始
if-1t v1, v5 :cond_0 #如果v1<v5,则跳转到cond_0标号处。
...
...
...
:cond_0
invoke-interface {v0, v1}, Ljava/util/List;->get(I)Ljava/lang/Object; #单个循环项
...
...
...
add-int/lit8 v1, v1, 0x1 #下一个索引 
goto :goto_0 #跳转到循环开始处


while循环

while(<迭代器>hasNext())
{
    <对象><对象名> = <迭代器>.next()
    [处理单个对象的代码体]
}


:goto_0 #迭代循环开始
invoke-interface {v4}, Ljava/util/Iterator;->hasNext()Z #开始迭代
...
...
...
invoke-interface {v4}, Ljava/util/Iterator;->next()Ljava/lang/Object #循环获取每一项
...
...
...
goto :goto_0 #跳转到循环开始处


两种循环生成的Smail代码非常相似,总的说来,迭代器循环有如下特点:

迭代器循环会调用迭代器的hasNext()方法检测循环条件是否满足。

迭代器循环调用迭代器的next()方法获取单个对象。

循环中使用goto指令来控制代码的流程。

for形式的迭代器循环展开后即为while形式的迭代器循环。

2.2.5 分支语句

switch分支语句

switch(判断项)
{
    case 判断项值1:
    break;

    case 判断项值1:
    break;

    case 判断项值1:
    break;

    default:
    break;
}


对应的Smail代码如下所示:

packed_switch p1, :pswitch_data_0 #packed_switch分支,pswitch_data_0指定case区域
...
...
...
:pswitch_data_0 #case区域,从0开始


2.2.6 异常捕捉语句

try/catch语句

try
{

}
catch(Exception e)
{

}


对应的Smail语句

:try_start_0 #第一个try开始
...
...
...
:try_end_0 #第一个try结束
...
...
...
:try_start_1 #第二个try开始
...
...
...
:try_end_1 #第二个try结束
...
...
...
:goto_0
...
...
...
:catch_0
...
...
...
:try_start_2 #第三个try开始
...
...
...
:try_end_2 #第三个try结束
...
...
...
:catch_1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: