整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

前端代码安全与混淆

前端代码安全与混淆

者:京东零售 周明亮

一、友商网页分析

1.1 亚马逊

亚马逊商详地址:
https://www.amazon.com/OtterBox-Commuter-Case-iPhone-Packaging

  • 所有交互事件在页面初始化时,不进行下发,等待通过 js 请求后下发 具体点击事件js内容
  • 采用自执行方式,防止代码格式化。【无法调用 Chrome 自带的代码格式化工具】
  • 采用自研式框架,非传统 react / vue / angular。大量通过 data-xx 标签进行数据传递,导致标签结构较为复杂。

1.2 淘宝

主要配合接口进行加密,采用多字段干扰,模板化加载。下发大量的模版数据,之后通过客户端进行填充。

客户端代码为传统的普通加密模式

1.3 拼多多

  • 传统普通加密方式,使用 React 框架。【有明显的 React 语法糖】
  • 关键的商详数据,需要强制进行登录操作,可以对账号进行封禁。

二、攻击者角度

  1. [Web逆向] 数某风控JS算法分析 常规网页加密调式
  2. Crack App | 某 H5 App 反调试对抗 反调式 APP 内 Webview
  3. Puppeteer 融入调试流程,调试体验爽翻了! 可模拟用户实际点击流程,进行流程化操作,此类方式,比较难以区分
  4. Node.js 安全最佳实践 常见的 Node JS 官方发布的被攻击类型。
  • 参考:NodeJS 官网指导手册?
  1. 通过几行 JS 就可以读取电脑上的所有数据? 旁路攻击,通过内存响应速度获取用户密码信息
  2. ?Qwik JS 框架 JS代码的拆分从常见的「编译时」(比如webpack分块)、「运行时」(比如dynamic import),变为「交互时」只有用户操作时,才会进行注入加载。
  3. 实践:天猫汽车商详页的SSR改造实践 天猫汽车商详页,改造原理本质上是基于 Qwik JS 。
  4. 聊聊前端安全之CSRF?
  • 非代码泄漏类,常规类型Web 攻击,基于代码破解后
  • XSS攻击:跨站脚本攻击(Cross-Site Scripting),攻击目标是为了盗取存储在客户端的cookie或者其他网站用于识别客户端身份的敏感信息。一旦获取到合法用户的信息后,攻击者甚至可以假冒合法用户与网站进行交互。
  • CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
  • 网络劫持攻击,主要是通过一些代理服务器,或者wifi等有中间件的网络请求,进行劫持,不法分子通过这种方式获取到用户的信息。
  • 控制台注入代码,不法分子通过各种提示诱骗用户在控制台做一些操作,从而获取用户信息。
  • 钓鱼攻击,
  • 电子邮件钓鱼:群发邮件,欺骗用户点击恶意的链接或附件,获取有价值的信息
  • 网站钓鱼:在网站上伪造一个网站,通常是模仿合法的某个网站。为了欺骗用户点击这个网站还会采取些辅助技术,比如钓鱼邮件、短信、电话
  • 防钓鱼
  • SPF记录,SPF是为了防范垃圾邮件而提出来的一种DNS记录类型,它是一种TXT类型的记录,它用于登记某个域名拥有的用来外发邮件的所有IP地址。
  • SafeBrowsing API,谷歌的一个随时可以通过互联网访问的API,允许允许浏览器在渲染之前检测URL的正确性。
  • DDOS:分布式拒绝服务攻击(Distributed Denial of Service),简单说就是发送大量请求是使服务器瘫痪
  • SQL注入攻击,通过对web连接的数据库发送恶意的SQL语句而产生的攻击,从而产生安全隐患和对网站的威胁,可以造成逃过验证或者私密信息泄露等危害
  • 点击劫持,点击劫持是指在一个Web页面中隐藏了一个透明的iframe,用外层假页面诱导用户点击,实际上是在隐藏的frame上触发了点击事件进行一些用户不知情的操作。
  1. AI 介入解释代码,加速代码反编译进程
  • 比如将友商代码放入 chatgpt 进行释义

这个只是部分代码,如果将完整代码,一段一段进行分析,你就可以得到完整上下文,不需要靠人去一段一段读取代码。

目前还有 ai 代码调试如:
https://github.com/shobrook/adrenaline?


