混淆技术研究笔记(六)如何基于yGuard实现?

news2025/4/28 6:30:12

logo
确定参考 <adjust> 作为入口后,就需要详细了解这部分代码的逻辑。

需要看yguard源码了,你会如何阅读一个完全不了解的源码?

我通常的策略都是找一个目标,添加代码依赖,写好demo,debug跟踪代码看。如果漫无目的的看,很难串起来整个流程,范围太大也容易迷失。

先在配置中增加 <adjust> 配置:

<adjust replacePathPolicy="lenient">
    <include name="**.*"/>
</adjust>

最快定位代码位置的方式就是搜索,可以搜 adjust,也可以搜 replacePathPolicy,搜索后发现了 AdjustSection 内部类(IDEA用ctrl+shift+num1标记),在 ObfuscatorTask 中,从名字来看这个是混淆的任务,对应的就是 <rename> 标签。在这个类中发现了 List<AdjustSection> adjustSections,然后搜索 adjustSections,发现几处使用的地方:

try {
  for (AdjustSection section : adjustSections) {
    section.prepare(this);
  }
} catch (BuildException be) {
  throw new BuildException(be.getMessage(), be.getLocation());
}

这是在准备什么东西,还没到关键的位置,继续往下找:

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

从这里看到,<adjust>会根据配置的 include 配置找到要处理的类,这个方法有用,我可以使用他来匹配要需要签名的类,顺手打个标记(ctrl+shitf+num2),再继续找:

public boolean filterName( final String inName, final StringBuffer outName ) {
  for(AdjustSection as : adjustSections) {
    if (as.contains(inName)) {
      filterNameImpl(inName, outName, as);
      return true;
    }
  }
  return false;
}

这里似乎在处理文件名,如果是前面要处理的文件,这里通过 StringBuffer outName 处理的文件名,鼠标滚轮点击该方法,追踪到调用的位置,在 GuardDB 中的 remapTo 方法里面:

if(resourceHandler != null && resourceHandler.filterName(inName, outNameBuffer))
{
  outName = outNameBuffer.toString();
  if(!outName.equals(inName))
  {
    replaceNameLog.append("  <resource name=\"");
    replaceNameLog.append(ClassTree.toUtf8XmlString(inName));
    replaceNameLog.append("\" map=\"");
    replaceNameLog.append(ClassTree.toUtf8XmlString(outName));
    replaceNameLog.append("\"/>\n");
  }
}
else
{
  outName = classTree.getOutName(inName);
}

这里会使用改后的文件名,那么改后文件名的生效肯定是在这里,这里可能会有写入jar的操作,这非常关键,但是我们先回到前面继续查找 adjustSections,发现了最后一处:

public boolean filterContent(InputStream in, OutputStream out, String resourceName) throws IOException
{
  for(AdjustSection as : adjustSections)
  {
    if(filterContentImpl(in, out, resourceName, as))
    {
      return true;
    }
  }
  return false;
}

这里是修改文件内容的地方,滚轮点击找调用位置,发现在 GuardDB 中的 remapTo 中的代码:

if(resourceHandler == null || !resourceHandler.filterContent(inStream, dataOutputStream, inName))
{
  byte[] bytes = new byte[(int)size];
  inStream.readFully(bytes);

  // outName = classTree.getOutName(inName);
  // Dump the data, while creating the digests
  dataOutputStream.write(bytes, 0, bytes.length);
}
else
{
  replaceContentsLog.append("  <resource name=\"");
  replaceContentsLog.append(ClassTree.toUtf8XmlString(inName));
  replaceContentsLog.append("\"/>\n");
}

这里修改的文件内容,修改的内容最后会写入jar,继续往下看,就发现了下面代码:

jarEntries.add(new Object[]{outEntry, baos.toByteArray()});

往下搜索 jarEntries 发现了下面代码:

for (int j = 0; j < jarEntries.size(); j++){
  Object[] array = (Object[]) jarEntries.get(j);
  JarEntry entry = (JarEntry) array[0];
  String name = entry.getName();
  // make sure the directory entries are written to the jar file
  if (!entry.isDirectory()){
    int index = 0;
    while ((index = name.indexOf("/", index + 1))>= 0){
      String directory = name.substring(0, index+1);
      if (!directoriesWritten.contains(directory)){
        directoriesWritten.add(directory);
        outJar.addDirectory(directory);
      }
    }
  }
  // write the entry itself
  outJar.addFile(entry.getName(), (byte[]) array[1]);
}

原来这里通过 outJar 就可以写入文件,而且也不必存在已有的文件,直接加个新的也可以,在 outJar.addFile 这行加个断点,clean 代码,然后在 package 上右键 debug(注意,需要在 pom.xml的dependencies中添加yguard依赖,然后定位到上面位置加断点):

在这里插入图片描述

这种方式是可以调试maven插件的,debug进入这里后,在表达式中执行代码,添加一个文件试试是否生效:

在这里插入图片描述

直接关闭断点执行完成,然后打开当时处理的module-b的jar包:

