Deobfuscate received server error messages

This commit is contained in:
Christopher Schnick 2022-12-01 11:27:07 +01:00
parent 5056b248a9
commit 778485b4a9
4 changed files with 115 additions and 22 deletions

View file

@ -12,6 +12,7 @@ import io.xpipe.beacon.exchange.MessageExchanges;
import io.xpipe.beacon.exchange.data.ClientErrorMessage;
import io.xpipe.beacon.exchange.data.ServerErrorMessage;
import io.xpipe.core.store.ShellStore;
import io.xpipe.core.util.Deobfuscator;
import io.xpipe.core.util.JacksonMapper;
import lombok.Builder;
import lombok.EqualsAndHashCode;
@ -276,8 +277,8 @@ public class BeaconClient implements AutoCloseable {
}
try {
var reader = JacksonMapper.newMapper().readerFor(ClientErrorMessage.class);
return Optional.of(reader.readValue(content));
var message = JacksonMapper.getDefault().treeToValue(content, ClientErrorMessage.class);
return Optional.of(message);
} catch (IOException ex) {
throw new ConnectorException("Couldn't parse client error message", ex);
}
@ -290,8 +291,9 @@ public class BeaconClient implements AutoCloseable {
}
try {
var reader = JacksonMapper.newMapper().readerFor(ServerErrorMessage.class);
return Optional.of(reader.readValue(content));
var message = JacksonMapper.getDefault().treeToValue(content, ServerErrorMessage.class);
Deobfuscator.deobfuscate(message.getError());
return Optional.of(message);
} catch (IOException ex) {
throw new ConnectorException("Couldn't parse server error message", ex);
}

View file

@ -0,0 +1,94 @@
package io.xpipe.core.util;
import io.xpipe.core.charsetter.NewLine;
import io.xpipe.core.process.OsType;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Deobfuscator {
public static void deobfuscate(Throwable throwable) {
if (!System.getenv().containsKey("XPIPE_MAPPING")) {
return;
}
String deobf = deobfuscateToString(throwable);
if (deobf == null) {
return;
}
try {
// "at package.class.method(source.java:123)"
Pattern tracePattern = Pattern.compile("\\s*at\\s+([\\w.$_]+)\\.([\\w$_]+)\\((.*):(\\d+)\\)(\\n|\\r\\n)");
Matcher traceMatcher = tracePattern.matcher(deobf);
List<StackTraceElement> stackTrace = new ArrayList<>();
while (traceMatcher.find()) {
String className = traceMatcher.group(1);
String methodName = traceMatcher.group(2);
String sourceFile = traceMatcher.group(3);
int lineNum = Integer.parseInt(traceMatcher.group(4));
stackTrace.add(new StackTraceElement(className, methodName, sourceFile, lineNum));
}
throwable.setStackTrace(stackTrace.toArray(StackTraceElement[]::new));
// Also deobfuscate any other exceptions
if (throwable.getCause() != null && throwable.getCause() != throwable) {
deobfuscate(throwable.getCause());
}
for (Throwable suppressed : throwable.getSuppressed()) {
if (suppressed != throwable) deobfuscate(suppressed);
}
} catch (Throwable ex) {
System.err.println("Deobfuscation failed");
ex.printStackTrace();
}
}
public static String deobfuscateToString(Throwable t) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
t.printStackTrace(pw);
String stackTrace = sw.toString();
stackTrace = stackTrace.replaceAll("at .+/(.+)", "at $1");
if (!System.getenv().containsKey("XPIPE_MAPPING")) {
return stackTrace;
}
try {
var file = Files.createTempFile("xpipe_stracktrace", null);
Files.writeString(file, stackTrace);
var proc = new ProcessBuilder(
"retrace." + (OsType.getLocal().equals(OsType.WINDOWS) ? "bat" : "sh"),
System.getenv("XPIPE_MAPPING"),
file.toString())
.redirectErrorStream(true);
var active = proc.start();
var out = new String(active.getInputStream().readAllBytes()).replaceAll("\\r\\n", NewLine.LF.getNewLineString());
var code = active.waitFor();
if (code == 0) {
return out;
} else {
System.err.println("Deobfuscation failed: " + out);
}
} catch (Exception ex) {
System.err.println("Deobfuscation failed");
ex.printStackTrace();
return null;
}
return stackTrace;
}
public static void printStackTrace(Throwable t) {
var s = deobfuscateToString(t);
System.err.println(s);
}
}

View file

@ -0,0 +1,13 @@
package io.xpipe.core.util;
public class ModuleHelper {
public static boolean isImage() {
return ModuleHelper.class
.getProtectionDomain()
.getCodeSource()
.getLocation()
.getProtocol()
.equals("jrt");
}
}

View file

@ -6,8 +6,6 @@ import io.xpipe.core.process.CommandProcessControl;
import io.xpipe.core.process.OsType;
import io.xpipe.core.process.ShellProcessControl;
import java.io.IOException;
public class XPipeInstallation {
public static String getInstallationBasePathForCLI(ShellProcessControl p, String cliExecutable) throws Exception {
@ -32,7 +30,7 @@ public class XPipeInstallation {
public static boolean containsCompatibleDefaultInstallation(ShellProcessControl p, String version) throws Exception {
var defaultBase = getDefaultInstallationBasePath(p);
var executable = getInstallationExecutable(p, defaultBase);
if (executable.isEmpty()) {
if (!p.executeBooleanSimpleCommand(p.getShellType().createFileExistsCommand(executable))) {
return false;
}
@ -44,14 +42,7 @@ public class XPipeInstallation {
public static String getInstallationExecutable(ShellProcessControl p, String installation) throws Exception {
var executable = getDaemonExecutablePath(p.getOsType());
var file = FileNames.join(installation, executable);
try (CommandProcessControl c =
p.command(p.getShellType().createFileExistsCommand(file)).start()) {
if (!c.startAndCheckExit()) {
throw new IOException("File not found: " + file);
}
return file;
}
return file;
}
public static String getDataBasePath(ShellProcessControl p) throws Exception {
@ -83,13 +74,6 @@ public class XPipeInstallation {
path = "/opt/xpipe";
}
try (CommandProcessControl c =
p.command(p.getShellType().createFileExistsCommand(path)).start()) {
if (!c.discardAndCheckExit()) {
throw new IOException("Installation not found in " + path);
}
}
return path;
}