三、防御者角度

  1. JS 代码混淆
  • 应对:普通开发者或者不懂编程的普通用户。实例:大部分网页
  • 进行代码混淆/加密,减少语义化理解。
  • 通过代码调试,查找特定 DOM 结点,反复断点调试,即可了解相关执行逻辑
  1. JS 虚拟机
  • 应对:专业编程开发者。实例:暂无
  • 通过 AST 转换 代码为二进制码,再通过虚拟机运行二进制码。
  • 会导致网页执行性能变差,执行加载更多 JS 文件
  • 无法进行断点提示,但是会把解密流程对外暴露。
  • 直接调用 JS 虚拟机,执行最小化JS片段,从而了解整个虚拟机的加密规则。
  1. 强制下载 APP 通过 Webview 打开
  • 应对:中高级编程开发者。实例如:拼多多等
  • H5 代码只是对外展示数据,关键内容提示用户下载 APP,增加调试难度
  • 用户不愿意下载APP,就会导致用户流失。
  1. 接口校验/字段混淆
  • 应对:Python 爬虫类,实例如:淘宝、好词好句网等等
  • 通过接口生成混淆模版,多字段随机发送,配置相关JS 模版框架。
  • 接口内容传输 base64 / aes 加解密处理,但是会留下解密 JS 在客户端,依旧能够被破解。
  • Token 强制校验,发送三次错误,直接不在返回数据,需要用户强制登录,容易导致用户流失。
  1. 自定义框架
  • 应对:Python 爬虫类,中高级编程开发者。实例如:亚马逊/淘宝。【还需要继续挖掘】
  • 爬虫无法第一时间获取相关按钮的 API 请求接口,需要等待 JS 返回。
  • 客户端存在大量无关数据,导致 dom 结点整体看起来无规律
  • JS 通过 接口请求返回,配合相关的 Token 参数,可以达到随机性下发

四、结论

4.1 大部分攻击者共同点

1)自身不愿意登录,或者偷取正常用户信息后,用于攻击

  • 如一些外挂程序,免费提供给外部用户,用户贪图小利,以为可以通过外部程序加快抢利
  • 实则被记录用户名,给到攻击者使用。

2)如果是公司行为,很可能会被记录IP,有法务风险。

  • 可以分析电脑名称,IP 地址
  • 可能会进行 IP 服务器代理,采用虚拟 IP,虚拟定位
  • 使用云服务器,如:阿里云 / 京东云,进行攻击相应的网站,京东云到京东网站。

3)多次进行尝试修改 token ,伪装发送请求

  • 伪造 UA
  • 开启调试模式

4)分析 DOM 结构特征 / 使用 Console 打印全局存储变量

5)通过 cookies 分析特定的关键词,全局搜索

6)网络请求时,查看函数执行栈,逐级往下寻找核心请求函数。

4.2 应对普通开发者外挂程序

  • 主要采用 puppeteer 就可以完全模拟用户操作流程,可以进行等待某个节点出现,之后再进行操作,不再需要传统的代码调试操作。直接操作 DOM 结点点击响应
  • 基于此类需求,需要经常变更 DOM 结点位置。增加业务方成本,每次都需要发版。如果是随机生成结点特征,需要开发自研框架,成本较高

4.3 应对Pyhton爬虫

1)前端代码采用传统加密方式

  • https://github.com/mishoo/UglifyJS?
  • https://github.com/terser/terser?
  • ?https://github.com/javascript-obfuscator/javascript-obfuscator
  • 更多倾向于 接口 加密方式,加固加 Token

2)入口在 APP 内的 业务

  • 本身调试需要需要额外链接机器,提高调试复杂度。
  • 配合 APP 自身监控,特定API 可以做到更加安全
  • 也只有此类业务,可以采用 JS 虚拟机方式

3)对关键词进行混淆处理,减少特征搜索

  • 可采用下面方式,只是举例,可以有更多方式。比如数组组合,对象组合等等
  • const GLOBAL_SOCKET_NAME='c6on6ne6ct'.concat('S6o').concat('c6ke6t').replace(/6/g, '')
  • 常规代码混淆中,对完整字符串,不会进行处理,导致会直接暴露关键字。


任何客户端加密混淆都会被破解,只要用心都能解决,我们能做的就是拖延被破解的时间,而不是什么都不做,那样只会被破解更快!

其实很多我们自己公司对外的页面,都有很多外露风险,包括不规范的日志输出,直接对外暴露加密的防刷 token。 比如:

?大家都可以自查下~

