源码先锋

源码先锋

关于代码混淆,看这篇就够了

admin 106 91
代码混淆一.基本概念

java的bytecode很容易通过JAD等反编译工具还原出源代码。这样势必不满足安全的定义。如何一定程度上保护需要防止被反编译的源代码呢?混淆(obfuscate)技术。注意:用obfuscate防盗版是根本不可能,连汇编这种东西都能被**掉,而java代码基本上等同于开源的同义词。用obfuscate只是为了增加反编译的难度,保护源代码的知识产权。混淆包照常运行,没有任何问题。可以使用反编译工具如jd-gui查看混淆后的包,验证混淆效果。

二.混淆技术名称混淆nameobfuscode

将有意义的类,字段、方法名称更改为无意义的字符串。生成的新名称越短,字节代码越小。在名称混淆的字节代码中,包,类,字段和方法名称已重命名,并且永远不能恢复原始名称。

流混淆FlowObfuscation

用于if,switch,while,for等关键字,对字节码进行细微的修改,模糊控制流,而不改变代码在运行时的行为。通常情况下,选择和循环等逻辑构造会被更改,因此它们不再具有直接等效的Java源代码。流模糊的字节码通常强制反编译器将一系列标签和非法的goto语句插入到它们生成的源代码中。源代码有时会因为反编译错误而变得更加模糊。

其他

异常混淆ExceptionObfuscation

字符串加密混淆StringEncryption

引用混淆ReferenceObfuscation

三.常用工具1.proguard

proguard是一个免费的Java类文件的压缩,优化,混肴器。它删除没有用的类,字段,方法与属性。使字节码最大程度地优化,使用简短且无意义的名字来重命名类、字段和方法
官网地址:

2.yGuard

yGuard是一款免费的Java混淆器(非开源),它有Java和.NET两个版本。yGuard完全免费,基于Ant任务运行,提供高可配置的混淆规则。
官网地址:

3.allatori

第二代Java混淆器。所谓第二代混淆器,不仅仅能进行字段混淆,还能实现流混淆。
命名混淆,流混淆,调试信息混淆,字符串编码,以及水印技术。对于教育和非商业项目来说这个混淆器是免费的。支持war和jar格式,支持对需要混淆代码的应用程序添加有效日期。
官网地址:

4.总结

推荐使用proguard:开源,使用简单,文档丰富完善。

四.工具对比

工具

官网地址

官方文档

开源免费

名称混淆

流混淆

maven支持

功能

proguard

yGuard

allatori

✕(免费用于教育和非商业项目)

减小包大小;混淆代码;添加水印

五.详细内容1.yGuard()

易于设置:yGuard是一个Ant任务!作为Ant任务,yGuard可以无缝集成到您在Ant、Maven和Gradle等众多构建系统中的部署过程中

高级收缩:yGuard通过依赖分析提供精细的代码收缩功能。

可配置/安全代码:yGuard提供高度可配置的名称混淆,可保护您的知识产权免受逆向工程。

开源:yGuard是完全开源的!与昂贵的商业产品相反,yGuard是并且永远都是免费的。

Java兼容性:要运行yGuard软件,您需要或更高版本以及或更高版本(它可能与任一软件的早期版本兼容,但尚未经过测试)。
yGuard与所有已发布的Java版本(最高Java17)兼容。但是,根据使用的版本,功能可能会略有不同。该文档包含不同版本支持的功能的详细说明。如果您打算将yGuard与Java以外的东西一起使用,还有一个关于3rd方JVM支持的部分。

ProGuard

是一个开源的Java类文件收缩器、优化器、混淆器和预验证器。因此,ProGuard处理的应用程序和库更小、更快,并且在一定程度上可以抵御逆向工程。

收缩步骤检测并删除未使用的类、字段、方法和属性。

优化器步骤优化字节码并删除未使用的指令。

混淆步骤使用简短无意义的名称重命名剩余的类、字段和方法。

最后的预验证步骤将预验证信息添加到类中,这是JavaMicroEdition和Java6及更高版本所必需的。

yGuard1.maven引用方式
buildpluginsplugin!--结合antrun来使用yguard--artifactIdmaven-antrun-plugin//groupIdartifactIdyguard//version/depency/depenciesexecutionsexecutionphasepackage/phasegoalsgoalrun/goal/goalsconfigurationtaskspropertyrefid=""name=""/!--echomessage="UsingMavenClasspath:${}"/--taskdefname="yguard"classname=""/yguard!--yguard配置--/yguard/tasks/configuration/execution/executions/plugin/plugins/build
2.yguard配置解析写在前面

yguard共分为两大任务配置:

