Java层逆向分析方法和技巧

公众号坚毅猿
• 阅读 289

Java层逆向分析方法和技巧

本公众号分享的所有技术仅用于学习交流,请勿用于其他非法活动,如有错漏,欢迎留言指正

Java层逆向分析方法和技巧

一、smali汇编

1. Dalvik字节码

  • java字节码、Dalvik字节码、机器码之间的关系?
    • 在Android上,Java代码首先经过编译器编译成Java字节码,然后再经过dx工具转换成Dalvik字节码,并打包到apk文件中(存储在DEX(Dalvik Executable)文件中),Dalvik字节码最终转换为机器码Android设备上运行。(因为大多数Android设备都是使用ARM处理器。因此,Android系统对应用程序的代码进行编译时,生成的是与ARM处理器兼容的机器码。)
    • 编译器编译Java代码成Java字节码是为了将Java代码转换成机器无关的中间代码,使得编译后的代码可以在任何支持JVM的系统上运行,不受机器的体系结构的限制。
    • Dalvik字节码是专门为Android系统优化的字节码,它比Java字节码更加高效。Dalvik字节码在一定程度上是Java字节码的一个子集
    • Java字节码和Dalvik字节码是两种不同的字节码格式,它们的区别主要体现在文件格式(java字节码文件扩展名为.class,Dalvik字节码文件扩展名为.dex。),存储方式(Java字节码在Java虚拟机中一次性加载,并在内存中存储;Dalvik字节码是以dalvik指令为单位存储的,仅加载需要的部分。),运行方式等方面(Java字节码直接在Java虚拟机中执行,Dalvik字节码在Dalvik虚拟机中执行,但需要经过dalvik指令集解释执行(Dalvik字节码->arm机器码)。)
  • Dalvik字节码和smali的关系?
    • Smali是一种对Dalvik字节码进行编码的人类可读的汇编语言,是对Dalvik指令的高级抽象。
    • Dalvik字节码 是以二进制形式存在于 .dex 文件中,而 smali 是以文本形式存在于磁盘文件中,是将 Dalvik字节码 反编译回文本文件的结果。
  • 为什么有了ART虚拟机,为什么还需要学Dalvik字节码?
    • Dalvik虚拟机是在启动应用程序时,将Dalvik字节码动态编译成机器码来实现运行的。所以对于每次打开一个应用程序,是需要进行编译的。
    • 在Android 5.0以后的版本,ART已经取代了Dalvik作为Android的默认虚拟机。
    • ART虚拟机机是在安装应用程序时,将Dalvik字节码预先编译成机器码来实现运行的。因此不再需要在每次启动应用程序时进行编译,提高运行速度。
    • 可以看到只是编译的时机不同而已。无论是Dalvik虚拟机还是ART虚拟机,dex文件是没有变的,即还是Dalvik字节码,反汇编得到Smali汇编,可以通过Smali汇编语言重写和修改应用程序的Dalvik字节码,以实现各种定制化效果和优化性能。

      2. Smali汇编

      寄存器命名方法:V与P

  • JAVA虚拟机使用了栈架构,Java字节码被执行时通过一个操作栈来进行解释,每个方法在运行时都有一个私有的操作栈。
  • Dalvik (ART)虚拟机基于寄存器架构,不是使用操作栈来执行字节码,而是使用寄存器,速度快,节省代码。
    • V命名法:所有变量(局部,参数都用V0,1,...命名)
    • P命名法:局部变量用V,参数用P,容易判断局部变量和参数 Java层逆向分析方法和技巧

      类型描述符

  • C 表示字符(char),用来存储 8 位字符,用一个32位的Dalvik寄存器来存储。
  • l 表示布尔型(boolean),用来存储 8 位布尔型,用一个32位的Dalvik寄存器来存储。
  • S 表示短整型(short),用来存储 16 位短整型,用一个32位的Dalvik寄存器来存储。
  • I 表示整型(int),用来存储 32 位有符号整数。用一个32位的Dalvik寄存器来存储。
  • J 表示长整型(long),用来存储 64 位有符号整数。用两个相邻的32位Dalvik寄存器来存储。
  • F 表示单精度浮点类型(float),用来存储 32 位单精度浮点数。用一个32位的Dalvik寄存器来存储。
  • D 表示双精度浮点类型(double),用来存储 64 位双精度浮点数。用两个相邻的32位Dalvik寄存器来存储。
  • v 表示无返回值的void类型
  • [ 表示数组类型,后面跟着一个 Smali 类型描述符,代表这个数组存储的数据类型。
    • [I表示int []
    • [II表示int[][],每多一维就加一个方括号,最多可以设置255维。
  • L +对象的全限定名表示对象类型(L``Package/Name/ObjectName;注意后面分号结束)
    • 比如String,其完整名称是java.lang.String,那么其全限定名就是java/lang/String;,即java.lang.String的”.”用”/”代替,并在末尾添加分号”;”做结束符.
    • 成员:Lcom/MyClass;``->``name:Ljava/lang/String;(`对象类型;->成员名:成员类型)``
    • 方法:LPackage/Name/objectName;``->``myFunc(III)Z(对象类型;->函数名:(参数类型)返回值)III表示3int参数
  • 函数:fun(Z [I [II Ljava/lang/String; J [Ljava/lang/Object;)Ljava/lang/String;(string fun(boolean, int[],int[][],String,long,Object[]))
    • 直接方法:static、构造、包含静态语句块(比如静态数组初始化)
    • 虚方法

      指令

  • 数据定义指令
  • 数据操作指令
  • 实例操作指令
  • 数组操作指令
  • 比较指令
  • 跳转指令
  • 字段操作指令
  • 数据转换指令
  • 算术指令
  • 空指令锁指令
  • 异常指令
  • 方法调用指令
  • 返回指令

    3. smali文件详解

# 文件头描述。<>中的内容表示必不可缺的,[]表示的是可选择的.
# 访问权限修饰符即所谓的public,protected,private即default.而非权限修饰符则指的是final,abstract.
#.class <访问权限修饰符> [非权限修饰符] <类名>  
#.super <父类名> 
#.source <源文件名称>
.class public Lcom/example/helloworld/HelloWorld; # .cLass 表示当前类,public 当前这个类的修饰符,类表示方式=L+包名+类名;
.super Ljava/lang/Object;  # .super 表示父类

# 在文件头之后便是文件的正文,即类的主体部分,包括类实现的接口描述,注解描述,字段描述和方法描述四部分.
# 接口描述
# .implements <接口名称>
# 注解描述
# .annotation [注解的属性] <注解类名> 
#     [注解字段=值] 
#     ... 
# .end
# 普通字段
# .field <访问权限修饰符> [非权限修饰符] <字段名>:<字段类型>
# 静态字段
# .field <访问权限> static [修饰词] <字段名>:<字段类型>
# 直接方法/虚方法
# .method <访问权限修饰符> [非访问权限修饰符] <方法原型> 
#    <.locals> 
#    [.parameter] 
#    [.prologue] 
#    [.line] 
#    <代码逻辑> 
# .end
.method public static main([Ljava/lang/String;)V  # 方法以 `.method`开始,以 `.end method` 结束
    .registers 4  # 声明总共需要使用4个寄存器.在Smali中,如果需要存储变量,必须先声明足够数量的寄存器。
    .parameter 1  # 参数使用寄存器个数是1  v开头的局部寄存器 p开头是参数寄存器
    .locals 3     # 局部使用寄存器个数是3  v开头的局部寄存器 p开头是参数寄存器.它用于声明非参数的寄存器个数(包含在registers声明的个数当中)
    .prologue     # 方法代码逻辑开始位置
    nop           # 空指令

    # 数据定义指令
    # const[/4、/16、/hight16] v1 xxx  # 将常量xxx赋值给v1寄存器,`/`后数字指的是指令操作数的长度,不写则默认是32位
    # const-wide[/16、/32、/hight16] v1 xxx    # 将双字型常量xxx赋值给v1寄存器,`/`后数字指的是指令操作数的长度,不写则默认是32位
    # const-string[/jumbo] v1 “aaa”     # 将字符串常量”aaa”赋给v1寄存器,过长时需要加上/jumbo(字符串长度超过 65535 个字节时)
    # const-class v1 La/b/TargetClass # 将Class常量a.b.TargetClass赋值给v1,等价于a.b.TargetClass.class
    const/16 v0, 0x8 # 将16bit的常量0x8加载到32bit寄存器v0低16bit中,Smali中的寄存器长度是32位,没有16位的。这里的16"指的是指令操作数的长度,而不是寄存器的长度。
    const/4 v1, 0x5 
    const/4 v2, 0x3
    # 数据操作指令
    move v1, v2
    # 数组操作指令
    new-array v0, v0, [I # 创建一个v0长度的int数组,数组首地址存储在寄存器 v0 中
    array-length v1, v0 # 把数组的长度存储在寄存器 v1 中
    # 实例操作指令
    new-instance v1, Ljava/lang/StringBuilder; # new StringBuilder类的实例,地址存放在v1中
    # 方法调用指令 直接方法是针对特定对象的方法调用,它直接在目标对象上调用方法,并返回结果。
    invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V # 构造方法
    # 跳转指令
    if-nez v0, :cond_0
    goto :goto_0
    :cond_0
    # 数据转换指令
    int-to-float v2, v2
    # 数据运算指令
    add-float v2, v2, v2
    # 比较指令
    cmpl-float v0, v2, v2 # 比较v2的值与v2的值,并将结果存储在v0中。
    # 字段操作指令 
    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; # v0 = System.out
    const-string v1, "Hello World" # 将字符串常量"Hello World"赋给v1寄存器
    # 方法调用指令 虚方法是一种动态调用方法的方式,它在运行时决定调用哪个方法。  v0.println(v1)
    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V  # v0.println(v1)    即System.out.println(v1)
    # 返回指令
    :goto_0
    return-void
.end method

还原成java代码

更多内容请移步公众号:坚毅猿

https://mp.weixin.qq.com/s?__biz=MzU5NzE2NjMyMw==&mid=2247487537&idx=1&sn=26d9fea3d93a5d998cdc5a317575f834&chksm=fe56c1adc92148bbb33f0335696a4f2f1403d6c1fdc7bc2036b4250876a3bd8c23a8a7331cf5&payreadticket=HGWO0ZYJbRzFkh4u1ETH6GEVMQki22kKGBZvvQE6s6nav5Fs2BZwrVUry8U1pH1kYqpz1xw#rd

点赞
收藏
评论区
推荐文章
限时发布!纯手打“RocketMQ笔记”
1JVM的内存区域布局java代码的执行步骤有三点java源码文件编译器字节码文件字节码文件JVM机器码机器码系统CPU执行JVM执行的字节码需要用类加载来载入;字节码文件可以来自本地文件,可以在网络上获取,也可以实时生成。就是说你可以跳过写java代码阶段,直接生成字节码交由JVM执行其中Jav
Wesley13 Wesley13
2年前
java开发C语言编译器:把C实现的快速排序算法编译成jvm字节码
有了前面一系列的铺垫和准备后,我们终于能走到至关重要的一刻。在本节,我们将用C语言开发快速排序算法,然后利用我们的编译器把它编译成java字节码,让C语言编写的快速排序算法能在java虚拟机上顺利执行,完成本节内容后,编译器可以正确的将下列代码编译成java字节码:voidquicksort(intA10,intp,intr){
Wesley13 Wesley13
2年前
java字节码操作
你知道如何操作JAVA字节码文件吗,这里将介绍与操作Java字节码有关的基本知识和操作Java字节码的方法及Demo,首先我们来看一下AOP的概念,AOP是OOP的延续,是AspectOrientedProgramming的缩写,意思是面向方面编程。如何操作JAVA字节码文件  本文将介绍与操作Java字节码有关的基本知识和操作Ja
Wesley13 Wesley13
2年前
java基础知识之JVM
JVM是运行java字节码的虚拟机,包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收,堆和一个存储方法域。运行:java源文件编译器字节码文件字节码文件JVM机器码,当一个程序从开始运行,这时虚拟机就开始实例化了,多个程序启动就会存在多个虚拟机实例。程序退出或者关闭,则虚拟机实例消亡,多个虚拟机实例之间数
Android逆向前期准备
本公众号分享的所有技术仅用于学习交流,请勿用于其他非法活动,如果错漏,欢迎留言指正《Android软件安全与逆向分析》丰生强《Android应用安全防护和逆向分析》姜维Android逆向前期准备一、前置知识为什么学习安卓逆向1.学习研究去广告破解内购自动化
APP反编译和回编译
本公众号分享的所有技术仅用于学习交流,请勿用于其他非法活动,如果错漏,欢迎留言指正APP反编译和回编译一、APKAPK是啥APK是AndroidPacKage的缩写,即Android安装包。apk文件头部二进制的标识是PK,dex文件头部二进制的标识的de
Android正向开发
本公众号分享的所有技术仅用于学习交流,请勿用于其他非法活动,如果错漏,欢迎留言指正Android正向开发一、HelloWorld工程文件目录结构最终效果应用程序的目录结构:1..idea(还有个.build)都是自动生成的文件,无需理会,无需手动编辑。2.
java快速入门
本公众号分享的所有技术仅用于学习交流,请勿用于其他非法活动,如果错漏,欢迎留言指正《Java加密与解密的艺术》c语言的实现请参照之前发布的这篇文章:java快速入门一、java基础.java.class不记得概念,回头看即可遇到陌生的api,查JDKAPI
Wesley13 Wesley13
2年前
Java 虚拟机中的运行时数据区分析
本文基于JDK1.8阐述分析运行过程我们都知道Java源文件通过编译器编译后,能产生相应的.Class文件,也就是字节码文件。而字节码文件通过Java虚拟机中的解释器,编译成特定机器上的机器码。跨平台的特性!在这里插入图片描述(https://imgblog.csdnimg.c
京东云开发者 京东云开发者
2个月前
打开java语言世界通往字节码世界的大门——ASM字节码操作类库
一、ASM介绍1、ASM是什么ASM是一个通用的Java字节码操作和分析框架。它可以用于修改现有类或直接以二进制形式动态生成类。ASM提供了一些常见的字节码转换和分析算法,可以从中构建定制的复杂转换和代码分析工具。ASM提供了与其他Java字节码框架类似的
公众号坚毅猿
公众号坚毅猿
Lv1
一入逆向深似海,从此头发是路人; 这里是一些踩坑笔记,希望对你有帮助; 千山万水总是情,点个关注行不行; 更多内容请移步到公众号:坚毅猿
文章
5
粉丝
1
获赞
5