今互联网时代,JavaScript已经成为了web前端开发的重点技术之一。其中,JavaScript代码的安全性问题一直是关注的焦点。为了保护JavaScript代码的安全性,很多人对其进行加密处理,众所周知,对于单纯的加密算法,通过反向工程或逆向分析也能够破解。在此情况下,JavaScript代码混淆技术成为了一种应对加密破解的有效措施。

一、JS加密算法

JS加密算法是指JavaScript代码通过异或加密、Base64加密、MD5加密、SHA1加密等方式对其内容进行加密处理。例如,下面的加密函数中,通过异或运算对字符串进行了加密:

Copy codefunction encryptByXOR(message, key) {
    var encrypted='';
    for (var i=0; i < message.length; i++) {
        var c=message.charCodeAt(i) ^ key.charCodeAt(i % key.length);
        encrypted +=String.fromCharCode(c);
    }
    return encrypted;
}

var message='Hello world';
var key='1234567890';

var encrypted_message=encryptByXOR(message, key);
console.log('加密后的字符串:', encrypted_message);

通过对源代码进行加密处理,能够为JavaScript代码的安全性提供一定的保障。不过,对于相同的JavaScript加密算法,破解者也可以使用同样的加解密算法进行反向操作。而且,使用加密算法会增加代码的体积,降低代码的执行速度。因此,人们开始思考是否有一种更好的方法确保JavaScript代码的安全性呢?

二、JS代码混淆技术

JS代码混淆技术可以将JavaScript代码转换成一个新的代码形式,使其难以理解和破解。这种转换通常包括将变量名和函数名替换为无意义的字符、删除代码中的空白和注释、将多行代码压缩成一行等操作。混淆后的代码和原代码在功能上是等效的,但是由于其结构和命名被混淆,甚至看起来无法读懂。

例如,下面的函数中,对一段JavaScript代码进行了简单的混淆处理:

Copy codefunction obfuscateCode(code) {
    var lines=code.split("\n");
    var obfuscatedCode="";
    lines.forEach(function(line) {
        obfuscatedCode +=line
            .replace(/var /g, "")
            .replace(/function /g, "")
            .replace(/return /g, "")
            .replace(/;/g, "")
            .replace(/{/g, "")
            .replace(/}/g, "");
    });
    return obfuscatedCode;
}

var original_code='function foo(a, b) {var c=a + b;return c;}';
var obfuscated_code=obfuscateCode(original_code);
console.log('原始代码:', original_code);
console.log('混淆代码:', obfuscated_code);

通过混淆技术,原始JavaScript代码变得更加复杂和难以理解,进而降低了破解的可行性。同时,相比与加密算法,混淆代码的运行速度和性能也更优秀。

三、JS解混淆技术

不过,对于代码混淆技术,我们同样可以将其污染(反混淆)。通过分析混淆代码的结构和操作,我们能够撰写出解混淆代码,进行JavaScript代码的还原。例如,混淆代码可能会将许多不同的变量名替换为相同的单个字符,或者将多个行的代码压缩到一个代码行中,可以通过自动化工具或手动方法来反混淆代码。

Copy codefunction unobfuscateCode(obfuscatedCode) {
    var unobfuscatedCode=obfuscatedCode
        .replace(/a /g, "var ")
        .replace(/b /g, "function ")
        .replace(/c /g, "return ")
        .replace(/[0-9]+/g, "")
        .replace(/=/g, "=")
        .replace(/\+/g, " + ")
        .replace(/;/g, ";\n")
        .replace(/}/g, "\n}\n")
        .replace(/^\n/, "");
    return unobfuscatedCode;
}

var obfuscated_code="b foo(a, b){c a + b}"; //从js.jiami.com上获得的混淆代码
var unobfuscated_code=unobfuscateCode(obfuscated_code);
console.log('混淆代码:', obfuscated_code);
console.log('原始代码:', unobfuscated_code);

四、JS代码安全实践

如何选择适当的JS代码安全实践方法,取决于应用程序所需的安全级别和安全需求。如果您需要保护代码内容的安全,可以使用加密算法。如果您的代码需要长期维护,可以使用代码混淆技术,使代码的可读性降低,这样也就不容易被别人拿来二次开发和篡改。如果您需要将代码保持可读性,但是又需要保护代码的安全性,可以采取混合应用加密算法和混淆技术相互结合,以最佳的方式实现代码保护。

总之,随着互联网技术的发展,对于网络安全的要求越来越高,对于JavaScript的加密和解密,技术发展均取得了一定的进步。但无论是黑客攻击还是加密解密技术,只是给我们提供了预防的参考方案,绝不能代替不断加强网站的安全性实践,并高度唤起安全保护意识。

