/* * 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 analyser.gui.actions.bytecodemod; import gui.actions.AbstractCanceableAction; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.swing.Icon; import javax.swing.JButton; import jerl.bcm.util.ClassInjContainer; import mereflect.MEClass; import mereflect.MEClassContext; import util.JadFileModifier; import analyser.gui.MainFrame; import analyser.gui.ProgressReporter; import analyser.gui.actions.InstallAndStartAction; import analyser.logic.BytecodeModificationMediator; import andreflect.ApkClassContext; import andreflect.gui.action.ApkInstallAndStartAction; public class PerformAction extends AbstractCanceableAction { private static final long serialVersionUID = 1850052413392021437L; protected static PerformAction m_inst = null; static final String MOD_MIDLET_NAME_POSTFIX = "'"; protected MainFrame m_mainFrame; public static PerformAction getInstance(MainFrame mainFrame) { if (m_inst == null) { m_inst = new PerformAction("Perform bytecode modifications", null); m_inst.setMainFrame(mainFrame); } return m_inst; } protected PerformAction(String arg0, Icon arg1) { super(arg0, arg1); } @Override public String getWorkDescription() { return "Perform bytecode modifications"; } @Override public void handleThrowable(Throwable t) { t.printStackTrace(); getMainFrame().showError("Bytecode modification error", t); getMainFrame().initBottomInfo(); } int curCtx; MEClassContext m_ctx; @Override public void run(ActionEvent e) throws Throwable { final MainFrame mainFrame = (MainFrame) getMainFrame(); BytecodeModificationMediator bmm = BytecodeModificationMediator.getInstance(); Set<MEClassContext> ctxs = bmm.getModifiedContexts(); StringBuffer log = new StringBuffer(); File modifiedJar = null; File modifiedJad = null; mainFrame.actionStarted(this); curCtx = 0; final int ctxCount = ctxs.size(); if (ctxCount == 0) { return; } Iterator<MEClassContext> ctxsI = ctxs.iterator(); int classCounter = 0; while (ctxsI.hasNext()) { File newJar = null; MEClassContext ctx = ctxsI.next(); log.append("Original: " + ctx.getContextName() + "\n"); mainFrame.setBottomInfo("Modifying " + ctx.getContextName()); newJar = bmm.performRegisteredModifications(new ProgressReporter() { int total; @Override public void reportEnd() { } @Override public void reportStart(int total) { this.total = total; } @Override public void reportWork(int finished) { mainFrame.actionReportWork( PerformAction.this, (curCtx * 100 + (finished * 100) / total) / ctxCount); } }, ctx, MOD_MIDLET_NAME_POSTFIX); Map<MEClass, ClassInjContainer> mods = bmm.getModifications(ctx); Iterator<MEClass> iClass = mods.keySet().iterator(); while (iClass.hasNext()) { classCounter++; MEClass c = iClass.next(); log.append(" .. " + c.getName() + "\n"); } if (newJar != null) { log.append("Modified: " + newJar.getAbsolutePath() + "\n"); modifiedJar = newJar; if (!(ctx instanceof ApkClassContext)) { // Try to modify jad as well try { File jadFile = new File(ctx.getContextName().replaceAll(".jar", ".jad")); String newJadFile = newJar.getAbsolutePath().replaceAll(".jar", ".jad"); if (jadFile.exists()) { log.append("Found jad: " + jadFile.getAbsolutePath() + "\n"); JadFileModifier jfm = new JadFileModifier(jadFile, newJadFile, null); modifiedJad = jfm.modify(newJar.getName(), MOD_MIDLET_NAME_POSTFIX, newJar.length()); log.append("Modified jad: " + newJadFile + "\n"); } } catch (IOException ignore) { } } } log.append("\n"); m_ctx = ctx; curCtx++; mainFrame.actionReportWork(this, (curCtx * 100) / ctxCount); } log.append("Done - modified " + curCtx + " application(s) and " + classCounter + " class(es).\n"); mainFrame.actionFinished(this); mainFrame.initBottomInfo(); JButton[] customButtons = null; if (curCtx == 1) { final File fModifiedJad = modifiedJad; final File fModifiedJar = modifiedJar; customButtons = new JButton[1]; customButtons[0] = new JButton("Install and run"); customButtons[0].addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { new Thread(new Runnable() { @Override public void run() { if (m_ctx instanceof ApkClassContext) { e.setSource(new Object[] { fModifiedJar , ((ApkClassContext) m_ctx).getXmlParser().getManifest().getLauncher() , ((ApkClassContext) m_ctx).getXmlParser().getManifest().getPackage() }); ApkInstallAndStartAction.getInstance((MainFrame) getMainFrame()).actionPerformed(e); } else { e.setSource(new Object[] { fModifiedJad, fModifiedJar }); InstallAndStartAction.getInstance((MainFrame) getMainFrame()).actionPerformed(e); } } }).start(); } }); } mainFrame.showText("Modification report", log.toString(), customButtons); } }