Smali

Smali是Android虚拟机的反汇编语言。在安卓逆向中是非常重要的,就类似于汇编语言。

推荐一个AS的插件,java2smali,可以将java代码转换为smali,可以让人更好的实践理解smali。

java 类型 smail 描述符

boolean Z

byte B

short S

char C

int I

long J

float F

double D

void V

数组 L

对象 [../../

Smali方法返回关键字和其后面可以跟的数据类型

return byte

return short

return int

return char

return boolean

return float

return-wide double

return-wide long

return-void void

return-object 数组

return-object object

数据转换指令

int-to-long 整型转为长整型

float-to-int 单精度浮点型转为整型

int-tobyte 整型转为字节类型

neg-int 求补指令,对整数求补

not-int 求反指令,对整数求反

比较指令

cmpl-类型 VC,VA,VB,比较VA,VB较小值

cmpg-类型 VC,VA,VB 比较VA,VB较大值

数据定义指令

const-string 字符串值

const-class 字节码对象赋值

const/4 最大存放4位数值(-8到7)

const/16 最大存放16位数值(-32768到32767)

const 最大存放32位数值

const/high16 只存放高16位数值

const-wide 最大存放64位数值

const-wide/16 最大存放16位数值

const-wide/32 最大存放32位数值

const-wide/high16 只存放高16位数值

变量操作指令

move v1,v2

将v2中的值移入到v1寄存器中(4位,支持int型)

move/from16 v1,v2

将16位的v2寄存器中的值移入到8位的v1寄存器这

move/16 v1,v2

将16位的v2寄存器中的值移入到16位的v1寄存器中

move-wide v1,v2

将16位的v2寄存器中的值移入到v1寄存器对中

move-wide/from16 v1,v2

将16位的v2寄存器(一组)中的值移入到8位的v1寄存器在中

move-wide/16 v1,v2

将16位的v2寄存器(一组)中的值移入到16位的v1寄存器中

move-object v1,v2

将v2中的对象指针移入到v1寄存器中

move-object v1,v2

将v2中的对象指针移入到v1寄存器中

move-object/from16 v1,v2

将16位的v2寄存器中的对象指针移入到v1(8位)寄存器中

move-object/16 v1,v2

将16位的v2寄存器中的对象指针移入到v1(16位)寄存器中

move-result v1

将这个指令的上一条指令计算结果,移入到v1寄存器中(需要配合invoke-staic、invoke-virtual等指令使用)

move-result-object v1

将上条计算结果的对象指针移入v1寄存器

move-result-wide

v1将上条计算结果(双字)的对象指针移入v1寄存器

move-exception

v1 将异常移入v1寄存器,用于捕获try-catch语句中的异常

条件跳转指令

if-eq vA,vB,:cond_ 如果vA等于vB,则跳转

if-ne vA,vB, :cond_ 如果vA不等于vB,则跳转

if-lt vA,vB, :cond_ 如果vA小于vB,则跳转

if-le vA,vB, :cond_ 如果vA小于等于vB,则跳转

if-gt vA,vB, :cond_ 如果vA大于vB,则跳转

if-ge vA,vB, :cond_ 如果vA大于等于vb,则跳转

if-eqz vA,:cond_ 如果vA等于0,则跳转

if-nez vA,:cond_ 如果vA不等于0,在跳转

if-ltz vA,:cond_ 如果vA小于0,则跳转

if-lez vA,:cond_ 如果vA小于等于0,则跳转

if-gtz vA,:cond_ 如果vA大于0,则跳转

if-gez vA,:cond_ 如果vA大于等于0,则跳转

Smali关键词

.class 包名+类名

.super 父类类名

.source 源文件名称

.implements 接口实现

.field 定义变量

.method/.end method 方法的开始与结束

.locals 方法内使用的v开头的寄存器个数

.prologue 表示方法中代码的开始处

.line 对于java中的行数

.paramter/.param 指定的方法参数

.annotation./end annotation 注解的开始和结束

.registers 表示该方法使用到的寄存器的个数

.local 表示方法中非参数的变量

.cond_N 条件分支,配合if使用

.goto_N goto跳转标记

寄存器

v变量表示方法中非参数变量

p变量表示方法中参数变量

方法调用指令

invoke-direct 调用私有方法和构造方法

invoke-virtual 调用普通的实例方法,(被public,protected或没有修饰符的方法)

invoke-static 调用静态方法

invoke-super 调用父类方法

invoke-interface 调用接口中的方法

查壳

厂商 常见特征so文件或其它文件 常见java代码(反编译classes.dex)
娜迦 libchaosvmp.so、libddog.so、libfdog.so
娜迦企业版 libedog.so
爱加密 libexec.so、libexecmain.so、ijiami.dat s.h.e.l.l.S
爱加密企业版 ijiami.ajm
梆梆免费版 libsecexe.so、libsecmain.so、libSecShell.so com.secneo.apkwrapper.ApplicationWrapper、com.SecShell.SecShell.ApplicationWrapper、com.secneo.apkwrapper.AW
梆梆企业版 libDexHelper.so、libDexHelper-x86.so
360 libprotectClass.so、libjiagu.so、libjiagu_art.so、libjiagu_x86.so、libjiagu_x64.so、libjiagu_a64.so com.stub.StubApp
通付盾 libegis.so、libNSaferOnly.so
网秦 libnqshield.so
百度 libbaiduprotect.so com.baidu.protect.StubApplication
阿里聚安全 aliprotect.dat、libsgmain.so、libsgsecuritybody.so、libmobisec.so
腾讯 libtup.so、libexec.so、libshell.so、mix.dex、lib/armeabi/mix.dex、lib/armeabi/mixz.dex com.tencent.StubShell.TxAppEntry
腾讯御安全 libtosprotection.armeabi.so、libtosprotection.armeabi-v7a.so、libtosprotection.x86.so
网易易盾 libnesec.so
APKProtect libAPKProtect.so
几维安全 libkwscmm.so、libkwscr.so、libkwslinker.so
顶像科技 libx3g.so
盛大 libapssec.so
瑞星 librsprotect.so

主流的Android app保护厂商的产品:梆梆、腾讯、爱加密、360、阿里和百度。这些厂商是来实现app的保护的相关原理如下:

360:将原有的dex文件加密后存储在libjiagu.so、libjiagu_art.so,在运行时动态释放并解密。

阿里:将原有的dex文件拆分为两部分,一部分主体保存为libmobisecy.so,另一部分包含了一部分class_data_item和code_item。在运行的时候将两部分释放在内存中,并修复相关的指针,恢复数据之间的连接关系。同时一些annotation_off被设置为无效的值。

百度:将一些class_data_item存储在dex文件的外部,在运行时恢复与主体的dex的连接关系。在dex文件加载后,其头部的魔数,校验和以及签名值会被擦除。同时某些方法被改写,使得其在执行前相关的指令才会被恢复,在执行之后便立即擦除。

梆梆:提前准备了一个odex或oat文件,并加密保存为外部的jar文件,运行时解密;同时hook了libc.so中的一些函数,如read,write,mmap等,监视其操作区域是否包含了dex的头部,保证无法使用这些函数对dex文件进行操作。

爱加密:同样是加密原有的dex文件,在运行时整体释放并解密,只不过其释放的处于固定路径下的临时文件的名字是随机的。

腾讯:提供选项可以指定需要保护的方法。如果某个方法被保护,则在dex文件中的相关class_data_item中无法看到其数据,即为一个假的class_data_item;在运行时释放真正的class_data_item并连接到dex文件上,但是其code_item却一直存在于原有的dex文件中。同样,一些annotation_off和debug_info_off被填充为无效值来阻止静态反编译。只支持在DVM环境下运行。