jsjiami.com

如果您对文章内容有不同看法,或者疑问,欢迎到评论区留言,或者私信我都可以。

也可以到上方网站,底部有我联系方式详谈。

码混淆

一.基本概念

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

二.混淆技术

名称混淆 name obfuscode

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

流混淆 Flow Obfuscation

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

其他

  • 异常混淆 Exception Obfuscation
  • 字符串加密混淆 String Encryption
  • 引用混淆 Reference Obfuscation

三.常用工具

1.proguard

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

2.yGuard

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

3.allatori

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

4.总结

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

四.工具对比

工具

官网地址

官方文档

开源免费

名称混淆

流混淆

maven支持

功能

proguard

https://www.guardsquare.com/proguard

https://www.guardsquare.com/manual/home

?


yGuard

https://www.yworks.com/products/yguard

https://yworks.github.io/yGuard/

?


allatori

https://allatori.com/


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

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

五.详细内容

1.yGuard(https://yworks.github.io/yGuard/)

  • 易于设置:yGuard 是一个 Ant 任务!作为 Ant 任务,yGuard 可以无缝集成到您在 Ant、Maven 和 Gradle 等众多构建系统中的部署过程中
  • 高级收缩:yGuard 通过依赖分析提供精细的代码收缩功能。
  • 可配置/安全代码:yGuard 提供高度可配置的名称混淆,可保护您的知识产权免受逆向工程。
  • 开源:yGuard 是完全开源的!与昂贵的商业产品相反,yGuard 是并且永远都是免费的。
  • Java 兼容性:要运行 yGuard 软件,您需要 JDK 1.7.x 或更高版本以及 Ant 1.5.x 或更高版本(它可能与任一软件的早期版本兼容,但尚未经过测试)。
    yGuard 与所有已发布的 Java 版本(最高 Java 17)兼容。但是,根据使用的版本,功能可能会略有不同。该文档包含不同版本支持的功能的详细说明。如果您打算将 yGuard 与 Java 以外的东西一起使用,还有一个关于3rd 方 JVM 支持的部分。

ProGuard

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

  • 收缩步骤检测并删除未使用的类、字段、方法和属性。
  • 优化器步骤优化字节码并删除未使用的指令。
  • 混淆步骤使用简短无意义的名称重命名剩余的类、字段和方法。
  • 最后的预验证步骤将预验证信息添加到类中,这是 Java Micro Edition 和 Java 6 及更高版本所必需的。

yGuard

1.maven引用方式

<build>
        <plugins>
            <plugin>
                <!--结合ant run 来使用yguard -->
                <artifactId>maven-antrun-plugin</artifactId>
                <dependencies>
                    <dependency>
                        <groupId>com.yworks</groupId>
                        <artifactId>yguard</artifactId>
                        <version>3.1.0</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <tasks>
                                <property refid="maven.compile.classpath" name="mvn.classpath"/>
                                <!-- <echo message="Using Maven Classpath: ${mvn.classpath}" /> -->
                                <taskdef name="yguard"
                    classname="com.yworks.yguard.YGuardTask"/>
                                
                                <yguard>
                                    <!-- yguard配置 -->
                                </yguard>
                                
                            </tasks>
                        </configuration>

                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

2.yguard配置解析

写在前面

  1. yguard共分为两大任务配置:
    1. rename 混淆名称 :主要用于修改类名,属性名,方法名以及参数名等。
    2. shrink 收缩代码 : 主要用于从多个入口点中删除所有无法访问的类、字段和方法。
  1. keep keep是rename和shrink的子元素,用于指定从父级rename或shrink任务中排除的元素。

注意事项

  1. 注意事项1 :如果项目需要shrink, shrink最好是配置在rename之前。即在进行代码混淆之前,先进行代码压缩。 因为压缩代码需要指定压缩的根代码。
    举个例子:比如制定main方法:<method name="void main(java.lang.String[])" class="com.ewa.pipe.dbtransfer.dpl.DplDbtransferApplication" /> 。如果先混淆,DplDbtransferApplication类名被修改的话(比如为A.class),该配置将会无效,所有代码都会被 shrink 删除,因为已经找不到DplDbtransferApplication这个类了(类名被修改)。
  2. 其他配置请参考官方文档。

配置说明

基础配置

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

keep配置说明

class元素

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

可见性(是否被收缩)

public

protected

friendly

private

none

-

-

-

-

public

*

-

-

-

protected

*

*

-

-

friendly

*

*

*

-

private

*

*

*

*

属性说明

  1. name 指定要保留的类名。 在shrink中,只会保留类名称,类中的属性和方法都会被删除掉。
  2. classes 保持类的可见性 :其值是上述:none , public , protected , friendly , private 。默认为none
  3. methods 保留方法的可见性 , 值同classes的描述。默认为none
  4. fields 保留属性的可见性 , 值同classes的描述。默认为none
  5. extends 保留对继承了该类的可见性 。1.在shrink中凡是继承了该类的子类都不会被删除。 2.在rename中凡是继承了该类的子类都不会被修改名称。
  6. implements 保留对实现该接口的可见性 。1.在shrink中凡是实现该接口的类都不会被删除。 2.在rename中凡是实现该接口的类都不会被修改名称。

注意事项

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

列1:

    <shrink logfile="${project.build.directory}/yshrink.log.xml">
       <keep>
            <!-- 保留NameTest类不被删除,但是内部的方法和会属性会被删除,不论私有还是共有。 -->
            <class name="com.arm.code.mix.base.NameTest"/>
			<!-- 保留所有公用的类,方法和属性。其关联的类,方法和属性会被保留,不会被删除  -->
       		<class classes="public" methods="public" fields="public"/>
			<!-- 保留所有继承了BaseClass的类不被删除,但是内部的方法和会属性会被删除,不论私有还是共有。  -->
			<class extends="com.arm.code.mix.base.BaseClass"/>
       </keep>
    </shrink>

列2:

    <shrink logfile="${project.build.directory}/yshrink.log.xml">
       <keep>
            <!-- 保留NameTest类不被删除,并保留其private级别的方法和属性 -->
            <class name="com.arm.code.mix.base.NameTest" methods="private" fields="private"/>
			<!-- 保留所有继承了BaseClass的类不被删除,并保留其private级别的方法和属性  -->
			<class extends="com.arm.code.mix.base.BaseClass" methods="private" fields="private"/>
       </keep>
    </shrink>

列3:

一下举列几个模式集的列子,模式集可以参考ant。

<!--  include shrink:不需要被删除的类,保留的类。rename:不需要被混淆的类名 -->
<class>
  <patternset>
    <include name="com.mycompany.**.*Bean"/>
    <exclude name="com.mycompany.secretpackage.*"/>
    <exclude name="com.mycompany.myapp.SecretBean"/>
	<!-- 由于 Ant'$'用作转义字符,因此如果您想将一个作为参数传递给任务,则必须使用两个连续的'$'s( )。'$$'-->
	<exclude name="org.w3c.sax?.**.*$$*"/> 
  </patternset>
</class>

method

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

<!-- 这将保留MyClass类的main和foo方法。此外,所有readObject和writeObject方法(用于序列化)都将保存在com.mycompany.myapp.data包的所有类中。
	请注意,您必须指定返回参数的类型,即使它是 void,并且您必须为所有类使用完全限定名称,即使是java.lang package. -->
<method class="com.mycompany.myapp.MyClass"
  name="void main(java.lang.String[])"/>
<method class="com.mycompany.myapp.MyClass"
  name="int foo(double[][], java.lang.Object)"/>
<method name="void writeObject(java.io.ObjectOutputStream)">
  <patternset>
    <include name="com.mycompany.myapp.data.*"/>
  </patternset>
</method>
<method name="void readObject(java.io.ObjectInputStream)">
  <patternset>
    <include name="com.mycompany.myapp.data.*"/>
  </patternset>
</method>

field

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

<!-- 保留MyClass类中的所有字段。
	此外,所有serialVersionUID字段(用于序列化)都将保存在com.mycompany.myapp.data包的所有类中。 -->
<field class="com.mycompany.myapp.MyClass" name="field"/>
<field name="serialVersionUID">
  <patternset>
    <include name="com.mycompany.myapp.data.*"/>
  </patternset>
</field>

package

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

    <package>
      <patternset>
		<!-- com.mycompany.myapp不被混淆。myapp下的包名还是会被混淆 -->
        <include name="com.mycompany.myapp.*"/>
		<!-- com.mycompany.myapp不被混淆。myapp下的包名也不会被混淆 -->
		<include name="com.mycompany.myapp.**"/>
      </patternset>
    </package>

3.几种情况下的使用方式

springboot项目

1.注意事项

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

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

  1. 本地打包之后启动项目失败:由于是本地idea将jdk设置成jdk17了,导致打包失败。 设置为jdk8后成功启动。
  2. 项目使用mybaties plus,项目里只有一个接口:public interface TimePullLogMapper extends BaseMapper<TimePullLog>{} , 造成混淆后打包报错:spring至少一个bean实现。后面加上:<class implements="com.arm.boot.core.base.BaseMapper"/> 后正常。==
  3. service的接口和实现都要暴露,不然spring的注入和nacos的服务发现都会存在问题。

3.配置模版

<keep>
                                            <!--包名不混淆配置-->
                                            <package>
                                                <patternset>
                                                    <include name="com.arm.oceansearch.**"/>
                                                </patternset>
                                            </package>
                                            <!--mybaites 相关的mapper混淆后,会造成boot项目启动失败 -->
                                            <class implements="com.arm.boot.core.base.BaseMapper"/>
                                            <!--mybaites默认生成sql时是使用的实体类的类名,所以不能混淆-->
                                            <class implements="com.arm.oceansearch.entity.BaseEntity"/>
                                            <!-- 本包的controller混淆后,无法读取mapping映射,原因未知。-->
                                            <class>
                                                <patternset>
                                                    <include name="com.arm.oceansearch.controller.*"/>
                                                </patternset>
                                            </class>
                                            <!-- service的接口和实现都要暴露,不然spring的注入和nacos的服务发现都会存在问题。 -->
                                            <class>
                                                <patternset>
                                                    <include name="com.arm.oceansearch.service.**"/>
                                                </patternset>
                                            </class>
                                            <!--main方法配置-->
                                            <method name="void main(java.lang.String[])"
                                                    class="com.arm.oceansearch.OceanSearchApplication"/>
                                        </keep>


简介

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

  • 收缩步骤检测并删除未使用的类、字段、方法和属性。
  • 优化器步骤优化字节码并删除未使用的指令。
  • 混淆步骤使用简短无意义的名称重命名剩余的类、字段和方法。
  • 最后的预验证步骤将预验证信息添加到类中,这是 Java Micro Edition 和 Java 6 及更高版本所必需的。

对反射的处理

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

  • Class.forName("SomeClass")
  • SomeClass.class
  • SomeClass.class.getField("someField")
  • SomeClass.class.getDeclaredField("someField")
  • SomeClass.class.getMethod("someMethod", null)
  • SomeClass.class.getMethod("someMethod", new Class[] { A.class,... })
  • SomeClass.class.getDeclaredMethod("someMethod", null)
  • SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class,... })
  • AtomicIntegerFieldUpdater.newUpdater(SomeClass.class, "someField")
  • AtomicLongFieldUpdater.newUpdater(SomeClass.class, "someField")
  • AtomicReferenceFieldUpdater.newUpdater(SomeClass.class, SomeType.class, "someField")

