9

基于Smali源码的安卓功能捆绑研究

 4 years ago
source link: https://www.freebuf.com/articles/terminal/228180.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在日常分析恶意软件过程中发现了很多恶意软件的捆绑方式,例如将两个app放入同一个安装包中同时释放,将一个app绑在另一个的头部释放等等。网上也有很多用来捆绑两个app的工具,但是总的来说还是很暴力不够优雅。以学习研究为目的,近期深入研究了安卓逆向后的smali源码,想到是否能够从底层源码进行功能捆绑。在进行多次实验后,发现此方法可行,接下来会从Smali机器语言、功能捆绑实现方法、自动化流程三个方面介绍基于Smali源码的安卓功能捆绑实现方法与原理。

Smali 机器语言

首先简要介绍一下smali,Smali是用于Dalvik(Android虚拟机)的反汇编程序实现,Smali支持注解、调试信息、行数信息等基本Java的基本特性。对应着来说,Smali和二进制逆向后的汇编语言很像,里面有寄存器、函数跳转等,需要着重知道的几个点如下:

Smali文件结构

一个Smali文件对应的是一个Java的类,对应一个.class文件,如果有内部类,需要写成ClassName$InnerClassA、ClassName$InnerClassB的形式。

Smali语言对象

Object类型,即引用类型的对象,在引用时,使用L开头,后面紧接着的是完整的包名,比如:java.lang.String对应的Smali语法则是Ljava/lang/String。

方法声明及调用

Smali引用方法的模板如下:

Lpackage/name/ObjectName;->MethodName(III)Z

第一部分Lpackage/name/ObjectName;用于声明具体的类型,以便JVM寻找。

第二部分MethodName(III)Z,其中MethodName为具体的方法名,()中的字符,表示了参数数量和类型,即3个int型参数,Z为返回值的类型,即返回Boolean类型

寄存器声明及使用

在Smali中,如果需要存储变量,必须先声明足够数量的寄存器。声明可使用的寄存器数量的方式为:.registers N,N代表需要的寄存器的总个数,同时,还有一个关键字.locals,它用于声明非参数的寄存器个数(包含在registers声明的个数当中),也叫做本地寄存器,只在一个方法内有效。

Dalvik指令集

常用的Dalvik虚拟机指令,其合集称为Dalvik指令集,其中包括:移位操作、返回操作、常量操作、调用操作、判断操作和属性操作等。具体指令内容可以参考Android官方的Dalvik相关文档。

了解认识Smali源码后就可以从samli代码上进行修改,如何修改源码使功能捆绑到另一个app而且保证功能的完整性,应该注意哪些重要的环节会在下面详细说明。

具体实现方法

目标apk反编译

利用安卓反编译工具,这里我使用的是apktool(可以在网上自行下载),将目标apk反编译成源文件格式。执行命令:

“java -jar apktool.jar d + 目标.apk”

反编译后的源文件及其用途:

i6BVFjB.jpg!web

Assets目录

该文件夹为安卓系统原生资源文件,该文件夹下的文件不会被编译,同时Android提供了一个工具类,方便操作获取assets文件下的文件。可将apk所需外部加载的资源信息,如图片、配置等放于该文件夹下。

Lib目录

目录下的子目录armeabi含有so文件。Eclipse在打包的时候会根据文件名的命名规则(lib***.so)去打包so文件,开头和结尾必须分别为“lib”和“.so”,否则不会打包到apk文件中。

Res目录

存放应用程序资源文件,包括图片,字符串等等,可根据自身需要直接对其进行修改。在apk回编译时会自动识别路径并加载已被修改的程序资源。

Smali目录

该目录主要是dex文件反编译得到的smali文件,也是在自动化捆绑过程中主要修改的文件目录。进行功能捆绑时需要直接在smali源文件上进行smali源码的插入与修改。应用程序反编译可能会出现多个smali文件目录,在进行源码修改过程中需要找准文件修改位置再进行操作。

AndroidManifest.xml

此文件是apk中最重要的文件之一。它是apk的全局配置文件,提供了android系统所需要的关于该应用的必要信息。主要包括:应用名称、版本、权限、引用的库文件、应用入口、调用服务声明等信息。

该文件可用于定位应用程序入口点、添加新的应用权限、声明新的服务和事件。在捆绑功能时要对该文件进行大量修改。

功能提取

利用安卓反编译工具,将需要捆绑的apk反编译并提取需要的功能。反编译后的文件需要提取以下重要部分:apk所需权限、服务、事件以及功能实现的smali源码。

在捆绑过程中并不是将整个apk依附在另一个apk之上,是将需要捆绑的apk中所需的功能进行提取并插入到其他的apk之中 因此需要提取修改的文件类型及数量较多,具体提取方法如下:

提取所需权限、服务、事件