rename混淆名称:主要用于修改类名,属性名,方法名以及参数名等。

shrink收缩代码:主要用于从多个入口点中删除所有无法访问的类、字段和方法。

keepkeep是rename和shrink的子元素,用于指定从父级rename或shrink任务中排除的元素。

注意事项

注意事项1:如果项目需要shrink,shrink最好是配置在rename之前。即在进行代码混淆之前,先进行代码压缩。因为压缩代码需要指定压缩的根代码。
举个例子:比如制定main方法:methodname="voidmain([])"class=""/。如果先混淆,DplDbtransferApplication类名被修改的话(比如为),该配置将会无效,所有代码都会被shrink删除,因为已经找不到DplDbtransferApplication这个类了(类名被修改)。

其他配置请参考官方文档。

配置说明基础配置
!--yguard是公用配置和rename以及shrink配置的容器标签--yguard!--必须至少指定一个inoutpair元素或一个非空inoutpairs元素才能运行yguard任务。此元素指定输入和输出jar文件的路径inout必须指定设置值,不指定报错1.in指定一个现有的jar/war文件,其中包含未收缩和未混淆的.class文件。2.out指定一个jar/war文件的路径,该文件将被创建并用于放置收缩和混淆过程的结果。3.resources如何处理资源文件,支持三种值:copy,auto,none。默认配置copy。1.copy直接复制资源文件默认情况下,只需将所有资源文件复制到输出jar中2.auto仅复制那些在压缩后仍包含一个或多个.class文件的目录中的资源文件。3.none丢弃所有资源文件。--inoutpairin="${}/${}.${}"out="${}/${}.${}"/externalclasses/externalclasses!--用于配置一些属性,保存类的调试信息:比如LineNumberTable:行号表;LocalVariableTable:本地变量表等。--attributename="SourceFile,LineNumberTable,LocalVariableTable"patternsetincludename="**"//patternset/attribute!--shrink用于收缩代码配置。1.logfileshrink过程的日志文件2.在shrink过程中,如果有错误代码,代码将会被替换为:thrownewInternalError("Badlyshrinked")。比如:当某些类的private属性被删除,但是public方法中有引用该属性,而属性被删除了,即会输出该异常错误。--shrinklogfile="${}/"createStubs="true"!--shrink中的keep和rename中的keep一致。--keepmethodname="voidmain([])"class="${mainclass}"//keep/shrink!--用于自定义某些配置和属性更改。1.mainclass用于设置主程序启动位置,该文件将不会被混淆。2.logfile混淆过程中的日志文件保存地址。名称以“.gz”结尾,yGuard将自动创建文件的gzip压缩版本,默认为当为false时,重新生成的内容。默认为false。4.replaceClassNameStrings是否混淆代码中某些字符串跟类名相关的东西。默认为true比如源码:("");混淆后:("");5.scramble是否随机混淆代码,默认false。如果为true即每次打包生成的类名将随机替换。比如第一次混淆为,第二次就为某些不必要混淆的数据,比如如下配置为Test注解,当配置到类上时,类中的所有东西不会被混淆;当配置到属性时,属性名称不会被混淆。--renamemainclass=""logfile="${}/"conservemanifest="false"replaceClassNameStrings="true"scramble="false"annotationClass=""!----keep/keep!--1.error-checking用于检测错误,检测到错误就失败停止。--propertyname="error-checking"value="pedantic"/!--2.error-checking可用于告诉重命名引擎在混淆期间使用不同的命名方案。目前可以将此属性设置为以下值之一(默认small,通常使用small就行了):small:将产生非常短的名称,即生成的jar文件将尽可能小。best:会产生很可能被反编译器和反汇编器误解的名称。使用这种命名方案,在大多数文件系统上甚至不可能成功解压缩或解压缩生成的jar文件(Windows、StandardUnix、StandardLinux、MacOS)。然而,这种方案占用了大量空间,并且生成的jar可能会变大(通常大约是两倍大小)。mix:是其他两个值的混合,这会导致合理的小但仍然难以反编译jar文件。--!--其他属性(language-conformity,overload-enabled,obfuscation-prefix,digests,expose-attributes)请参考官方文档。--propertyname="naming-scheme"value="small"//rename/yguard
keep配置说明class元素

class用于在rename和shrink过程中排除某些类,字段和方法。其是keep的子元素。以下是配置说明(-表示会被收缩,即被删除):

可见性(是否被收缩)

public

protected

frily

private

none

-

-

-

-

public

*

-

-

-

protected

*

*

-

-

frily

*

*

*

-

private

*

*

*

*

属性说明

