Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[ASM] Bytecode and Runtime contradict each other [Fixed]
#1
Alien 
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<ClassNodenodes 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(crClassWriter.COMPUTE_MAXS); 
         
RemappingClassAdapter rca = new RemappingClassAdapter(cwmapper); 
         
cr.accept(rcaClassReader.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();
        }
    }
Reply
#2
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.
Reply
#3
(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
Reply
#4
You need to change method parameters and field types. are you doing that?
Reply
#5
(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.

[Image: YIFV6BG.png]

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());
                    }
                }
            }
        }
    }
Reply
#6
can you show the java and the bytecode of a method that isn't being transformed properly.
Reply
#7
(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.
Reply
 


Forum Jump:


Users browsing this thread: 1 Guest(s)

About The Bytecode Club

We're a community focused on Reverse Engineering, we try to target Java/Android but we also include other langauges/platforms. We pride ourselves in supporting and free and open sourced applications.

Website