根据反编译后AndroidManifest.xml文件中标签类别进行提取,例如<uses-permission />、<service />、<receiver></receiver>、<activity />等,分别对权限、服务、事件进行相应的提取。提取时应注意提取的元素要与所需应用功能相对应,不要提取多余的元素。

提取smali源码

提取源码前要根据配置文件找到应用入口从而定位smali位置存放位置,smali源码在同一功能的实现上会分为多个文件,多个文件具有相同功能名称并用”$”区分不同的文件,如下图:

yM3mqy6.jpg!web Cache功能通过8个smali文件实现,提取时需要将8个文件同时提取才能保证功能的完整性。

修改目标apk源文件

在进行捆绑操作的过程中需要修改如下几类文件:AndroidManifest.xml、两个apk反编译后的smali源码文件、Assets资源文件。

1、AndroidManifest.xml文件修改:

①对比目标apk自身权限与待插入功能所需权限,添加所需权限声明;

②添加带插入功能所需的服务<service />与事件<receiver></receiver>声明;

③确定应用程序入口位置,活动中的<intent-filter>标签包含

”<action android:name =“android.intent.action.MAIN”/>”则是应用程序的入口点。

④根据程序入口点修改添加服务与事件”<… android:name= >”中的路径信息。

2、Smali源码修改:

①根据程序入口点找到所在smali文件,找到入口特征:

”.method protected onCreate(Landroid/os/Bundle;)V”定位程序入口位置;

②修改寄存器值”.locals x”,在程序起调自身后添加待插入功能的函数调用;

③将需捆绑功能smali文件复制到入口文件所在目录下,修改所有smali文件中包名路径,与入口文件路径相同。

文件回编译

“apktool b[uild] <target dir> -o <app_path>”

输入修改后的文件路径与捆绑后的应用名称,apktool工具会将整个文件重新编译,并在当前目录下生成apk安装包。

安装包签名

所有重编译的安卓程序需要签名后才可以在手机上安装运行,可使用apksigner.jar对apk进行签名。

java -jar ApkSigner.jar -keystore keystorePath -alias alias [-pswd password] apkPath(or directory)

获取秘钥有以下两种方法:

①编译过程中使用的安卓密钥可使用安装Android studio时构建的Android调试密钥库,调试密钥库位于系统目录中的“.android”隐藏目录中,默认密码为“android”。

3auAFbj.jpg!web

②通过keytool工具生成秘钥

keytool -genkey -alias key.keystore -keyalg RSA -validity 30000 -keystore key.keystore

根据提示运行结束后会在当前目录下回生成一个key.keystore文件,该文件为所需秘钥。

运行与调试

将回编译后的apk在安卓虚拟机或是手机上运行,捆绑后的apk正常安装运行则说明各个功能捆绑成功,之后可根据捆绑功能进行具体功能测试。

运行过程中可能会出现以下错误状况:

错误一:apk无法安装

解决方法:检查apk是否完成签名,比较签名前后apk大小,签名成功的apk会比签名前的apk大;检查AndroidManifest.xml文件中插入元素是否完整,所有标签是否配对。

错误二:运行应用程序后闪退

解决方法:利用Android工具DDMS进行动态调试。

自动化流程

如果每一个app都需要手动操作以上步骤还是不够优雅,自动化完成才是最终的目的,在上面已经具体说明了重点环节和注意事项,接下来简单说一下自动化流程。

根据具体实现方法对自动化流程进行编程实现。主要有以下步骤:

步骤一:将待插入的功能提取后放置于同一文件夹下,方便程序捆绑时统一读取

步骤二:逆向目标apk,比较apk权限与功能所需要权限是否相同,并添加未声明权限与服务

步骤三:需找逆向后的apk入口,插入步骤一的功能,并根据插入功能数量修改smali源码信息

步骤四:添加修改标识

步骤五:回编译apk文件,并对apk签名

通过以上五个步骤即可将功能捆绑在已有app之上,捆绑结束后需要对新apk文件在实体手机上进行测试验证,具体验证方法如下:

1、安装测试:在实体手机(虚拟环境也行,不过可能会报错)上下载捆绑后的apk并安装,检测安装过程中是否提示错误信息。

2、运行测试:安装成功后运行软件,检测程序是否可以正常启动,原apk是否有损坏。

3、功能测试:验证原apk所有功能是否正常,检测捆绑功能可否正常运行,运行过程中是否出现报错信息。

总结

以上方法可以在smali源码的基础上对安卓功能进行捆绑,过程比较复杂但是主要是以学习为目的,在这个过程中会对安卓有更深入的认识,大家可以根据流程学习操作,如有不对的地方,欢迎交流。

*本文原创作者:Kriston,本文属FreeBuf原创奖励计划,未经许可禁止转载


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK