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:

修改方法成功:

多人游戲也開放了:

啟動器命令行參數里加上這行:

完美:
