How to print out all methods called during runtime

2019-02-20 20:47发布

问题:

I want to print out all methods that get called during runtime. They should be printed out in the order that they're called in and if they're called multiple times, they should be printed multiple times.

This can be used for reverse engineering - seeing which functions get called when you press a button or do a specific action.

I want to use Java agents and instrumentation for this.

回答1:

This can be done using Java Agents and an instrumentation library.

Java agent - Separate code that can be made to run before the main part of the code.

Instrumentation - Changing the source code during the load-time of a program.

Code for making it work

(taken from appcrawler and modified slightly):

The agent.jar source code:

SimpleTransformer.java:

package test;

import java.security.*;
import java.lang.instrument.*;
import java.util.*;
import javassist.*;

public class SimpleTransformer implements ClassFileTransformer {

  public SimpleTransformer() {
    super();
  }

  public byte[] transform(ClassLoader loader, String className, Class redefiningClass, ProtectionDomain domain, byte[] bytes) throws IllegalClassFormatException {
    return transformClass(redefiningClass,bytes);
  }

  private byte[] transformClass(Class classToTransform, byte[] b) {
    ClassPool pool = ClassPool.getDefault();
    CtClass cl = null;
    try {
      cl = pool.makeClass(new java.io.ByteArrayInputStream(b));
      CtBehavior[] methods = cl.getDeclaredBehaviors();
      for (int i = 0; i < methods.length; i++) {
        if (methods[i].isEmpty() == false) {
          changeMethod(methods[i]);
        }
      }
      b = cl.toBytecode();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
    finally {
      if (cl != null) {
        cl.detach();
      }
    }
    return b;
  }

  private void changeMethod(CtBehavior method) throws NotFoundException, CannotCompileException {
    /*if (method.getName().equals("doIt")) {
      method.insertBefore("System.out.println(\"started method at \" + new java.util.Date());");
      method.insertAfter("System.out.println(\"ended method at \" + new java.util.Date());");
    }*/



          //MY CODE
      //!Modifier.isAbstract(method.getModifiers()) -- abstract methods can't be modified. If you get exceptions, then add this to the if statement.
      //native methods can't be modified.
      if (!Modifier.isNative(method.getModifiers())) {
          String insertString = "System.out.println(\"started method " + method.getName() + "\");";
          method.insertBefore(insertString);
      }
  }

SimpleMain.java:

package test;

import java.lang.instrument.Instrumentation;

public class SimpleMain {
  public static void premain(String agentArguments, Instrumentation instrumentation) {  
    instrumentation.addTransformer(new SimpleTransformer());
  } 
}

MANIFEST.mf:

Manifest-Version: 1.0
Boot-Class-Path: javassist.jar
Premain-Class: test.SimpleMain

Take these files and package them into a jar file. Also make sure to include files from javassist.jar (downloadable from www.javassist.org) and tools.jar (found in Program Files/Java/jdk/lib/). Not sure if the second one is necessary, but the article says it is for some reason.

Now you can use this jar file as a java agent.

java -javaagent:agent.jar YourJavaProgram

And voila. The java agent will instrument all methods and print out every method called during execution.