[ASM] Bytecode and Runtime contradict each other [Fixed] - Dank Vader - 11-30-2015
Here is the code snippet I am using to rename classes
PHP Code: import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.commons.Remapper; import org.objectweb.asm.commons.RemappingClassAdapter; import org.objectweb.asm.commons.SimpleRemapper; import org.objectweb.asm.tree.ClassNode; //Imports showing which ASM (5.0.4) classes I'm using
private static void doStuff() throws IOException { File jar = new File("Input.jar"); //Get's the ClassNodes for each class in a jar ArrayList<ClassNode> nodes = BCU.loadClasses(jar); //Appends '_Test' to each class except for the main class. Remapper mapper = new SimpleRemapper(getRename(nodes)); //Loop through the ClassNodes updating references to renamed classes for (ClassNode cn : nodes) { ClassReader cr = new ClassReader(BCU.getNodeBytes(cn)); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS); RemappingClassAdapter rca = new RemappingClassAdapter(cw, mapper); cr.accept(rca, ClassReader.EXPAND_FRAMES); cr = new ClassReader(cw.toByteArray()); cr.accept(cn,ClassReader.EXPAND_FRAMES); } //Export the nodes as a new jar file and copy over the META-INF folder BCU.saveAsJar(nodes, "Output.jar"); }
When run on any jar file, fields in the exported jar are duplicated. The ones at the bottom have updated references to the renamed classes.
Example Output:
PHP Code: public static String VERSION; public static final ConfigFile config; public static String VERSION; public static final ConfigFile_Test config;
Edit: I have a temporary fix by extending ClassNode and removing the duplicates post-rename. But I'd like something more efficient. This will do for now though.
Edit 2: I've run into a problem when exporting classes [Edit in regards to Edit 2: Found the problem. Edit 1 still applies. I would like to improve this]
Edit 3: My currently working approach:
Code: @Override
public void handle(ActionEvent event) {
try {
Timer t = new Timer();
File jar = Main.getTargetJar();
MappingGen.setLast(jar);
Map<String, ClassNode> nodes = JarUtil.loadClasses(jar);
System.out.println("Generating new names for " + nodes.size() + " classes... ");
Map<String, MappedClass> renamed = MappingGen.getRename(tab.getObfuscation(), nodes);
Map<String, byte[]> out = new HashMap<String, byte[]>();
System.out.println("Renaming " + renamed.size() + " classes... ");
int workIndex = 1;
SkidMapper mapper = new SkidMapper(renamed);
for (ClassNode cn : nodes.values()) {
ClassReader cr = new ClassReader(ASMUtil.getNodeBytes(cn));
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
ClassVisitor remapper = new RemappingClassAdapter(cw, mapper);
cr.accept(remapper, ClassReader.EXPAND_FRAMES);
cr = new ClassReader(cw.toByteArray());
cw = new ClassWriter(0);
cr.accept(cw, ClassReader.SKIP_DEBUG);
out.put(renamed.get(cn.name).getRenamed(), cw.toByteArray());
//
String percentStr = "" + ((workIndex + 0.000000001f) / (renamed.size() - 0.00001f)) * 100;
percentStr = percentStr.substring(0, percentStr.length() > 5 ? 5 : percentStr.length());
System.out.println(" " + workIndex + "/" + renamed.size() + " [" + percentStr + "%]");
workIndex++;
}
System.out.println("Saving classes...");
JarUtil.saveAsJar(out, tab.getExportedName(), tab.yes());
System.out.println("Done! Completion time: " + (t.getTime()) + " Milliseconds");
} catch (Exception e) {
e.printStackTrace();
}
}
RE: [ASM] Duplicate fields when renaming classes - Bibl - 12-07-2015
to be honest, i havent really used asm's remapper. instead i made my own which iterates through all of the classes/fields/methods and then renames stuff that way. i would recommend you do that as well.
RE: [ASM] Duplicate fields when renaming classes - Dank Vader - 12-13-2015
(12-07-2015, 08:04 AM)Bibl Wrote: to be honest, i havent really used asm's remapper. instead i made my own which iterates through all of the classes/fields/methods and then renames stuff that way. i would recommend you do that as well.
I've switched to this and can't seem to figure out how to update class names. Everything else is working though.
Edit: Nevermind. I had the classreaders/writers in the wrong order. Thanks for the tip! Currently stuck at updating the references to the renamed objects/classes. I can't get all the method calls from variables to be renamed and classes keep trying to refer to their original names despite signatures and descriptions being updated.
Here's an example jar so that you can look for yourselves. Text and screens can only do so much: http://b.1339.cf/cladyky.zip
RE: [ASM] Duplicate fields when renaming classes - Bibl - 12-17-2015
You need to change method parameters and field types. are you doing that?
RE: [ASM] Duplicate fields when renaming classes - Dank Vader - 12-17-2015
(12-17-2015, 02:10 AM)Bibl Wrote: You need to change method parameters and field types. are you doing that?
Method parameters and field types are correct.
The jar itself is in the last post but here's what's run to rename everything.
Code: @SuppressWarnings("rawtypes")
public void rename(Map<String, MappedClass> renamemap) {
// rename the class
MappedClass classmapping = renamemap.get(name);
name = classmapping.getRenamed();
outerClass = StringUtil.fixDesc(outerClass, renamemap.values());
signature = StringUtil.fixDesc(signature, renamemap.values());
superName = StringUtil.fixDesc(superName, renamemap.values());
sourceDebug = "Nope";
sourceFile = "Nope";
// rename fields and update descriptions
for (int i = 0; i < fields.size(); i++) {
FieldNode fn = (FieldNode) fields.get(i);
fn.desc = StringUtil.fixDesc(fn.desc, renamemap.values());
fn.signature = StringUtil.fixDesc(fn.signature, renamemap.values());
MappedField mf = classmapping.getFields().get(fn.name);
if (mf == null) {
continue;
}
String newName = mf.getRenamed();
fn.name = newName;
}
// rename methods and local variables
for (int i = 0; i < methods.size(); i++) {
MethodNode mn = (MethodNode) methods.get(i);
mn.desc = StringUtil.fixDesc(mn.desc, renamemap.values());
mn.signature = StringUtil.fixDesc(mn.signature, renamemap.values());
if (mn.name.equals("main")) {
MappingGen.setMain(name);
}
MappedMethod mm = classmapping.getMethods().get(mn.name);
if (mm != null) {
mn.name = mm.getRenamed();
}
InsnList instructionList = mn.instructions;
List localVars = mn.localVariables;
if (localVars != null) {
for (Object o : localVars) {
LocalVariableNode lvn = (LocalVariableNode) o;
// TODO: Local variable renaming
lvn.desc = StringUtil.fixDesc(lvn.desc, renamemap.values());
lvn.signature = StringUtil.fixDesc(lvn.signature, renamemap.values());
}
}
if (instructionList != null) {
Iterator instructIter = instructionList.iterator();
while (instructIter.hasNext()) {
Object o = instructIter.next();
if (o instanceof AnnotationNode) {
AnnotationNode an = ((AnnotationNode) o);
an.desc = StringUtil.fixDesc(an.desc, renamemap.values());
} else if (o instanceof FieldInsnNode) {
FieldInsnNode fin = ((FieldInsnNode) o);
fin.desc = StringUtil.fixDesc(fin.desc, renamemap.values());
fin.name = StringUtil.fixField(fin.name, renamemap.get(fin.owner));
fin.owner = StringUtil.fixDesc(fin.owner, renamemap.values());
} else if (o instanceof InvokeDynamicInsnNode) {
// I don't think this is ever called
// Multiple test cases and this is never called
InvokeDynamicInsnNode idn = ((InvokeDynamicInsnNode) o);
idn.desc = StringUtil.fixDesc(idn.desc, renamemap.values());
idn.desc = StringUtil.fixMethod(idn.name, renamemap.get(idn.bsm.getOwner()));
// TODO: Is this even required?
int tag = 0;
String owner = "dank";
String name = "dank";
String desc = "dank";
idn.bsm = new Handle(tag, owner, name, desc);
} else if (o instanceof MethodInsnNode) {
MethodInsnNode min = ((MethodInsnNode) o);
min.desc = StringUtil.fixDesc(min.desc, renamemap.values());
min.owner = StringUtil.fixDesc(min.owner, renamemap.values());
min.name = StringUtil.fixMethod(min.name, renamemap.get(min.owner));
} else if (o instanceof MultiANewArrayInsnNode) {
MultiANewArrayInsnNode manain = ((MultiANewArrayInsnNode) o);
manain.desc = StringUtil.fixDesc(manain.desc, renamemap.values());
} else if (o instanceof TypeInsnNode) {
TypeInsnNode tin = ((TypeInsnNode) o);
tin.desc = StringUtil.fixDesc(tin.desc, renamemap.values());
}
}
}
}
}
RE: [ASM] Bytecode and Runtime contradict each other - Bibl - 12-20-2015
can you show the java and the bytecode of a method that isn't being transformed properly.
RE: [ASM] Bytecode and Runtime contradict each other - Dank Vader - 12-23-2015
(12-20-2015, 05:13 AM)Bibl Wrote: can you show the java and the bytecode of a method that isn't being transformed properly.
They are transformed though. That's what I've run into. If you look at the jars I provided the bytecode references all have been updated. The class names/definitions, the methods (declarations and references, parameters), field types, method local variables. It's all updated. But at runtime it just flips out and tries to use the old names.
|