首页 > 编程知识 正文

安卓哪些不能混淆,android依赖库混淆

时间:2023-05-03 20:44:29 阅读:111623 作者:4848

文章从我的个人博客转载

正文的前半部分在对照Proguard文档(Manul中的Introduce部分)翻译的同时添加个人理解。 如与原文不同,请以原文为中心。 后半部分是几个步骤的验证。

介绍

模糊化工具(ProGuard )对Java class文件进行shrinker (压缩)优化(模糊化)和预验证(验证)。 shrinker (压缩)在此步骤中,找到并删除未使用的类、变量、方法和属性。 名为optimization的步骤通过分析方法的字节码进行优化。 对于class、fields和methods,“混淆”将被替换为简短且无意义的名称。 第一步是减小代码量,在更高效地执行的同时,也更难被逆向选择。 从Java Micro Edition和Java 6开始,预验证的信息将在最后一个验证步骤的过程中添加到class文件中。

上述每个步骤都是可选的。 例如,ProGuard只能进行preverify,可以更有效地执行。

首先,ProGuard )读取输入的jars (可以是aars、wars、ears、zips、apks或目录)。 然后开始进行shrinker“压缩”、优化器“优化”、观察器“模糊化”和预验证器“验证”。 可以选择性地让ProGuard (模糊化工具)执行各种类型的优化操作。 模糊处理工具将更改后的结果写入一个或多个输出的jars (或aars、wars、ears、zips、apks或目录)。

混淆工具必须明确输入jars包(aars、wars、ears、zips、apks或目录)。 这些libraries本质上是你用来编译的代码。 混淆工具重建类之间的依赖关系,以便正确地进行整个过程。 依赖包(Library jars )经常不会更改,但需要放在最终App的环境中。

入口点)。

在“压缩步骤”(shrinker )中,模糊处理工具从这些点(入口点)进入,以递归方式查找并确定使用哪个类和哪个类成员。 所有其他类和类成员都将被丢弃

在优化步骤(optimizer )中,模糊化工具进一步优化代码。 在这些优化过程中,非入口点的类和方法将变为私有静态或最终,未使用的参数将被删除,并且某些方法将变为内敛方法

在“模糊化步骤”(obfuscator )中,模糊化工具会重命名不是入口点的类和类成员。 在这个过程中,成为入口的地方,依然会为他们保留原来的名字

预验证阶段(preverifier )是唯一不需要知道入口点的阶段

反射

在反射和introspection代码的自动处理中存在几个特殊的问题。 在处理模糊化工具时,代码中的类和类成员将根据相应类的名称或成员名称动态创建或调用。 这些位置必须定义为入口点。 例如,名为Class.forName ()的构造函数在运行时指向任何类。 此外,例如,有时会从配置文件中读取类名。 这通常很难计算那些类是否需要被保留。 因此,在模糊化工具的结构中,必须简单地用同一操作-keep进行指定。

但是,模糊处理工具有助于发现和处理以下情况:

class.forname(someclass ) )

SomeClass.class

some class.class.getfield (some field ) )。

some class.class.getdeclaredfield (some field ) )。

some class.class.get method (' some method ',new Class[] {} )

some class.class.get method (' some method ',new Class[] { A.class } )

some class.class.get method (' some method ',new Class[] { A.class,B.class } ) ) )。

some class.class.getdeclaredmethod (' some method ',new Class[] {} )

some class.class.getdeclaredmethod (some method ),newclass ) {a.class}}

some class.class.getdeclaredmethod (some method ),newclass ) {a.class,B.class }

atomicintegerfieldupdater.new updater (some class.class,' someField ' ) )。

AtomicLongF

ieldUpdater.newUpdater(SomeClass.class, "someField")

AtomicReferenceFieldUpdater.newUpdater(SomeClass.class, SomeType.class, "someField")

类和类成员的名字会不一样,但是构造方法必然是相同的,由此,混淆器能够认出他们。被引用的类和类的成员在压缩(shrinking)阶段会被保留,同时,string 类型的参数也会在混淆时(obfuscation)被准确的修改。

除此之外,混淆器会提供一些建议:是否保留一些出现的类和类成员。举例,混淆器会标记(SomeClass)Class.forName(variable).newInstance()这样的构造器。因为这些方法可能会指向其他类,这些可能是类,也可能是接口,或者是继承自这些接口或者类的类。你需要在配置中做相应的处理。

为了能够得到正确的混淆结果,你应该对进行混淆的代码多少有所熟悉。当面临大量反射代码时,混淆代码需要进行大量的试验,并处理错误,特别是对于内部代码没有足够的信息的情况下。

以上是对官方文档首页的翻译内容。

具体的验证

该部分不是翻译内容,是根据ProGuard 的使用方法和文档首页,对上述三个步骤的具体验证。

由于大部分情况下,Android的混淆只需要考虑Obfuse这个步骤,因为很多第三方依赖包的混淆规则会把 shrink和optimize去掉(比如友盟)。所以先验证这一步。

