MC禁止多人游戏引发的破解
问题起因
朋友玩服务器,发现从6.8日9:00开始1.16.5客户端无法进入多人模式。
搜索发现好像是当天8号才出现这个问题。
摸索
对mc不太熟,觉得是微软搞了什么动作,抓包啥的,没抓到。
在语言资源里找到这串字符串:
简单说就是玩的离dao线ban模式,没权限给你多人模式。那为啥以前离线模式可以玩呢?不懂,研究一下Minecraft源代码。
Minecraft逆向
一开始我是直接jadx打开主程序,发现没什么东西。通过上面字符串引用的签名,直接搜索,发现有个引用的地方调用了账号验证方法,然后抛出异常:
找到jar包:
发现有个传统验证方法,但是应该已经弃用了:
源码分析
一开始找到了一个项目:yushijinhun/authlib-injector: Build your own Minecraft authentication system.
(https://github.com/yushijinhun/authlib-injector)
但是我试了,不行,可能是版本啥的问题。
于是尝试去找反混淆的项目,比如MCP啥的,看MC的源码。
这里用了forge的开发环境,反混淆映射表是mapped_official频道的,可以看authlib包的源代码:
找来找去找到了mc主类和主界面类:
感觉不远了,继续找:
检查是否多人模式。跟进去,调用了authlib。
socialInteractionsService
是一个接口,通过工厂方法创建一个com.mojang.authlib.yggdrasil.YggdrasilSocialInteractionsService
实例。发现这里就是判断是否允许多人登录的地方:
修改方法逻辑
本来想用forge写一个mod修改,但是好像不行,或者我没找到文档。然后想着mod里写一个动态hook,尝试了一下没成功。最后想着用javaagent动态修改方法字节码。
找了一些教程,研究了一下,踩了很多坑,找了很多bug。
具体解决的问题:
1.ClassPool.getDefault()
报错java.lang.NoClassDefFoundError
,因为依赖冲突,加上shade插件。
2.javassist.NotFoundException
:classname传进去的是左斜杠分隔路径的类名,跟了下javassist源码才知道类池用点分隔的类名查。
3.转换成字节码时找不到类:应该是javassist编译的时候没有引入这个包,后来直接删掉了,因为这个检测的方法功能单一,全部置true。
步骤如下:
创建一个maven项目(java8),编辑配置:
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.example agnet 1.0-SNAPSHOT jar agnet http://maven.apache.org UTF-8 org.apache.maven.plugins maven-jar-plugin 2.4 org.example.PremainTest true true org.apache.maven.plugins maven-compiler-plugin 8 8 maven-shade-plugin 3.1.0 package shade true javassist org.example.javassist junit junit 3.8.1 test org.javassist javassist 3.27.0-GA
创建一个org.example.PremainTest
类:
package org.example; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; import java.lang.instrument.UnmodifiableClassException; import java.security.ProtectionDomain; public class PremainTest { public static void premain(String agentArgs, Instrumentation inst) { System.out.println("agent跑起来啦!====================================="); inst.addTransformer(new JavassistTransformer(), true); } public static void agentmain(String agentArgs, Instrumentation inst) throws UnmodifiableClassException { inst.addTransformer(new JavassistTransformer(), true); Class classes[] = inst.getAllLoadedClasses(); for (int i = 0; i < classes.length; i++) { if (classes[i].getName().equals("YggdrasilSocialInteractionsService")) { System.out.println("成功转换类:" + classes[i].getName()); inst.retransformClasses(classes[i]); break; } } } static class JavassistTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { if ("com/mojang/authlib/yggdrasil/YggdrasilSocialInteractionsService".equals(className)) { try { System.out.println("正在转换类名:" + className); ClassPool classPool = ClassPool.getDefault(); CtClass clazz = classPool.get("com.mojang.authlib.yggdrasil.YggdrasilSocialInteractionsService"); CtMethod method = clazz.getDeclaredMethod("checkPrivileges"); // 修改方法 method.setBody("{" + " chatAllowed = true;" + " serversAllowed = true;" + " realmsAllowed = true;" + " }"); System.out.println("成功修改checkPrivileges方法!方法签名:" + method.getLongName()); byte[] bytes = clazz.toBytecode(); clazz.detach(); return bytes; } catch (Throwable e) { e.printStackTrace(); } } return classfileBuffer; } } }
mvn package
编译,生成jar包。
回到forge项目(或者启动器),运行参数加上-javaagent
:
修改方法成功:
多人游戏也开放了:
启动器命令行参数里加上这行:
完美:
