/** * Get the statement at a specific label. If there is no statement stored, attempts to disassemble * the instruction at the label's virtual address. If the address is outside of the file area, * logs an error and returns a Halt statement by default. * * @param label The label for which to get the statement * @return The statement object at label. */ public final RTLStatement getStatement(RTLLabel label) { if (!statementMap.containsKey(label)) { AbsoluteAddress address = label.getAddress(); Instruction instr = getInstruction(address); // If we did not get an instruction, add an artificial Halt for recovery if (instr == null) { RTLHalt halt = new RTLHalt(); halt.setLabel(label); putStatement(halt); logger.error("ERROR: Replacing unknown instruction with HALT."); if (Options.debug.getValue()) throw new DisassemblyException("Disassembly failed at " + address); } else { try { StatementSequence seq = arch.getRTLEquivalent(address, instr); for (RTLStatement s : seq) { putStatement(s); } } catch (Exception e) { logger.error("Error during translation of instruction to IL"); e.printStackTrace(); RTLStatement skip = new RTLSkip(); skip.setLabel(label); skip.setNextLabel(new RTLLabel(new AbsoluteAddress(address.getValue() + 1))); putStatement(skip); } assert statementMap.containsKey(label) : "Disassembly did not produce label: " + label; } } return statementMap.get(label); }
public SymbolEntry(BinaryInputBuffer inBuf, Map<Integer, String> stringTable) throws IOException { // Parse name byte[] nameArray = new byte[8]; inBuf.read(nameArray); // If first 4 bytes are 0, the next 4 are an offset into the string table if ((nameArray[0] == 0) && (nameArray[1] == 0) && (nameArray[2] == 0) && (nameArray[3] == 0)) { int stringOffset = ((nameArray[7] & 0xFF) << 24) | ((nameArray[6] & 0xFF) << 16) | ((nameArray[5] & 0xFF) << 8) | (nameArray[4] & 0xFF); String nameString = stringTable.get(stringOffset); if (nameString != null) { Name = nameString; } else { logger.warn("No name in string table for symbol at specified offset " + stringOffset); Name = "Anonymous(" + stringOffset + ")"; } } else { StringBuilder nBuilder = new StringBuilder(); for (int i = 0; i < 8; i++) { if ((nameArray[i] & 0xFF) > 32 && (nameArray[i] & 0xFF) < 128) nBuilder.append((char) (nameArray[i] & 0xFF)); } Name = nBuilder.toString(); } Value = inBuf.readDWORD(); int section = inBuf.readWORD(); if (section >= 32768) SectionNumber = section - 65536; else SectionNumber = section; Type = inBuf.readWORD(); StorageClass = inBuf.readBYTE(); NumberOfAuxSymbols = inBuf.readBYTE(); }
/** * Stores a statement in the program. If a statement already exists with the same label, it is * replaced. * * @param stmt The statement to be stored. Has to contain a proper label. */ public final void putStatement(RTLStatement stmt) { RTLStatement existing = statementMap.get(stmt.getLabel()); if (existing != null) { if (existing.equals(stmt)) return; logger.debug("Replacing statement at " + stmt.getLabel()); } statementMap.put(stmt.getLabel(), stmt); }
public AbsoluteAddress getAddressForSymbol(String symbol) { for (ExecutableImage module : modules) { AbsoluteAddress a = module.getSymbolFinder().getAddressFor(symbol); if (a != null) return a; } logger.error("Could not find address for symbol \"" + symbol + "\""); return null; }
/** Resolves symbols between the loaded modules. */ private void resolveSymbols() { Iterator<UnresolvedSymbol> sIter = unresolvedSymbols.iterator(); while (sIter.hasNext()) { UnresolvedSymbol unresolvedSymbol = sIter.next(); ExportedSymbol symbol = exportedSymbols.get(removeDecoration(unresolvedSymbol.getName())); if (symbol != null) { logger.debug("Resolving symbol " + unresolvedSymbol.getName()); unresolvedSymbol.resolve(symbol.getAddress()); sIter.remove(); } } }
/** * Gets the assembly instruction at the specified virtual address. * * @param address a virtual address * @return the assembly instruction at the specified address */ public final Instruction getInstruction(AbsoluteAddress address) { Instruction instr = assemblyMap.get(address); if (instr != null) { return instr; } else { // No real instructions in prologue/epilogue if (harness.contains(address) || isStub(address)) return null; ExecutableImage module = getModule(address); long fp = -1; if (module == null) { logger.error("No module for address " + address + ". Cannot disassemble instruction!"); } else { fp = module.getFilePointer(address); // Also check whether fp is out of the int range, since the X86Disassembler actually // performs this cast in its implementation. if (fp < 0 || (int) fp < 0) { logger.error("Requested instruction outside of file area: " + address); } else { if (!module.isCodeArea(address)) { logger.error("Requested instruction outside code section: " + address); return null; } instr = module.getDisassembler().decodeInstruction(fp); if (instr == null) { logger.error("Instruction could not be disassembled at: " + address); } } } if (instr != null) putInstruction(address, instr); return instr; } }
public class ReverseCFATransformerFactory implements StateTransformerFactory { @SuppressWarnings("unused") private static final Logger logger = Logger.getLogger(ReverseCFATransformerFactory.class); private SetMultimap<Location, CFAEdge> reverseCFA; private Location sink; public ReverseCFATransformerFactory(Set<CFAEdge> cfa) { reverseCFA = HashMultimap.create(); Set<Location> nonSinks = new HashSet<Location>(); for (CFAEdge e : cfa) { reverseCFA.put(e.getTarget(), e); nonSinks.add(e.getSource()); } FastSet<Location> sinks = new FastSet<Location>(); for (Location l : reverseCFA.keySet()) { if (!nonSinks.contains(l)) { sinks.add(l); } } if (sinks.size() == 1) { sink = sinks.pick(); } else if (sinks.size() == 0) { throw new RuntimeException("CFA has no sink!"); } else { // Generate artificial exit node sink = new Location(new AbsoluteAddress(0xFFFFFF01L)); for (Location l : sinks) { reverseCFA.put(sink, new CFAEdge(l, sink, new RTLSkip())); } } } @Override public Set<CFAEdge> getTransformers(AbstractState a) { return reverseCFA.get(a.getLocation()); } @Override public Location getInitialLocation() { return sink; } }
/** For all unresolved symbols, install simple stubs. */ public void installStubs() { if (mainModule instanceof AbstractCOFFModule) { stubLibrary = new Win32StubLibrary(arch); } else if (mainModule instanceof ELFModule) { stubLibrary = new LinuxStubLibrary(arch); } Iterator<UnresolvedSymbol> sIter = unresolvedSymbols.iterator(); while (sIter.hasNext()) { UnresolvedSymbol unresolvedSymbol = sIter.next(); AbsoluteAddress address = stubLibrary.resolveSymbol(unresolvedSymbol.getFromLibrary(), unresolvedSymbol.getName()); if (address != null) { // logger.debug("Installing stack height stub for " + unresolvedSymbol.getName()); unresolvedSymbol.resolve(address); sIter.remove(); } } if (!unresolvedSymbols.isEmpty()) logger.warn("Unresolved symbols remaining: " + unresolvedSymbols); }
@Override public RTLNumber readMemoryLocation(RTLMemoryLocation m) throws IOException { if (!(m.getAddress() instanceof RTLNumber)) return null; AbsoluteAddress va = new AbsoluteAddress((RTLNumber) m.getAddress()); long fp = getFilePointer(va); if (getSectionNumber(fp) >= 0) { assert m.getBitWidth() % 8 == 0 : "Non-byte-aligned memory reference!"; long val = 0; int bytes = m.getBitWidth() / 8; // OR together the least significant bytes inBuf.seek(fp); for (int i = 0; i < bytes - 1; i++) { val = val | ((long) inBuf.readBYTE()) << (i * 8); } // do not mask the MSB with 0xFF, so we get sign extension for free val = val | (((long) inBuf.readINT8()) << (bytes - 1) * 8); // logger.debug("Read constant value " + val + " from address " + m + " (file offset: " + // Long.toHexString(fp) + ") in image."); return ExpressionFactory.createNumber(val, m.getBitWidth()); } logger.debug("No value can be read from image for address " + m); return null; }
@Override public void run() { Runtime runtime = Runtime.getRuntime(); // Jakstab Algorithm System.out.println("Starting CPA algorithm."); AbstractState start = cpa.initStartState(transformerFactory.getInitialLocation()); worklist.add(start); reached.add(start); if (art != null) art.setRoot(start); // Set up precisions Precision precision = cpa.initPrecision(transformerFactory.getInitialLocation(), null); Map<Location, Precision> precisionMap = new HashMap<Location, Precision>(); precisionMap.put(start.getLocation(), precision); int steps = 0; statesVisited = 0; final int stepThreshold = 1000; long startTime = System.currentTimeMillis(); long lastSteps = 0; long lastTime = 0; while (!worklist.isEmpty() && !stop && (!failFast || isSound())) { statesVisited++; if (++steps == stepThreshold) { // Helps limit memory usage long now = System.currentTimeMillis(); System.gc(); long gcTime = System.currentTimeMillis() - now; logger.debug("Time for GC: " + gcTime + "ms"); now = System.currentTimeMillis(); long duration = Math.max(1, now - lastTime); long speed = (1000L * (statesVisited - lastSteps) / duration); // speed = Math.min(speed, 1000); logger.warn( "*** Reached " + reached.size() + " states, processed " + statesVisited + " states after " + (now - startTime) + "ms, at " + speed + " states/second" + (transformerFactory instanceof ResolvingTransformerFactory ? ", " + program.getInstructionCount() + " instructions." : ".")); logger.info( String.format( " Allocated heap memory: %.2f MByte", (runtime.totalMemory() - runtime.freeMemory()) / (1024.0 * 1024.0))); steps = 0; // StatsPlotter.plot((now - startTime) + "\t" + statesVisited // +"\t" + program.getInstructionCount() + "\t" + gcTime + "\t" // + speed); lastSteps = statesVisited; lastTime = now; if (Options.timeout.getValue() > 0 && (System.currentTimeMillis() - startTime > Options.timeout.getValue() * 1000)) { logger.error("Timeout after " + Options.timeout.getValue() + "s!"); stop = true; } } // We need the state before precision refinement for building the // ART AbstractState unadjustedState = worklist.pick(); /* * if (unadjustedState.getLocation().getAddress().toString().equals( * "0x00401189") //|| * unadjustedState.getLocation().getAddress().toString * ().equals("0x0040119b") || * unadjustedState.getLocation().getAddress * ().toString().equals("0x00401078") || * unadjustedState.getLocation( * ).getAddress().toString().equals("0x0040100a") ) * System.out.println("Debug " + * unadjustedState.getLocation().getAddress().toString()); */ precision = precisionMap.get(unadjustedState.getLocation()); Pair<AbstractState, Precision> pair = cpa.prec(unadjustedState, precision, reached); // Warning: The refined a is not stored in "reached", only used for // successor calculation AbstractState a = pair.getLeft(); /* * CompositeState a1 = (CompositeState) a; BasedNumberValuation a2 = * (BasedNumberValuation)a1.getComponent(1); if * (a2.getStore().isTop()) System.out.println("Debug TOP Value"); */ precision = pair.getRight(); precisionMap.put(a.getLocation(), precision); // logger.debug("Picked from worklist: " + a.getIdentifier()); // getTransformers() and post() might throw exceptions try { // For each outgoing edge // if (a.isTop()) // System.out.println("Debug TOP"); /* * if * (a.getLocation().getAddress().toString().equals("0x004106cd") * || * (a.getLocation().getAddress().toString().equals("0x00410498") * && a.getLocation().getIndex() >= 0)) * System.out.println("Debug Transformer Factory:" + * a.getLocation().getAddress().toString()); */ // Set<CFAEdge> s = transformerFactory.getTransformers(a); // System.out.println("Debug Transformer Factory:" + // a.getLocation().getAddress().toString()); for (CFAEdge cfaEdge : transformerFactory.getTransformers(a)) { Precision targetPrecision = precisionMap.get(cfaEdge.getTarget()); if (targetPrecision == null) { targetPrecision = cpa.initPrecision(cfaEdge.getTarget(), cfaEdge.getTransformer()); precisionMap.put(cfaEdge.getTarget(), targetPrecision); } // Prefix everything by current location for easier // debugging // Logger.setGlobalPrefix(cfaEdge.getSource().toString()); // if // (cfaEdge.getSource().getAddress().toString().equals("0x00401027") // && // cfaEdge.getTarget().getAddress().toString().equals("0x0040102e")) // System.out.println("Debug Edge"); // Calculate the set of abstract successors Set<AbstractState> successors = cpa.post(a, cfaEdge, targetPrecision); if (successors.isEmpty()) { logger.debug("No successors along edge " + cfaEdge + ", reached halt?"); continue; } // logger.debug("via edge " + cfaEdge.toString() + " " + // successors.size() + " successors."); // Process every successor for (AbstractState succ : successors) { // logger.debug("Processing new post state: " + // succ.getIdentifier()); // Try to merge the new state with an existing one Set<AbstractState> statesToRemove = new FastSet<AbstractState>(); Set<AbstractState> statesToAdd = new FastSet<AbstractState>(); for (AbstractState r : reached.where(0, ((CompositeState) succ).getComponent(0))) { AbstractState merged = cpa.merge(succ, r, targetPrecision); if (!merged.equals(r)) { // logger.debug("Merge of " + // succ.getIdentifier() + " and " + // r.getIdentifier() + " produced new state " + // merged.getIdentifier()); statesToRemove.add(r); statesToAdd.add(merged); } } // replace the old state in worklist and reached with // the merged version for (AbstractState r : statesToRemove) { reached.remove(r); worklist.remove(r); // art.remove(r); } for (AbstractState r : statesToAdd) { // Only add r to the worklist if it hasn't been // reached yet if (reached.add(r)) { worklist.add(r); if (art != null) art.addChild(unadjustedState, r); } } // if not stopped add to worklist if (!cpa.stop(succ, reached, targetPrecision) || this.program.checkSMPos(((CompositeState) succ).getLocation().getAddress())) { worklist.add(succ); reached.add(succ); if (art != null) art.addChild(unadjustedState, succ); } } // end for each outgoing edge } } catch (StateException e) { if (e.getState() == null) { e.setState(a); } if (art != null && !unadjustedState.equals(e.getState())) art.addChild(unadjustedState, e.getState()); throw e; } } long endTime = System.currentTimeMillis(); if (endTime - startTime > 0) { logger.info( "Processed " + statesVisited + " states at " + (1000L * statesVisited / (endTime - startTime)) + " states/second"); logger.info( String.format( "Allocated heap memory: %.2f MByte", (runtime.totalMemory() - runtime.freeMemory()) / (1024.0 * 1024.0))); } completed = worklist.isEmpty(); }
/** @author Johannes Kinder */ @SuppressWarnings("unused") class SymbolEntry { private static final Logger logger = Logger.getLogger(SymbolEntry.class); // Storage classes used by MS compilers private static final int IMAGE_SYM_CLASS_EXTERNAL = 2; private static final int IMAGE_SYM_CLASS_STATIC = 3; private static final int IMAGE_SYM_CLASS_LABEL = 6; private static final int IMAGE_SYM_CLASS_FUNCTION = 101; private static final int IMAGE_SYM_CLASS_FILE = 103; private final String Name; private final long Value; private final int SectionNumber; private final int Type; private final int StorageClass; private final int NumberOfAuxSymbols; public SymbolEntry(BinaryInputBuffer inBuf, Map<Integer, String> stringTable) throws IOException { // Parse name byte[] nameArray = new byte[8]; inBuf.read(nameArray); // If first 4 bytes are 0, the next 4 are an offset into the string table if ((nameArray[0] == 0) && (nameArray[1] == 0) && (nameArray[2] == 0) && (nameArray[3] == 0)) { int stringOffset = ((nameArray[7] & 0xFF) << 24) | ((nameArray[6] & 0xFF) << 16) | ((nameArray[5] & 0xFF) << 8) | (nameArray[4] & 0xFF); String nameString = stringTable.get(stringOffset); if (nameString != null) { Name = nameString; } else { logger.warn("No name in string table for symbol at specified offset " + stringOffset); Name = "Anonymous(" + stringOffset + ")"; } } else { StringBuilder nBuilder = new StringBuilder(); for (int i = 0; i < 8; i++) { if ((nameArray[i] & 0xFF) > 32 && (nameArray[i] & 0xFF) < 128) nBuilder.append((char) (nameArray[i] & 0xFF)); } Name = nBuilder.toString(); } Value = inBuf.readDWORD(); int section = inBuf.readWORD(); if (section >= 32768) SectionNumber = section - 65536; else SectionNumber = section; Type = inBuf.readWORD(); StorageClass = inBuf.readBYTE(); NumberOfAuxSymbols = inBuf.readBYTE(); } public String getName() { return Name; } public long getValue() { return Value; } public int getNumberOfAuxSymbols() { return NumberOfAuxSymbols; } public int getStorageClass() { return StorageClass; } /** * Does the symbol refer to a global variable or external function? * * @return true if the symbol is a global variable or external function, false otherwise. */ public boolean isExternal() { return StorageClass == IMAGE_SYM_CLASS_EXTERNAL; } /** * Does the symbol refer to a static value, i.e. a string literal or a jump table? * * @return true, if the value field refers to the offset of the value relative to it's section * start, false if it is something else. */ public boolean isStatic() { return StorageClass == IMAGE_SYM_CLASS_STATIC; } /** * Does the symbol refer to a jump label? If true, the value field contains the offset of the jump * target relative to the section. * * @return true if the symbol points to a jump label, false otherwise. */ public boolean isLabel() { return StorageClass == IMAGE_SYM_CLASS_LABEL; } public int getSectionNumber() { return SectionNumber; } @Override public String toString() { return "Name: " + Name + " Storage class: " + StorageClass + " Section number: " + SectionNumber + " Value: " + Long.toHexString(Value); } }
/** * There is one singleton Program object for all modules currently under analysis. It stores all * non-analysis information about the analyzed programs, including statements, the current control * flow graph, and symbols. * * @author Johannes Kinder */ public final class Program { private static final Logger logger = Logger.getLogger(Program.class); private static Program programInstance; /** * Get the singleton Program object. * * @return the singleton instance of the Program class */ public static Program getProgram() { return programInstance; } /** * Initially creates the Program object. * * @param arch An Architecture object with architecture specific information * @return the new singleton instance of the Program class */ public static Program createProgram(Architecture arch) { // assert programInstance == null; programInstance = new Program(arch); return programInstance; } private final Architecture arch; private RTLLabel start; private Map<RTLLabel, RTLStatement> statementMap; private Map<AbsoluteAddress, Instruction> assemblyMap; private ExecutableImage mainModule; private List<ExecutableImage> modules; private ControlFlowGraph cfg; private final Map<String, ExportedSymbol> exportedSymbols; private final Set<UnresolvedSymbol> unresolvedSymbols; private Set<RTLLabel> unresolvedBranches; private StubProvider stubLibrary; private Harness harness; public enum TargetOS { WINDOWS, LINUX, UNKNOWN }; private TargetOS targetOS; private Program(Architecture arch) { this.arch = arch; this.targetOS = TargetOS.UNKNOWN; modules = new LinkedList<ExecutableImage>(); assemblyMap = new TreeMap<AbsoluteAddress, Instruction>(); statementMap = new HashMap<RTLLabel, RTLStatement>(2000); exportedSymbols = new HashMap<String, ExportedSymbol>(); unresolvedSymbols = new FastSet<UnresolvedSymbol>(); unresolvedBranches = new FastSet<RTLLabel>(); } /** * Loads the module containing the main function. This function should be called last for correct * symbol resolution. * * @param moduleFile the file to load * @return the ExecutableImage class for the loaded module * @throws IOException * @throws BinaryParseException */ public ExecutableImage loadMainModule(File moduleFile) throws IOException, BinaryParseException { ExecutableImage module = loadModule(moduleFile); mainModule = module; setEntryAddress(module.getEntryPoint()); installStubs(); return module; } /** * Loads a secondary (library or stub) module for analysis. Automatically determines the correct * file type. * * @param moduleFile the file to load * @return the ExecutableImage class for the loaded module * @throws IOException * @throws BinaryParseException */ public ExecutableImage loadModule(File moduleFile) throws IOException, BinaryParseException { // First try to load it as a PE file, then object file, ELF and finally raw binary code // The right thing to do would be some smart IDing of the file type, but // this exception chaining works for now... ExecutableImage module = null; try { module = new PEModule(moduleFile, getArchitecture()); targetOS = TargetOS.WINDOWS; } catch (BinaryParseException e) { try { module = new ObjectFile(moduleFile, getArchitecture()); } catch (BinaryParseException e2) { try { module = new ELFModule(moduleFile, getArchitecture()); targetOS = TargetOS.LINUX; } catch (BinaryParseException e3) { module = new RawModule(moduleFile, getArchitecture()); } } } for (ExecutableImage existingModule : modules) { if (existingModule.getMaxAddress().getValue() >= module.getMinAddress().getValue() && existingModule.getMinAddress().getValue() <= module.getMaxAddress().getValue()) { throw new RuntimeException("Virtual addresses of modules overlap!"); } } modules.add(module); unresolvedSymbols.addAll(module.getUnresolvedSymbols()); for (ExportedSymbol symbol : module.getExportedSymbols()) { exportedSymbols.put(removeDecoration(symbol.getName()), symbol); } resolveSymbols(); return module; } private String removeDecoration(String s) { if (s.charAt(0) == '@' || s.charAt(0) == '_') s = s.substring(1); int i = s.indexOf('@'); if (i >= 0) s = s.substring(0, i); return s; } /** Resolves symbols between the loaded modules. */ private void resolveSymbols() { Iterator<UnresolvedSymbol> sIter = unresolvedSymbols.iterator(); while (sIter.hasNext()) { UnresolvedSymbol unresolvedSymbol = sIter.next(); ExportedSymbol symbol = exportedSymbols.get(removeDecoration(unresolvedSymbol.getName())); if (symbol != null) { logger.debug("Resolving symbol " + unresolvedSymbol.getName()); unresolvedSymbol.resolve(symbol.getAddress()); sIter.remove(); } } } /** * Returns the address of the given procedure within the given library. Procedures present within * the analyzed modules are given precedence over stub functions. * * @param library * @param procedure * @return the virtual address of the procedure */ public AbsoluteAddress getProcAddress(String library, String procedure) { ExportedSymbol expSymbol = exportedSymbols.get(procedure); if (expSymbol != null) { return expSymbol.getAddress(); } else { return stubLibrary.resolveSymbol(library, procedure); } } public boolean isStub(AbsoluteAddress a) { return a.getValue() >= StubProvider.STUB_BASE; } public boolean isImport(AbsoluteAddress a) { if (isStub(a)) return true; ExecutableImage m = getModule(a); if (m == null) return false; return m.isImportArea(a); } /** For all unresolved symbols, install simple stubs. */ public void installStubs() { if (mainModule instanceof AbstractCOFFModule) { stubLibrary = new Win32StubLibrary(arch); } else if (mainModule instanceof ELFModule) { stubLibrary = new LinuxStubLibrary(arch); } Iterator<UnresolvedSymbol> sIter = unresolvedSymbols.iterator(); while (sIter.hasNext()) { UnresolvedSymbol unresolvedSymbol = sIter.next(); AbsoluteAddress address = stubLibrary.resolveSymbol(unresolvedSymbol.getFromLibrary(), unresolvedSymbol.getName()); if (address != null) { // logger.debug("Installing stack height stub for " + unresolvedSymbol.getName()); unresolvedSymbol.resolve(address); sIter.remove(); } } if (!unresolvedSymbols.isEmpty()) logger.warn("Unresolved symbols remaining: " + unresolvedSymbols); } /** * Install a harness that sets up the symbolic environment before calling main and provides a * return point with a termination statement. * * @param harness the harness object to install */ public void installHarness(Harness harness) { this.harness = harness; harness.install(this); } /** * Set the program entry point to the given label. * * @param label the new entry point */ public void setStart(RTLLabel label) { this.start = label; } /** * Set the program entry point to the given address. * * @param entryAddress the new entry address */ public void setEntryAddress(AbsoluteAddress entryAddress) { setStart(new RTLLabel(entryAddress)); } /** * Get the main module. * * @return the main module */ public ExecutableImage getMainModule() { return mainModule; } /** * Get the module that contains the specified virtual address at runtime. * * @param a a virtual address * @return the module to which the given virtual address belongs. */ public ExecutableImage getModule(AbsoluteAddress a) { for (ExecutableImage module : modules) { if (module.getFilePointer(a) >= 0) return module; } return null; } public Iterator<AbsoluteAddress> codeAddressIterator() { return getMainModule().codeBytesIterator(); } /** * Get the statement at a specific label. If there is no statement stored, attempts to disassemble * the instruction at the label's virtual address. If the address is outside of the file area, * logs an error and returns a Halt statement by default. * * @param label The label for which to get the statement * @return The statement object at label. */ public final RTLStatement getStatement(RTLLabel label) { if (!statementMap.containsKey(label)) { AbsoluteAddress address = label.getAddress(); Instruction instr = getInstruction(address); // If we did not get an instruction, add an artificial Halt for recovery if (instr == null) { RTLHalt halt = new RTLHalt(); halt.setLabel(label); putStatement(halt); logger.error("ERROR: Replacing unknown instruction with HALT."); if (Options.debug.getValue()) throw new DisassemblyException("Disassembly failed at " + address); } else { try { StatementSequence seq = arch.getRTLEquivalent(address, instr); for (RTLStatement s : seq) { putStatement(s); } } catch (Exception e) { logger.error("Error during translation of instruction to IL"); e.printStackTrace(); RTLStatement skip = new RTLSkip(); skip.setLabel(label); skip.setNextLabel(new RTLLabel(new AbsoluteAddress(address.getValue() + 1))); putStatement(skip); } assert statementMap.containsKey(label) : "Disassembly did not produce label: " + label; } } return statementMap.get(label); } /** * Stores a statement in the program. If a statement already exists with the same label, it is * replaced. * * @param stmt The statement to be stored. Has to contain a proper label. */ public final void putStatement(RTLStatement stmt) { RTLStatement existing = statementMap.get(stmt.getLabel()); if (existing != null) { if (existing.equals(stmt)) return; logger.debug("Replacing statement at " + stmt.getLabel()); } statementMap.put(stmt.getLabel(), stmt); } public boolean containsLabel(RTLLabel label) { return statementMap.containsKey(label); } public final int getStatementCount() { return statementMap.size(); } public final int getInstructionCount() { return assemblyMap.size(); } public Harness getHarness() { return harness; } /** * Gets the assembly instruction at the specified virtual address. * * @param address a virtual address * @return the assembly instruction at the specified address */ public final Instruction getInstruction(AbsoluteAddress address) { Instruction instr = assemblyMap.get(address); if (instr != null) { return instr; } else { // No real instructions in prologue/epilogue if (harness.contains(address) || isStub(address)) return null; ExecutableImage module = getModule(address); long fp = -1; if (module == null) { logger.error("No module for address " + address + ". Cannot disassemble instruction!"); } else { fp = module.getFilePointer(address); // Also check whether fp is out of the int range, since the X86Disassembler actually // performs this cast in its implementation. if (fp < 0 || (int) fp < 0) { logger.error("Requested instruction outside of file area: " + address); } else { if (!module.isCodeArea(address)) { logger.error("Requested instruction outside code section: " + address); return null; } instr = module.getDisassembler().decodeInstruction(fp); if (instr == null) { logger.error("Instruction could not be disassembled at: " + address); } } } if (instr != null) putInstruction(address, instr); return instr; } } /** * Stores an assembly instruction at the given address, overwriting any existing instruction. * * @param addr the virtual address to save the instruction at * @param instr the assembly instruction * @return true if there was no instruction stored for that address before, false otherwise. */ public final boolean putInstruction(AbsoluteAddress addr, Instruction instr) { // logger.info(addr + " " + instr.toString(addr.getValue(), new DummySymbolFinder())); return assemblyMap.put(addr, instr) == null; } /** * Get the string representation of the assembly instruction at the given address. * * @param addr a virtual address * @return a string representation of the assembly code at the given address */ public String getInstructionString(AbsoluteAddress addr) { Instruction instr = getInstruction(addr); if (instr == null) return "NON_EXISTENT"; return instr.toString(addr.getValue(), symbolFinder(addr)); } /** * Get the string representation of the specified assembly instruction assuming it is located at * the given address. * * @param addr a virtual address * @param instr an assembly instruction * @return a string representation of the assembly code at the given address */ public String getInstructionString(AbsoluteAddress addr, Instruction instr) { if (instr == null) return "NON_EXISTENT"; return instr.toString(addr.getValue(), symbolFinder(addr)); } public String getSymbolFor(RTLLabel label) { SymbolFinder symFinder = symbolFinder(label.getAddress()); if (symFinder.hasSymbolFor(label.getAddress())) { return symFinder.getSymbolFor(label.getAddress()); } else { return label.toString(); } } public String getSymbolFor(AbsoluteAddress addr) { return symbolFinder(addr).getSymbolFor(addr); } public AbsoluteAddress getAddressForSymbol(String symbol) { for (ExecutableImage module : modules) { AbsoluteAddress a = module.getSymbolFinder().getAddressFor(symbol); if (a != null) return a; } logger.error("Could not find address for symbol \"" + symbol + "\""); return null; } private SymbolFinder symbolFinder(AbsoluteAddress addr) { if (isStub(addr)) return stubLibrary.getSymbolFinder(); ExecutableImage module = getModule(addr); return (module == null) ? DummySymbolFinder.getInstance() : module.getSymbolFinder(); } public Set<RTLLabel> getUnresolvedBranches() { return unresolvedBranches; } public void setUnresolvedBranches(Set<RTLLabel> unresolvedBranches) { this.unresolvedBranches = unresolvedBranches; } /** @return the assemblyMap */ public final Map<AbsoluteAddress, Instruction> getAssemblyMap() { return assemblyMap; } public TargetOS getTargetOS() { return targetOS; } /** * Returns all variables used in the program. At the current state of the implementation, this * includes only registers and flags. * * @return A set containing all variables used in this program. */ public SetOfVariables getUsedVariables() { SetOfVariables result = new SetOfVariables(); for (CFAEdge edge : cfg.getEdges()) result.addAll(((RTLStatement) edge.getTransformer()).getUsedVariables()); return result; } public Architecture getArchitecture() { return arch; } public Collection<ExportedSymbol> getSymbols() { return exportedSymbols.values(); } public ControlFlowGraph getCFG() { return cfg; } public void setCFA(Set<CFAEdge> cfa) { cfg = new FineGrainedCFG(cfa); } public RTLLabel getStart() { return start; } public int countIndirectBranches() { int res = 0; for (Map.Entry<AbsoluteAddress, Instruction> entry : assemblyMap.entrySet()) { Instruction instr = entry.getValue(); if (instr instanceof BranchInstruction) { BranchInstruction branch = (BranchInstruction) instr; if (branch.isIndirect()) { // if branch target is not a memory operand pointing into a static data area of the binary // (imports) if (branch.getBranchDestination() instanceof MemoryOperand) { MemoryOperand memOp = (MemoryOperand) branch.getBranchDestination(); // Import calls have only displacement if (memOp.getBase() == null && memOp.getIndex() == null) { AbsoluteAddress disp = new AbsoluteAddress(memOp.getDisplacement()); // Check whether displacement points into import table ExecutableImage module = getModule(disp); if (module instanceof PEModule && ((PEModule) module).getImportTable().containsKey(disp)) continue; } } res++; // logger.verbose(entry.getKey() + "\t" + getInstructionString(entry.getKey())); } } } return res; } public LinkedList<BranchInstruction> getIndirectBranches() { LinkedList<BranchInstruction> indirectBranches = new LinkedList<BranchInstruction>(); for (Map.Entry<AbsoluteAddress, Instruction> entry : assemblyMap.entrySet()) { Instruction instr = entry.getValue(); if (instr instanceof BranchInstruction) { BranchInstruction branch = (BranchInstruction) instr; if (branch.isIndirect()) { indirectBranches.add(branch); } } } return indirectBranches; } }
public void stop() { logger.fatal(Characters.starredBox("Interrupt! Stopping CPA Algorithm!")); stop = true; }
/** * A reduced interval congruence analysis with regioned memory. Inspired by Codesurfer's VSA domain. * * @author Johannes Kinder */ public class IntervalAnalysis implements ConfigurableProgramAnalysis { public static void register(AnalysisProperties p) { p.setShortHand('i'); p.setName("Interval analysis"); p.setDescription("Compute strided intervals with region information."); p.setExplicit(true); } private static final Logger logger = Logger.getLogger(IntervalAnalysis.class); private AbstractValueFactory<IntervalElement> valueFactory; public IntervalAnalysis() { valueFactory = new IntervalElementFactory(); } @Override public Precision initPrecision(Location location, StateTransformer transformer) { return new IntervalPrecision(); } /* * @see org.jakstab.analysis.ConfigurableProgramAnalysis#initStartState(org.jakstab.cfa.Location) */ @Override public AbstractState initStartState(Location location) { // IntervalState init = new IntervalState(); /*init.setValue(Program.getProgram().getArchitecture().stackPointer(), new IntervalElement(MemoryRegion.STACK, 0, 0, 0, 32));*/ // return init; return new ValuationState(valueFactory); } /* * @see org.jakstab.analysis.ConfigurableProgramAnalysis#merge(org.jakstab.analysis.AbstractState, org.jakstab.analysis.AbstractState) */ @Override public AbstractState merge(AbstractState s1, AbstractState s2, Precision precision) { // Widen s2 towards s1. // return ((IntervalState)s2).widen((IntervalState)s1); if (s2.isTop() || s1.isBot()) return s2; if (s1.isTop()) return s1; ValuationState current = (ValuationState) s2; ValuationState towards = (ValuationState) s1; ValuationState widenedState = new ValuationState(valueFactory); // Widen variable valuations for (Iterator<Map.Entry<RTLVariable, AbstractDomainElement>> entryIt = current.variableIterator(); entryIt.hasNext(); ) { Map.Entry<RTLVariable, AbstractDomainElement> entry = entryIt.next(); RTLVariable var = entry.getKey(); IntervalElement v = (IntervalElement) entry.getValue(); widenedState.setVariableValue(var, v.widen((IntervalElement) towards.getVariableValue(var))); } // Widen memory for (EntryIterator<MemoryRegion, Long, AbstractDomainElement> entryIt = current.storeIterator(); entryIt.hasEntry(); entryIt.next()) { MemoryRegion region = entryIt.getLeftKey(); Long offset = entryIt.getRightKey(); IntervalElement v = (IntervalElement) entryIt.getValue(); int bitWidth = v.getBitWidth(); widenedState.setMemoryValue( region, offset, bitWidth, v.widen((IntervalElement) towards.getMemoryValue(region, offset, bitWidth))); } return widenedState; } /* * @see org.jakstab.analysis.ConfigurableProgramAnalysis#post(org.jakstab.analysis.AbstractState, org.jakstab.analysis.StateTransformer, org.jakstab.analysis.Precision) */ @Override public Set<AbstractState> post(final AbstractState state, CFAEdge cfaEdge, Precision precision) { final RTLStatement statement = (RTLStatement) cfaEdge.getTransformer(); final ValuationState iState = (ValuationState) state; return Collections.singleton( statement.accept( new DefaultStatementVisitor<AbstractState>() { @Override protected AbstractState visitDefault(RTLStatement stmt) { return state; } @Override public AbstractState visit(RTLVariableAssignment stmt) { ValuationState post = new ValuationState(iState); Writable lhs = stmt.getLeftHandSide(); RTLExpression rhs = stmt.getRightHandSide(); AbstractDomainElement evaledRhs = iState.abstractEval(rhs); // Check for stackpointer alignment assignments (workaround for gcc compiled files) RTLVariable sp = Program.getProgram().getArchitecture().stackPointer(); if (lhs.equals(sp) && rhs instanceof RTLOperation) { RTLOperation op = (RTLOperation) rhs; if (op.getOperator().equals(Operator.AND) && op.getOperands()[0].equals(sp) && op.getOperands()[1] instanceof RTLNumber) { evaledRhs = iState.getVariableValue(sp); logger.warn("Ignoring stackpointer alignment at " + stmt.getAddress()); } } post.setVariableValue((RTLVariable) lhs, evaledRhs); return post; } @Override public AbstractState visit(RTLMemoryAssignment stmt) { ValuationState post = new ValuationState(iState); RTLMemoryLocation m = stmt.getLeftHandSide(); RTLExpression rhs = stmt.getRightHandSide(); AbstractDomainElement evaledRhs = iState.abstractEval(rhs); AbstractDomainElement evaledAddress = iState.abstractEval(m.getAddress()); post.setMemoryValue(evaledAddress, m.getBitWidth(), evaledRhs); return post; } @Override public AbstractState visit(RTLAssume stmt) { ValuationState post = new ValuationState(iState); RTLExpression assumption = stmt.getAssumption(); // TODO: implement assume if (assumption instanceof RTLOperation) { RTLOperation op = (RTLOperation) assumption; switch (op.getOperator()) { case UNSIGNED_LESS_OR_EQUAL: RTLExpression lhs = op.getOperands()[0]; RTLExpression rhs = op.getOperands()[1]; IntervalElement evaledLhs = (IntervalElement) iState.abstractEval(lhs); IntervalElement evaledRhs = (IntervalElement) iState.abstractEval(rhs); if (evaledRhs.getLeft() >= 0) { IntervalElement uLessInt = new IntervalElement( evaledRhs.getRegion(), 0, evaledRhs.getRight(), 1, evaledLhs.getBitWidth()); // TODO: Implement meet for interval elements for optimal result // uLessInt = uLessInt.meet(evaledLhs); // if uLessInt.isBot() return Collections.emptySet(); // cheap but sound solution for now: only use new interval if it has less // elements if (uLessInt.size() < evaledLhs.size()) { if (lhs instanceof RTLVariable) { post.setVariableValue((RTLVariable) lhs, uLessInt); } else if (lhs instanceof RTLMemoryLocation) { RTLMemoryLocation m = (RTLMemoryLocation) lhs; AbstractDomainElement evaledAddress = iState.abstractEval(m.getAddress()); post.setMemoryValue(evaledAddress, m.getBitWidth(), uLessInt); } } } break; default: // nothing } } return post; } @Override public AbstractState visit(RTLAlloc stmt) { ValuationState post = new ValuationState(iState); Writable lhs = stmt.getPointer(); MemoryRegion newRegion; if (stmt.getAllocationName() != null) { newRegion = MemoryRegion.create(stmt.getAllocationName()); } else { // TODO: Detect whether this allocation is unique to allow strong updates newRegion = MemoryRegion.createAsSummary("alloc" + stmt.getLabel()); } IntervalElement basePointer = new IntervalElement(newRegion, ExpressionFactory.createNumber(0, 32)); if (lhs instanceof RTLVariable) { post.setVariableValue((RTLVariable) lhs, basePointer); } else { RTLMemoryLocation m = (RTLMemoryLocation) lhs; AbstractDomainElement evaledAddress = iState.abstractEval(m.getAddress()); post.setMemoryValue(evaledAddress, m.getBitWidth(), basePointer); } return post; } @Override public AbstractState visit(RTLHavoc stmt) { ValuationState post = new ValuationState(iState); // Only create a single state with the havoc range, since this analysis // is not path sensitive post.setVariableValue( stmt.getVariable(), // new IntervalElement(ExpressionFactory.getInstance().createNumber(0, // stmt.getVariable().getBitWidth()), // (RTLNumber)stmt.getMaximum())); new IntervalElement( MemoryRegion.GLOBAL, 0, ((RTLNumber) stmt.getMaximum()).longValue(), 1, stmt.getVariable().getBitWidth())); return post; } @Override public AbstractState visit(RTLUnknownProcedureCall stmt) { ValuationState post = new ValuationState(iState); for (RTLVariable var : stmt.getDefinedVariables()) { post.setVariableValue(var, IntervalElement.getTop(var.getBitWidth())); } post.setMemoryValue( IntervalElement.getTop( Program.getProgram().getArchitecture().getAddressBitWidth()), 32, IntervalElement.getTop(32)); return post; } })); } @Override public AbstractState strengthen( AbstractState s, Iterable<AbstractState> otherStates, CFAEdge cfaEdge, Precision precision) { ValuationState state = (ValuationState) s; ValuationState strengthenedState = null; for (AbstractState t : otherStates) { // TODO: Does not work correctly if BoundedAddressTracking returns more than // one successor state. if (t instanceof BasedNumberValuation) { BasedNumberValuation exState = (BasedNumberValuation) t; for (Map.Entry<RTLVariable, BasedNumberElement> entry : exState.getVariableValuation()) { RTLVariable var = entry.getKey(); BasedNumberElement exVal = entry.getValue(); if (exVal.isTop() || exVal.isNumberTop()) continue; if (state.getVariableValue(var).isTop()) { if (strengthenedState == null) { strengthenedState = new ValuationState(state); } strengthenedState.setVariableValue( var, new IntervalElement(exVal.getRegion(), exVal.getNumber())); // logger.debug("Strengthened state " + state.getIdentifier() + // " by setting " + var + " to " + state.getValue(var)); } } } } return strengthenedState == null ? state : strengthenedState; } @Override public Pair<AbstractState, Precision> prec( AbstractState s, Precision precision, ReachedSet reached) { return Pair.create(s, precision); } @Override public boolean stop(AbstractState s, ReachedSet reached, Precision precision) { return CPAOperators.stopJoin(s, reached, precision); } private RTLExpression addClause(RTLExpression formula, RTLExpression clause) { if (formula != null) { return ExpressionFactory.createAnd(formula, clause); } else { return clause; } } public RTLExpression getStateFormula(ValuationState state) { RTLExpression result = null; for (Iterator<Map.Entry<RTLVariable, AbstractDomainElement>> entryIt = state.variableIterator(); entryIt.hasNext(); ) { Map.Entry<RTLVariable, AbstractDomainElement> entry = entryIt.next(); RTLVariable var = entry.getKey(); IntervalElement interval = (IntervalElement) entry.getValue(); if (interval.size() == 1) { result = addClause( result, ExpressionFactory.createEqual( var, ExpressionFactory.createNumber(interval.getLeft(), var.getBitWidth()))); } else { if (!interval.leftOpen()) { result = addClause( result, ExpressionFactory.createLessOrEqual( ExpressionFactory.createNumber(interval.getLeft(), var.getBitWidth()), var)); } if (!interval.rightOpen()) { result = addClause( result, ExpressionFactory.createLessOrEqual( var, ExpressionFactory.createNumber(interval.getRight(), var.getBitWidth()))); } } } if (result == null) { result = ExpressionFactory.TRUE; } return result; } }
/** * An abstract class that encapsulates the common features of MS COFF and PE files. * * @author Johannes Kinder */ public abstract class AbstractCOFFModule implements ExecutableImage { @SuppressWarnings("unused") private static final Logger logger = Logger.getLogger(AbstractCOFFModule.class); protected BinaryFileInputBuffer inBuf; protected COFF_Header coff_header; protected SectionHeader[] section_headers; protected Disassembler disassembler; @Override public final long getFilePointer(AbsoluteAddress va) { long fp = getFilePointerFromRVA(va.getValue() - getBaseAddress()); if (fp >= 0) return fp; else return -1; } @Override public final boolean isCodeArea(AbsoluteAddress va) { int section = getSectionNumber(va); if (section < 0) return false; else return isCodeSection(section); } /** Returns the file pointer equivalent of the given RVA. */ protected final long getFilePointerFromRVA(long rva) { int sct = getSectionNumberByRVA(rva); if (sct < 0) return -1; if (rva - getSectionHeader(sct).VirtualAddress > getSectionHeader(sct).SizeOfRawData) return -1; return (rva - getSectionHeader(sct).VirtualAddress) + getSectionHeader(sct).PointerToRawData; } /** Returns the RVA for a given file pointer. */ protected final long getRVAFromFilePointer(long filePointer) { int sct = getSectionNumber(filePointer); if (sct < 0) return -1; return ((filePointer - getSectionHeader(sct).PointerToRawData) + getSectionHeader(sct).VirtualAddress); } /** * Returns the number of the section the given virtual address is in. * * @param va the virtual address * @return the section number */ protected final int getSectionNumber(AbsoluteAddress va) { return getSectionNumberByRVA(va.getValue() - getBaseAddress()); } /** * Returns the number of the section a given file pointer lies in. * * @param fp the file pointer * @return the section number */ protected final int getSectionNumber(long fp) { for (int i = 0; i < getNumberOfSections(); i++) if (getSectionHeader(i).PointerToRawData <= fp && (getSectionHeader(i).PointerToRawData + getSectionHeader(i).SizeOfRawData) > fp) return i; return -1; } /** * Returns the number of the section a given RVA lies in. * * @param rva the Relative Virtual Address * @return the section number */ protected abstract int getSectionNumberByRVA(long rva); /** * Returns the virtual address that corresponds to a given file pointer * * @param fp the file pointer * @return the virtual address */ public final AbsoluteAddress getVirtualAddress(long fp) { long rva = getRVAFromFilePointer(fp); if (rva >= 0) return new AbsoluteAddress(rva + getBaseAddress()); else return null; } protected final int getNumberOfSections() { return section_headers.length; } public AbsoluteAddress getMaxAddress() { long highAddress = Long.MIN_VALUE; for (int i = 0; i < getNumberOfSections(); i++) { highAddress = Math.max( getSectionHeader(i).VirtualAddress + getSectionHeader(i).SizeOfRawData, highAddress); } highAddress += getBaseAddress(); return new AbsoluteAddress(highAddress); } public AbsoluteAddress getMinAddress() { long lowAddress = Long.MAX_VALUE; for (int i = 0; i < getNumberOfSections(); i++) { lowAddress = Math.min(getSectionHeader(i).VirtualAddress, lowAddress); } lowAddress += getBaseAddress(); return new AbsoluteAddress(lowAddress); } protected final SectionHeader getSectionHeader(int index) { return section_headers[index]; } protected final boolean isCodeSection(int section) { return getSectionHeader(section).isCodeSection(); } protected abstract long getBaseAddress(); @Override public RTLNumber readMemoryLocation(RTLMemoryLocation m) throws IOException { if (!(m.getAddress() instanceof RTLNumber)) return null; AbsoluteAddress va = new AbsoluteAddress((RTLNumber) m.getAddress()); long fp = getFilePointer(va); if (getSectionNumber(fp) >= 0) { assert m.getBitWidth() % 8 == 0 : "Non-byte-aligned memory reference!"; long val = 0; int bytes = m.getBitWidth() / 8; // OR together the least significant bytes inBuf.seek(fp); for (int i = 0; i < bytes - 1; i++) { val = val | ((long) inBuf.readBYTE()) << (i * 8); } // do not mask the MSB with 0xFF, so we get sign extension for free val = val | (((long) inBuf.readINT8()) << (bytes - 1) * 8); // logger.debug("Read constant value " + val + " from address " + m + " (file offset: " + // Long.toHexString(fp) + ") in image."); return ExpressionFactory.createNumber(val, m.getBitWidth()); } logger.debug("No value can be read from image for address " + m); return null; } public byte[] getByteArray() { return inBuf.getByteArray(); } @Override public Iterator<AbsoluteAddress> codeBytesIterator() { return new Iterator<AbsoluteAddress>() { long fp = 0; int sec = -1; { moveToNextCodeSection(); } private void moveToNextCodeSection() { sec++; while (sec < getNumberOfSections() && !isCodeSection(sec)) { sec++; } if (sec >= getNumberOfSections()) { fp = -1; sec = -1; } else { fp = getSectionHeader(sec).PointerToRawData; } } private void moveToNextCodeByte() { fp++; if (fp >= getSectionHeader(sec).PointerToRawData + getSectionHeader(sec).SizeOfRawData) { moveToNextCodeSection(); if (sec < 0) { return; } } } @Override public boolean hasNext() { return (fp >= 0); } @Override public AbsoluteAddress next() { if (!hasNext()) throw new IndexOutOfBoundsException(); AbsoluteAddress res = getVirtualAddress(fp); moveToNextCodeByte(); return res; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } @Override public Disassembler getDisassembler() { if (disassembler == null) { disassembler = new X86Disassembler(inBuf); } return disassembler; } }
/** * States corresponding to a line number within a pre-recorded trace. Each state stores a reference * to the trace to allow direct access to recorded PC values. */ public class TraceReplayState implements UnderApproximateState { @SuppressWarnings("unused") private static final Logger logger = Logger.getLogger(TraceReplayState.class); public static TraceReplayState BOT = new TraceReplayState(); private final AbsoluteAddress cur; private final SetMultimap<AbsoluteAddress, AbsoluteAddress> succ; private TraceReplayState() { super(); cur = new AbsoluteAddress(0xF0000B07L); succ = null; } public TraceReplayState(SetMultimap<AbsoluteAddress, AbsoluteAddress> succ, AbsoluteAddress cur) { this.succ = succ; this.cur = cur; } @Override public String getIdentifier() { return cur.toString(); } @Override public Location getLocation() { throw new UnsupportedOperationException(); } /** * Get the value of the program counter at the current state. * * @return an AbsoluteAddress corresponding to the PC value for the analyzed module. */ public AbsoluteAddress getCurrentPC() { return cur; } /** * Get the value of the program counter at the current state's successor state. * * @return an AbsoluteAddress corresponding to the next PC value for the analyzed module. */ public Set<AbsoluteAddress> getNextPC() { return succ.get(cur); } @Override public AbstractState join(LatticeElement l) { throw new UnsupportedOperationException(); } @Override public Set<Tuple<RTLNumber>> projectionFromConcretization(RTLExpression... expressions) { // Only concretize expression requests from transformerFactory // Warning: If this method is invoked with 2 parameters for other reasons, it will // likely fail! if (expressions.length != 2) return null; // If not on trace, don't concretize if (isBot()) return null; RTLExpression condition = expressions[0]; RTLExpression target = expressions[1]; RTLNumber cCondition; RTLNumber cTarget; Set<Tuple<RTLNumber>> res = new FastSet<Tuple<RTLNumber>>(); for (AbsoluteAddress successor : getNextPC()) { RTLNumber nextPC = successor.toNumericConstant(); if (target instanceof RTLNumber) { // If target is a number, this is a direct jump, and maybe conditional cTarget = (RTLNumber) target; if (condition instanceof RTLNumber) { // Direct, unconditional jump cCondition = (RTLNumber) condition; } else if (target.equals(nextPC)) { // Conditional jump that is taken according to the trace cCondition = ExpressionFactory.TRUE; } else { // Conditional jump that is not taken cCondition = ExpressionFactory.FALSE; } } else { // Target is not a number, so this is an indirect jump assert (condition instanceof RTLNumber) : "There should be no conditional indirect jumps in x86!"; cCondition = (RTLNumber) condition; cTarget = nextPC; } res.add(Tuple.create(cCondition, cTarget)); } return res; } @Override public boolean isBot() { return this == BOT; } @Override public boolean isTop() { return false; } @Override public int hashCode() { return cur.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; return cur.equals(((TraceReplayState) obj).cur); } @Override public boolean lessOrEqual(LatticeElement l) { TraceReplayState other = (TraceReplayState) l; if (other.isTop() || this.isBot()) return true; return this.equals(l); } public String toString() { if (isBot()) return "Trace@BOT"; return "Trace@" + getCurrentPC() + ": Next: " + getNextPC(); } }
/** * The main CPA worklist algorithm. * * @author Johannes Kinder */ public class CPAAlgorithm implements Algorithm { private static final Logger logger = Logger.getLogger(CPAAlgorithm.class); private final Program program; private final StateTransformerFactory transformerFactory; private final ConfigurableProgramAnalysis cpa; private final ReachedSet reached; private final AbstractReachabilityTree art; private final Worklist<AbstractState> worklist; private final boolean failFast; private long statesVisited; private boolean completed = false; private volatile boolean stop = false; /** * Instantiates a new CPA algorithm with a forward location analysis, a default forward * transformer factory and worklist suitable for an analysis of a complete and already * reconstructed control flow automaton. * * @param program The program object * @param cpas The list of analyses to be performed */ public static CPAAlgorithm createForwardAlgorithm( Program program, ConfigurableProgramAnalysis... cpas) { ConfigurableProgramAnalysis cpa = new CompositeProgramAnalysis(new LocationAnalysis(), cpas); return new CPAAlgorithm( program, cpa, new CFATransformerFactory(program.getCFA()), new FastSet<AbstractState>()); } /** * Instantiates a new CPA algorithm with a backward location analysis, a default backward * transformer factory and worklist suitable for an analysis of a complete and already * reconstructed control flow automaton. * * @param program The program object * @param cpas The list of backward analyses to be performed */ public static CPAAlgorithm createBackwardAlgorithm( Program program, ConfigurableProgramAnalysis... cpas) { ConfigurableProgramAnalysis cpa = new CompositeProgramAnalysis(new BackwardLocationAnalysis(), cpas); return new CPAAlgorithm( program, cpa, new ReverseCFATransformerFactory(program.getCFA()), new FastSet<AbstractState>()); } public CPAAlgorithm( Program program, ConfigurableProgramAnalysis cpa, StateTransformerFactory transformerFactory, Worklist<AbstractState> worklist) { this(program, cpa, transformerFactory, worklist, false); } public CPAAlgorithm( Program program, ConfigurableProgramAnalysis cpa, StateTransformerFactory transformerFactory, Worklist<AbstractState> worklist, boolean failFast) { super(); this.program = program; this.cpa = cpa; this.transformerFactory = transformerFactory; this.worklist = worklist; this.failFast = failFast; if (Options.errorTrace.getValue() || Options.asmTrace.getValue()) art = new AbstractReachabilityTree(); else art = null; reached = new ReachedSet(); this.program.setReachedSet(reached); } /** * After a run of the algorithm, returns the set of reached states. * * @return the set of reached (and kept) states. */ public ReachedSet getReachedStates() { return reached; } public AbstractReachabilityTree getART() { return art; } public long getNumberOfStatesVisited() { return statesVisited; } /** Returns whether the algorithm terminated normally. */ public boolean isCompleted() { return completed; } /** * Returns whether the algorithm had to make unsound assumptions. Always true for analyses on * complete CFAs. * * @return true if the analysis required unsound assumptions. */ public boolean isSound() { if (transformerFactory instanceof ResolvingTransformerFactory) { return ((ResolvingTransformerFactory) transformerFactory).isSound(); } else { return true; } } @Override public void run() { Runtime runtime = Runtime.getRuntime(); // Jakstab Algorithm System.out.println("Starting CPA algorithm."); AbstractState start = cpa.initStartState(transformerFactory.getInitialLocation()); worklist.add(start); reached.add(start); if (art != null) art.setRoot(start); // Set up precisions Precision precision = cpa.initPrecision(transformerFactory.getInitialLocation(), null); Map<Location, Precision> precisionMap = new HashMap<Location, Precision>(); precisionMap.put(start.getLocation(), precision); int steps = 0; statesVisited = 0; final int stepThreshold = 1000; long startTime = System.currentTimeMillis(); long lastSteps = 0; long lastTime = 0; while (!worklist.isEmpty() && !stop && (!failFast || isSound())) { statesVisited++; if (++steps == stepThreshold) { // Helps limit memory usage long now = System.currentTimeMillis(); System.gc(); long gcTime = System.currentTimeMillis() - now; logger.debug("Time for GC: " + gcTime + "ms"); now = System.currentTimeMillis(); long duration = Math.max(1, now - lastTime); long speed = (1000L * (statesVisited - lastSteps) / duration); // speed = Math.min(speed, 1000); logger.warn( "*** Reached " + reached.size() + " states, processed " + statesVisited + " states after " + (now - startTime) + "ms, at " + speed + " states/second" + (transformerFactory instanceof ResolvingTransformerFactory ? ", " + program.getInstructionCount() + " instructions." : ".")); logger.info( String.format( " Allocated heap memory: %.2f MByte", (runtime.totalMemory() - runtime.freeMemory()) / (1024.0 * 1024.0))); steps = 0; // StatsPlotter.plot((now - startTime) + "\t" + statesVisited // +"\t" + program.getInstructionCount() + "\t" + gcTime + "\t" // + speed); lastSteps = statesVisited; lastTime = now; if (Options.timeout.getValue() > 0 && (System.currentTimeMillis() - startTime > Options.timeout.getValue() * 1000)) { logger.error("Timeout after " + Options.timeout.getValue() + "s!"); stop = true; } } // We need the state before precision refinement for building the // ART AbstractState unadjustedState = worklist.pick(); /* * if (unadjustedState.getLocation().getAddress().toString().equals( * "0x00401189") //|| * unadjustedState.getLocation().getAddress().toString * ().equals("0x0040119b") || * unadjustedState.getLocation().getAddress * ().toString().equals("0x00401078") || * unadjustedState.getLocation( * ).getAddress().toString().equals("0x0040100a") ) * System.out.println("Debug " + * unadjustedState.getLocation().getAddress().toString()); */ precision = precisionMap.get(unadjustedState.getLocation()); Pair<AbstractState, Precision> pair = cpa.prec(unadjustedState, precision, reached); // Warning: The refined a is not stored in "reached", only used for // successor calculation AbstractState a = pair.getLeft(); /* * CompositeState a1 = (CompositeState) a; BasedNumberValuation a2 = * (BasedNumberValuation)a1.getComponent(1); if * (a2.getStore().isTop()) System.out.println("Debug TOP Value"); */ precision = pair.getRight(); precisionMap.put(a.getLocation(), precision); // logger.debug("Picked from worklist: " + a.getIdentifier()); // getTransformers() and post() might throw exceptions try { // For each outgoing edge // if (a.isTop()) // System.out.println("Debug TOP"); /* * if * (a.getLocation().getAddress().toString().equals("0x004106cd") * || * (a.getLocation().getAddress().toString().equals("0x00410498") * && a.getLocation().getIndex() >= 0)) * System.out.println("Debug Transformer Factory:" + * a.getLocation().getAddress().toString()); */ // Set<CFAEdge> s = transformerFactory.getTransformers(a); // System.out.println("Debug Transformer Factory:" + // a.getLocation().getAddress().toString()); for (CFAEdge cfaEdge : transformerFactory.getTransformers(a)) { Precision targetPrecision = precisionMap.get(cfaEdge.getTarget()); if (targetPrecision == null) { targetPrecision = cpa.initPrecision(cfaEdge.getTarget(), cfaEdge.getTransformer()); precisionMap.put(cfaEdge.getTarget(), targetPrecision); } // Prefix everything by current location for easier // debugging // Logger.setGlobalPrefix(cfaEdge.getSource().toString()); // if // (cfaEdge.getSource().getAddress().toString().equals("0x00401027") // && // cfaEdge.getTarget().getAddress().toString().equals("0x0040102e")) // System.out.println("Debug Edge"); // Calculate the set of abstract successors Set<AbstractState> successors = cpa.post(a, cfaEdge, targetPrecision); if (successors.isEmpty()) { logger.debug("No successors along edge " + cfaEdge + ", reached halt?"); continue; } // logger.debug("via edge " + cfaEdge.toString() + " " + // successors.size() + " successors."); // Process every successor for (AbstractState succ : successors) { // logger.debug("Processing new post state: " + // succ.getIdentifier()); // Try to merge the new state with an existing one Set<AbstractState> statesToRemove = new FastSet<AbstractState>(); Set<AbstractState> statesToAdd = new FastSet<AbstractState>(); for (AbstractState r : reached.where(0, ((CompositeState) succ).getComponent(0))) { AbstractState merged = cpa.merge(succ, r, targetPrecision); if (!merged.equals(r)) { // logger.debug("Merge of " + // succ.getIdentifier() + " and " + // r.getIdentifier() + " produced new state " + // merged.getIdentifier()); statesToRemove.add(r); statesToAdd.add(merged); } } // replace the old state in worklist and reached with // the merged version for (AbstractState r : statesToRemove) { reached.remove(r); worklist.remove(r); // art.remove(r); } for (AbstractState r : statesToAdd) { // Only add r to the worklist if it hasn't been // reached yet if (reached.add(r)) { worklist.add(r); if (art != null) art.addChild(unadjustedState, r); } } // if not stopped add to worklist if (!cpa.stop(succ, reached, targetPrecision) || this.program.checkSMPos(((CompositeState) succ).getLocation().getAddress())) { worklist.add(succ); reached.add(succ); if (art != null) art.addChild(unadjustedState, succ); } } // end for each outgoing edge } } catch (StateException e) { if (e.getState() == null) { e.setState(a); } if (art != null && !unadjustedState.equals(e.getState())) art.addChild(unadjustedState, e.getState()); throw e; } } long endTime = System.currentTimeMillis(); if (endTime - startTime > 0) { logger.info( "Processed " + statesVisited + " states at " + (1000L * statesVisited / (endTime - startTime)) + " states/second"); logger.info( String.format( "Allocated heap memory: %.2f MByte", (runtime.totalMemory() - runtime.freeMemory()) / (1024.0 * 1024.0))); } completed = worklist.isEmpty(); } public void stop() { logger.fatal(Characters.starredBox("Interrupt! Stopping CPA Algorithm!")); stop = true; } }
/** @author Johannes Kinder */ public class SubstitutionElement implements AbstractValue { public static SubstitutionElement TOP = new SubstitutionElement(null); public static SubstitutionElement BOT = new SubstitutionElement(null); @SuppressWarnings("unused") private static final Logger logger = Logger.getLogger(SubstitutionElement.class); private final RTLExpression expression; public SubstitutionElement(RTLExpression e) { expression = e; } public RTLExpression getExpression() { return expression; } /* * @see org.jakstab.analysis.AbstractValue#concretize() */ @Override public Set<RTLNumber> concretize() { if (expression instanceof RTLNumber) { return Collections.singleton((RTLNumber) expression); } else { // the "full" set return RTLNumber.ALL_NUMBERS; } } /* * @see * org.jakstab.analysis.AbstractValue#join(org.jakstab.analysis.LatticeElement * ) */ @Override public SubstitutionElement join(LatticeElement l) { if (l.isBot()) return this; SubstitutionElement other = (SubstitutionElement) l; if (this.isBot()) return other; if (expression.equals(other.getExpression())) return this; return TOP; } /* * @see org.jakstab.analysis.LatticeElement#isBot() */ @Override public boolean isBot() { return this == BOT; } /* * @see org.jakstab.analysis.LatticeElement#isTop() */ @Override public boolean isTop() { return this == TOP; } /* * @see * org.jakstab.analysis.LatticeElement#lessOrEqual(org.jakstab.analysis. * LatticeElement) */ @Override public boolean lessOrEqual(LatticeElement l) { if (l.isTop() || this.isBot()) return true; if (isTop() || l.isBot()) return false; SubstitutionElement other = (SubstitutionElement) l; if (expression.equals(other.getExpression())) return true; return false; } @Override public String toString() { return expression.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((expression == null) ? 0 : expression.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; SubstitutionElement other = (SubstitutionElement) obj; if (expression == null) { if (other.expression != null) return false; } else if (!expression.equals(other.expression)) return false; return true; } @Override public boolean hasUniqueConcretization() { return (expression instanceof RTLNumber); } }
public RawModule(File file, Architecture architecture) throws IOException { logger.info("Loading image as raw binary..."); InputStream inStream = new FileInputStream(file); inBuf = new BinaryFileInputBuffer(inStream); baseAddress = new AbsoluteAddress(0x0); }
/** @author Johannes Kinder */ public class RawModule implements ExecutableImage { @SuppressWarnings("unused") private static final Logger logger = Logger.getLogger(RawModule.class); private final BinaryFileInputBuffer inBuf; private final AbsoluteAddress baseAddress; private Disassembler disassembler; public RawModule(File file, Architecture architecture) throws IOException { logger.info("Loading image as raw binary..."); InputStream inStream = new FileInputStream(file); inBuf = new BinaryFileInputBuffer(inStream); baseAddress = new AbsoluteAddress(0x0); } @Override public Disassembler getDisassembler() { if (disassembler == null) { disassembler = new X86Disassembler(inBuf); } return disassembler; } @Override public AbsoluteAddress getEntryPoint() { return baseAddress; } @Override public Set<ExportedSymbol> getExportedSymbols() { return Collections.emptySet(); } @Override public long getFilePointer(AbsoluteAddress va) { return va.getValue() - baseAddress.getValue(); } @Override public AbsoluteAddress getMaxAddress() { return new AbsoluteAddress(baseAddress.getValue() + inBuf.getSize()); } @Override public AbsoluteAddress getMinAddress() { return baseAddress; } @Override public SymbolFinder getSymbolFinder() { return new DummySymbolFinder(); } @Override public Set<UnresolvedSymbol> getUnresolvedSymbols() { return Collections.emptySet(); } @Override public AbsoluteAddress getVirtualAddress(long fp) { return new AbsoluteAddress(baseAddress.getValue() + fp); } @Override public boolean isCodeArea(AbsoluteAddress va) { return true; } @Override public boolean isReadOnly(AbsoluteAddress a) { return false; } @Override public RTLNumber readMemoryLocation(RTLMemoryLocation m) { // TODO Auto-generated method stub return null; } @Override public Iterator<AbsoluteAddress> codeBytesIterator() { throw new UnsupportedOperationException( "Code iteration not yet implemented for " + this.getClass().getSimpleName() + "!"); } @Override public byte[] getByteArray() { return inBuf.getByteArray(); } }