MiraiForum

    • 注册
    • 登录
    • 搜索
    • 热门
    • 最新
    • 未解决
    • 标签
    • 群组
    • 友情链接
    1. 主页
    2. Karlatemp
    3. 最佳
    • 资料
    • 关注 0
    • 粉丝 13
    • 主题 16
    • 帖子 389
    • 最佳 50
    • 有争议的 1
    • 群组 3

    Karlatemp 发布的最佳帖子

    • Mirai解密 - 2022/4/1 活动

      欢迎来到Mirai解密

      活动详细

      持续时间 7天
      开始时间 四月一日

      游戏形式类似信息安全中的ctf比赛,但是题目的设置并不完全是ctf的类型,这是为了让不会信息安全的玩家也能通过搜索引擎和动脑来参与到游戏中。

      前三位完成解密的玩家可以获得奖品一份,游戏为个人参加,请勿泄漏各种信息给其他人

      活动地址

      发布在 摸鱼区
      Karlatemp
      Karlatemp
    • MCL 无法使用的相关解决方法 - 2022/3/25

      由于 gitee 停止 raw 的直接访问, 我们已经将 mirai-console-loader 的数据文件从 gitee 搬出

      要继续使用使用 mirai-console-loader, 您需要进行以下的配置

      1. 打开 $MCL/config.json
      2. 找到

      "mirai_repo": "....."
      

      将其地址修改为以下地址中的一个 (注意不要删除双引号 "" (英文半角))

      • https://repo.mirai.mamoe.net/keep/mcl
      • https://mirai.mamoe.net/assets/mcl
      • https://mcl.repo.mamoe.net/
      • https://repo.itxtech.org/
      发布在 官方公告
      Karlatemp
      Karlatemp
    • RE: 【每日沙雕图】沙雕小别墅

      8RNCZ17.jpg
      8GIjpg
      wg

      发布在 摸鱼区
      Karlatemp
      Karlatemp
    • [JVM][LowLevel][JDK 9+] JVM 权限逃逸技术

      前言: 仅研究 JDK 9+, JDK 8- 无研究意义

      从 Java 9 开始, Java 引入了一个新的概念, 模块(Module). 模块的存在, 限制了反射技术, 在 JDK 16 中, 直接反射越权修改 java.base 甚至会得到错误 java.lang.reflect.InaccessibleObjectException, 对于某些需要的 devops 而言意味着无法完成预期操作


      在阅读 java.lang.reflect.AccessibleObject 源码后, 有如下代码片段

      
         /**
          * If the given AccessibleObject is a {@code Constructor}, {@code Method}
          * or {@code Field} then checks that its declaring class is in a package
          * that can be accessed by the given caller of setAccessible.
          */
          void checkCanSetAccessible(Class<?> caller) {
              // do nothing, needs to be overridden by Constructor, Method, Field
          }
      
          final void checkCanSetAccessible(Class<?> caller, Class<?> declaringClass) {
              checkCanSetAccessible(caller, declaringClass, true);
          }
      
          private boolean checkCanSetAccessible(Class<?> caller,
                                                Class<?> declaringClass,
                                                boolean throwExceptionIfDenied) {
              if (caller == MethodHandle.class) {
                  throw new IllegalCallerException();   // should not happen
              }
      
              Module callerModule = caller.getModule();
              Module declaringModule = declaringClass.getModule();
      
              if (callerModule == declaringModule) return true;
              if (callerModule == Object.class.getModule()) return true;
              if (!declaringModule.isNamed()) return true;
      
              String pn = declaringClass.getPackageName();
              int modifiers;
              if (this instanceof Executable) {
                  modifiers = ((Executable) this).getModifiers();
              } else {
                  modifiers = ((Field) this).getModifiers();
              }
      
              // class is public and package is exported to caller
              boolean isClassPublic = Modifier.isPublic(declaringClass.getModifiers());
              if (isClassPublic && declaringModule.isExported(pn, callerModule)) {
                  // member is public
                  if (Modifier.isPublic(modifiers)) {
                      logIfExportedForIllegalAccess(caller, declaringClass);
                      return true;
                  }
      
                  // member is protected-static
                  if (Modifier.isProtected(modifiers)
                      && Modifier.isStatic(modifiers)
                      && isSubclassOf(caller, declaringClass)) {
                      logIfExportedForIllegalAccess(caller, declaringClass);
                      return true;
                  }
              }
      
              // package is open to caller
              if (declaringModule.isOpen(pn, callerModule)) {
                  logIfOpenedForIllegalAccess(caller, declaringClass);
                  return true;
              }
      
              if (throwExceptionIfDenied) {
                  // not accessible
                  String msg = "Unable to make ";
                  if (this instanceof Field)
                      msg += "field ";
                  msg += this + " accessible: " + declaringModule + " does not \"";
                  if (isClassPublic && Modifier.isPublic(modifiers))
                      msg += "exports";
                  else
                      msg += "opens";
                  msg += " " + pn + "\" to " + callerModule;
                  InaccessibleObjectException e = new InaccessibleObjectException(msg);
                  if (printStackTraceWhenAccessFails()) {
                      e.printStackTrace(System.err);
                  }
                  throw e;
              }
              return false;
          }
      
      

      有两个关键判断逻辑: declaringModule.isExported(pn, callerModule), declaringModule.isOpen(pn, callerModule)

      阅读 Module.java 后发现有 implAddExports 方法, 通过 IDEA 查找调用引用发现了 java.lang.System 有访问此方法的 JDK Internal API

      
          private static void setJavaLangAccess() {
              // Allow privileged classes outside of java.lang
              SharedSecrets.setJavaLangAccess(new JavaLangAccess() {
                  public Module defineModule(ClassLoader loader,
                                             ModuleDescriptor descriptor,
                                             URI uri) {
                      return new Module(null, loader, descriptor, uri);
                  }
                  public Module defineUnnamedModule(ClassLoader loader) {
                      return new Module(loader);
                  }
                  public void addReads(Module m1, Module m2) {
                      m1.implAddReads(m2);
                  }
                  public void addReadsAllUnnamed(Module m) {
                      m.implAddReadsAllUnnamed();
                  }
                  public void addExports(Module m, String pn, Module other) {
                      m.implAddExports(pn, other);
                  }
                  public void addExportsToAllUnnamed(Module m, String pn) {
                      m.implAddExportsToAllUnnamed(pn);
                  }
                  public void addOpens(Module m, String pn, Module other) {
                      m.implAddOpens(pn, other);
                  }
                  public void addOpensToAllUnnamed(Module m, String pn) {
                      m.implAddOpensToAllUnnamed(pn);
                  }
                  public void addOpensToAllUnnamed(Module m, Set<String> concealedPackages, Set<String> exportedPackages) {
                      m.implAddOpensToAllUnnamed(concealedPackages, exportedPackages);
                  }
                  public void addUses(Module m, Class<?> service) {
                      m.implAddUses(service);
                  }
              });
          }
      

      找到了 JDK 提供的后门之后, 我们只需要调用 SharedSecrets.getJavaLangAccess().addExports 就能开后门了....
      不对,目前还无法调用 SharedSecrets, 还需要一些手段....

      在 java.lang.reflect 中翻到了一个特别的东西, java.lang.reflect.Proxy, 她是破局的关键中心

      抱着好奇的心里, 我尝试了使用 Proxy 实现 jdk.internal.access 中的一个接口玩玩

          public static void main(String[] args) throws Exception {
              var obj = Proxy.newProxyInstance(
                      Usffsa.class.getClassLoader(),
                      new Class[]{Class.forName("jdk.internal.access.JavaLangAccess")},
                      new InvocationHandler() {
                          @Override
                          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                              return null;
                          }
                      }
              );
              System.out.println(obj);
          }
      

      没想到, 运行成功了(eg: 没有对应权限(Exported)是不能实现对应接口的), 迎接着激动的心情, 输出了更多的详细信息

              System.out.println(obj);
              System.out.println(obj.getClass());
              System.out.println(obj.getClass().getModule());
              System.out.println(Object.class.getModule().isExported("jdk.internal.access", obj.getClass().getModule()));
      
      null
      class com.sun.proxy.jdk.proxy1.$Proxy0
      module jdk.proxy1
      true
      

      破局点找到了, java.lang.reflect.Proxy 拥有打开模块访问的权利, 然后尝试对该模块进行注入

          public static void main(String[] args) throws Exception {
              var ccl = new ClassLoader(Usffsa.class.getClassLoader()) {
                  Class<?> defineClass(byte[] code) {
                      return defineClass(null, code, 0, code.length);
                  }
              };
              var obj = Proxy.newProxyInstance(
                      ccl,
                      new Class[]{Class.forName("jdk.internal.access.JavaLangAccess")},
                      (proxy, method, args1) -> null
              );
              var writer = new ClassWriter(0); // org.objectweb.asm.ClassWriter
              writer.visit(Opcodes.V1_8, 0,
                      obj.getClass().getPackageName().replace('.', '/') + "/Test0",
                      null,
                      "java/lang/Object",
                      null
              );
              var injectedClass = ccl.defineClass(writer.toByteArray());
              System.out.println("Proxy     Module  : " + obj.getClass().getModule());
              System.out.println("Injected  Module  : " + injectedClass.getModule());
              System.out.println("Is Same Module    : " + (injectedClass.getModule() == obj.getClass().getModule()));
          }
      
      Proxy     Module  : module jdk.proxy1
      Injected  Module  : module jdk.proxy1
      Is Same Module    : true
      

      至此已经破开了 JVM 的模块限制的死局, 实际应用可参考 [Karlatemp/UnsafeAccessor]

      发布在 技术交流板块
      Karlatemp
      Karlatemp
    • MiraiForum Markdown 额外语法

      本帖子将展示 MiraiForum 的额外 markdown 语法


      隐藏文字

      这里是MiraiForum

      +=[要隐藏的内容]=+
      

      自定义文本颜色

      Colored Text Here

      %(#66ccff)[Colored Text Here]
      

      折叠内容

      要折叠的内容

      注: 咱不支持嵌套引用

      > ^fold
      >
      > 要折叠的内容
      >
      
      发布在 官方公告
      Karlatemp
      Karlatemp
    • RE: 机器人被封了...

      叫你装色图插件

      发布在 使用交流
      Karlatemp
      Karlatemp
    • RE: 要高考了,好紧张

      @RainChan 是一个变态

      发布在 摸鱼区
      Karlatemp
      Karlatemp
    • Java 动态模块系统 | Java dynamic module system

      前言

      在使用高版本的时候, 总会不可避免的接触到模块系统, 比如反射操作 java.base 已经十分困难. 既然 JDK 内部可以享受到模块的保护, 那么我们自己的代码是否也可以享受到模块系统的保护呢

      当然可以,而且也不是非常麻烦。

      使用模块,你将面对以下问题

      • 得到反射保护, 外部代码将不能通过反射强行修改/调用私有成员
      • 更严格的访问控制, 不能直接访问非 required 的模块
      • 失去 --add-opens=....=ALL-UNNAMED 的归属判断
      • 需要专门的 ClassLoader / 需要自行实现 ClassLoader

      使用模块的适用情况

      • 需要编写严格的附属(插件)系统
      • 需要保护自身代码/保护自身内存空间
      • 觉得弄着好玩

      定义一个模块

      注: 此处的定义指的是, 通过运行时代码在运行时定义一个模块.
      而不是大多数资料说的直接写一个 module-info.java

      要定义一个模块, 首先需要一个模块的描述符文件 (ModuleDescriptor), 可以从以编码文件读取 (ModuleDescriptor.read(InputStream) <- module-info.class), 也可以在运行时动态生成一个(ModuleDescriptor.newModule("name_of_module").build())

      jvm 通过包来区分模块, 而一个模块的全部包都需要提前指定, jvm 才会为这些包分配到一个模块内

      var moduleDescriptor = ModuleDescriptor.newModule("my.custom_module")
          .packages(Set.of("io.github.karlatemp.jmse.main"))
          .exports("io.github.karlatemp.jmse.main")
          .build();
      

      这里我们已经拥有了一个模块的描述符, 现在我们还需要一个模块描述的引用, 以及一个模块查找器以让 jvm 可以找到我们的模块

      var myModuleReference = new ModuleReference(
          moduleDescriptor, null
      ) {
          @Override public ModuleReader open() throws IOException {
              throw new UnsupportedOperationException();
          }
      }
      var myModuleFinder = new ModuleFinder() {
          @Override
          public Optional<ModuleReference> find(String name) {
              if (name.equals(moduleDescriptor.name())) {
                  return Optional.of(myModuleReference);
              }
              return Optional.empty();
          }
      
          @Override
          public Set<ModuleReference> findAll() {
              return Set.of(myModuleReference);
           }
      };
      

      最后,定义一个模块

      var bootLayer = ModuleLayer.boot();
      var myConfiguration = bootLayer.configuration().resolve(
          myModuleFinder, ModuleFinder.of(), Set.of(moduleDescriptor.name())
      );
      var classLoader = ClassLoader.getSystemClassLoader();
      var controller = ModuleLayer.defineModules(
          myConfiguration, List.of(bootLayer), $ -> classLoader
      );
      
      Class.forName("io.github.karlatemp.jmse.main.ModuleMain", false, classLoader)
          .getMethod("launch")
          .invoke(null);
      

      ServiceLoader / Class.forName(Module, String)

      还记得 需要专门的 ClassLoader / 需要自行实现 ClassLoader 吗, 虽然在上文已经成功定义了一个模块,但是只要使用 ServiceLoader / Class.forName(Module, String), 那么将无法找到对应的类, 因为一般的 ClassLoader 并没有专门处理动态加载的模块

      Analyze

      通过进行调用分析, 最终可以发现以上两个东西最终都进入到了下面的方法

      public class ClassLoader {
          final Class<?> loadClass(Module module, String name) {
              synchronized (getClassLoadingLock(name)) {
                  // First, check if the class has already been loaded
                  Class<?> c = findLoadedClass(name);
                  if (c == null) {
                      c = findClass(module.getName(), name);
                  }
                  if (c != null && c.getModule() == module) {
                      return c;
                  } else {
                      return null;
                  }
              }
          }
          protected Class<?> findClass(String moduleName, String name) {
              if (moduleName == null) {
                  try {
                      return findClass(name);
                  } catch (ClassNotFoundException ignore) { }
              }
              return null;
          }
      }
      

      不难发现, 由于默认没有处理模块, 导致指定搜索模块的时候将搜索不到动态定义的模块

      而 jdk.internal.loader.ClassLoader$AppClassLoader 并没有处理通过 ModuleLayer.defineModule 定义的模块, 于是也不能直接将模块定义到系统类加载器

      自行实现类加载器

      自行实现类加载器十分简单,只需要

      public class MyCustomClassLoader extends URLClassLoader {
          String moduleName;
      
          @Override
          protected Class<?> findClass(String moduleName, String name) {
              // System.out.println("Find class: " + moduleName + "/" + name);
              if (this.moduleName.equals(moduleName)) {
                  try {
                      return findClass(name);
                  } catch (ClassNotFoundException ignored) {
                  }
              }
              return super.findClass(moduleName, name);
          }
      }
      

      使用 JDK 内置的类加载器

      只需要实现 ModuleReference.open(): ModuleReader, 然后使用

      var controller = ModuleLayer.defineModulesWithOneLoader(
              myConfiguration,
              List.of(bootLayer),
              ClassLoader.getSystemClassLoader().getParent()
      );
      var classLoader = controller.layer().findLoader(moduleDescriptor.name());
      

      即可使用 JDK 内置的内加载器


      完整参考

      • java-module-system-explore
        • BootModuleByStandard.java
        • MyCustomClassLoader.java
      发布在 技术交流板块
      Karlatemp
      Karlatemp
    • 常用资源整合
      分区 title 链接
      - 常见问题 QA https://mirai.mamoe.net/topic/71/
      DEV 忽略某个用户的全部消息 https://mirai.mamoe.net/topic/327/
      DEV 在 bot 发言前进行检查 https://mirai.mamoe.net/topic/599/
      DEV 在 console 命令系统以外的地方使用权限系统 https://mirai.mamoe.net/topic/535/
      DEV 发送网络图片 https://mirai.mamoe.net/topic/337/
      DEV 在 MemberJoinEvent 发出的 @ 无法识别 https://github.com/mamoe/mirai/issues/1559
      DEV 群名片修改事件没有触发 https://github.com/mamoe/mirai/issues/1570
      DEV 自定义 mirai-console 日志系统 https://github.com/mamoe/mirai-console/issues/412
      USE Mirai Console Loader https://mirai.mamoe.net/topic/177/
      USE chat-command https://github.com/project-mirai/chat-command
      USE LuckPerms Mirai - 高级权限服务插件 https://mirai.mamoe.net/topic/68
      USE 命令无法执行 https://mirai.mamoe.net/topic/184/
      USE Mirai api http https://github.com/project-mirai/mirai-api-http
      DOC Mirai UserManual https://github.com/mamoe/mirai/blob/dev/docs/UserManual.md
      DOC SDK List https://github.com/mamoe/mirai/tree/dev/docs#社区-sdk

      Notes:

      • 此处列出的插件为最核心最常用的插件, 需要更多插件请查看 https://mirai.mamoe.net/category/6/
      • 如果有较为常见的问题此处没有列出的可以在本贴内回复
      zhoudu created this issue in mamoe/mirai

      closed 群内@机器人,mirai没有识别出该消息是At类型,识别成PlainText类型 #1559

      Moyulingjiu created this issue in mamoe/mirai

      closed 群名片修改事件无法触发 #1570

      LovesAsuna created this issue in mamoe/mirai-console

      closed 第三方日志系统接管 #412

      发布在 官方公告
      Karlatemp
      Karlatemp
    • RE: 2.5版本机器人撤回群员消息
      MessageSource.recall(source);
      
      发布在 开发交流
      Karlatemp
      Karlatemp
    • RE: LuckPerms - Mirai - 高级权限服务插件

      @Cuki 你需要让她至少执行一次命令(比如/help, 只需要让她发出去就行),为了减少数据占用单纯的聊天是不会进行数据初始化的

      发布在 插件发布
      Karlatemp
      Karlatemp
    • (WIP) java-flatlaf-style-setup ----- Java GUI look and feel setup

      优化 java gui 主题(显示)


      Maven: com.kasukusakura:java-flatlaf-style-setup

      Download (.mirai2.jar)

      GitHub: KasukuSakura/java-flatlaf-style-setup

      Snapshot:

      726e1f7d-96a9-4179-a07d-2f6967a53f1a-image.png

      发布在 其他项目发布
      Karlatemp
      Karlatemp
    • RE: 询问下如何区分表情包和图片

      如果是完全重复可以根据图片的 id 来判断

      发布在 开发交流
      Karlatemp
      Karlatemp
    • LuckPerms - Mirai - 高级权限服务插件

      LuckPerms - Mirai

      • Repo: Karlatemp/LuckPerms-Mirai
      • Original Repo: lucko/LuckPerms
      • Issue report: new issue
      • Platform request
        • >= mirai-core 2.0-RC
        • >= mirai-console 2.0-RC
      • Download

      一款高级易使用的 mirai-console 权限服务插件

      Snapshot

      3.png
      0.png
      2.png
      1.png


      Install

      Way 1. By MCL: mcl --update-package io.github.karlatemp:luckperms --channel nightly --type plugin

      Way 2. Download release from Releases. Then put it into plugins

      Usages

      LuckPerms-Mirai 基于 LuckPerms 开发, 详细用法请百度/谷歌/阅读 LuckPerms wiki

      LuckPerms wiki

      LuckPerms-Mirai 的身份上下文使用 context 实现, 可以在聊天中使用
      /lp user <****> info 查看上下文

      下面是一些示例命令

      
      // 授予群聊管理员(包含群主)一项权限
      /lp group default permission set AdminPermission admin=true
      
      // 授予群主一条权限
      /lp group default permission set OwnerPermission level=owner
      
      // 授予管理员(不含群主)一条权限
      /lp group default permission set OwnerPermission level=admin
      
      // 授予在某个群的所有人一条权限
      /lp group default permission set PermissionInGroup group=1234567890
      
      // 授予某个群的群聊管理员一条权限
      /lp group default permission set PermissionInGroup group=1234567890 admin=true
      
      
      // 创建系统管理组
      /lp creategroup root
      /lp group root permission set *
      /lp user 1234567890 parent set root
      
      // 开启权限调试模式 (debug(verbose) mode)
      // 查看具体权限名
      // WARNING: Dont run this command in chatting
      /lp verbose on
      
      // 开启权限调试模式 (debug(verbose) mode), 并在 Web 查看
      /lp verbose record
      //WAIT.....
      /lp verbose upload
      
      

      发布在 插件发布
      Karlatemp
      Karlatemp
    • RE: 【闲聊】作为一个皇帝,怎样才能让名字比较响亮

      黄太君

      发布在 摸鱼区
      Karlatemp
      Karlatemp
    • RE: 插件打包时如何带上依赖的 jar

      如果是 Gradle, 使用 shadowJar

      发布在 开发交流
      Karlatemp
      Karlatemp
    • RE: 萌新解难
      if (message.any { it is At && it.target == bot.id }) {}
      if (message.stream().anyMatch( it -> it instanceof At && ((At) it).getTarget() == bot.getId() )) {}
      
      发布在 使用交流
      Karlatemp
      Karlatemp
    • RE: 【每日沙雕图】沙雕小别墅

      -6feec107c84c591a.jpg
      -50e38b63e17612fc.jpg

      发布在 摸鱼区
      Karlatemp
      Karlatemp
    • RE: 一个愚蠢的问题,用kotlin写事件监听时对语法的不解:event ->

      lambda本来就可以写多行代码呀

      发布在 开发交流
      Karlatemp
      Karlatemp
    • RE: 新人提问,如何让群主/管理能开关bot在该群的所有响应

      https://mirai.mamoe.net/topic/327/

      发布在 使用交流
      Karlatemp
      Karlatemp
    • 1
    • 2
    • 3
    • 1 / 3