/*
* Copyright (C) 2012 Sony Mobile Communications AB
*
* This file is part of ApkAnalyser.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package andreflect.gui.action.injection;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import javax.swing.Icon;
import jerl.bcm.inj.Injection;
import mereflect.MEClass;
import mereflect.MEMethod;
import org.jf.dexlib.ClassDataItem;
import org.jf.dexlib.ClassDataItem.EncodedMethod;
import org.jf.dexlib.ClassDefItem;
import org.jf.dexlib.CodeItem;
import org.jf.dexlib.MethodIdItem;
import org.jf.dexlib.Util.AccessFlags;
import analyser.gui.MainFrame;
import analyser.gui.Selection;
import analyser.gui.actions.bytecodemod.AbstractTreeBytecodeModAction;
import analyser.logic.BytecodeModificationMediator;
import analyser.logic.RefClass;
import analyser.logic.Reference;
import andreflect.ApkClassContext;
import andreflect.DexClass;
import andreflect.DexMethod;
import andreflect.injection.ItemCreator;
import andreflect.injection.impl.DalvikMethodOffsetInstanceHash;
public class DalvikMethodOffsetInstanceFinalizeAction extends AbstractTreeBytecodeModAction {
protected DalvikMethodOffsetInstanceFinalizeAction(String arg0, Icon arg1) {
super(arg0, arg1);
}
private static final long serialVersionUID = -1874918510046504264L;
protected static DalvikMethodOffsetInstanceFinalizeAction m_inst = null;
protected static DalvikMethodOffsetInstanceFinalizeAction m_inst_oneclass = null;
int traverseCount, traverseIndex = 0;
DexClass dexClass = null;
public static DalvikMethodOffsetInstanceFinalizeAction getInstance(MainFrame mainFrame)
{
if (m_inst == null)
{
m_inst = new DalvikMethodOffsetInstanceFinalizeAction("Print finalize all local classes", null);
m_inst.setMainFrame(mainFrame);
}
return m_inst;
}
public static DalvikMethodOffsetInstanceFinalizeAction getInstanceOneClass(MainFrame mainFrame)
{
if (m_inst_oneclass == null)
{
m_inst_oneclass = new DalvikMethodOffsetInstanceFinalizeAction("Print finalize this class", null);
m_inst_oneclass.setMainFrame(mainFrame);
}
return m_inst_oneclass;
}
@Override
protected Injection getInjection(String className, String methodSignature)
throws Throwable {
// not used
return null;
}
@Override
public void run(ActionEvent e) throws Throwable {
Object obj = Selection.getSelectedObject();
if (obj == null || !(obj instanceof Reference)) {
return;
}
if (obj instanceof RefClass) {
DexClass dexClass = (DexClass) ((RefClass) obj).getMEClass();
modifyClass(dexClass);
} else {
Reference ref = (Reference) obj;
if (ref != null) {
traverseCount = 0;
traverseIndex = 0;
getTraverseCount(ref);
if (traverseCount != 0) {
traverseInside(ref);
}
}
}
if (isRunning()) {
getMainFrame().actionFinished(this);
}
((MainFrame) getMainFrame()).getSelectedTree().repaint();
}
protected void getTraverseCount(Reference ref) throws Throwable {
if (ref instanceof RefClass) {
traverseCount++;
} else {
Iterator<Reference> i = ref.getChildren().iterator();
while (i.hasNext()) {
getTraverseCount(i.next());
}
}
}
protected void traverseInside(Reference ref) throws Throwable {
if (ref instanceof RefClass) {
DexClass dexClass = (DexClass) ((RefClass) ref).getMEClass();
modifyClass(dexClass);
getMainFrame().actionReportWork(this, 100 * traverseIndex++ / traverseCount);
} else {
Iterator<Reference> i = ref.getChildren().iterator();
while (i.hasNext()) {
traverseInside(i.next());
}
}
}
private void modifyClass(DexClass dexClass) {
DexMethod dexMethod = hasFinalize(dexClass);
if (dexMethod == null
&& dexClass.isInterface() == false) {
dexMethod = injectFinalizeMethod(dexClass);
}
if (dexMethod != null) {
createInjection(dexMethod);
}
}
private DexMethod injectFinalizeMethod(DexClass dexClass) {
DexMethod res = null;
ClassDefItem classDefItem = dexClass.getClassDefItem();
ClassDataItem classDataItem = classDefItem.getClassData();
EncodedMethod[] virtualMethodsArray = null;
ArrayList<EncodedMethod> virtualMethods = new ArrayList<EncodedMethod>();
if (classDataItem.getVirtualMethods() != null
&& classDataItem.getVirtualMethods().length != 0) {
for (EncodedMethod virtualMethod : classDataItem.getVirtualMethods()) {
virtualMethods.add(virtualMethod);
}
}
ItemCreator ic = new ItemCreator(((ApkClassContext) (dexClass.getResource().getContext())).getDex());
MethodIdItem finalizeMethodIdItem = ic.addMethodIdItem(classDefItem.getClassType().getTypeDescriptor(),
"V", new String[0], "finalize");
CodeItem finalizeCodeItem = ic.addEmptyVirtualCodeItem();
EncodedMethod finalizeEncodedMethod = new EncodedMethod(finalizeMethodIdItem,
AccessFlags.PROTECTED.getValue(),
finalizeCodeItem);
virtualMethods.add(finalizeEncodedMethod);
Collections.sort(virtualMethods);
virtualMethodsArray = new EncodedMethod[virtualMethods.size()];
virtualMethods.toArray(virtualMethodsArray);
classDataItem.virtualMethods = virtualMethodsArray;
res = new DexMethod(dexClass, finalizeEncodedMethod);
res.setAccessFlags(DexMethod.ACC_PROTECTED);
res.setNameIndex(0); //ignored
res.setDescriptorIndex(0); //ignored
res.setAttributes(null); //ignored
res.m_dexInvokations = new ArrayList<MEMethod.Invokation>();
res.setExceptions(new MEClass[0]);
return res;
}
private void createInjection(DexMethod method) {
DalvikMethodOffsetInstanceHash inj = new DalvikMethodOffsetInstanceHash(getMethodSignature(method)
, method.getMEClass().getName(), false);
int totalRegisters = method.getEncodedMethod().codeItem.getRegisterCount();
int parameterRegisters = method.getEncodedMethod().method.getPrototype().getParameterRegisterCount();
int thisRegister = totalRegisters - parameterRegisters - 1;
inj.setRegister((short) thisRegister);
BytecodeModificationMediator.getInstance().registerModification(
method.getMEClass().getResource().getContext(),
method.getMEClass(),
inj,
method);
((MainFrame) getMainFrame()).getMidletTree().findAndMarkNode(method, Reference.MODIFIED);
}
public DexMethod hasFinalize(DexClass clazz) {
MEMethod[] methods = clazz.getMethods("finalize");
if (methods != null) {
for (MEMethod method : methods) {
DexMethod dexMethod = (DexMethod) method;
if (!dexMethod.isPrivate()) {
return dexMethod;
}
}
}
return null;
}
}