/*
* VpcCFGMain.java - This file is part of the Jakstab project.
* Copyright 2007-2015 Johannes Kinder <jk@jakstab.org>
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
*/
package org.jakstab;
import java.io.*;
import org.jakstab.transformation.DeadCodeElimination;
import org.jakstab.transformation.VpcCfgReconstruction;
import org.jakstab.util.*;
import org.jakstab.analysis.*;
import org.jakstab.analysis.explicit.BoundedAddressTracking;
import org.jakstab.cfa.ControlFlowGraph;
import org.jakstab.cfa.IntraproceduralCFG;
import org.jakstab.loader.*;
import org.jakstab.ssl.Architecture;
import antlr.ANTLRException;
public class VpcCfgMain {
private static Logger logger = Logger.getLogger(VpcCfgMain.class);;
private static volatile Algorithm activeAlgorithm;
private static volatile Thread mainThread;
public static void main(String[] args) {
mainThread = Thread.currentThread();
StatsTracker stats = StatsTracker.getInstance();
// Parse command line
Options.parseOptions(args);
Main.logBanner();
/////////////////////////
// Parse SSL file
Architecture arch;
try {
arch = new Architecture(Options.sslFilename.getValue());
} catch (IOException e) {
logger.fatal("Unable to open SSL file!", e);
return;
} catch (ANTLRException e) {
logger.fatal("Error parsing SSL file!", e);
return;
}
long overallStartTime = System.currentTimeMillis();
/////////////////////////
// Parse executable
Program program = Program.createProgram(arch);
File mainFile = new File(Options.mainFilename).getAbsoluteFile();
String baseFileName = null;
try {
// Load additional modules
for (String moduleName : Options.moduleFilenames) {
logger.warn("Parsing " + moduleName + "...");
File moduleFile = new File(moduleName).getAbsoluteFile();
program.loadModule(moduleFile);
}
// Load main module last
logger.warn("Parsing " + Options.mainFilename + "...");
program.loadMainModule(mainFile);
// Use main module as base name if we have none yet
if (baseFileName == null)
baseFileName = getBaseFileName(mainFile);
} catch (FileNotFoundException e) {
logger.fatal("File not found: " + e.getMessage());
return;
} catch (IOException e) {
logger.fatal("IOException while parsing executable!", e);
//e.printStackTrace();
return;
} catch (BinaryParseException e) {
logger.fatal("Error during parsing!", e);
//e.printStackTrace();
return;
}
logger.info("Finished parsing executable.");
// Add surrounding "%DF := 1; call entrypoint; halt;"
program.installHarness(new DefaultHarness());
int slashIdx = baseFileName.lastIndexOf('\\');
if (slashIdx < 0) slashIdx = baseFileName.lastIndexOf('/');
if (slashIdx < 0) slashIdx = -1;
slashIdx++;
stats.record(baseFileName.substring(slashIdx));
stats.record(Main.version);
// Catches control-c and System.exit
Thread shutdownThread = new Thread() {
@Override
public void run() {
if (mainThread.isAlive() && activeAlgorithm != null) {
//stop = true; // Used for CFI checks
activeAlgorithm.stop();
try {
mainThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Runtime.getRuntime().addShutdownHook(shutdownThread);
// Add shutdown on return pressed for eclipse
if (!Options.background.getValue() && System.console() == null) {
logger.info("No console detected (eclipse?). Press return to terminate analysis and print statistics.");
Thread eclipseShutdownThread = new Thread() {
public void run() {
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
System.exit(1);
}
};
eclipseShutdownThread.start();
}
// Necessary to stop shutdown thread on exceptions being thrown
try {
//VpcTrackingAnalysis.useAsVpc = new MemoryReference(MemoryRegion.STACK, -1092, 32);
// Always do VPC sensitive BAT here
Options.cpas.setValue("v");
// No need to be sound
logger.error(Characters.DOUBLE_LINE_FULL_WIDTH);
ControlFlowReconstruction cfr = new ControlFlowReconstruction(program);
try {
runAlgorithm(cfr);
} catch (RuntimeException r) {
logger.error("!! Runtime exception during Control Flow Reconstruction! Trying to shut down gracefully.");
r.printStackTrace();
}
if (!cfr.isCompleted()) {
logger.error("WARNING: Analysis interrupted, CFG might be incomplete!");
}
ProgramGraphWriter graphWriter = new ProgramGraphWriter(program);
graphWriter.writeDisassembly(baseFileName + "_jak.asm");
String procName = Options.procedureGraph.getValue();
ControlFlowGraph procCFG = null;
if (!procName.equals("")) {
procCFG = new IntraproceduralCFG(program.getCFG(), procName);
if (procCFG.getBasicBlocks().size() == 0)
procCFG = null;
else
graphWriter.writeAssemblyBasicBlockGraph(procCFG, baseFileName + "_" + procName + "_asmcfg");
}
logger.error("Reconstructing VPC CFG");
VpcCfgReconstruction vcfgRec = new VpcCfgReconstruction(cfr.getART());
vcfgRec.run();
if (procCFG != null) {
procCFG = new IntraproceduralCFG(vcfgRec.getTransformedCfg(), procName);
if (Options.simplifyVCFG.getValue() > 0) {
DeadCodeElimination dce = new DeadCodeElimination(procCFG.getEdges(), true);
dce.run();
procCFG = new ControlFlowGraph(dce.getCFA());
}
}
//vcfgRec.simplifyCFG();
graphWriter.writeAssemblyBasicBlockGraph(vcfgRec.getTransformedCfg(), baseFileName + "_asmvcfg");
//graphWriter.writeAssemblyVCFG(baseFileName + "_asmvcfg2", vcfgRec);
if (procCFG != null) {
graphWriter.writeAssemblyBasicBlockGraph(procCFG, baseFileName + "_" + procName + "_asmvcfg");
graphWriter.writeTopologyGraph(procCFG, baseFileName + "_" + procName + "_vtopo");
}
//graphWriter.writeVpcBasicBlockGraph(baseFileName + "_vcfg", vcfgRec);
long overallEndTime = System.currentTimeMillis();
logger.error("Total runtime for reconstruction: " + String.format("%8dms", (overallEndTime - overallStartTime)));
stats.record(Options.basicBlocks.getValue() ? "y" : "n");
stats.record(Options.summarizeRep.getValue() ? "y" : "n" );
stats.record(BoundedAddressTracking.varThreshold.getValue());
stats.record(BoundedAddressTracking.heapThreshold.getValue());
stats.record(Math.round((overallEndTime - overallStartTime)/1000.0));
stats.print();
// Kills the keypress-monitor-thread.
try {
Runtime.getRuntime().removeShutdownHook(shutdownThread);
System.exit(0);
} catch (IllegalStateException e) {
// Happens when shutdown has already been initiated by Ctrl-C or Return
}
} catch (Throwable e) {
System.out.flush();
e.printStackTrace();
Runtime.getRuntime().removeShutdownHook(shutdownThread);
// Kills eclipse shutdown thread
System.exit(1);
}
}
private static void runAlgorithm(Algorithm a) {
activeAlgorithm = a;
a.run();
activeAlgorithm = null;
}
@SuppressWarnings("unused")
private static final void appendToFile(String filename, String text) {
try {
FileWriter statsFile = new FileWriter(filename, true);
statsFile.append(text);
statsFile.close();
} catch (Exception e) {
logger.error("Cannot write to outputfile!", e);
}
}
private static String getBaseFileName(File file) {
String baseFileName = file.getAbsolutePath();
// Get name of the analyzed file without file extension if it has one
if (file.getName().contains(".")) {
int dotIndex = file.getPath().lastIndexOf('.');
if (dotIndex > 0) {
baseFileName = file.getPath().substring(0, dotIndex);
}
}
return baseFileName;
}
}