From 9a5415a199d032e2bbfd462ad4f60ace6b070d5e Mon Sep 17 00:00:00 2001 From: sirjonasxx <36828922+sirjonasxx@users.noreply.github.com> Date: Sun, 27 Dec 2020 21:30:47 +0100 Subject: [PATCH] unity wasm crack --- G-Earth/pom.xml | 14 +- .../services/unity_tools/WasmCodePatcher.java | 225 ++++++++++++++++++ 2 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 G-Earth/src/main/java/gearth/services/unity_tools/WasmCodePatcher.java diff --git a/G-Earth/pom.xml b/G-Earth/pom.xml index 207afac..25c618c 100644 --- a/G-Earth/pom.xml +++ b/G-Earth/pom.xml @@ -12,7 +12,7 @@ 1.8 - 9.2.14.v20151106 + 9.4.35.v20201120 @@ -190,8 +190,20 @@ javax-websocket-server-impl ${jettyVersion} + + org.eclipse.jetty + jetty-http + ${jettyVersion} + + + + G-Earth + G-Wasm + 1.0 + + diff --git a/G-Earth/src/main/java/gearth/services/unity_tools/WasmCodePatcher.java b/G-Earth/src/main/java/gearth/services/unity_tools/WasmCodePatcher.java new file mode 100644 index 0000000..07477a6 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/unity_tools/WasmCodePatcher.java @@ -0,0 +1,225 @@ +package gearth.services.unity_tools; + +import wasm.disassembly.InvalidOpCodeException; +import wasm.disassembly.instructions.Expression; +import wasm.disassembly.instructions.Instr; +import wasm.disassembly.instructions.InstrType; +import wasm.disassembly.instructions.control.CallInstr; +import wasm.disassembly.instructions.variable.LocalVariableInstr; +import wasm.disassembly.modules.Module; +import wasm.disassembly.modules.indices.FuncIdx; +import wasm.disassembly.modules.indices.LocalIdx; +import wasm.disassembly.modules.indices.TypeIdx; +import wasm.disassembly.modules.sections.code.Func; +import wasm.disassembly.modules.sections.export.Export; +import wasm.disassembly.modules.sections.export.ExportDesc; +import wasm.disassembly.modules.sections.imprt.Import; +import wasm.disassembly.modules.sections.imprt.ImportDesc; +import wasm.disassembly.types.FuncType; +import wasm.disassembly.types.ResultType; +import wasm.disassembly.types.ValType; +import wasm.misc.Function; + +import java.io.*; +import java.util.*; + +public class WasmCodePatcher { + + private final Module module; + private String file; + + public WasmCodePatcher(String file) throws IOException, InvalidOpCodeException { + module = new Module(file); + this.file = file; + } + + public void patch() throws IOException, InvalidOpCodeException { + FuncIdx returnByteId = findReturnByteFunc(); + FuncIdx setkey = findSetKeyFunc(); + FuncIdx outgoingIdx = findOutFunc(); + FuncIdx incomingIdx = findInFunc(); + + hook(setkey, "g_chacha_setkey"); + copyEmptyHook(returnByteId, "_gearth_returnbyte_copy", "g_chacha_returnbyte"); + copyEmptyHook(outgoingIdx, "_gearth_outgoing_copy", "g_outgoing_packet"); + copyEmptyHook(incomingIdx, "_gearth_incoming_copy", "g_incoming_packet"); + + module.assembleToFile(file); + } + + + private FuncIdx findOutFunc() { + TypeIdx expectedTypeIdx = module.getTypeSection().getTypeIdxForFuncType(new FuncType( + new ResultType(Arrays.asList(ValType.I32, ValType.I32, ValType.I32)), + new ResultType(Collections.emptyList()) + )); + + outerloop: + for (int i = 0; i < module.getCodeSection().getCodesEntries().size(); i++) { + FuncIdx currentIdx = new FuncIdx(i + module.getImportSection().getTotalFuncImports(), module); + + Func func = module.getCodeSection().getByIdx(currentIdx); + if (func.getLocalss().size() != 0) continue; + if (!module.getFunctionSection().getByIdx(currentIdx).equals(expectedTypeIdx)) continue; + + List expression = func.getExpression().getInstructions(); + + if (expression.size() != 6) continue; + + if (expression.get(0).getInstrType() != InstrType.LOCAL_GET) continue; + if (expression.get(1).getInstrType() != InstrType.LOCAL_GET) continue; + if (expression.get(2).getInstrType() != InstrType.LOCAL_GET) continue; + if (expression.get(3).getInstrType() != InstrType.I32_LOAD) continue; + if (expression.get(4).getInstrType() != InstrType.I32_CONST) continue; + if (expression.get(5).getInstrType() != InstrType.CALL) continue; + + return currentIdx; + } + + return null; + } + private FuncIdx findSetKeyFunc() { + FuncType expectedType = new FuncType( + new ResultType(Arrays.asList(ValType.I32, ValType.I32, ValType.I32, ValType.I32)), + new ResultType(Collections.emptyList()) + ); + + List expectedExpr = Arrays.asList(InstrType.I32_CONST, InstrType.I32_LOAD8_S, + InstrType.I32_EQZ, InstrType.IF, InstrType.BLOCK, InstrType.LOCAL_GET, InstrType.I32_CONST, + InstrType.LOCAL_GET, InstrType.I32_LOAD, InstrType.I32_CONST, InstrType.I32_CONST, InstrType.I32_CONST, + InstrType.CALL); + + outerloop: + for (int i = 0; i < module.getCodeSection().getCodesEntries().size(); i++) { + FuncIdx funcIdx = new FuncIdx(i + module.getImportSection().getTotalFuncImports(), module); + + Function function = new Function(module, funcIdx); + if (!function.getFuncType().equals(expectedType)) continue; + if (!(function.getLocalsFloored().size() == 1 && function.getLocalsFloored().get(0) == ValType.I32)) continue; + if (function.getCode().getInstructions().size() != expectedExpr.size()) continue; + + for (int j = 0; j < function.getCode().getInstructions().size(); j++) { + Instr instr = function.getCode().getInstructions().get(j); + if (instr.getInstrType() != expectedExpr.get(j)) continue outerloop; + } + + return funcIdx; + } + + return null; + } + private FuncIdx findReturnByteFunc() { + FuncType expectedType = new FuncType( + new ResultType(Arrays.asList(ValType.I32, ValType.I32, ValType.I32)), + new ResultType(Collections.singletonList(ValType.I32)) + ); + + outerloop: + for (int i = 0; i < module.getCodeSection().getCodesEntries().size(); i++) { + FuncIdx funcIdx = new FuncIdx(i + module.getImportSection().getTotalFuncImports(), module); + + Function function = new Function(module, funcIdx); + if (!function.getFuncType().equals(expectedType)) continue; + if (function.getLocalsFloored().size() != 0) continue; + if (function.getCode().getInstructions().size() != 30) continue; + + List expr = function.getCode().getInstructions(); + if (expr.get(expr.size() - 1).getInstrType() != InstrType.I32_XOR) continue; + + return funcIdx; + } + + return null; + } + private FuncIdx findInFunc() { + FuncType expectedType = new FuncType( + new ResultType(Arrays.asList(ValType.I32, ValType.I32, ValType.I32, ValType.I32, ValType.I32)), + new ResultType(Collections.emptyList()) + ); + + List expectedExpr = Arrays.asList(InstrType.I32_CONST, InstrType.I32_LOAD8_S, + InstrType.I32_EQZ, InstrType.IF, InstrType.LOCAL_GET, InstrType.I32_LOAD, InstrType.LOCAL_TEE, + InstrType.IF); + + outerloop: + for (int i = 0; i < module.getCodeSection().getCodesEntries().size(); i++) { + FuncIdx funcIdx = new FuncIdx(i + module.getImportSection().getTotalFuncImports(), module); + + Function function = new Function(module, funcIdx); + if (!function.getFuncType().equals(expectedType)) continue; + if (!(function.getLocalsFloored().size() == 1 && function.getLocalsFloored().get(0) == ValType.I32)) continue; + if (function.getCode().getInstructions().size() != expectedExpr.size()) continue; + + for (int j = 0; j < function.getCode().getInstructions().size(); j++) { + Instr instr = function.getCode().getInstructions().get(j); + if (instr.getInstrType() != expectedExpr.get(j)) continue outerloop; + } + + return funcIdx; + } + + return null; + } + + private void copyEmptyHook(FuncIdx orgFuncIdx, String exportName, String hookname) throws InvalidOpCodeException, IOException { + // copies the method, empties the first one + // export the copy + // hooks to the emptied one + + Func func = module.getCodeSection().getByIdx(orgFuncIdx); + FuncType funcType = module.getTypeSection().getByFuncIdx(orgFuncIdx); + + // copy the function + Function copy = new Function(funcType, func.getLocalss(), func.getExpression()); + FuncIdx copyIdx = copy.addToModule(module); + + module.getExportSection().getExports().add(new Export(exportName, new ExportDesc(copyIdx))); + + + // clear & hook original function, let it return whatever JS returns + Import imp = new Import( + "env", + hookname, + new ImportDesc(module.getTypeSection().getTypeIdxForFuncType(new FuncType( + funcType.getParameterType(), + funcType.getResultType() + ))) + ); + FuncIdx hookIdx = module.getImportSection().importFunction(imp); + + CallInstr call = new CallInstr(hookIdx); + List newInstrs = new ArrayList<>(); + for (int i = 0; i < funcType.getParameterType().typeList().size(); i++) { + newInstrs.add(new LocalVariableInstr(InstrType.LOCAL_GET, new LocalIdx(i))); + } + newInstrs.add(call); + func.setExpression(new Expression(newInstrs)); + + } + + private void hook(FuncIdx funcIdx, String jsFunctionName) throws InvalidOpCodeException, IOException { + FuncType funcType = module.getTypeSection().getByFuncIdx(funcIdx); + + Import imp = new Import( + "env", + jsFunctionName, + new ImportDesc(module.getTypeSection().getTypeIdxForFuncType(new FuncType( + funcType.getParameterType(), + new ResultType(Collections.emptyList()) + ))) + ); + FuncIdx hookIdx = module.getImportSection().importFunction(imp); + + CallInstr call = new CallInstr(hookIdx); + + Func root = module.getCodeSection().getByIdx(funcIdx); + List newInstrs = new ArrayList<>(); + for (int i = 0; i < funcType.getParameterType().typeList().size(); i++) { + newInstrs.add(new LocalVariableInstr(InstrType.LOCAL_GET, new LocalIdx(i))); + } + newInstrs.add(call); + newInstrs.addAll(root.getExpression().getInstructions()); + root.getExpression().setInstructions(newInstrs); + } + +}