在这里插入图片描述
很容易就成功了,比预想的要顺利一些。

在后续的处理过程中,我写代码还有一个特点,不考虑太多(说明考虑了一点)的设计,先用最直接的手段实现功能,等功能完成后再去全局设计进行重构调整。如果一上来就想着如何设计,万一最后行不通就白费了,而且设计没有尽头,想要完美的设计可能需要纠结很久才有结果,在这种重构调整比较容易的情况下,先动手,后设计。

上面解决了最难的写入jar包的问题,再次查看 outJar 的上下文时,关注到了对应的 inJar,查看 remapTo 方法发现下面的代码:

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;
    JarEntry outEntry = new JarEntry(outName);

    DataOutputStream classOutputStream;
    if (digestStrings == null){
      digestStrings = new String[]{"SHA-1", "MD5"};
    }
    MessageDigest[] digests = new MessageDigest[digestStrings.length];
    // Create an OutputStream piped through a number of digest generators for the manifest
    classOutputStream = fillDigests(baos, digestStrings, digests);

    // Dump the classfile, while creating the digests
    cf.write(classOutputStream);
    classOutputStream.flush();
    Object[] entry = {outEntry, baos.toByteArray()};
    jarEntries.add(entry);
    baos.reset();
    // Now update the manifest entry for the class with new name and new digests
    updateManifest(i, inName, outName, digests);
  }
}

这段代码中可以看到 inName 是原始的名字,outName 是经过混淆处理后的名字(没混淆类名就不变,混淆就变),后面还有 classOutputStream是类的字节码内容,是我们想要加密的内容。这个方法的位置太好了,这就是我想要匹配文件,获取字节码内容的地方。

接下来要考虑的是如何配置要对哪些内容进行签名,一开始我想参考 <adjust> 中的 <include> 实现,上面提到过的 as.createEntries(inFilesList) 是一种可行的方式,但是我最后选择了参考 <class> 中的 <patternset>,这也是一种获取匹配类的方式。

准备工作都已经好了,接下来就该动手实现了,此时我还有一个问题,yguard中是如何创建对象的,xml和类是如何结合的,于是我找到了 ant 开发者文档,我们下篇先简单翻译下文档看看ant中的基本规则,磨刀不误砍柴工说的就是现在。

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

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

相关文章

大功率回馈式直流电子负载箱的运用

大功率回馈式直流电子负载箱能够模拟各种负载条件&#xff0c;可以在实验室环境中对电源、电池、太阳能电池板等电子设备进行全面的性能测试和模拟负载&#xff0c;具有高功率输出、高精度、高稳定性和高可靠性的特点&#xff0c;能够满足各种应用场景的需求。 电源测试方面大功…

深入了解桶排序:原理、性能分析与 Java 实现

桶排序&#xff08;Bucket Sort&#xff09;是一种排序算法&#xff0c;通常用于将一组数据分割成有限数量的桶&#xff08;或容器&#xff09;&#xff0c;然后对每个桶中的数据进行排序&#xff0c;最后将这些桶按顺序合并以得到排好序的数据集。 桶排序原理 确定桶的数量&am…

eclipse 某个文件不能编辑

今天打开eclipse 突然发现有一个文件不能编辑&#xff0c;左下角发现此文件被修改为只读&#xff0c; 右键此文件-->properties--> Resource -->在Attributes中&#xff0c;取消Read-only选项--> Apply 此时&#xff0c;发现eclipse 右下角 变为Writable。再次编辑…

windows查看登陆的IP

我的电脑右键打开管理 筛选当前日志 4648 可以查看到IP了

象棋小游戏【小游戏】(Java课设)

系统类型 Java实现的小游戏 使用范围 适合作为Java课设&#xff01;&#xff01;&#xff01; 部署环境 jdk1.8Idea或eclipse 运行效果 本系统源码地址&#xff1a;https://download.csdn.net/download/qq_50954361/88388732 更多Java课设系统源码地址&#xff1a;更多J…

Android---java线程优化 偏向锁、轻量级锁和重量级锁

java 中的线程是映射到操作系统原生线程之上的&#xff0c;如果要阻塞或唤醒一个线程就需要操作系统的帮忙&#xff0c;这就需要从用户态转换到核心态。状态转换需要花费很多时间&#xff0c;如下代码所示&#xff1a; private Object lock new Object();private int value;p…

研发必会-异步编程利器之CompletableFuture(含源码 中)

近期热推文章&#xff1a; 1、springBoot对接kafka,批量、并发、异步获取消息,并动态、批量插入库表; 2、SpringBoot用线程池ThreadPoolTaskExecutor异步处理百万级数据; 3、基于Redis的Geo实现附近商铺搜索(含源码) 4、基于Redis实现关注、取关、共同关注及消息推送(含源码) 5…

Avalonia常用小控件Charts

1.项目下载地址&#xff1a;https://gitee.com/confusedkitten/avalonia-demo 2.UI库Semi.Avalonia&#xff0c;项目地址 https://github.com/irihitech/Semi.Avalonia 3.Charts库&#xff0c;LiveChartsCore.SkiaSharpView.Avalonia&#xff0c;Nuget获取只有预览库&#x…

