js: fix deobfuscator, use new ES2015 module builder

This commit is contained in:
Alexey Andreev 2024-03-13 15:44:18 +01:00
parent 055d5df367
commit 32ae1ab8f0
11 changed files with 84 additions and 72 deletions

View File

@ -151,6 +151,17 @@ final class JS {
return result; return result;
} }
public static <T> JSArray<T> wrap(T[] array) {
if (array == null) {
return null;
}
var result = new JSArray<T>(array.length);
for (int i = 0; i < array.length; ++i) {
result.set(i, array[i]);
}
return result;
}
public static <T extends JSObject> WrapFunction<T[], JSArray<T>> arrayWrapper() { public static <T extends JSObject> WrapFunction<T[], JSArray<T>> arrayWrapper() {
return JS::wrap; return JS::wrap;
} }

View File

@ -126,6 +126,7 @@ final class JSMethods {
String.class, JSObject.class); String.class, JSObject.class);
public static final ValueType JS_OBJECT = ValueType.object(JSObject.class.getName()); public static final ValueType JS_OBJECT = ValueType.object(JSObject.class.getName());
public static final ValueType OBJECT = ValueType.object("java.lang.Object");
public static final ValueType JS_ARRAY = ValueType.object(JSArray.class.getName()); public static final ValueType JS_ARRAY = ValueType.object(JSArray.class.getName());
private static final MethodReference[] INVOKE_METHODS = new MethodReference[13]; private static final MethodReference[] INVOKE_METHODS = new MethodReference[13];
private static final MethodReference[] CONSTRUCT_METHODS = new MethodReference[13]; private static final MethodReference[] CONSTRUCT_METHODS = new MethodReference[13];

View File

