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

1 Answer 1

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.
Sign up to request clarification or add additional context in comments.

7 Comments

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() ?
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"..).
the problem is that I am not getting any error but also not seeing any result
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.
Btw, you did change my append( to insert(h, , right? And what exactly is h's value?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.