支持

  1. 可单独使用。首先,下载一个ProGuard 版本或者构建 ProGuard从源头。然后可以通过调用目录中的脚本直接从命令行执行 ProGuard bin:
    linux/mac:bin/proguard.sh -injars path/to/my-application.jar \ -outjars path/to/obfuscated-application.jar \ -libraryjars path/to/java/home/lib/rt.jar
    windows:bin\proguard.bat -injars path/to/my-applicati^ -outjars path/to/obfuscated-application.jar ^ -libraryjars path/to/java/home/lib/rt.jar
  2. Gradle 模式
  3. ant模式
  4. Maven模式:(没有正式提供 maven 集成,也无法提供支持,但有可用的解决方案,但 Guardsquare 不保证它们提供的功能。)来源实现:
    https://github.com/wvengen/proguard-maven-plugin
    https://github.com/dingxin/proguard-maven-plugin

错误解析

  1. [proguard] Error: The input doesn't contain any classes. Did you specify the proper '-injars' options?
处理:<inFilter>com/ewa/pipe/**</inFilter>, inFilter标签设置为包路径地址,把‘.’换成‘/’。

injar : 指定target中的一个目标地址:这里指定编译后的 classes文件夹。  inFilter 指定的是 classes的内部的文件夹(package)地址。
<!-- 加载文件的过滤器,就是你的工程目录了-->
                    <inFilter>com/arm/code/**</inFilter>
                    <!-- 对什么东西进行加载,这里仅有classes成功,毕竟你也不可能对配置文件及JSP混淆吧-->
                    <injar>classes</injar>

以下是一个例子说明,如果你想更多的有用信息,请查看文档(https://www.guardsquare.com/manual/configuration/usage)