首页 > 其他分享 >混淆技术研究笔记(六)如何基于yGuard实现?

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

时间:2023-10-07 10:04:41浏览次数:38  
标签:混淆 outName adjustSections 代码 笔记 inName yGuard entry new

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中的基本规则,磨刀不误砍柴工说的就是现在。

标签:混淆,outName,adjustSections,代码,笔记,inName,yGuard,entry,new
From: https://blog.51cto.com/isea533/7732805

相关文章

  • 【爬虫实战】用python爬小红书某话题的笔记,以#杭州亚运会#为例
    目录一、爬取目标二、爬虫代码讲解2.1分析过程2.2爬虫代码三、演示视频四、获取完整代码一、爬取目标您好!我是@马哥python说,一名10年程序猿。最近的亚运会大家都看了吗。除了振奋人心,还主打一个爱憎分明(主要针对小日子和韩国),看了的小伙伴都懂得!我用python爬取了小红书上#杭......
  • 《流畅的Python》 读书笔记 231007(第二章第一部分)
    第2章数据结构ABC语言是Python的爸爸~很多点子在现在看来都很有Python风格:序列的泛型操作、内置的元组和映射类型、用缩进来架构的源码、无需变量声明的强类型不管是哪种数据结构,字符串、列表、字节序列、数组、XML元素,抑或是数据库查询结果,它们都共用一套丰富的操作:迭......
  • 组合数学学习/复习笔记
    模板(前置芝士)P1226【模板】快速幂|取余运算目的:顾名思义,快速求解乘方。实现:挺好写的。题目传送门代码P3811【模板】乘法逆元开longlong!!定义:若\(a*x\equiv1\pmodb\),且\(a\)与\(b\)互质,那么就能定义\(x\)是\(a\)在模\(p\)意义下的逆元,记为\(a^{......
  • 【学习笔记】(13) 平衡树——记住不的板子
    TreapSplay无旋Treap——fhqtreap简介就是没有旋转操作的Treap,一些性质什么的都跟Treap类似。算法介绍(1)merge(x,y)将两棵“有序”(x中元素的权值最大值小于y中元素权值最小值)的Treap合并成一棵。intch[N][2],sz[N],pri[N],val[N];//val为权值,pri为优先级,sz......
  • 虚树 学习笔记
    2023/10/6发现找不到题做了,决定学习新算法。经过在一些题单中的翻找,决定学习虚树。Part1.引入以一道例题来引入虚树吧。[HEOI2014]大工程给定一棵有\(n\)个点的树,边权均为\(1\)。现在有\(q\)次询问。每次询问取\(k\)个点出来建立完全图。定义连接两个点的代价为......
  • 国庆笔记
    1、 快的保护慢的:比如使用guava保护redis,使用redis保护mysql。人多力量大(集群):一个Mysql不行,就分库分表;一个redis不行,就redis集群;主不行,从可以帮忙扛读流量;尽可能懒:能一会做,就别现在做,能异步就别同步;比如读集群通过异步推送数据,能接受一定时延,就不同步。 https://ju......
  • RK3588开发笔记(一):基于方案商提供的宿主机交叉编译Qt5.12.10
    前言  rk3588开发车机,方案上提供的宿主机只是编译rksdk的版本,并未编译好Qt,那么需要自行交叉编译Qt系统。选择的Qt的版本为5.12.10。 宿主机准备  下载并打开宿主机,只有sdk,并没有交叉编译的Qt。   Qt准备  下载Qt5.12.10的开源软件(方案商提供)。  ......
  • openGauss学习笔记-91 openGauss 数据库管理-内存优化表MOT管理-内存表特性-使用MOT-M
    openGauss学习笔记-91openGauss数据库管理-内存优化表MOT管理-内存表特性-使用MOT-MOT使用MOT外部支持工具为了支持MOT,修改了以下外部openGauss工具。请确保使用的工具是最新版本。下面将介绍与MOT相关的用法。有关这些工具及其使用方法的完整说明,请参阅《工具与命令参考》。91......
  • U9C学习笔记
    建立物料清单BOM时,必须钩选“主批量“,否则建好之后重新再打开窗体,建好的树型BOM会断层。建立完之后,必须每一层物料都全部审核,否则MPS计算时无法展开多阶物料。  MPS计算完成之后,在”计划者工作台“可以查看到”结束净算“,说明已计算完成。 MPS计算时查看错误日志。......
  • 软件设计开发笔记6:基于QT的Modbus RTU从站
      Modbus是一种常见的工业系统通讯协议。在我们的设计开发工作中经常使用到它。作为一种主从协议,在上一篇我们实现了MobusRTU主站工具,接下来这一篇中我们将简单实现一个基于QT的MobusRTU从站工具。1、概述  ModbusRTU从站应用很常见,有一些是通用的,有一些是专用的。而这里......