Android Class文件格式及反编译原理

Java虚拟机(JVM)

对于JAVA程序来讲JVM就是一台计算机,和计算机一样它有处理器、内存、堆栈以及指令系统,Java程序在JVM上运行,它不需要考虑真实的物理计算机是什么平台,所以同一个Android APK可以在不同的硬件平台上运行。

Android虚拟机(Dalvik)

Dalvik虚拟机是Google专门为Android系统开发的,它是在apache的java虚拟机基础上针对移动设备做了专门的优化,允许在有限的内存中同时运行多个虚拟机的实例,降低每个实例对CPU和内存的资源占用,确保能够在CPU和内存相对有限的移动设备上也能适用。

ART

ART是在Android 4.4系统新增的一种应用运行模式,但是由于该模式相对于Dalivk会占用更大的存储空间,所以在4.4上默认是关闭的。一直到Android 5.0才默认开放。

Java 的跨平台特性决定了其可执行文件只能是与平台无关的字节码,而不是机器码。而Java虚拟机的主要工作之一就是当应用在运行时,将平台无法识别的字节码翻译成机器码。 ART模式与Dalvik模式最大的不同在于,Dalvik模式是在应用运行时将字节码解释成机器码,而ART模式是在应用安装时就将字节码翻译成机器码,所以应用在运行时效率要比Dalvik高。另外ART是基于寄存器的而Dalvik基于堆栈,和Dalvik相比没有了堆栈操作。

Android 应用开发涉及到的几个文件

  1. Java文件

应用程序源代码文件。

  1. Class文件

Java源码编译生成的目标文件,相当于C代码编译出来的.o文件。Java语言为了实现跨平台的目的,所以定义了一套与操作系统和硬件平台无关的字节码

格式,class文件里面记录的就是Java源码编译后的字节码文件。

  1. Dex文件

Android平台上的可执行文件,相当于windows平台上的.exe文件,该文件专门为在Android Dalvik虚拟机上运行而设计的,它将多个class字节码文件按照固定的格式整合优化成一个dex字节码文件。

  1. APK文件

Android应用的安装文件,它是由dex可执行文件以及资源文件如图片、布局等文件打包而成。

Class字节码文件分析

文件结构:

类型

名称

数量

u4

magic

1

u2

minor_version

1

u2

major_version

1

u2

constant_pool_count

1

cp_info

constant_pool

constant_pool_count - 1

u2

access_flags

1

u2

this_class

1

u2

super_class

1

u2

interfaces_count

1

u2

interfaces

interfaces_count

u2

fields_count

1

field_info

fields

fields_count

u2

methods_count

1

method_info

methods

methods_count

u2

attribute_count

1

attribute_info

attributes

attributes_count

 

  1. Magic

class文件的魔数,class文件的魔数是一个固定的值:0XCAFEBABE

 

  1. minor_version  major_version:

文件版本号,对应JDK版本。

  1. constant_pool_count和constant_pool:

常量池数和常量,常量池中记录了java文件中的所有的常量类型包括字符串、类名、方法名等,除了JAVA源代码中定义的的常量,常量池中还包含下面几种类型:

  1. 类和接口的全限定名
  2. 字段的名称和描述符
  3. 方法的名称和描述符

除此之外常量池还描述了类的引用信息。

总共有如下12种类型:

入口类型

标志值

描述

CONSTANT_Utf8

1

JAVA源码中的各种字符串,字符串常量,类的全限定名,字段、方法的描述符,引用的字段和方法的描述符等

CONSTANT_Integer

3

int类型字面值

CONSTANT_Float

4

float类型字面值

CONSTANT_Long

5

long类型字面值

CONSTANT_Double

6

double类型字面值

CONSTANT_Class

7

对一个类或接口的符号引用

CONSTANT_String

8

String类型字面值

CONSTANT_Fieldref

9

对一个字段的符号引用

CONSTANT_Methodref

10

对一个类中声明的方法的符号引用

CONSTANT_InterfaceMethodref

11

对一个接口中声明的方法的符号引用

CONSTANT_NameAndType

12

对一个字段或方法的部分符号引用

CONSTANT_MethodHandle

15

表示方法句柄

CONSTANT_MethodType

