1

I am trying to generate(and modify) the code of an output class from another class using the ByteCode Engineering Library (by Apache) .

String class_name = c_gen.getClassName();
    Method[] Methods = c_gen.getMethods();

    for (int i=0;i<Methods.length;i++)
    {
        MethodGen m_gen = new MethodGen(Methods[i], class_name, cpg);
        InstructionList il = m_gen.getInstructionList();
        InstructionHandle h;
           il.insert(h,factory.createInvoke("ClassName","printSomething",   Type.VOID,new Type[]{Type.STRING}, INVOKESTATIC));
    }

so I am trying to call printSomething from ClassName for every method.The problem is that I don't know how to actually pass the string argument to the method printSomething

Andy Guibert
  • 41,446
  • 8
  • 38
  • 61
Oussama Achech
  • 131
  • 1
  • 11

1 Answers1

1

You will need to push the string argument on the stack before the invokestatic. This is done with the LDC opcode. Something like:

il.insert( new LDC(cpg.addString("MyString")));

The outline looks like this:

JavaClass clazz = Repository.lookupClass( class_name );
ClassGen c_gen = new ClassGen( clazz );
ConstantPoolGen cpg = new ConstantPoolGen( clazz.getConstantPool() );
InstructionFactory factory = new InstructionFactory( c_gen, cpg );

Methods [] methods = clazz.getMethods();

for ( int i = 0; i < methods.length; i ++ )
{
    if ( m.isAbstract() || m.isNative() || .... )
        continue;

    MethodGen m_gen = new MethodGen( methods[i], class_name, cpg );
    InstructionList il = m_gen.getInstructionList();

    il.insert( factory.createInvoke("ClassName", "printSomething",
        Type.VOID, new Type[]{Type.STRING}, INVOKESTATIC) );
    il.insert( factory.createPush( "StringToPrint" ) );

    methods[i] = m_gen.getMethod();
}

clazz.setConstantPool( cpg.getFinalConstantPool() );
clazz.setMethods( methods ); // might be redundant

clazz.dump( new File( .... ) );

A few notes:

  • Since we're inserting, every insert will prepend to the method. This is why we first insert the instruction opcode, and then the arguments (in reverse), so that the actual sequence will be ldc #stringref; invokestatic #methodref.
  • We need to replace the ConstantPool and the Methods with our modified versions of them.
Kenney
  • 9,003
  • 15
  • 21
  • I've done this : `il.append(new LDC(cpg.addString(Methods[i].getName()))); il.insert(h,factory.createInvoke("ClassName","printSomething", Type.VOID,new Type[]{Type.STRING}, INVOKESTATIC));` but nothing happens. Does the LDC takes the string parameter from the Methods[i].getName() ? – Oussama Achech Oct 31 '15 at 22:57
  • My guess is you're getting a class validation error? I wasn't sure what `h` is initialized as, but you probably want `insert( h, `. (I did say "something like"..). – Kenney Oct 31 '15 at 23:26
  • the problem is that I am not getting any error but also not seeing any result – Oussama Achech Oct 31 '15 at 23:33
  • That probably depends on how you generate the class file and how and where you load it. If you save the generated class file to a file and `javap -c` it, do you see your injected code? Btw, you can't redefine a class when the current ClassLoader knows about it. Best to test it by first running your code, injecting an equivalent `System.out.println( "Hello" );` against a `class Main { public void foo() {} public static main(String[]argv){ foo(); }}` and running that class from the commandline. – Kenney Oct 31 '15 at 23:52
  • Btw, you did change my `append(` to `insert(h, `, right? And what exactly is `h`'s value? – Kenney Oct 31 '15 at 23:59
  • I think that append inserts to the end , by I need to insert an instruction to the start of a method – Oussama Achech Nov 01 '15 at 11:25
  • I added an outline for clarity (untested). If you still have trouble, please post a minimal complete code example so I can reproduce your work. – Kenney Nov 01 '15 at 15:29