下面的验证步骤,涉及三个类,java打包的命令(Java 环境),proguard.jar包(混淆器,进行整个混淆过程的jar包),proguard.pro文件(写入具体混淆的规则)和Intelij(用来查看class文件)等内容。

Obfuse 步骤验证

这个步骤,如上所说,主要是对类,方法进行名字的修改,也是 Android 混淆中最重要的部分。为了验证这个过程,我做了下面的demo操作。

首先写了3个类:

package com.dove.home;

class HelloWorld {

public HelloWorld(){

System.out.println("Hello World");

}

}

package com.dove.home;

class HelloWorld2 {

public HelloWorld2(){

System.out.println("Hello World2");

}

}

package com.dove.home;

class Main {

public static void main(String[] args) {

HelloWorld helloWorld = new HelloWorld();

}

}

然后编译,打包

javac com/dove/home/Main.java

javac com/dove/home/HelloWorld2.java

javac com/dove/home/HelloWorld.java

//注意在进行下面步骤的时候,我把 com/dove/home 下的 java源码删了

jar -cvf main_source.jar com

然后使用混淆器,混淆器其具体使用方法,主要是调用proguard.jar包,然后配置 proguard.pro文件进行具体的参数设置。

下面是我proguard.pro文件内容

# 源码文件

-injars main_source.jar

# 混淆后输出文件

-outjars main_source_out.jar

# java 核心 jar 不能混淆

-libraryjars /lib/rt.jar

-libraryjars /lib/jce.jar

# 全部不混淆,即三个class文件都会保持原样

-keep class com.dove.home.Main{*;}

-keep class com.dove.home.HelloWorld{*;}

-keep class com.dove.home.HelloWorld2{*;}

具体的混淆命令,同时参考下图(该步骤会生成混淆后的jar包)

java -jar proguard.jar @proguard.pro

注意:然后修改 proguard.pro 文件,内容如下

-injars main_source.jar

# 注意输出包的名字改了

-outjars main_source_proguard_out.jar

-libraryjars /lib/rt.jar

-libraryjars /lib/jce.jar

-keep class com.dove.home.Main{*;}

# 删除了HelloWorld的 keep

-keep class com.dove.home.HelloWorld2{*;}

同样运行上面的混淆命令,生成另一个混淆后的包

最后对三个包进行对比,通过代码逆向,进行验证,最快的方式是把生成的 jar 包,当做第三方依赖包直接导入Intellij 中(有decode的功能),如下图,三个包的区别

最初是的源码包和保留HelloWorld,HelloWorld2以及Main入口点的包是一样的,不同的是最后没有保留HelloWorld入口点的包,它的HelloWorld变成了a,而Main和HelloWorld2都正常没有被修改。

Optimize 步骤验证

同样,修改 proguard.pro 文件,内容如下,然后运行混淆命令,生成新的 jar 包

-injars main_source.jar

# 输出包名改了,方便对比

-outjars main_source_proguard_not_optimize_out.jar

-libraryjars /lib/rt.jar

-libraryjars /lib/jce.jar

# 加上不进行优化的限制

-dontoptimize

-keep class com.dove.home.Main{*;}

-keep class com.dove.home.HelloWorld2{*;}

同上,导入IntelliJ,对比上一步中混淆后的 jar 包,发现名字没啥变化,但内容不一样了

首先是没有添加 -dontoptimize

package com.dove.home;

final class a {

public a() {

System.out.println("Hello World");

}

}

然后是添加了 -dontoptimize

package com.dove.home;

class a {

public a() {

System.out.println("Hello World");

}

}

如上述译文中所说,optimize 会进行代码优化,不是入口点的代码,会变成final,private等等,该步骤验证完毕。

Shrink 步骤验证

修改 proguard.pro 文件,进行压缩,同时不对 HelloWorld,HelloWorld2进行入口点的保留

-injars main_source.jar

-outjars main_source_proguard_shrink_out.jar

-libraryjars /lib/rt.jar

-libraryjars /lib/jce.jar

-keep class com.dove.home.Main{*;}

# 注意对比之前,删除了HelloWorld和HelloWorld2的 keep

修改 proguard.pro 文件,不进行压缩,同样不对 HelloWorld,HelloWorld2进行入口点的保留

-injars main_source.jar

-outjars main_source_proguard_not_shrink_out.jar

-libraryjars /lib/rt.jar

-libraryjars /lib/jce.jar

# 添加不进行压缩

-dontshrink

-keep class com.dove.home.Main{*;}

其结果对比,添加了 -dontshrink标志

未添加 -dontshrink标志

此处消失的b其实就是HelloWorld2,而留下的a则是HelloWorld,原因很简单,因为Main里面持有了HelloWorld的引用,而HelloWorld2则从未被用到,所以就被抛弃了。

由此验证,shrink阶段,Proguard(混淆器)会把无用类文件等删除,一些被动态获取的类就需要注意了,需要进行-keep操作,使其成为入口点。

以上就是对混淆整个过程的验证。对于 Android 混淆,一些需要注意的东西,会在下一篇文章中记录。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。