@ -205,8 +205,10 @@ class JSValueMarshaller {
} else if (type instanceof ValueType.Object) { } else if (type instanceof ValueType.Object) {
if (type.isObject(String.class)) { if (type.isObject(String.class)) {
return type; return type;
} else { } else if (typeHelper.isJavaScriptClass(((ValueType.Object) type).getClassName())) {
return JSMethods.JS_OBJECT; return JSMethods.JS_OBJECT;
} else {
return JSMethods.OBJECT;
} }
} else { } else {
return type; return type;

View File

@ -18,9 +18,6 @@
let logging = false; let logging = false;
let deobfuscation = false; let deobfuscation = false;
if (typeof deobfuscator !== 'undefined') {
deobfuscator();
}
function tryConnect() { function tryConnect() {
let ws = new WebSocket("ws://localhost:{{PORT}}/ws"); let ws = new WebSocket("ws://localhost:{{PORT}}/ws");
@ -82,7 +79,7 @@ function runSingleTest(test, callback) {
console.log("Running test " + test.name); console.log("Running test " + test.name);
} }
if (deobfuscation) { if (deobfuscation) {
const fileName = test.file + ".teavmdbg"; const fileName = test.file.path + ".teavmdbg";
if (lastDeobfuscatorFile === fileName) { if (lastDeobfuscatorFile === fileName) {
if (lastDeobfuscatorPromise === null) { if (lastDeobfuscatorPromise === null) {
runSingleTestWithDeobfuscator(test, lastDeobfuscator, callback); runSingleTestWithDeobfuscator(test, lastDeobfuscator, callback);
@ -100,7 +97,7 @@ function runSingleTest(test, callback) {
xhr.onreadystatechange = () => { xhr.onreadystatechange = () => {
if (xhr.readyState === 4) { if (xhr.readyState === 4) {
const newDeobfuscator = xhr.status === 200 const newDeobfuscator = xhr.status === 200
? deobfuscator.create(xhr.response, "http://localhost:{{PORT}}/" + test.file) ? createDeobfuscator(xhr.response, "http://localhost:{{PORT}}/" + test.file.path)
: null; : null;
if (lastDeobfuscatorFile === fileName) { if (lastDeobfuscatorFile === fileName) {
lastDeobfuscator = newDeobfuscator; lastDeobfuscator = newDeobfuscator;
@ -139,7 +136,9 @@ function runSingleTestWithDeobfuscator(test, deobfuscator, callback) {
}; };
window.addEventListener("message", listener); window.addEventListener("message", listener);
iframe.contentWindow.$rt_decodeStack = deobfuscator; iframe.contentWindow.$rt_decodeStack = deobfuscator != null
? deobfuscator.deobfuscate.bind(deobfuscator)
: null;
iframe.contentWindow.postMessage(test, "*"); iframe.contentWindow.postMessage(test, "*");
}; };
window.addEventListener("message", handshakeListener); window.addEventListener("message", handshakeListener);

View File

@ -18,7 +18,10 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<script src="deobfuscator.js"></script> <script type="module">
import { create } from './deobfuscator.js'
window.createDeobfuscator = create;
</script>
<script src="client.js"></script> <script src="client.js"></script>
</head> </head>
<body> <body>

View File

@ -44,7 +44,8 @@ val generateJs by tasks.register<JavaExec>("generateJs") {
"org.teavm.tooling.deobfuscate.js.Deobfuscator", "org.teavm.tooling.deobfuscate.js.Deobfuscator",
"\$teavm_deobfuscator", "\$teavm_deobfuscator",
layout.buildDirectory.dir("teavm").get().asFile.absolutePath, layout.buildDirectory.dir("teavm").get().asFile.absolutePath,
"deobfuscator.js" "deobfuscator.js",
"none"
) )
} }
@ -59,6 +60,7 @@ val generateLibJs by tasks.register<JavaExec>("generateLibJs") {
"deobfuscator", "deobfuscator",
layout.buildDirectory.dir("teavm-lib").get().asFile.absolutePath, layout.buildDirectory.dir("teavm-lib").get().asFile.absolutePath,
"deobfuscator-lib.js", "deobfuscator-lib.js",
"es2015"
) )
} }

View File

@ -16,6 +16,7 @@
package org.teavm.tooling.deobfuscate.js; package org.teavm.tooling.deobfuscate.js;
import java.io.File; import java.io.File;
import org.teavm.backend.javascript.JSModuleType;
import org.teavm.tooling.ConsoleTeaVMToolLog; import org.teavm.tooling.ConsoleTeaVMToolLog;
import org.teavm.tooling.TeaVMProblemRenderer; import org.teavm.tooling.TeaVMProblemRenderer;
import org.teavm.tooling.TeaVMTargetType; import org.teavm.tooling.TeaVMTargetType;
@ -35,6 +36,7 @@ public final class Compiler {
tool.setEntryPointName(args[1]); tool.setEntryPointName(args[1]);
tool.setTargetDirectory(new File(args[2])); tool.setTargetDirectory(new File(args[2]));
tool.setTargetFileName(args[3]); tool.setTargetFileName(args[3]);
tool.setJsModuleType(JSModuleType.valueOf(args[4].toUpperCase()));
tool.setObfuscated(true); tool.setObfuscated(true);
tool.setOptimizationLevel(TeaVMOptimizationLevel.ADVANCED); tool.setOptimizationLevel(TeaVMOptimizationLevel.ADVANCED);

View File

@ -24,6 +24,8 @@ import org.teavm.debugging.information.DebugInformation;
import org.teavm.debugging.information.GeneratedLocation; import org.teavm.debugging.information.GeneratedLocation;
import org.teavm.debugging.information.SourceLocation; import org.teavm.debugging.information.SourceLocation;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
import org.teavm.jso.JSClass;
import org.teavm.jso.JSExport;
import org.teavm.jso.ajax.XMLHttpRequest; import org.teavm.jso.ajax.XMLHttpRequest;
import org.teavm.jso.core.JSArray; import org.teavm.jso.core.JSArray;
import org.teavm.jso.core.JSObjects; import org.teavm.jso.core.JSObjects;
@ -33,6 +35,7 @@ import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Int8Array; import org.teavm.jso.typedarrays.Int8Array;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@JSClass
public final class Deobfuscator { public final class Deobfuscator {
private static final JSRegExp FRAME_PATTERN = new JSRegExp("" private static final JSRegExp FRAME_PATTERN = new JSRegExp(""
+ "(^ +at ([^(]+) *\\((.+):([0-9]+):([0-9]+)\\) *$)|" + "(^ +at ([^(]+) *\\((.+):([0-9]+):([0-9]+)\\) *$)|"
@ -60,6 +63,7 @@ public final class Deobfuscator {
xhr.send(); xhr.send();
} }
@JSExport
public Frame[] deobfuscate(String stack) { public Frame[] deobfuscate(String stack) {
List<Frame> frames = new ArrayList<>(); List<Frame> frames = new ArrayList<>();
for (String line : splitLines(stack)) { for (String line : splitLines(stack)) {
@ -125,14 +129,16 @@ public final class Deobfuscator {
decodedFileName = decodedFileName.substring(decodedFileName.lastIndexOf('/') + 1); decodedFileName = decodedFileName.substring(decodedFileName.lastIndexOf('/') + 1);
} }
Frame frame = createEmptyFrame(); var javaLineNumber = -1;
frame.setClassName(method.getClassName());
frame.setMethodName(method.getName());
frame.setFileName(decodedFileName);
if (location != null) { if (location != null) {
frame.setLineNumber(location.getLine()); javaLineNumber = location.getLine();
} }
result.add(frame); result.add(new Frame(
method.getClassName(),
method.getName(),
decodedFileName,
javaLineNumber
));
} }
if (result.isEmpty()) { if (result.isEmpty()) {
@ -143,12 +149,12 @@ public final class Deobfuscator {
} }
private static Frame createDefaultFrame(String fileName, String functionName, int lineNumber) { private static Frame createDefaultFrame(String fileName, String functionName, int lineNumber) {
Frame frame = createEmptyFrame(); return new Frame(
frame.setFileName(fileName); "<JS>",
frame.setMethodName(functionName != null ? functionName : "<unknown function>"); functionName != null ? functionName : "<unknown function>",
frame.setClassName("<JS>"); fileName,
frame.setLineNumber(lineNumber); lineNumber
return frame; );
} }
private static String[] splitLines(String text) { private static String[] splitLines(String text) {
@ -165,9 +171,6 @@ public final class Deobfuscator {
return result.toArray(new String[0]); return result.toArray(new String[0]);
} }
@JSBody(script = "return {};")
private static native Frame createEmptyFrame();
@JSBody(params = "f", script = "window.$rt_decodeStack = f;") @JSBody(params = "f", script = "window.$rt_decodeStack = f;")
private static native void setDeobfuscateFunction(DeobfuscateFunction f); private static native void setDeobfuscateFunction(DeobfuscateFunction f);

View File

@ -1,24 +0,0 @@
/*
* Copyright 2021 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.tooling.deobfuscate.js;
import org.teavm.jso.JSObject;
import org.teavm.jso.typedarrays.ArrayBuffer;
public interface DeobfuscatorJs extends JSObject {
DeobfuscateFunction create(ArrayBuffer buffer, String classesFileName);
}

View File

@ -16,31 +16,20 @@
package org.teavm.tooling.deobfuscate.js; package org.teavm.tooling.deobfuscate.js;
import java.io.IOException; import java.io.IOException;
import org.teavm.jso.JSBody; import org.teavm.jso.JSExport;
import org.teavm.jso.typedarrays.ArrayBuffer; import org.teavm.jso.typedarrays.ArrayBuffer;
public final class DeobfuscatorLib implements DeobfuscatorJs { public final class DeobfuscatorLib {
private DeobfuscatorLib() { private DeobfuscatorLib() {
} }
@Override @JSExport
public DeobfuscateFunction create(ArrayBuffer buffer, String classesFileName) { public static Deobfuscator create(ArrayBuffer buffer, String classesFileName) {
try { try {
return new Deobfuscator(buffer, classesFileName)::deobfuscate; return new Deobfuscator(buffer, classesFileName);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public static void main(String[] args) {
install(new DeobfuscatorLib());
}
@JSBody(params = "instance", script =
"deobfuscator.create = function(buffer, classesFileName) {"
+ "return instance.create(buffer, classesFileName);"
+ "}"
)
private static native void install(DeobfuscatorJs js);
} }

View File

@ -15,19 +15,43 @@
*/ */
package org.teavm.tooling.deobfuscate.js; package org.teavm.tooling.deobfuscate.js;
import org.teavm.jso.JSObject; import org.teavm.jso.JSExport;
import org.teavm.jso.JSProperty; import org.teavm.jso.JSProperty;
public interface Frame extends JSObject { public class Frame {
@JSProperty private String className;
void setClassName(String className); private String fileName;
private String methodName;
private int lineNumber;
@JSProperty public Frame(String className, String methodName, String fileName, int lineNumber) {
void setFileName(String fileName); this.className = className;
this.methodName = methodName;
@JSProperty this.fileName = fileName;
void setMethodName(String methodName); this.lineNumber = lineNumber;
}
@JSProperty
void setLineNumber(int lineNumber); @JSExport
@JSProperty
public String getClassName() {
return className;
}
@JSExport
@JSProperty
public String getFileName() {
return fileName;
}
@JSExport
@JSProperty
public String getMethodName() {
return methodName;
}
@JSExport
@JSProperty
public int getLineNumber() {
return lineNumber;
}
} }