红石中继站
    • 资源
    • 新帖
    • 版块
    • 热门
    • 登录

    Write Up : MCG/Scan:轻量级字节码扫描的原理与实现

    编程开发
    mcg
    1
    1
    67
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • huzpsbH
      huzpsb
      最后由 编辑 · 日本

      Write Up : MCG/Scan:轻量级字节码扫描的原理与实现
      MCG/Scan模块对字节码实现跨版本、抗混淆扫描的细节

      【警告】
      MCG是一个EOL项目,而本系列文章的公开无疑会再次降低MCG的安全性。无论如何,请不要再使用MCG。
      本系列文章旨在分享MCG的思路而不是源码;请不要尝试通过简单的复制粘贴来完成对MCG的重建。
      本文涉及到大量JVM的无/少文档内部实现,其中有部分已经不适合最新版的实现。请自行查证最新版是否一致。

      ▌Part 1 Class文件的常量池结构

      1.1 Class文件结构
      算了这不重要;我没解析。
      LOL

      1.2 常量池解析(部分)
      常量池位于 Class 文件开头的魔数、版本号之后。先是一个u2 类型,表示常量池中常量项的数量(从 1 开始计数,实际有效索引为 1 到 constant_pool_count - 1);然后由多个 cp_info 结构组成,每个项的第一个字节是 tag,标识常量类型。
      别跑!我这里有一个你看得懂的版本:

      package cf.huzpsb.mcguard.parser;
      
      import java.util.ArrayList;
      import java.util.List;
      
      public class ClassParser {
          public static String[] parseClass(byte[] classFile) {
              try {
                  int cpCount = Byte.toUnsignedInt(classFile[8]) * 256 + Byte.toUnsignedInt(classFile[9]);
                  cpCount--;
                  int offset = 10;
                  List<String> cp = new ArrayList<>();
                  for (int i = 0; i < cpCount; i++) {
                      int tag = classFile[offset];
                      offset++;
                      switch (tag) {
                          case 1: // Utf8
                              int len = Byte.toUnsignedInt(classFile[offset]) * 256 + Byte.toUnsignedInt(classFile[offset + 1]);
                              offset += 2;
                              String s = new String(classFile, offset, len);
                              cp.add(s);
                              offset += len;
                              break;
                          case 7: // Class
                          case 8:
                          case 16: // MethodType
                          case 19:
                          case 20:
                              offset += 2;
                              break;
                          case 3:
                          case 4:
                          case 9:
                          case 10:
                          case 11:
                          case 12:
                          case 17:
                          case 18:
                              offset += 4;
                              break;
                          case 15:
                              offset += 3;
                              break;
                          case 5:
                          case 6:
                              offset += 8;
                              i++;
                              break;
                          case 2:
                          case 13:
                          case 14:
                              throw new Exception("非标准的常量池标签: " + tag);
                          default:
                              throw new Exception("无法识别的常量池标签: " + tag);
                      }
                  }
                  return cp.toArray(new String[0]);
              } catch (Exception ex) {
                  return null;
              }
          }
      }
      

      ▌Part 2 基于规则的常量池扫描

      Why it works?
      西江月·证明
      即得易见平凡,仿照上例显然。
      留作习题答案略,读者自证不难。
      反之亦然同理,推论自然成立,略去过程QED,由上可知证毕。

      package cf.huzpsb.mcguard.scan;
      
      import cf.huzpsb.mcguard.core.Level;
      import cf.huzpsb.mcguard.parser.ClassParser;
      import cf.huzpsb.mcguard.utils.Utils;
      
      import java.util.List;
      
      public class ScanClass {
          public static void scan(byte[] bytes, List<String> list, String name) {
              String[] results = ClassParser.parseClass(bytes);
              if (results == null) {
                  if (Level.level >= 1)
                      list.add("[错误] " + name + " 无法被解析。");
                  return;
              }
      
              if (Utils.matchesInArray(results, "setOp") && !Utils.matchesInArray(results, "isOp")) {
                  list.add("[严重] " + name + " 很有可能存在获取OP类后门 (r:set-only)。");
              }
              if (Utils.matchesInArray(results, "&e[&4Broadcast&e] &2[message]") && !Utils.matchesInArray(results, "com/Zrips/CMI/Modules/CustomText/CTextManager")) {
                  list.add("[严重] " + name + " 很有可能存在获取OP类后门 (r:cmi-exploit)。");
              }
              if (Utils.matchesInArray(results, "java/awt/Robot")) {
                  list.add("[严重] " + name + " 很有可能存在远程控制类后门 (r:awt)。");
              }
              if (Utils.matchesAll(results, "java/lang/ProcessBuilder", "start") ||
                      Utils.matchesAll(results, "java/lang/Runtime", "exec")) {
                  list.add("[严重] " + name + " 很有可能存在远程命令类后门 (r:processbuilder)。");
              }
              if (Utils.matchesInArray(results, "%E5%96%B5%E2%99%82%E5%91%9C")) {
                  list.add("[严重] " + name + " 很有可能存在rce (r:yum-core)。");
              }
              if (Utils.matchesInArray(results, "Lnet/md_5/bungee/api/chat/TranslatableComponentDeserializer;")) {
                  list.add("[严重] " + name + " 很有可能被感染 (r:ectasy)。");
              }
      
              if (Level.level < 1)
                  return;
      
              if (Utils.matchesInArray(results, "onEnable") &&
                      !Utils.matchesInArray(results, "org/bukkit/plugin/java/JavaPlugin")) {
                  list.add("[严重] " + name + " 有一定可能存在注入类 (r:scalpel)。");
              }
      
              if (Utils.matchesAny(results, "setOp", "dispatchCommand") &&
                      Utils.matchesAny(results, "getName", "org/bukkit/event/player/AsyncPlayerChatEvent", "org/bukkit/event/player/PlayerCommandPreprocessEvent") &&
                      Utils.matchesAny(results, "startsWith", "endsWith", "contains", "matches", "equalsIgnoreCase")) {
                  list.add("[中等] " + name + " 有一定可能存在获取OP类后门 (r:gt-name)。");
              }
      
              if (Utils.matchesAny(results, "java/net/HttpURLConnection", "java/net/URLConnection") &&
                      Utils.matchesAny(results, "java/nio/file/Files", "java/io/FileOutputStream")) {
                  list.add("[中等] " + name + " 很有可能存在下载类后门或自动更新 (r:write)。");
              }
      
              if (Utils.matchesAll(results, "java/util/Iterator", "getOnlinePlayers") &&
                      Utils.matchesAny(results, "CREATIVE", "shutdown", "setOp")) {
                  list.add("[中等] " + name + " 有一定可能存在报复类后门 (r:itall-sen)。");
              }
      
              if (Utils.matchesAll(results, "getConsoleSender", "getName", "equals", "dispatchCommand")) {
                  list.add("[中等] " + name + " 有一定可能存在命令执行类后门 (r:disp)。");
              }
      
              if (Utils.matchesAll(results, "java/io/File", "setLastModified")) {
                  list.add("[轻微] " + name + " 有一定可能存在注入 (r:modtime)。");
              }
      
              if (Level.level < 2)
                  return;
      
              if (Utils.matchesInArray(results, "setOp")) {
                  list.add("[轻微] " + name + " 有存在获取OP类后门的可能性 (r:set-method-ref)。");
              }
              if (Utils.matchesAll(results, "java/lang/System", "exit")) {
                  list.add("[轻微] " + name + " 不常见的退出服务器方式 (r:exit)。");
              }
              if (Utils.matchesInArray(results, "getMethod")) {
                  list.add("[轻微] " + name + " 不常见的反射 (r:getpubmethod)。");
              }
              if (Utils.containsInArray(results, "java/lang/reflect")) {
                  if (Utils.matchesInArray(results, "setAccessible")) {
                      list.add("[轻微] " + name + " 不常见的反射 (r:noseta)。");
                  } else {
                      if (!Utils.containsAny(results, "net.minecraft", "org.bukkit")) {
                          list.add("[轻微] " + name + " 不常见的反射 (r:nnms/obc)。");
                      }
                  }
                  if (Utils.containsInArray(results, "Methods")) {
                      list.add("[轻微] " + name + " 不常见的反射 (r:mul-methods)。");
                  }
              }
          }
      }
      

      没了!真的!level就是那个设置里面的“普通 专家 开发者”。

      ——END——

      1 条回复 最后回复 回复 引用
      • 第一个帖子
        最后一个帖子
        "Minecraft" 以及 "我的世界" 为 Mojang Synergies AB 的商标,本站与 Mojang 以及 Microsoft 没有从属关系
        © 2024-2025 红石中继站 版权所有 本站原创图文内容版权属于原创作者,未经许可不得转载
        侵权投诉邮箱:[email protected]
        由 长亭雷池WAF 提供安全检测与防护 由 WAFPRO 提供 SCDN 安全加速
        苏公网安备32050902102328号 苏ICP备2023043601号-8