name指定要保留的类名。在shrink中,只会保留类名称,类中的属性和方法都会被删除掉。

classes保持类的可见性:其值是上述:none,public,protected,frily,private。默认为none

methods保留方法的可见性,值同classes的描述。默认为none

fields保留属性的可见性,值同classes的描述。默认为none

exts保留对继承了该类的可见性。1.在shrink中凡是继承了该类的子类都不会被删除。2.在rename中凡是继承了该类的子类都不会被修改名称。

implements保留对实现该接口的可见性。1.在shrink中凡是实现该接口的类都不会被删除。2.在rename中凡是实现该接口的类都不会被修改名称。

注意事项

以上属性可以单独使用,一可以混合使用。其exts/implements可以和classes,methods,fields混合使用。参考列2说明。

列1:

shrinklogfile="${}/"keep!--保留NameTest类不被删除,但是内部的方法和会属性会被删除,不论私有还是共有。--classname=""/!--保留所有公用的类,方法和属性。其关联的类,方法和属性会被保留,不会被删除--classclasses="public"methods="public"fields="public"/!--保留所有继承了BaseClass的类不被删除,但是内部的方法和会属性会被删除,不论私有还是共有。--classexts=""//keep/shrink

列2:

shrinklogfile="${}/"keep!--保留NameTest类不被删除,并保留其private级别的方法和属性--classname=""methods="private"fields="private"/!--保留所有继承了BaseClass的类不被删除,并保留其private级别的方法和属性--classexts=""methods="private"fields="private"//keep/shrink

列3:

一下举列几个模式集的列子,模式集可以参考ant。!--includeshrink:不需要被删除的类,保留的类。rename:不需要被混淆的类名--classpatternsetincludename="**.*Bean"/excludename="*"/excludename=""/!--由于Ant'39;s()。'$'--excludename="?.**.*$*"//patternset/class
method

method用于在rename和shrink过程中排除方法。其是keep的子元素。以下是配置说明(-表示会被收缩,即被删除):

!--这将保留MyClass类的main和foo方法。此外,所有readObject和writeObject方法(用于序列化)都将保存在包的所有类中。请注意,您必须指定返回参数的类型,即使它是void,并且您必须为所有类使用完全限定名称,即使是--methodclass=""name="voidmain([])"/methodclass=""name="intfoo(double[][],)"/methodname="voidwriteObject()"patternsetincludename="*"//patternset/methodmethodname="voidreadObject()"patternsetincludename="*"//patternset/method
field

field您可以按名称指定应从收缩或名称混淆中要保留的字段

!--保留MyClass类中的所有字段。此外,所有serialVersionUID字段(用于序列化)都将保存在包的所有类中。--fieldclass=""name="field"/fieldname="serialVersionUID"patternsetincludename="*"//patternset/field
package

package用于从重命名过程中排除某些包的名称。它不能用于收缩(shrink)过程。这对类、方法或字段名称没有影响。

packagepatternset!--不被混淆。myapp下的包名还是会被混淆--includename="*"/!--不被混淆。myapp下的包名也不会被混淆--includename="**"//patternset/package
3.几种情况下的使用方式springboot项目

1.注意事项

yguard插件执行要放在springboot打包项目之前,因为反置的话,会造成jar中的springboot的启动相关类被混淆,而造成启动项目失败。

2.项目使用失败的问题收集总结

本地打包之后启动项目失败:由于是本地idea将jdk设置成jdk17了,导致打包失败。设置为jdk8后成功启动。

项目使用mybatiesplus,项目里只有一个接口:publicinterfaceTimePullLogMapperextsBaseMapperTimePullLog{},造成混淆后打包报错:spring至少一个bean实现。后面加上:classimplements=""/后正常。==

service的接口和实现都要暴露,不然spring的注入和nacos的服务发现都会存在问题。

3.配置模版

keep!--包名不混淆配置--packagepatternsetincludename="**"//patternset/package!--mybaites相关的mapper混淆后,会造成boot项目启动失败--classimplements=""/!--mybaites默认生成sql时是使用的实体类的类名,所以不能混淆--classimplements=""/!--本包的controller混淆后,无法读取mapping映射,原因未知。--classpatternsetincludename="*"//patternset/class!--service的接口和实现都要暴露,不然spring的注入和nacos的服务发现都会存在问题。--classpatternsetincludename="**"//patternset/class!--main方法配置--methodname="voidmain([])"class=""//keep
简介

ProGuard是一个开源的Java类文件收缩器、优化器、混淆器和预验证器。因此,ProGuard处理的应用程序和库更小、更快,并且在一定程度上可以抵御逆向工程。

