混淆技术研究笔记(八)扩展yGuard实现签名

news2024/11/16 13:27:02

logo
前面铺垫了这么多,终于开始实现签名反篡改的功能了。

下载 yGuard 源码(https://github.com/yWorks/yGuard),然后先修改一处错误,在 settings.gradle 中定义的项目名是错的(和github上的名字不一样,git clone 下载会使用 github 定义的名字yGuard,估计作者本地建的项目名是 yguard),将里面的 rootProject.name = 'yguard' 改成 rootProject.name = 'yGuard' 即可。

一开始的想法是要参考 <adjust>实现,所以连代码都挨着 AdjustSection 类,写在了 ObfuscatorTask 内部:
在这里插入图片描述
假设我们允许配置一个 <sign>元素,在 ObfuscatorTask 创建变量和对应的 createSign 方法:
在这里插入图片描述
接下来先把 signSection 安排到我们在 混淆技术研究笔记(六)如何基于yGuard实现? 中指定的地方,当时指定了一个地方获取要签名的类:

for (AdjustSection as : adjustSections)
{
  as.createEntries(inFilesList);
}

一个读取混淆前后名字和内容的地方:

if (inName.endsWith(CLASS_EXT))
{
  if (fileFilter == null || fileFilter.accepts(inName)){
    // Write the obfuscated version of the class to the output Jar
    ClassFile cf = ClassFile.create(inStream);
    fireObfuscatingClass(Conversion.toJavaClass(cf.getName()));
    cf.remap(classTree, replaceClassNameStrings, log);
    String outName = createClassFileName(inName, cf) + CLASS_EXT;
    //省略部分代码
    updateManifest(i, inName, outName, digests);
  }
}

还有一个能写入到jar包中的地方:

// write the entry itself
outJar.addFile(entry.getName(), (byte[]) array[1]);

下面详细介绍这部分实现。

1. 实现配置签名指定的类

虽然想参考<adjust>下面的方法:

for (AdjustSection as : adjustSections)
{
  as.createEntries(inFilesList);
}

但是因为 <adjust> 获取的是资源文件,不是类,直接 copy 来用不合适,既然是获取类,就直接参考 <class> 下面的 <patternset>,找到 <class> 的实现:

public class ClassSection extends PatternMatchedClassesSection implements Mappable {
}

从这里发现只要继承 PatternMatchedClassesSection 就能使用 addEntries( Collection entries, ZipFileSet zf)获取要签名的类,因此修改前面定义的 SignSection

public static class SignSection extends PatternMatchedClassesSection {
  @Override
  public void addEntries( final Collection entries, final String matchedClass ) {
    
  }
}

继承后必须实现抽象方法 addEntries,这个方法在PatternMatchedClassesSection 中被调用了,追踪public void addEntries( Collection entries, ZipFileSet zf)方法调用的位置发现在 ExposeSection (对应 <keep> 标签)中存在下面的代码:
在这里插入图片描述
这里按顺序把 <class>,<method>,<field>等元素中的配置添加到了外部的 entries 中,这个变量存储了 <keep> 中混淆的全部配置。追踪 ExposeSection#createEntries 方法发现在 ObfuscatorTask#execute 中执行的,那么我们第一个获取签名类的地方就在这里。
在这里插入图片描述
SignSection 中添加和 expose.createEntries 一样的方法,并且copy里面的部分代码:

public static class SignSection extends PatternMatchedClassesSection {
  protected Project project;
  private Collection entries = new ArrayList( 20 );

  public SignSection( final Project project ) {
    this.project = project;
  }

  @Override
  public void addEntries( final Collection entries, final String matchedClass ) {
    entries.add(matchedClass);
  }

  public Collection createEntries( Collection srcJars ) throws IOException {
    for ( Iterator it = srcJars.iterator(); it.hasNext(); ) {
      File file = (File) it.next();
      ZipFileSet zipFile = new ZipFileSet();
      zipFile.setProject(project);
      zipFile.setSrc(file);
      addEntries(entries, zipFile);
    }
    return entries;
  }
}

由于参考代码中 zipFile.setProject(project); 这里需要 Project 类,因此修改了构造方法,增加了 Project 参数,同时修改 createSign 方法:

public SignSection createSign() {
  if(signSection == null) {
    signSection = new SignSection(getProject());
  } else {
    throw new BuildException("Only one sign element allowed!");
  }
  return signSection;
}

继续参考前面的 expose 调用的位置,增加 signSection 代码:

if (expose != null){
  rules = expose.createEntries(inFilesList);
} else {
  rules = new ArrayList(20);
}
//增加下面代码
if(signSection != null) {
  signSection.createEntries(inFilesList);
}

先不要继续其他的逻辑,完成一个功能后先测试验证是否正确,没问题后在继续。

2. 发个小版本测试(1)的功能

yGuard 中gradle.properties当前指定的版本如下:

VERSION_MAJOR=4.0
VERSION_MINOR=1-SNAPSHOT

既然是快照版就不改版本号了,在 build.gradlepublishing 下面的 repositories 中添加 mavenLocal() 发布到本地:

publishing {
  repositories {
    mavenLocal()
    maven {
      url 'https://oss.sonatype.org/service/local/staging/deploy/maven2'
      credentials {
        username SONATYPE_NEXUS_USERNAME
        password SONATYPE_NEXUS_PASSWORD
      }
    }
  }
  //省略其他
}

还要注意本文开头修改的 settings.gradle 配置。

刷新 Gradle 配置,然后点击 publishToMavenLocal
在这里插入图片描述

如果jdk8编译出错可以刷新gradle配置试试

将 yguard-module-parent 中 module-yguard 的依赖改为快照版:

<dependency>
    <groupId>com.yworks</groupId>
    <artifactId>yguard</artifactId>
    <version>4.0.1-SNAPSHOT</version>
</dependency>

signSection.createEntries(inFilesList); 行添加断点,在 <rename>标签下面添加 <sign/>

<rename logfile="${project.build.directory}/yguard.log.xml"
        replaceClassNameStrings="true">
  <sign>
      <patternset>
          <include name="org.example.a."/>
          <include name="org.example.b."/>
          <include name="org.example.c.util.FileUtil"/>
      </patternset>
  </sign>
  <!-- 其他配置 -->

右键debug运行package打包,代码运行过断点这里后可以看到包含的类:
在这里插入图片描述
这里说明我们已经获取到混淆前要进行签名的类了,我们继续进行后续的操作。

3. 记录混淆后的类信息

后面两处关键的地方都在 GuardDB中,这个对象在 ObfuscatorTask 中初始化和调用的,从这里先把 SignSection 设置到 GuardDB 中:
在这里插入图片描述
在使用前set进去:
在这里插入图片描述
接下来就可以在 GuardDB 中使用了:
在这里插入图片描述
这里增加了406~410行的代码,需要给 signSection 添加两个方法:

public static class SignSection extends PatternMatchedClassesSection {
  protected Project project;
  private Collection entries = new ArrayList<>(20);
  private List<byte[]> bytes = new ArrayList<>(20);
  
  //省略其他无关方法
  
  public boolean contains(String name) {
    return entries.contains(name);
  }

  public void addBytes(byte[] bytes) {
    this.bytes.add(bytes);
  }

}

这样就拿到了所有混淆后的数据,接下来就是计算签名并写入到jar包中。

4. 签名并写入jar包

混淆技术研究笔记(四)反篡改介绍 中我们使用 hutool 的工具类实现了私钥加密的方法,我们这里直接用,首先添加 hutool 的依赖,在 build.gradle 中添加依赖:

dependencies {
    annotation project(':annotation')
    implementation project(':annotation')
    implementation 'org.ow2.asm:asm:9.2'
    implementation 'org.apache.ant:ant:1.10.12'
    implementation 'cn.hutool:hutool-all:5.7.22'
    testImplementation 'junit:junit:4.13-beta-3'
}

新增加的 implementation 'cn.hutool:hutool-all:5.7.22'。刷新 gradle,然后在 SignSection 中实现签名和写入 jar 的方法,先增加一个属性用于设置写入的文件名:

private String name = "sign";

public void setName( final String name ) {
  this.name = name;
}

public String getName() {
  return name;
}

然后是根据一定算法计算签名:

public byte[] sign() {
  if(entries.size() != bytes.size()) {
    throw new BuildException("Number of entries and bytes must be equal!");
  }
  List<String> signs = new ArrayList<>(bytes.size());
  MD5 md5 = new MD5();
  for (final byte[] data : bytes) {
    //计算md5
    signs.add(md5.digestHex(data));
  }
  //排序,避免读取顺序影响
  Collections.sort(signs);
  //拼一串
  StringBuffer sb = new StringBuffer();
  for (final String sign : signs) {
    sb.append(sign);
  }
  //私钥签名
  return encryptHex(sb.toString()).getBytes(StandardCharsets.UTF_8);
}

private String encryptHex(String str) {
  String home = System.getProperty("user.home");
  String privateKeyPath = home + File.separator + ".yguard" + File.separator + "license-keys.pri";
  RSA rsa = new RSA(FileUtil.readBytes(privateKeyPath), null);
  byte[] bytes = rsa.encrypt(str, StandardCharsets.UTF_8, KeyType.PrivateKey);
  return HexUtil.encodeHexStr(bytes);
}

注意这里会使用私钥,私钥在 混淆技术研究笔记(四)反篡改介绍 中有示例代码可以生成。

修改 GuardDB 调用上面的方法:
在这里插入图片描述
接下来是测试功能。

5. 测试完整功能

打包到本地maven仓库,发现前面中文注释由于编码问题有乱码,移除后重新发布。

在 module-yguard 中debug运行,在写入jar的地方断点看看效果。
在这里插入图片描述
发现 inName 包含 .class 后缀导致无法匹配,因此这里去掉最后的 .class 后缀再进行匹配:

if(signSection != null && signSection.contains(inName.substring(0, inName.length() - 6))) {
 signSection.addBytes((byte[]) objects[1]);
}

改完发布再次测试到签名时,又发现了新问题:
在这里插入图片描述
此时要签名的文件有5个,但是bytes只有2个,说明我们需要的文件还没获取全。

这就涉及到一个顺序问题了,我们目前的实现会在所有 <inoutpair> 配置的 jar 包上执行一遍,我们实现的又是多模块混淆,因此想要签名获取所有的文件,就只能在最后一个文件中写入签名信息,只有最后一个的时候是全的,因此我们需要 <sign> 能指定要给哪个 jar 包添加签名,还要修改对应 jar 包为最后一个 <inoutpair> (也可以默认写入最后一个 <inoutpair> 配置的 jar 包,但是这种隐藏的方式还要特别强调才不容易出错,不如更明确的指定出来)。
在这里插入图片描述
观察这里的代码可以看到 out[i] 代表了当前处理的那个 jar 包,而且是生成的 jar,这里是 File 类型,因此我们可以在 <sign> 中指定 jar 文件,改动如下:

public static class SignSection extends PatternMatchedClassesSection {
    //省略其他
    private File jar;

    public File getJar() {
      return jar;
    }

    public void setJar( final File jar ) {
      this.jar = jar;
    }

修改 GuardDB 中写入的地方,要匹配文件名:

if(signSection != null && signSection.getJar().equals(out[i])) {
  outJar.addFile(signSection.getName(), signSection.sign());
}

发布后,修改 <sign> 配置如下:

<sign name="sign.txt" jar="..\module-a\target\module-a-${project.version}.jar">
    <patternset>
        <include name="org.example.a."/>
        <include name="org.example.b."/>
        <include name="org.example.c.util.FileUtil"/>
    </patternset>
</sign>

这里指定 jar 为最后一个 <inoutpair>out 值:

<inoutpair in="..\module-b\target\module-b-${project.version}.jar"
           out="..\module-b\target\module-b-${project.version}.jar"/>
<inoutpair in="..\module-c\target\module-c-${project.version}.jar"
           out="..\module-c\target\module-c-${project.version}.jar"/>
<inoutpair in="..\module-a\target\module-a-${project.version}.jar"
           out="..\module-a\target\module-a-${project.version}.jar"/>

如果一切正常,就会写入到 module-a 中,再次 DEBUG:
在这里插入图片描述
这次一切正常,执行完成后,打开 module-a 的 jar 包查看:
在这里插入图片描述
签名成功的写入到了 jar 包中,签名的功能到这里就实现完成了。

有了签名后,想要起作用,还需要在运行时对 jar 包内容的进行反篡改校验,这部分内容在 混淆技术研究笔记(四)反篡改介绍 有介绍,需要依赖具体的运行环境才能测试,这里就不具体实现了。

整个系列的主要内容和过程已经呈现出来了,后面还会有一篇最后的总结,会从前面几篇提取一些内容摘抄出来,只要看过前面这八篇,第九篇总结也没必要看。因为第九篇只能在微信公众号查看(搜索 MyBatis),并且最后一篇是付费文章。如果你觉得这个系列对你有帮助,可以多多转发,也可以付费支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1131017.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

国密 SM2 SSL 证书 Nginx 安装指南 linux版

一、获取国密证书 1、在您完成申请西部GDCA服务器证书的流程后&#xff0c;下载证书将获取一个证书包&#xff0c;有以下 *.***.com_sign.crt&#xff1a;签名证书 *.***.com_sign.key&#xff1a;签名证书私钥 *.***.com_encrypt.crt&#xff1a;加密证书 *.***.com_encr…

弗洛伊德(Floyd)算法求个顶点之间最短路径问题(详解+图解)

弗洛伊德算法&#xff0c;也称为迪科斯彻算法&#xff0c;是一种用于寻找图形中所有最短路径的算法。它的基本思想是通过一定的规则逐步更新每个节点的最短路径估计值&#xff0c;直到每个节点的最短路径估计值收敛为止。 具体来说&#xff0c;弗洛伊德算法通过求解所有点对之…

澳福外汇还不会超短线交易,可以了解一下混沌理论

很多投资者还不会使用超短线交易盈利&#xff0c;澳福外汇认为投资者还没有了解混沌理论的三大原则&#xff0c; 混沌理论有三个原则&#xff1a; 1、能量永远会遵循阻力最小的途径 2、始终存在着通常不可见的根本结构&#xff0c;这个结构决定阻力最小的途径。 3、 这种始终存…

护眼灯有效果吗?五款好用热门的护眼台灯推荐

可以肯定的是&#xff0c;护眼灯一般可以达到护眼的效果。看书和写字时&#xff0c;光线应适度&#xff0c;不宜过强或过暗&#xff0c;护眼灯光线较柔和&#xff0c;通常并不刺眼&#xff0c;眼球容易适应&#xff0c;可以防止光线过强或过暗导致的用眼疲劳。如果平时生活中需…

新生儿蒙古斑:原因、科普和注意事项

引言&#xff1a; 新生儿蒙古斑是一种常见的皮肤状况&#xff0c;通常在新生儿期间出现。尽管它通常是无害的&#xff0c;但许多父母对它感到担忧&#xff0c;不清楚它的原因和如何处理。本文将科普新生儿蒙古斑的原因&#xff0c;提供相关信息&#xff0c;以及为父母和监护人…

【图解 LeetCode 房屋染色 动态规划思想 + 代码实现】

LeetCode 房屋染色 动态规划 问题描述&#xff1a; 假如有一排房子&#xff0c;共 n 个&#xff0c;每个房子可以被粉刷成 k 种颜色中的一种&#xff0c;你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。 当然&#xff0c;因为市场上不同颜色油漆的价格不同&#x…

视频特效制作软件 After Effects 2024 mac中文版新增功能

After Effects 2024 mac是一款专业的视频特效和动态图形设计软件&#xff0c;它可以帮助用户创建各种令人惊叹的视觉效果&#xff0c;例如粒子系统、合成特效、绿屏抠像等。AE2024软件支持动画制作&#xff0c;包括关键帧动画、形状动画、运动跟踪等工具&#xff0c;可以创建复…

IBM展示非冯·诺依曼架构AI芯片NorthPole

我们正处于人工智能的“寒武纪大爆发”时期。在过去的十年中&#xff0c;人工智能已经从理论和小型测试发展到企业规模的使用案例。但是&#xff0c;用于运行人工智能系统的硬件虽然越来越强大&#xff0c;但在设计时却没有考虑到当今的人工智能。随着人工智能系统规模的扩大&a…

使用C# RDLC环境搭建

搭建C# RDLC环境 在vs环境中&#xff0c;菜单扩展>管理扩展 用来打开报表文件的 用来新建报表文件的 搜索Microsoft Reporting Services Projects 选择第一个进行下载 安装完以上两个即可进行报表文件的创建和预览 reportview组件 推荐nuget安装&#xff1a;Install-…

RabbitMQ 笔记

一、win10安装erlang 1.1 安装erLang语言&#xff0c;配置环境变量 erLang官网地址 1.2 配置环境变量 &#xff08;1&#xff09;添加系统变量ERLANG_HOME &#xff08;2&#xff09;path路径&#xff0c;指向bin目录 1.3 配置完成后再cmd命令窗口erl -version可以查看…

ubuntu安装golang

看版本&#xff1a;https://go.dev/dl/ 下载&#xff1a; wget https://go.dev/dl/go1.21.3.linux-amd64.tar.gz卸载已有的go&#xff0c;可以apt remove go&#xff0c;也可以which go之后删除那个go文件&#xff0c;然后&#xff1a; rm -rf /usr/local/go && tar…

苹果手机怎么设置壁纸?解锁设置壁纸的2种方法!

手机壁纸便是我们常说的屏幕背景图&#xff0c;一张好看的手机壁纸能使我们的心情变得愉悦。这个壁纸可以是风景、美食、喜欢的偶像、自己养的宠物&#xff0c;或者是你的家人、朋友。 拥有特殊含义的照片会更让人想要设置成壁纸。苹果手机怎么设置壁纸&#xff1f;本文将给大…

18.1 Socket 原生套接字抓包

原生套接字抓包的实现原理依赖于Windows系统中提供的ioctlsocket函数&#xff0c;该函数可将指定的网卡设置为混杂模式&#xff0c;网卡混杂模式&#xff08;Promiscuous Mode&#xff09;是常用于计算机网络抓包的一种模式&#xff0c;也称为监听模式。在混杂模式下&#xff0…

从github下载文件时遇到报错(Unable to render code block)解决办法

1、报错情况 2、解决办法 https://ghproxy.com/ &#xff08;GitHub 文件 , Releases , archive , gist , raw.githubusercontent.com 文件代理加速下载服务&#xff09;

提高生产力,开启高效办公——ConceptDraw Office办公软件套件

在当今快节奏的工作环境中&#xff0c;一款强大的办公软件套件对于提高工作效率和生产力至关重要。ConceptDraw Office&#xff0c;作为一款专业的办公软件套件&#xff0c;凭借其强大的功能和易用性&#xff0c;成为了市场上备受瞩目的办公利器。本文将带您深入了解ConceptDra…

Qt扫盲-QPixmap理论总结

QPixmap 理论总结 一、概述二、读写 Image 文件三、Pixmap 像素图信息四、Pixmap 格式转换五、Pixmap 像素转换 一、概述 QPixmap 也是一个用得很多的描述图像的类&#xff0c;也是界面显示相关的时候用得很多的。 QPixmap类是一个可以用作绘画设备的屏幕外图像表示。Qt提供了…

文生图——DALL-E 3 —论文解读——第一版

概述 本文主要是DALLE 3官方第一版技术报告&#xff08;论文&#xff09;的解读&#xff0c;原文《Improving Image Generation with Better Captions》论文解读。该文要提升文生图的效果&#xff0c;将技术点放到了&#xff0c;提升指令跟随能力上&#xff0c;然后顺藤摸瓜分为…

Linux下挂载大于2T的硬盘

fdisk和gdisk命令的区别 fdisk和gdisk命令都可以给磁盘分区&#xff0c;但是在公司里&#xff0c;如果磁盘大于2T&#xff0c;不能用fdisk分区&#xff0c;只能用gdisk命令分区。 gdisk可以分128个主分区&#xff0c;用gdisk分区没有逻辑分区和扩展分区之说&#xff0c;只有主…

【FPGA零基础学习之旅#17】搭建串口收发与储存双口RAM系统

&#x1f389;欢迎来到FPGA专栏~搭建串口收发与储存双口RAM系统 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;FPGA学习之旅 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0…

超声波清洗机品牌哪些好用?好评不断的超声波清洗机推荐

超声波清洗机目前的使用范围逐渐变广&#xff0c;一开始超声波清洗机只出现在大型的工业领域中的零件清洗&#xff0c;逐渐衍生到现在&#xff0c;出现了小型的超声波清洗机&#xff0c;可以让大家可以在家也使用上超声波清洗机。眼镜是现在大部分都离不开视线辅助的一个工具&a…