16

标志方法类型

CONSTANT_InvokeDynamic

18

表示一个动态方法调用点

 

每种常量类型结构表

常量

字节项

类型

描述

CONSTANT_Utf8

tag

u1

1

length

u2

 

bytes

u1

 

CONSTANT_Integer

tag

u1

3

bytes

u4

 

CONSTANT_Float

tag

u1

4

bytes

u4

 

CONSTANT_Long

tag

u1

5

bytes

u8

 

CONSTANT_Double

tag

u1

6

bytes

u8

 

CONSTANT_Class

tag

u1

7

index

u2

 

CONSTANT_String

tag

u1

8

index

u2

 

CONSTANT_Fieldref

tag

u1

9

index

u2

指向CONSTANT_Class的索引值

index

u2

指向CONSTANT_NameAndType的索引值

CONSTANT_Methodref

tag

u1

10

index

u2

指向CONSTANT_Class的索引值

index

u2

指向CONSTANT_Class的索引值

CONSTANT_InterfaceMethodref

tag

u1

11

index

u2

指向CONSTANT_Class的索引值

index

u2

指向CONSTANT_Class的索引值

CONSTANT_NameAndType

tag

u1

12

index

u2

指向该字段或方法名称常量的索引值

index

u2

指向该字段或方法描述符常量的索引值

CONSTANT_MethodHandle

tag

u1

15

reference_kind

u1

值必须1~9,它决定了方法句柄的的类型

reference_index

u2

对常量池的索引

CONSTANT_MethodType

tag

u1

16

index

u2

对常量池中方法描述符的索引

CONSTANT_InvokeDynamic

tag

u1

18

index

u2

对引导方法表的索引

index

u2

指向CONSTANT_NameAndType的索引值

 

  1. Access_flags

它定义了java源文件中类或者接口的类型

标志名

含义

设置者

ACC_PUBLIC

0x0001

public类型

类和接口

ACC_FINAL

0x0010

类为final类型

ACC_SUPER

0x0020

使用新型的invokespecial语义

类和接口

ACC_INTERFACE

0x0200

接口类型,不是类类型

接口

ACC_ABSTRACT

0x0400

abstract类型

接口和部分类

 

  1. this_class

对常量池的索引。在this_class位置的常量池入口必须为CONSTANT_Class表,该表索引指向一个CONSTANT_Utf8,该CONSTANT_Utf8存放该类的全限定名。

  1. super_class

对常量池索引,在super_class位置的常量池入口是一个指向该类超类全限定名的CONSTANT_Class入口,如果该类直接继承自object类,那索引值为0.

  1. interfaces_count和interfaces

由该类自己实现的或者该类继承后扩展的接口。

APK安全

根据上面介绍的Class文件格式可以看到,Class文件完整描述了Java源码中的所有信息,所以根据固定的class文件格式,可以反编译出java源码。

我们用一个简单的java源文件举例:

原始的源码文件helloworld.java如下:

编译生成的helloworld.class如下:

反编译器查看helloworld.class的文件如下:

反编译后,我们能看到源码文件的所有实现。

Android的APK安装包中使用的是dex文件,之前我们说过dex文件格式是class文件的合并优化,也有其固定的文件格式。所以同样能够被反编译出来。

如果我们的APK能够被反编译出来,不但泄漏了我们的产品实现原理,而且对方还可以修改源码,篡改功能并重新发布。

目前APK安全主要有以下几种方法:

  1. 代码native化

利用android的NDK最大化的用C/C++语言来实现功能,比如支持宝。

  1. 代码混淆技术

class常量池中的CONSTANT_Utf8_info中包含了源码文件中的所有字符信息,比如类的全限定名,方法名,变量名,字符串常量以及代码注释。而这些方法,变量名往往都能代表具体的功能含义,比如读、写操作的方法名中往往带有read、write。反编译者能够根据这些读懂我们的产品功能。

代码混淆技术就是将class文件中这些能够代表具体含义的字符替换成没有意义的字符,比如将read、wirte替换成aaaa、bbbb,从而增加反编译后的阅读难度。

  1. 虚拟机保护(VMP)技术

