首页
社区
课程
招聘
[原创]Java层逆向分析方法和技巧
发表于: 2023-2-8 22:11 7399

[原创]Java层逆向分析方法和技巧

2023-2-8 22:11
7399

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

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,容易判断局部变量和参数

      类型描述符

  • 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文件详解

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
# 文件头描述。<>中的内容表示必不可缺的,[]表示的是可选择的.
# 访问权限修饰符即所谓的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代码

 

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

 

386K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6E0M7q4)9J5k6i4N6W2K9i4S2A6L8W2)9J5k6i4q4I4i4K6u0W2j5$3!0E0i4K6u0r3M7#2)9K6c8W2)9#2k6W2)9#2k6X3u0A6P5W2)9K6c8p5#2*7g2e0g2z5P5V1f1J5e0X3A6y4P5f1#2%4i4K6y4p5i4K6y4p5i4K6t1$3j5h3#2H3i4K6y4n7L8h3W2V1i4K6y4p5x3U0t1@1y4K6b7^5y4K6f1K6y4#2)9J5y4X3q4E0M7q4)9K6b7X3W2V1P5q4)9K6c8o6q4Q4x3U0k6S2L8i4m8Q4x3@1u0K6L8W2)9K6c8o6t1$3k6o6W2X3k6h3p5K6k6o6V1K6j5e0g2V1z5e0V1^5j5$3c8U0y4h3p5K6x3e0M7#2y4K6g2X3z5o6x3@1i4K6t1$3j5h3#2H3i4K6y4n7j5$3S2C8M7$3#2Q4x3@1c8X3k6e0f1$3j5K6q4S2k6r3x3&6x3U0p5@1z5r3u0T1j5U0x3K6k6U0l9K6x3K6f1$3z5e0k6S2y4r3j5J5k6U0p5@1x3o6y4V1y4X3x3I4k6X3c8U0y4$3u0U0x3U0l9K6y4X3t1@1x3U0f1H3z5o6M7$3j5e0y4T1k6o6S2U0x3U0y4S2z5r3p5%4x3K6x3I4j5$3j5#2i4K6t1$3j5h3#2H3i4K6y4n7M7r3q4&6M7X3g2S2k6s2c8A6j5$3E0W2N6q4)9K6c8p5S2s2g2@1)9H3h3W2W2v1j5W2u0*7c8X3E0Z5y4s2f1I4c8g2c8t1y4V1N6q4g2V1#2c8K9$3V1J5x3X3E0w2c8@1u0K9N6Y4k6c8c8e0k6K6y4X3&6S2N6U0g2r3M7K6u0n7h3Y4N6J5g2W2g2J5P5e0S2g2x3i4m8t1x3h3E0k6M7i4m8*7x3i4S2%4i4K6t1K6M7X3b7`.


[招生]科锐逆向工程师培训(2025年3月11日实地,远程教学同时开班, 第52期)!

最后于 2023-2-9 11:04 被公众号坚毅猿编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (1)
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
感谢,已学习
2023-11-10 09:49
0
游客
登录 | 注册 方可回帖
返回