![]() |
|
||||||||
| Consortium Activities Projects Forge Events | |||||||||
ASM |
Introduction to the ASMDEX Bytecode FrameworkWhat is ASMDEX ?ASMDEX is a bytecode manipulation library as ASM but it handles the DEX bytecode used by Android executables. Only the core library and and a tool to convert bytecode to code generating it (asmdexifier) are available. The underlying principle while developing ASMDEX was to keep it very similar to ASM to ease the cost of porting tools done for Oracle Java bytecode to Android bytecode. Differences between ASM and ASMDEXAlthough the underlying principle was to keep ASMDEX close to ASM, there are still a lot of differences that the user must keep in mind :
The last constraint limits the use of ASMDEX. Otherwise, the most severe programming constraint for the ASMDEX user is probably the third one. One way to get around is to try to introduce as few changes as possible and rather rely on external (but potentially generated) method to perform the real work. A future release of ASMDEX may provide a generic register allocation method visitor to simplify the development of transformations. A simple exampleHere is a canvas of a tool that logs some method calls according to a policy that identifies the methods to check. To keep the tutorial short and generic, we will not describe the class implementing the policy. We will not describe the generation of the log methods either. The entry pointThis is a very simple entry point. We consider the case where the classes.dex file has been extracted from the APK. You may want to perform the change in place. But in any case, you must sign your application again to install it.
public class AnnotateCalls {
public static void main(String args[]) {
FileOutputStream os = null;
try {
int api = Opcodes.ASM4;
File inFile;
File outFile;
... // Argument validation
AnnotRulesManager rm = ...; // Rules to apply
ApplicationReader ar = new ApplicationReader(api, inFile);
ApplicationWriter aw = new ApplicationWriter();
ApplicationVisitor aa = new ApplicationAdapterAnnotateCalls(api, rm, aw);
ar.accept(aa, 0);
byte [] b = aw.toByteArray();
os = new FileOutputStream(outFile);
os.write(b);
} catch (IOException e) { // recovery
} finally { // cleanup }
}
}
The application visitor
This is a new element specific to ASMDEX and represents the complete
code unit (classes.dex). We use the end visitor to dump the new class.
The system will take care of sorting the elements as they should be.
public class ApplicationAdapterAnnotateCalls extends ApplicationVisitor {
final LogClassWriter logClassWriter;
final AnnotRulesManager ruleManager;
public ApplicationAdapterAnnotateCalls(int api, AnnotRulesManager rm, ApplicationVisitor av) {
super(api, av);
ruleManager = rm;
logClassWriter = new LogClassWriter(rm,av);
}
@Override
public ClassVisitor visitClass(int access, String name, String [] signature, String superName, String [] interfaces) {
ClassVisitor cv = av.visitClass(access, name, signature, superName, interfaces);
ClassAdapterAnnotateCalls ca = new ClassAdapterAnnotateCalls(api, ruleManager,cv);
return ca;
}
@Override
public void visitEnd() {
logClassWriter.addLogClass();
av.visitEnd();
}
}
The class visitorThere is nothing interesting in this one. It delegates all the work to the method visitor.
public class ClassAdapterAnnotateCalls extends ClassVisitor {
final private AnnotRulesManager ruleManager;
public ClassAdapterAnnotateCalls(int api, AnnotRulesManager ruleManager, ClassVisitor cv) {
super(api, cv);
this.ruleManager = ruleManager;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String[] signature,
String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
MethodAdapterAnnotateCalls ma = new MethodAdapterAnnotateCalls(api, ruleManager, mv);
return ma;
}
}
The method visitorThis is where the real work is done. The method visiting method calls is redefined to generate a new method call followed by the original one when the method should be logged. Whether something must be done is in the result of the call to the rule manager. If this is a function, it should be understood as the name of a method in the generated log class. This log method is static and takes as many arguments as the controlled method.
public class MethodAdapterAnnotateCalls extends MethodVisitor {
final private AnnotRulesManager ruleManager;
public MethodAdapterAnnotateCalls(int api, AnnotRulesManager ruleManager, MethodVisitor mv) {
super(api, mv);
this.ruleManager = ruleManager;
}
@Override
public void visitMethodInsn(int opcode, java.lang.String owner, java.lang.String name, java.lang.String desc, int[] arguments) {
boolean isStatic;
String signature;
switch (opcode) {
case Opcodes.INSN_INVOKE_STATIC:
case Opcodes.INSN_INVOKE_STATIC_RANGE:
isStatic=true;
break;
default:
isStatic=false;
}
String logItName = ruleManager.log(owner,name,desc,isStatic);
if (logItName != null) {
if (isStatic) signature = "V" + MethodSignature.popType(desc);
else signature = "V" + owner + MethodSignature.popType(desc);
int opcodeStatic = (opcode < 0x74) ? Opcodes.INSN_INVOKE_STATIC : Opcodes.INSN_INVOKE_STATIC_RANGE;
mv.visitMethodInsn(opcodeStatic, LogClassWriter.LOG_CLASSNAME, logItName, signature, arguments);
}
mv.visitMethodInsn(opcode, owner, name, desc, arguments);
}
}
We need to get the list of parameter's types without the result type and this is done by "poping" the result type from the representation. Here is the relevant code from the MethodSignature class:
public static String popType(String desc) {
return desc.substring(nextTypePosition(desc,0));
}
public static int nextTypePosition(String desc, int pos) {
while(desc.charAt(pos) == '[') pos ++;
if (desc.charAt(pos) == 'L') pos = desc.indexOf(';', pos);
pos ++;
return pos;
}
| ||
Copyright © 1999-2009, OW2 Consortium | contact | webmaster | Last modified at 2012-10-21 06:10 PM |