VMP是代码混淆的一种衍生技术,虚拟机保护软件首先定义一套自己的字节码指令集,然后用该字节码替换应用中的原始字节码指令,同时为该应用增加一个虚拟机解释引擎,应用在真实使用时,该解释引擎会读取指令并解释执行。整个过程不会对应用的功能产生任何影响。

目前VMP技术有了更进一步的发展,除了上面提到的指令集替换,还可以对dex文件进行加密处理,使整个应用更加安全可靠。目前国内这方面,做的比较好的有爱加密、梆梆等公司。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android应用程序中常见的类错误包括: 1. ClassNotFoundException:这种错误通常是由于应用程序中缺少依赖库或类文件而引起的。解决方法是检查项目的依赖关系,并确保所有必需的库和类文件都存在。 2. NoClassDefFoundError:这种错误通常是由于应用程序编译时未能正确包含类文件而引起的。解决方法是检查应用程序的编译设置并确保所有必需的类文件都被正确包含。 3. NoSuchMethodError:这种错误通常是由于应用程序使用了不存在的方法而引起的。解决方法是检查代码中的方法调用,并确保调用的方法存在于目标类中。 4. VerifyError:这种错误通常是由于应用程序包含了无效的字节码而引起的。解决方法是检查应用程序的编译设置并确保所有字节码都是有效的。 如果以上方法都不能解决问题,你可以尝试清除应用程序的缓存或重新安装应用程序。 ### 回答2: Android中的.class错误通常是由于编译错误或引入错误的问题引起的。在Android开发中,.class文件是Java编译器生成的字节码文件,它包含了编译后的Java代码。 首先,当我们在编译Android项目时,如果代码中存在语法错误、缺失的引入或其他编译错误,编译器会生成.class错误。解决这种错误的最佳方法是仔细检查代码,查找并修复所有的语法错误和缺失的引入。 另外,有时候我们可能会在项目中引入错误的库文件或依赖项,这也会导致.class错误。在这种情况下,我们需要检查项目的依赖项和库文件,并确保它们与我们的项目兼容和正确。 此外,还可能发生一些其他错误,如.class文件不存在、.class文件被误删等。在这种情况下,我们需要确认我们的项目中是否包含所需的.class文件,并在必要时进行恢复或重新构建。 总之,当遇到Android .class错误时,我们应该先检查代码的语法错误和引入错误,然后检查项目的依赖项和库文件是否正确,最后确认所需的.class文件是否存在。如果仍然无法解决问题,我们可以尝试重新构建项目或咨询开发社区寻求帮助。 ### 回答3: 当我们在编写Android应用程序时,如果我们在项目中使用了某个类文件,却出现了Android .class报错的情况,有一些可能的原因需要我们检查和解决。 首先,我们需要检查是否已正确导入所需的类文件。在Android开发中,我们通常使用import语句来导入所需的类,以便在代码中使用。如果我们忘记导入类文件或者导入错误的类文件,就会出现Android .class报错。我们可以通过检查import语句的正确性来解决这个问题。 其次,我们需要确保所使用的类文件已经存在于项目的类路径中。Android开发使用的类文件通常是在SDK中提供的库文件或我们自己创建的类文件。如果我们忘记将这些类文件包含在项目的类路径中,或者包含了错误的类文件,就会导致Android .class报错。我们可以通过检查项目的类路径配置来解决这个问题。 另外,Android .class报错还可能是因为我们正在使用的类文件版本与当前Android环境不兼容。在Android开发中,不同的Android版本可能会提供不同的类文件或类文件版本。如果我们使用了错误的Android类文件版本,就会出现Android .class报错。我们可以通过检查项目的Android目标版本和所使用的类文件版本来解决这个问题。 最后,我们还需要检查是否有重复的类文件或类文件命名冲突。在Android开发中,我们常常会使用不同的库文件或自定义类文件来实现功能。如果这些类文件之间存在重复或命名冲突,就会导致Android .class报错。我们可以通过检查项目的类文件命名和确保没有重复的类文件来解决这个问题。 总结来说,Android .class报错可能是由于导入错误、类文件不在类路径中、版本不兼容或命名冲突等原因所导致。我们可以通过检查导入语句、类路径配置、Android版本、类文件命名和重复文件来解决这个问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值