Vue3模块找不到问题解决:找不到模块‘vue ‘。你的意思是将“模块解决方案”选项设置为“节点”,还是添加ali

Vue3 vite 项目引入 vue 报错 Cannot find module ‘vue‘. Did you mean to set the ‘moduleResolution‘ option to ‘node‘, or to add ali 在项目中找到 tsconfig.json 文件 找到配置项里的 "moduleResolution": "bundler", 将其改成 &q…

简述WPF中MVVM的设计思想

近年来&#xff0c;随着WPF在生产、制造、工控等领域应用越来越广泛&#xff0c;对WPF的开发需求也在逐渐增多&#xff0c;有很多人不断的从Web、WinForm开发转向了WPF开发。 WPF开发有很多新的概念及设计思想&#xff0c;如数据驱动、数据绑定、依赖属性、命令、控件模板、数…

win10 配置静态IP脚本

静态IP配置方法&#xff1a; 1、新建一个txt文本&#xff0c;加入以下代码 netsh interface ip set address name"以太网" sourcestatic addr192.168.101.11 mask255.255.255.0 gateway192.168.101.12、将TXT另存为ANSI编码&#xff01;将TXT另存为ANSI编码&#x…

2023年中国舞台烟雾机产量、销量及市场规模分析[图]

舞台烟雾机是一种用于舞台表演和演出的设备&#xff0c;它能够产生各种形式的烟雾效果&#xff0c;以增强舞台表演的视觉效果和氛围。舞台烟雾机通常由气泵、烟雾发生器、控制器和烟雾管道等组成&#xff0c;可以通过控制器调节烟雾的浓度、颜色和流量&#xff0c;以满足不同演…

C++ 位图与布隆过滤器

目录 前言位图场景演示应用场景模拟实现问题例题 布隆过滤器例子理解应用 例题 前言 位图与布隆过滤器是用来在海量数据中判断一个数据在不在的问题的数据结构&#xff0c;这种数据结构在存储空间上大大的优于红黑树、哈希等数据结构 位图 我们为了处理一个数据在海量数据中…

Linux CentOS8安装gitlab_ce步骤

1 下载安装包 wget --content-disposition https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/8/gitlab-ce-15.0.2-ce.0.el8.x86_64.rpm/download.rpm2 安装gitlab yum install policycoreutils-python-utilsrpm -Uvh gitlab-ce-15.0.2-ce.0.el8.x86_64.rpm3 更新配…

Gpt-4多模态功能强势上线,景联文科技多模态数据采集标注服务等您来体验!

就在上个月&#xff0c;OpenAI 宣布对ChatGPT 进行重大更新&#xff0c;该模型不仅能够通过文字输入进行识别和分析&#xff0c;还能够通过语音、图像甚至视频等多种模态的输入来获取、识别、分析和输出信息。这一重要技术突破&#xff0c;将促进多模态自然语言处理的发展&…

ESP32-WROOM-32无法进入下载模式进行程序上传的问题

结论 先说结论&#xff0c;ESP32-WROOM-32无法进入下载模式通过串口进行程序上传&#xff0c;可能是GPIO2引脚没有通过下拉电阻拉低&#xff0c;导致无法进入正确的启动模式。 启动模式 ESP32启动时会打印rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) 复位源rs…

mysql面试题49:MySQL中不同text数据类型的最大长度

该文章专注于面试&#xff0c;面试只要回答关键点即可&#xff0c;不需要对框架有非常深入的回答&#xff0c;如果你想应付面试&#xff0c;是足够了&#xff0c;抓住关键点 面试官&#xff1a;MySQL中TEXT数据类型的最大长度 在MySQL中&#xff0c;TEXT数据类型用于存储较大…

idea怎么设置作者信息(详细)

目录 一&#xff1a;在Java类的开头自动注释作者名字和日期等信息 二&#xff1a;给Java的方法注释作者名字和日期等信息 1. 不可修改的模板&#xff1a;Postfix Completion 2. 可修改的模板&#xff1a;Live Templates tips&#xff1a;首先给大家推荐两款好用的免费软件&…

【Linux】多进程编程

目录 1. 进程基础知识 2. 查看进程 3. 杀死进程 4. 获取进程标识符 5. 进程创建 6. 进程终止 7. 进程等待 8. 进程程序替换 9. 进程间通信之管道 9.1 匿名管道 9.2 命名管道&#xff08;FIFO&#xff09; 10. 进程间通信之共享内存 11. 进程间通信之信号 11.1 Li…

Linux传统跨进程通信原理

文章目录 前言一、进程隔离二、进程空间划分&#xff1a;用户空间(User Space)/内核空间(Kernel Space)三、系统调用&#xff1a;用户态与内核态四、Linux下传统IPC跨进程通信原理1、发送进程通过系统调用&#xff0c;将需要发送的数据拷贝到Linux进程的内核空间中的缓存区(数据…