From 8400d6eab92f8048e69f3911949a3f391fae3e0a Mon Sep 17 00:00:00 2001 From: SuspiciousActivity <38253929+SuspiciousActivity@users.noreply.github.com> Date: Mon, 24 Sep 2018 20:41:22 +0200 Subject: [PATCH] Added full Unicode support (Java style) --- src/me/ByteEdit/edit/Assembler.java | 109 ++++++++++-------- src/me/ByteEdit/edit/Disassembler.java | 107 +++++++++-------- src/me/ByteEdit/main/Main.java | 13 ++- src/me/ByteEdit/utils/ClassUtil.java | 3 +- src/me/ByteEdit/utils/UnicodeUtils.java | 145 ++++++++++++++++++++++++ 5 files changed, 275 insertions(+), 102 deletions(-) create mode 100644 src/me/ByteEdit/utils/UnicodeUtils.java diff --git a/src/me/ByteEdit/edit/Assembler.java b/src/me/ByteEdit/edit/Assembler.java index 83c56fc..92ef725 100644 --- a/src/me/ByteEdit/edit/Assembler.java +++ b/src/me/ByteEdit/edit/Assembler.java @@ -13,7 +13,6 @@ import java.util.function.BiFunction; import javax.swing.text.BadLocationException; -import org.apache.commons.lang3.StringEscapeUtils; import org.objectweb.asm.Handle; import org.objectweb.asm.Label; import org.objectweb.asm.Type; @@ -47,6 +46,7 @@ import me.ByteEdit.main.Main; import me.ByteEdit.utils.ClassUtil; import me.ByteEdit.utils.CustomBufferedReader; import me.ByteEdit.utils.SwitchIntContainer; +import me.ByteEdit.utils.UnicodeUtils; public class Assembler { @@ -66,21 +66,22 @@ public class Assembler { clazz.version = Integer.parseInt(s.substring(12)); while ((s = read.readLine()).startsWith("// ")) { if (s.startsWith("// #Signature: ")) { - clazz.signature = s.substring(15).equals("null") ? null : s.substring(15); + clazz.signature = s.substring(15).equals("null") ? null : UnicodeUtils.unescape(s.substring(15)); } else if (s.startsWith("// #OuterClass: ")) { - clazz.outerClass = s.substring(16).equals("null") ? null : s.substring(16); + clazz.outerClass = s.substring(16).equals("null") ? null : UnicodeUtils.unescape(s.substring(16)); } else if (s.startsWith("// #OuterMethod: ")) { String[] split = s.split(" "); if (split.length > 3) { - clazz.outerMethod = split[2]; - clazz.outerMethodDesc = split[3]; + clazz.outerMethod = UnicodeUtils.unescape(split[2]); + clazz.outerMethodDesc = UnicodeUtils.unescape(split[3]); } } else if (s.equals("// #InnerClasses:")) { clazz.innerClasses = new ArrayList<>(); } else { String split[] = s.split(" "); - clazz.innerClasses.add(new InnerClassNode(split[1], split[2].equals("null") ? null : split[2], - split[3].equals("null") ? null : split[3], Integer.parseInt(split[4].substring(2), 16))); + clazz.innerClasses.add(new InnerClassNode(UnicodeUtils.unescape(split[1]), + split[2].equals("null") ? null : UnicodeUtils.unescape(split[2]), + split[3].equals("null") ? null : UnicodeUtils.unescape(split[3]), Integer.parseInt(split[4].substring(2), 16))); } } { @@ -89,14 +90,14 @@ public class Assembler { String interfaces = s.substring(s.lastIndexOf(" implements ") + 12); clazz.interfaces = new ArrayList<>(); for (String interf : interfaces.split(", ")) { - clazz.interfaces.add(interf); + clazz.interfaces.add(UnicodeUtils.unescape(interf)); } s = s.substring(0, s.lastIndexOf(" implements ")); } - clazz.superName = s.substring(s.lastIndexOf(" extends ") + 9); + clazz.superName = UnicodeUtils.unescape(s.substring(s.lastIndexOf(" extends ") + 9)); s = s.substring(0, s.lastIndexOf(" extends ")); String[] split = s.split(" "); - clazz.name = split[split.length - 1]; + clazz.name = UnicodeUtils.unescape(split[split.length - 1]); clazz.access = ClassUtil.ACC_SUPER; switch (split[split.length - 2]) { case "class": @@ -133,7 +134,7 @@ public class Assembler { } } s = read.readLine(); - clazz.sourceFile = s.substring(16).equals("null") ? null : s.substring(16); + clazz.sourceFile = s.substring(16).equals("null") ? null : UnicodeUtils.unescape(s.substring(16)); while (!(s = read.readLine()).equals("// #Fields")) {} clazz.fields = new ArrayList<>(); clazz.methods = new ArrayList<>(); @@ -147,7 +148,7 @@ public class Assembler { annotationsForNext.add(s); } else { if (s.startsWith("// #Signature: ")) { - signature = s.substring(15); + signature = UnicodeUtils.unescape(s.substring(15)); } else { String[] split = s.split(" "); boolean hasValue = false; @@ -158,8 +159,8 @@ public class Assembler { if (hasValue) { split = s.substring(0, s.indexOf(" = ")).split(" "); } - String name = split[split.length - 1]; - String desc = split[split.length - 2]; + String name = UnicodeUtils.unescape(split[split.length - 1]); + String desc = UnicodeUtils.unescape(split[split.length - 2]); value = getValue((String) value, desc); int access = 0; if (split.length > 2) { @@ -259,7 +260,7 @@ public class Assembler { tryCatchBlocksToParse.clear(); stage = 0; } else if (s.startsWith("// #Signature: ")) { - node.signature = s.substring(15); + node.signature = UnicodeUtils.unescape(s.substring(15)); } else if (s.equals("// #TryCatch:")) { stage = 1; } else if (s.equals("// #LocalVars:")) { @@ -285,7 +286,7 @@ public class Assembler { String[] sp = st.split(":"); int start = Integer.parseInt(sp[3].substring(0, sp[3].length() - 2)); int end = Integer.parseInt(sp[4].substring(0, sp[4].length() - 4)); - String signat = sp[5].equals("null") ? null : sp[5]; + String signat = sp[5].equals("null") ? null : UnicodeUtils.unescape(sp[5]); LabelNode _start = null; LabelNode _end = null; for (Entry entry : methodLabelMap.entrySet()) { @@ -296,8 +297,9 @@ public class Assembler { _end = new LabelNode(entry.getKey()); } } - node.localVariables.add(new LocalVariableNode(sp[0], sp[1].substring(1, sp[1].length() - 2), signat, _start, - _end, Integer.parseInt(sp[2].substring(0, sp[2].length() - 2)))); + node.localVariables + .add(new LocalVariableNode(sp[0], UnicodeUtils.unescape(sp[1].substring(1, sp[1].length() - 2)), signat, + _start, _end, Integer.parseInt(sp[2].substring(0, sp[2].length() - 2)))); } for (String st : tryCatchBlocksToParse) { String[] sp = st.split(" "); @@ -318,7 +320,8 @@ public class Assembler { _handler = new LabelNode(entry.getKey()); } } - node.tryCatchBlocks.add(new TryCatchBlockNode(_start, _end, _handler, sp[0].equals("null") ? null : sp[0])); + node.tryCatchBlocks.add(new TryCatchBlockNode(_start, _end, _handler, + sp[0].equals("null") ? null : UnicodeUtils.unescape(sp[0]))); } clazz.methods.add(node); // signature = null; @@ -327,13 +330,13 @@ public class Assembler { if (s.contains(" throws ")) { String exceptions = s.substring(s.lastIndexOf(" throws ") + 8); for (String excp : exceptions.split(", ")) { - node.exceptions.add(excp); + node.exceptions.add(UnicodeUtils.unescape(excp)); } s = s.substring(0, s.lastIndexOf(" throws ")); } String[] split = s.split(" "); - node.desc = split[split.length - 1]; - node.name = split[split.length - 2]; + node.desc = UnicodeUtils.unescape(split[split.length - 1]); + node.name = UnicodeUtils.unescape(split[split.length - 2]); int access = 0; if (split.length > 2) { String cons = consolidateStrings(split, 0, split.length - 2); @@ -433,7 +436,7 @@ public class Assembler { private static AnnotationNode parseAnnotation(String s) { s = s.substring(1); String[] split = s.split(" "); - AnnotationNode node = new AnnotationNode(split[0]); + AnnotationNode node = new AnnotationNode(UnicodeUtils.unescape(split[0])); if (split.length > 1) { if (node.values == null) { node.values = new ArrayList<>(); @@ -451,25 +454,27 @@ public class Assembler { String split3[] = value.substring(2, value.length() - 2).split(", "); List list = new ArrayList<>(); for (String rofl : split3) { + rofl = UnicodeUtils.unescape(rofl); if (rofl.startsWith("\"")) { int index = rofl.lastIndexOf("\""); list.add(rofl.substring(1, index)); } else if (!rofl.startsWith("(")) { int index = rofl.indexOf(";"); list.add(new String[] { rofl.substring(0, index) + ";", rofl.substring(index).substring(2) }); - } else { // anderer typ mit casten + } else { // other type with casting list.add(ClassUtil.getCastedValue(rofl.split(" ")[1], rofl.split("\\) ")[0].substring(1))); } } node.values.add(list); } else { + value = UnicodeUtils.unescape(value); if (value.startsWith("\"")) { int index = value.lastIndexOf("\""); node.values.add(value.substring(1, index)); } else if (!value.startsWith("(")) { int index = value.indexOf(";"); node.values.add(new String[] { value.substring(0, index) + ";", value.substring(index).substring(2) }); - } else { // anderer typ mit casten + } else { // other type with casting node.values.add(ClassUtil.getCastedValue(value.split(" ")[1], value.split("\\) ")[0].substring(1))); } } @@ -526,7 +531,7 @@ public class Assembler { case "S": return Short.parseShort(s); case "Ljava/lang/String;": - return StringEscapeUtils.unescapeJava(s.substring(1, s.length() - 1)).replace("\\n", "\n").replace("\\r", "\r"); + return UnicodeUtils.unescape(s.substring(1, s.length() - 1)); default: { return null; } @@ -570,8 +575,8 @@ public class Assembler { } else { for (String asd : l.split(", ")) { if (!asd.startsWith("(")) { - arr.add(asd); - } else { // anderer typ mit casten + arr.add(UnicodeUtils.unescape(asd)); + } else { // other type with casting if (asd.startsWith("(label) ")) { int labelNr = Integer.parseInt(asd.split(" ")[1]); boolean found = false; @@ -609,8 +614,8 @@ public class Assembler { } else { for (String asd : st.split(", ")) { if (!asd.startsWith("(")) { - arr.add(asd); - } else { // anderer typ mit casten + arr.add(UnicodeUtils.unescape(asd)); + } else { // other type with casting if (asd.startsWith("(label) ")) { int labelNr = Integer.parseInt(asd.split(" ")[1]); boolean found = false; @@ -696,8 +701,7 @@ public class Assembler { split2[3].substring(6, split2[3].length() - 1).toCharArray(), Integer.parseInt(split2[1].substring(5)), Integer.parseInt(split2[2].substring(5)) }); } else if (str.startsWith("\"") && str.endsWith("\"")) { - val = StringEscapeUtils.unescapeJava(str.substring(1, str.length() - 1)).replace("\\n", "\n").replace("\\r", - "\r"); + val = UnicodeUtils.unescape(str.substring(1, str.length() - 1)); } else if (str.endsWith("l")) { val = Long.parseLong(str.substring(0, str.length() - 1)); } else if (str.endsWith("f")) { @@ -1315,42 +1319,50 @@ public class Assembler { case "getstatic": { String target = split[2]; int index = target.lastIndexOf("/"); - return new FieldInsnNode(178, target.substring(0, index), target.substring(index + 1), split[1]); + return new FieldInsnNode(178, UnicodeUtils.unescape(target.substring(0, index)), + UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1])); } case "putstatic": { String target = split[2]; int index = target.lastIndexOf("/"); - return new FieldInsnNode(179, target.substring(0, index), target.substring(index + 1), split[1]); + return new FieldInsnNode(179, UnicodeUtils.unescape(target.substring(0, index)), + UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1])); } case "getfield": { String target = split[2]; int index = target.lastIndexOf("/"); - return new FieldInsnNode(180, target.substring(0, index), target.substring(index + 1), split[1]); + return new FieldInsnNode(180, UnicodeUtils.unescape(target.substring(0, index)), + UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1])); } case "putfield": { String target = split[2]; int index = target.lastIndexOf("/"); - return new FieldInsnNode(181, target.substring(0, index), target.substring(index + 1), split[1]); + return new FieldInsnNode(181, UnicodeUtils.unescape(target.substring(0, index)), + UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1])); } case "invokevirtual": { String target = split[2]; int index = target.lastIndexOf("/"); - return new MethodInsnNode(182, target.substring(0, index), target.substring(index + 1), split[1], false); + return new MethodInsnNode(182, UnicodeUtils.unescape(target.substring(0, index)), + UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]), false); } case "invokespecial": { String target = split[2]; int index = target.lastIndexOf("/"); - return new MethodInsnNode(183, target.substring(0, index), target.substring(index + 1), split[1], false); + return new MethodInsnNode(183, UnicodeUtils.unescape(target.substring(0, index)), + UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]), false); } case "invokestatic": { String target = split[2]; int index = target.lastIndexOf("/"); - return new MethodInsnNode(184, target.substring(0, index), target.substring(index + 1), split[1], false); + return new MethodInsnNode(184, UnicodeUtils.unescape(target.substring(0, index)), + UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]), false); } case "invokeinterface": { String target = split[2]; int index = target.lastIndexOf("/"); - return new MethodInsnNode(185, target.substring(0, index), target.substring(index + 1), split[1], true); + return new MethodInsnNode(185, UnicodeUtils.unescape(target.substring(0, index)), + UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]), true); } case "invokedynamic": { String str = s.substring(16, s.length() - 2).replace("", ""); @@ -1367,15 +1379,15 @@ public class Assembler { int.class); constr.setAccessible(true); args.add(constr.newInstance(new Object[] { Integer.parseInt(vals.get(0).substring(6)), - vals.get(3).substring(6, vals.get(3).length() - 1).toCharArray(), + UnicodeUtils.unescape(vals.get(3).substring(6, vals.get(3).length() - 1)).toCharArray(), Integer.parseInt(vals.get(1).substring(5)), Integer.parseInt(vals.get(2).substring(5)) })); } catch (Exception e) { throw e; } } else if (stage == 2) { - args.add(new Handle(Integer.parseInt(vals.get(4).substring(5)), vals.get(1).substring(7), - vals.get(0).substring(6), vals.get(2).substring(6), - Boolean.parseBoolean(vals.get(3).substring(13)))); + args.add(new Handle(Integer.parseInt(vals.get(4).substring(5)), + UnicodeUtils.unescape(vals.get(1).substring(7)), UnicodeUtils.unescape(vals.get(0).substring(6)), + UnicodeUtils.unescape(vals.get(2).substring(6)), Boolean.parseBoolean(vals.get(3).substring(13)))); vals.clear(); } stage = 0; @@ -1398,13 +1410,14 @@ public class Assembler { _args[stage] = o; stage++; } - return new InvokeDynamicInsnNode(sp[0].substring(7), sp[1].substring(6), - new Handle(Integer.parseInt(sp[7].substring(6)), sp[4].substring(8), sp[3].substring(7), sp[5].substring(7), + return new InvokeDynamicInsnNode(UnicodeUtils.unescape(sp[0].substring(7)), UnicodeUtils.unescape(sp[1].substring(6)), + new Handle(Integer.parseInt(sp[7].substring(6)), UnicodeUtils.unescape(sp[4].substring(8)), + UnicodeUtils.unescape(sp[3].substring(7)), UnicodeUtils.unescape(sp[5].substring(7)), Boolean.parseBoolean(sp[6].substring(14))), _args); } case "new": { - return new TypeInsnNode(187, split[1]); + return new TypeInsnNode(187, UnicodeUtils.unescape(split[1])); } case "newarray": { return new IntInsnNode(188, ClassUtil.getArrayIDByType(split[1])); @@ -1431,7 +1444,7 @@ public class Assembler { return new InsnNode(195); } case "multianewarray": { - return new MultiANewArrayInsnNode(split[1], Integer.parseInt(split[2])); + return new MultiANewArrayInsnNode(UnicodeUtils.unescape(split[1]), Integer.parseInt(split[2])); } case "ifnull": { int labelNr = Integer.parseInt(s.split(" ")[1]); diff --git a/src/me/ByteEdit/edit/Disassembler.java b/src/me/ByteEdit/edit/Disassembler.java index e063c45..f130ff4 100644 --- a/src/me/ByteEdit/edit/Disassembler.java +++ b/src/me/ByteEdit/edit/Disassembler.java @@ -37,6 +37,7 @@ import org.objectweb.asm.tree.VarInsnNode; import me.ByteEdit.utils.ClassUtil; import me.ByteEdit.utils.OpcodesReverse; +import me.ByteEdit.utils.UnicodeUtils; public class Disassembler { @@ -79,7 +80,7 @@ public class Disassembler { s += "// #Annotations\n"; if (classNode.visibleAnnotations != null && !classNode.visibleAnnotations.isEmpty()) { for (AnnotationNode annotationNode : classNode.visibleAnnotations) { - s += "@" + annotationNode.desc; + s += "@" + UnicodeUtils.escape(annotationNode.desc); if (annotationNode.values != null && !annotationNode.values.isEmpty()) { s += " ("; boolean valBefore = true; @@ -94,10 +95,10 @@ public class Disassembler { boolean w8ing = false; for (String rofl : arr) { if (!w8ing) { - s += rofl + "/"; + s += UnicodeUtils.escape(rofl) + "/"; w8ing = true; } else { - s += rofl + "]"; + s += UnicodeUtils.escape(rofl) + "]"; } } s += ", "; @@ -110,10 +111,10 @@ public class Disassembler { boolean w8ing = false; for (String rofl : arr) { if (!w8ing) { - s += rofl + "/"; + s += UnicodeUtils.escape(rofl) + "/"; w8ing = true; } else { - s += rofl; + s += UnicodeUtils.escape(rofl); } } } else { @@ -137,37 +138,39 @@ public class Disassembler { } } s += "// #Class v:" + classNode.version + "\n"; - s += "// #Signature: " + classNode.signature + "\n"; - s += "// #OuterMethod: " + (classNode.outerMethod == null ? "null" : (classNode.outerMethod + " " + classNode.outerMethodDesc)) - + "\n"; - s += "// #OuterClass: " + classNode.outerClass + "\n"; + s += "// #Signature: " + UnicodeUtils.escape(classNode.signature) + "\n"; + s += "// #OuterMethod: " + (classNode.outerMethod == null ? "null" + : (UnicodeUtils.escape(classNode.outerMethod) + " " + UnicodeUtils.escape(classNode.outerMethodDesc))) + "\n"; + s += "// #OuterClass: " + UnicodeUtils.escape(classNode.outerClass) + "\n"; s += "// #InnerClasses:\n"; if (classNode.innerClasses != null) { for (InnerClassNode icn : classNode.innerClasses) { - s += "// " + icn.name + " " + icn.outerName + " " + icn.innerName + " 0x" + Integer.toHexString(icn.access) + "\n"; + s += "// " + UnicodeUtils.escape(icn.name) + " " + UnicodeUtils.escape(icn.outerName) + " " + + UnicodeUtils.escape(icn.innerName) + " 0x" + Integer.toHexString(icn.access) + "\n"; } } - s += ClassUtil.getAccessFlagsClass(classNode.access) + classNode.name + " "; - s += "extends " + classNode.superName + " "; + s += ClassUtil.getAccessFlagsClass(classNode.access) + UnicodeUtils.escape(classNode.name) + " "; + s += "extends " + UnicodeUtils.escape(classNode.superName) + " "; if (classNode.interfaces != null && !classNode.interfaces.isEmpty()) { String interfaceStr = ""; for (String interfc : classNode.interfaces) { if (interfaceStr.isEmpty()) { - interfaceStr += interfc; + interfaceStr += UnicodeUtils.escape(interfc); } else { - interfaceStr += ", " + interfc; + interfaceStr += ", " + UnicodeUtils.escape(interfc); } } if (!interfaceStr.isEmpty()) s += "implements " + interfaceStr + " "; } - s += "{\n// #SourceFile: " + (classNode.sourceFile == null ? "null" : classNode.sourceFile) + "\n\n// #Fields\n"; + s += "{\n// #SourceFile: " + (classNode.sourceFile == null ? "null" : UnicodeUtils.escape(classNode.sourceFile)) + + "\n\n// #Fields\n"; for (FieldNode fn : classNode.fields) { if (fn.signature != null) - s += "\t// #Signature: " + fn.signature + "\n"; + s += "\t// #Signature: " + UnicodeUtils.escape(fn.signature) + "\n"; if (fn.visibleAnnotations != null && !fn.visibleAnnotations.isEmpty()) { for (AnnotationNode annotationNode : fn.visibleAnnotations) { - s += "\t@" + annotationNode.desc; + s += "\t@" + UnicodeUtils.escape(annotationNode.desc); if (annotationNode.values != null && !annotationNode.values.isEmpty()) { s += " ("; boolean valBefore = true; @@ -182,10 +185,10 @@ public class Disassembler { boolean w8ing = false; for (String rofl : arr) { if (!w8ing) { - s += rofl + "/"; + s += UnicodeUtils.escape(rofl) + "/"; w8ing = true; } else { - s += rofl + "]"; + s += UnicodeUtils.escape(rofl) + "]"; } } s += ", "; @@ -198,10 +201,10 @@ public class Disassembler { boolean w8ing = false; for (String rofl : arr) { if (!w8ing) { - s += rofl + "/"; + s += UnicodeUtils.escape(rofl) + "/"; w8ing = true; } else { - s += rofl; + s += UnicodeUtils.escape(rofl); } } } else { @@ -224,7 +227,8 @@ public class Disassembler { s += "\n"; } } - s += "\t" + ClassUtil.getAccessFlagsFull(fn.access).replace("varargs", "transient") + fn.desc + " " + fn.name; + s += "\t" + ClassUtil.getAccessFlagsFull(fn.access).replace("varargs", "transient") + UnicodeUtils.escape(fn.desc) + " " + + UnicodeUtils.escape(fn.name); if (fn.value != null) { s += " = " + ClassUtil.getDecompiledValue(fn.value, fn.desc); } @@ -235,7 +239,7 @@ public class Disassembler { try { s += "\t// #Max: l:" + mn.maxLocals + " s:" + mn.maxStack + "\n"; if (mn.signature != null) - s += "\t// #Signature: " + mn.signature + "\n"; + s += "\t// #Signature: " + UnicodeUtils.escape(mn.signature) + "\n"; String[] dis = disassembleMethod(classNode.name, mn); s += dis[2]; s += dis[1]; @@ -256,10 +260,10 @@ public class Disassembler { boolean w8ing = false; for (String rofl : arr) { if (!w8ing) { - s += rofl + "/"; + s += UnicodeUtils.escape(rofl) + "/"; w8ing = true; } else { - s += rofl + "]"; + s += UnicodeUtils.escape(rofl) + "]"; } } s += ", "; @@ -272,10 +276,10 @@ public class Disassembler { boolean w8ing = false; for (String rofl : arr) { if (!w8ing) { - s += rofl + "/"; + s += UnicodeUtils.escape(rofl) + "/"; w8ing = true; } else { - s += rofl; + s += UnicodeUtils.escape(rofl); } } } else { @@ -301,14 +305,15 @@ public class Disassembler { if (mn.equals(methodToFind)) { lineFound = s.split("\\n").length; } - s += "\t" + ClassUtil.getAccessFlagsFull(mn.access) + mn.name + " " + mn.desc + " "; + s += "\t" + ClassUtil.getAccessFlagsFull(mn.access) + UnicodeUtils.escape(mn.name) + " " + UnicodeUtils.escape(mn.desc) + + " "; if (mn.exceptions != null && !mn.exceptions.isEmpty()) { String exceptionStr = ""; for (String exc : mn.exceptions) { if (exceptionStr.isEmpty()) { - exceptionStr += exc; + exceptionStr += UnicodeUtils.escape(exc); } else { - exceptionStr += ", " + exc; + exceptionStr += ", " + UnicodeUtils.escape(exc); } } if (!exceptionStr.isEmpty()) @@ -343,14 +348,15 @@ public class Disassembler { } if (mn.localVariables != null) { for (LocalVariableNode lvn : mn.localVariables) { - localVarTable += "\t// " + lvn.name + ": " + lvn.desc + " i:" + lvn.index + " s:" + labels.get(lvn.start.getLabel()) + " e:" - + labels.get(lvn.end.getLabel()) + " sig:" + lvn.signature + "\n"; + localVarTable += "\t// " + UnicodeUtils.escape(lvn.name) + ": " + UnicodeUtils.escape(lvn.desc) + " i:" + lvn.index + " s:" + + labels.get(lvn.start.getLabel()) + " e:" + labels.get(lvn.end.getLabel()) + " sig:" + + UnicodeUtils.escape(lvn.signature) + "\n"; } } if (mn.tryCatchBlocks != null) { for (TryCatchBlockNode tcbn : mn.tryCatchBlocks) { - tryCatchTable += "\t// " + tcbn.type + " s:" + labels.get(tcbn.start.getLabel()) + " e:" + labels.get(tcbn.end.getLabel()) - + " h:" + labels.get(tcbn.handler.getLabel()) + "\n"; + tryCatchTable += "\t// " + UnicodeUtils.escape(tcbn.type) + " s:" + labels.get(tcbn.start.getLabel()) + " e:" + + labels.get(tcbn.end.getLabel()) + " h:" + labels.get(tcbn.handler.getLabel()) + "\n"; } } for (AbstractInsnNode n : mn.instructions.toArray()) { @@ -363,8 +369,8 @@ public class Disassembler { switch (n.getClass().getSimpleName()) { case "FieldInsnNode": { FieldInsnNode node = (FieldInsnNode) n; - return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + node.desc + " " + node.owner + "/" + node.name - + "\n"; + return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + UnicodeUtils.escape(node.desc) + " " + + UnicodeUtils.escape(node.owner) + "/" + UnicodeUtils.escape(node.name) + "\n"; } case "LabelNode": { LabelNode node = (LabelNode) n; @@ -380,7 +386,7 @@ public class Disassembler { arr.add(null); } else { if (o instanceof String) { - arr.add((String) o); + arr.add(UnicodeUtils.escape((String) o)); } else if (o instanceof LabelNode) { arr.add("(label) " + labels.get(((LabelNode) o).getLabel())); } else { @@ -400,7 +406,7 @@ public class Disassembler { arr.add(null); } else { if (o instanceof String) { - arr.add((String) o); + arr.add(UnicodeUtils.escape((String) o)); } else if (o instanceof LabelNode) { arr.add("(label) " + labels.get(((LabelNode) o).getLabel())); } else { @@ -421,10 +427,11 @@ public class Disassembler { } case "InvokeDynamicInsnNode": { InvokeDynamicInsnNode node = (InvokeDynamicInsnNode) n; - String s = "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " [\n\t\t\tname: " + node.name + "\n\t\t\tdesc: " - + node.desc + "\n\t\t\tHandle: [\n\t\t\t\tname: " + node.bsm.getName() + "\n\t\t\t\towner: " + node.bsm.getOwner() - + "\n\t\t\t\tdesc: " + node.bsm.getDesc() + "\n\t\t\t\tisInterface: " + node.bsm.isInterface() + "\n\t\t\t\ttag: " - + node.bsm.getTag() + "\n\t\t\t]\n\t\t\targs: [\n"; + String s = "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " [\n\t\t\tname: " + UnicodeUtils.escape(node.name) + + "\n\t\t\tdesc: " + UnicodeUtils.escape(node.desc) + "\n\t\t\tHandle: [\n\t\t\t\tname: " + + UnicodeUtils.escape(node.bsm.getName()) + "\n\t\t\t\towner: " + UnicodeUtils.escape(node.bsm.getOwner()) + + "\n\t\t\t\tdesc: " + UnicodeUtils.escape(node.bsm.getDesc()) + "\n\t\t\t\tisInterface: " + node.bsm.isInterface() + + "\n\t\t\t\ttag: " + node.bsm.getTag() + "\n\t\t\t]\n\t\t\targs: [\n"; for (Object l : node.bsmArgs) { if (l.getClass().getName().equals("org.objectweb.asm.Type")) { Type type = (Type) l; @@ -447,9 +454,9 @@ public class Disassembler { s += "\n\t\t\t\t]\n"; } else if (l.getClass().getName().equals("org.objectweb.asm.Handle")) { Handle h = (Handle) l; - s += "\t\t\t\tHandle: [\n\t\t\t\t\tname: " + h.getName() + "\n\t\t\t\t\towner: " + h.getOwner() - + "\n\t\t\t\t\tdesc: " + h.getDesc() + "\n\t\t\t\t\tisInterface: " + h.isInterface() + "\n\t\t\t\t\ttag: " - + h.getTag(); + s += "\t\t\t\tHandle: [\n\t\t\t\t\tname: " + UnicodeUtils.escape(h.getName()) + "\n\t\t\t\t\towner: " + + UnicodeUtils.escape(h.getOwner()) + "\n\t\t\t\t\tdesc: " + UnicodeUtils.escape(h.getDesc()) + + "\n\t\t\t\t\tisInterface: " + h.isInterface() + "\n\t\t\t\t\ttag: " + h.getTag(); s += "\n\t\t\t\t]\n"; } else { s += "\t\t\t\t" + ClassUtil.getDecompiledValue(l, "") + "\n"; @@ -460,16 +467,17 @@ public class Disassembler { } case "MethodInsnNode": { MethodInsnNode node = (MethodInsnNode) n; - return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + node.desc + " " + node.owner + "/" + node.name - + "\n"; + return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + UnicodeUtils.escape(node.desc) + " " + + UnicodeUtils.escape(node.owner) + "/" + UnicodeUtils.escape(node.name) + "\n"; } case "TypeInsnNode": { TypeInsnNode node = (TypeInsnNode) n; - return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.desc + "\n"; + return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + UnicodeUtils.escape(node.desc) + "\n"; } case "MultiANewArrayInsnNode": { MultiANewArrayInsnNode node = (MultiANewArrayInsnNode) n; - return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + node.desc + " " + node.dims + "\n"; + return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + UnicodeUtils.escape(node.desc) + " " + node.dims + + "\n"; } case "LdcInsnNode": { LdcInsnNode node = (LdcInsnNode) n; @@ -535,7 +543,6 @@ public class Disassembler { String s = "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " [\n\t\t\tmin: " + node.min + "\n\t\t\tmax: " + node.max + "\n\t\t\tdefault: " + labels.get(node.dflt.getLabel()) + "\n\t\t\tlabels: [\n"; for (LabelNode l : node.labels) { - // System.out.println(l + ", " + l.getLabel()); s += "\t\t\t\t" + labels.get(l.getLabel()) + "\n"; } s += "\t\t\t]\n\t\t]\n"; diff --git a/src/me/ByteEdit/main/Main.java b/src/me/ByteEdit/main/Main.java index 635758d..596e3c0 100644 --- a/src/me/ByteEdit/main/Main.java +++ b/src/me/ByteEdit/main/Main.java @@ -77,6 +77,7 @@ import me.ByteEdit.edit.SearchBox; import me.ByteEdit.edit.TypeOpenBox; import me.ByteEdit.utils.ClassUtil; import me.ByteEdit.utils.OpcodesReverse; +import me.ByteEdit.utils.UnicodeUtils; public class Main extends JFrame { @@ -404,8 +405,8 @@ public class Main extends JFrame { line = line.substring(2); if (line.startsWith("invoke")) { String[] split = line.split(" "); - String desc = split[1]; - String className = split[2]; + String desc = UnicodeUtils.unescape(split[1]); + String className = UnicodeUtils.unescape(split[2]); String[] nameSplit = className.split("/"); String methodName = nameSplit[nameSplit.length - 1]; className = className.substring(0, className.length() - methodName.length() - 1); @@ -425,6 +426,14 @@ public class Main extends JFrame { break; } } + } else if (line.startsWith("new ")) { + String[] split = line.split(" "); + String className = UnicodeUtils.unescape(split[1]); + ClassNode classNode = classNodes.get(className + ".class"); + if (classNode == null) { + return; + } + selectFile(className + ".class"); } } diff --git a/src/me/ByteEdit/utils/ClassUtil.java b/src/me/ByteEdit/utils/ClassUtil.java index 54b4f81..3c09944 100644 --- a/src/me/ByteEdit/utils/ClassUtil.java +++ b/src/me/ByteEdit/utils/ClassUtil.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import org.apache.commons.lang3.StringEscapeUtils; import org.objectweb.asm.Type; public class ClassUtil { @@ -451,7 +450,7 @@ public class ClassUtil { public static String getDecompiledValue(Object o, String desc) { switch (o.getClass().getName()) { case "java.lang.String": { - return "\"" + StringEscapeUtils.escapeJava((String) o).replace("\n", "\\n").replace("\r", "\\r") + "\""; + return "\"" + UnicodeUtils.escape((String) o) + "\""; } case "java.lang.Integer": { if (desc.equals("Z")) { diff --git a/src/me/ByteEdit/utils/UnicodeUtils.java b/src/me/ByteEdit/utils/UnicodeUtils.java new file mode 100644 index 0000000..74b9dc4 --- /dev/null +++ b/src/me/ByteEdit/utils/UnicodeUtils.java @@ -0,0 +1,145 @@ +package me.ByteEdit.utils; + +public class UnicodeUtils { + + public static String unescape(String s) { + if (s == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + char unicodeBuffer = 0; + int stage = 0; + for (char c : s.toCharArray()) { + if (c == '\\' && stage == 0) { + stage = 1; + continue; + } + if (stage == 1) { + switch (c) { + case 'b': + sb.append('\b'); + stage = 0; + break; + case 't': + sb.append('\t'); + stage = 0; + break; + case 'n': + sb.append('\n'); + stage = 0; + break; + case 'r': + sb.append('\r'); + stage = 0; + break; + case 'f': + sb.append('\f'); + stage = 0; + break; + case '"': + sb.append('"'); + stage = 0; + break; + case '\\': + sb.append('\\'); + stage = 0; + break; + case 'u': + stage++; + break; + default: + throw new IllegalArgumentException("'\\" + c + "' is invalid."); + } + continue; + } else if (stage > 1 && stage < 6) { // \u1234 => up to 6 + try { + int i = Integer.parseInt(Character.toString(c), 16); + unicodeBuffer += i << (4 * (5 - stage++)); // 4 bytes each, shift left byte * 4..3..2..1 and add to unicodeBuffer + if (stage == 6) { + sb.append(unicodeBuffer); + stage = 0; + unicodeBuffer = 0; + } + } catch (NumberFormatException e) { + throw new IllegalArgumentException("'" + c + "' is not a valid hex value."); + } + continue; + } + sb.append(c); + } + if (stage != 0) { + if (unicodeBuffer != 0) { + throw new IllegalArgumentException("The unicode at the end is incomplete."); + } else { + throw new IllegalArgumentException("'\\' at the end is invalid."); + } + } + return sb.toString(); + } + + public static String escape(String s) { + if (s == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + for (char c : s.toCharArray()) { + if (isPrintable(c)) { + sb.append(c); + } else { + switch (c) { + case '\b': + sb.append("\\b"); + break; + case '\t': + sb.append("\\t"); + break; + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\f': + sb.append("\\f"); + break; + case '"': + sb.append("\\\""); + break; + case '\\': + sb.append("\\\\"); + break; + default: + sb.append(String.format("\\u%04X", (int) c)); + } + } + } + return sb.toString(); + } + + private static boolean isPrintable(char c) { + switch (c) { + case 'Ä': + case 'ä': + case 'Ö': + case 'ö': + case 'Ü': + case 'ü': + case 'ß': + case '€': + case '©': + case '®': + case '«': + case '»': + case '§': + case '£': + case '¥': + return true; + case '"': + case '\\': + return false; + default: + return c >= ' ' && c <= '~'; + } + } + +}