收缩步骤检测并删除未使用的类、字段、方法和属性。

优化器步骤优化字节码并删除未使用的指令。

混淆步骤使用简短无意义的名称重命名剩余的类、字段和方法。

最后的预验证步骤将预验证信息添加到类中,这是JavaMicroEdition和Java6及更高版本所必需的。

对反射的处理

反射和内省对于任何代码的自动处理都存在特殊的问题。在ProGuard中,代码中动态创建或调用(即按名称)的类或类成员也必须指定为入口点。例如,()构造可以在运行时引用任何类。通常不可能计算必须保留哪些类(使用它们的原始名称),因为类名可能是从配置文件中读取的,例如。因此,您必须在ProGuard配置中指定它们,同样简单-keep选项

("SomeClass")

("someField")

("someField")

("someMethod",null)

("someMethod",newClass[]{,})

("someMethod",null)

("someMethod",newClass[]{,})

(,"someField")

(,"someField")

(,,"someField")

支持

可单独使用。首先,下载一个ProGuard版本或者构建ProGuard从源头。然后可以通过调用目录中的脚本直接从命令行执行ProGuardbin:
linux/mac:bin//to/\-outjarspath/to/\-libraryjarspath/to/java/home/lib/
windows:bin\/to/my-applicati^-outjarspath/to/^-libraryjarspath/to/java/home/lib/

Gradle模式

ant模式



错误解析

[proguard]Error:Theinputdoesn''-injars'options?

处理:inFiltercom/ewa/pipe/**/inFilter,inFilter标签设置为包路径地址,把‘.’换成‘/’。injar:指定target中的一个目标地址:这里指定编译后的classes文件夹。inFilter指定的是classes的内部的文件夹(package)地址。!--加载文件的过滤器,就是你的工程目录了--inFiltercom/arm/code/**/inFilter!--对什么东西进行加载,这里仅有classes成功,毕竟你也不可能对配置文件及JSP混淆吧--injarclasses/injar

以下是一个例子说明,如果你想更多的有用信息,请查看文档()

configuration!--是否将生成的PG文件安装部署--attachfalse/attach!--是否混淆--obfuscatetrue/obfuscate!--指定生成文件分类--!--attachArtifactClassifierpg/attachArtifactClassifier--!--加载文件的过滤器,就是你的工程目录了--inFiltercom/arm/code/**/inFilter!--对什么东西进行加载,这里仅有classes成功,毕竟你也不可能对配置文件及JSP混淆吧--injarclasses/injar!--输出目录--outputDirectory${}/outputDirectoryoutjar${}.${}/outjaroptions!--JDK目标版本1.7--/option!--不做收缩(删除注释、未被引用代码)--option-dontshrink/option!--不做优化(变更代码实现逻辑)--option-dontoptimize/option!--不忽略非公用类文件及成员--option-dontskipnonpubliclibraryclasses/optionoption-dontskipnonpubliclibraryclassmembers/option!--优化时允许访问并修改有修饰符的类和类的成员--option-allowaccessmodification/option!--确定统一的混淆类的成员名称来增加混淆,防止冲突--option-useuniqueclassmembernames/option!--不混淆所有包名,Spring配置中有大量固定写法的包名--option-keeppackagenames/option!--不混淆所有特殊的类--option-keepattributesExceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod/option!--不混淆所有的set/get方法,毕竟项目中使用的部分第三方框架(例如Shiro)会用到大量的set/get映射--option-keepclassmemberspublicclass*{voidset*(***);***get*();}/option!--不混淆的SpringBoot类及其中的方法--{methods;}/option!--不混淆job包下的所有类名,且类中的方法也不混淆--!--_**{methods;}/option--!--不混淆filter包下的所有类名,这里主要是对Shiro的路踢人过滤器混淆,对类的属性和方法进行了混淆--!--_**/option--!--不混淆model包中的所有类以及类的属性及方法,实体包,混淆了会导致ORM框架及前端无法识别--!--_**{*;}/option--!--,util包不混淆的类的属性及方法,实体包--!--{*;}/option--!--不混淆凭证包下的所有类名,但对类中的属性、方法进行混淆,原因是Spring配置中用到了这个类名--!--/option--!--/option--!--不混淆job包下的所有类名,且类中的方法也不混淆--!--**{methods;}/option--/options!--添加依赖,这里你可以按你的需要修改,这里测试只需要一个JRE的Runtime包就行了--libslib${}/lib//liblib${}/lib//lib!--lib${}/lib/security/local_/lib--!--lib${}/lib/security/US_export_/lib--lib${}/lib/ext/sunjce_/lib/libs/configuration