/* Copyright 2014 Google Inc. All Rights Reserved. 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 com.google.security.zynamics.binnavi.standardplugins.pathfinder; import static org.junit.Assert.assertEquals; import com.google.security.zynamics.binnavi.API.disassembly.BasicBlock; import com.google.security.zynamics.binnavi.API.disassembly.CodeNode; import com.google.security.zynamics.binnavi.API.disassembly.CouldntLoadDataException; import com.google.security.zynamics.binnavi.API.disassembly.Database; import com.google.security.zynamics.binnavi.API.disassembly.EdgeType; import com.google.security.zynamics.binnavi.API.disassembly.Function; import com.google.security.zynamics.binnavi.API.disassembly.Module; import com.google.security.zynamics.binnavi.API.disassembly.PartialLoadException; import com.google.security.zynamics.binnavi.API.disassembly.TagManager; import com.google.security.zynamics.binnavi.API.disassembly.View; import com.google.security.zynamics.binnavi.API.disassembly.ViewEdge; import com.google.security.zynamics.binnavi.API.disassembly.ViewNode; import com.google.security.zynamics.binnavi.CConfigLoader; import com.google.security.zynamics.binnavi.Database.CDatabase; import com.google.security.zynamics.binnavi.Database.CJdbcDriverNames; import com.google.security.zynamics.binnavi.Database.Exceptions.InvalidDatabaseException; import com.google.security.zynamics.binnavi.Database.Exceptions.InvalidDatabaseVersionException; import com.google.security.zynamics.binnavi.Database.Exceptions.InvalidExporterDatabaseFormatException; import com.google.security.zynamics.binnavi.Database.Exceptions.LoadCancelledException; import com.google.security.zynamics.binnavi.Database.MockClasses.MockSqlProvider; import com.google.security.zynamics.binnavi.Tagging.CTag; import com.google.security.zynamics.binnavi.Tagging.CTagManager; import com.google.security.zynamics.binnavi.Tagging.TagType; import com.google.security.zynamics.zylib.types.trees.Tree; import com.google.security.zynamics.zylib.types.trees.TreeNode; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.awt.Color; import java.io.IOException; import java.util.List; @RunWith(JUnit4.class) public final class PathFinderTest { private Module m_notepad; private Module m_kernel32; private static BasicBlock findBlock(final Function function, final long offset) throws CouldntLoadDataException { if (!function.isLoaded()) { function.load(); } for (final BasicBlock block : function.getGraph().getNodes()) { if (block.getAddress().toLong() == offset) { return block; } } return null; } private static ViewEdge findEdge(final List<ViewEdge> edges, final long source, final long target) { for (final ViewEdge edge : edges) { if ((((CodeNode) edge.getSource()).getAddress().toLong() == source) && (((CodeNode) edge.getTarget()).getAddress().toLong() == target)) { return edge; } } return null; } private static Function findFunction(final Module module, final long offset) { for (final Function function : module.getFunctions()) { if (function.getAddress().toLong() == offset) { return function; } } return null; } private static ViewNode findNode(final List<ViewNode> nodes, final long address) { for (final ViewNode node : nodes) { if (((CodeNode) node).getAddress().toLong() == address) { return node; } } return null; } @Before public void setUp() throws IOException, IllegalStateException, CouldntLoadDataException, InvalidDatabaseException, InvalidExporterDatabaseFormatException, com.google.security.zynamics.binnavi.Database.Exceptions.CouldntLoadDriverException, com.google.security.zynamics.binnavi.Database.Exceptions.CouldntConnectException, com.google.security.zynamics.binnavi.Database.Exceptions.CouldntInitializeDatabaseException, com.google.security.zynamics.binnavi.Database.Exceptions.CouldntLoadDataException, InvalidDatabaseVersionException, LoadCancelledException { final String[] parts = CConfigLoader.loadPostgreSQL(); final CDatabase database = new CDatabase("None", CJdbcDriverNames.jdbcPostgreSQLDriverName, parts[0], "test_disassembly", parts[1], parts[2], parts[3], false, false); final Database db = new Database(database); database.connect(); database.load(); final MockSqlProvider provider = new MockSqlProvider(); final TagManager nodeTagManager = new TagManager(new CTagManager(new Tree<CTag>(new TreeNode<CTag>(new CTag(0, "", "", TagType.NODE_TAG, provider))), TagType.NODE_TAG, provider)); final TagManager viewTagManager = new TagManager(new CTagManager(new Tree<CTag>(new TreeNode<CTag>(new CTag(0, "", "", TagType.VIEW_TAG, provider))), TagType.VIEW_TAG, provider)); m_notepad = new Module(db, database.getContent().getModule(1), nodeTagManager, viewTagManager); m_notepad.load(); m_kernel32 = new Module(db, database.getContent().getModule(2), nodeTagManager, viewTagManager); m_kernel32.load(); } @Test public void testFirstBlock() throws CouldntLoadDataException, PartialLoadException { // Tests 100337E -> 1005179 -> 1007568 where all calls are in the first block // of the respective functions. // Tests path finding from the beginning to the end of a single function final Function startFunction = findFunction(m_notepad, 0x100337E); final BasicBlock startBlock = findBlock(startFunction, 0x10033C2); final Function endFunction = findFunction(m_notepad, 0x1007568); final BasicBlock endBlock = findBlock(endFunction, 0x1007568); final View view = PathFinder.createPath(m_notepad, startBlock, endBlock, null, null); assertEquals(3, view.getGraph().nodeCount()); assertEquals(2, view.getGraph().edgeCount()); } // @Test // public void testFoo() throws CouldntLoadDataException, CouldntSaveDataException // { // // TODO: Bring this test back in msw3prt.idb // // final Function startFunction = findFunction(m_foo, 0x5FEF8426); // final BasicBlock startBlock = findBlock(startFunction, 0x5FEF8426); // // final Function endFunction = findFunction(m_foo, 0x5FEFF06D); // final BasicBlock endBlock = findBlock(endFunction, 0x5FEFF0DB); // // final View view = PathFinder.createPath(m_foo, startBlock, endBlock, null, null); // // assertEquals(46, view.getGraph().nodeCount()); // assertEquals(49, view.getGraph().edgeCount()); // } @Test public void testInsideFunction() throws CouldntLoadDataException, PartialLoadException { // Tests path finding from the beginning to the end of a single function final Function startFunction = findFunction(m_notepad, 0x01002B87); final BasicBlock startBlock = findBlock(startFunction, 0x1002B87); final BasicBlock endBlock = findBlock(startFunction, 0x100336A); final View view = PathFinder.createPath(m_notepad, startBlock, endBlock, null, null); assertEquals(96, view.getGraph().nodeCount()); assertEquals(150, view.getGraph().edgeCount()); } @Test public void testInsideFunctionPartial() throws CouldntLoadDataException, PartialLoadException { // Tests path finding somewhere inside a function final Function startFunction = findFunction(m_notepad, 0x01002452); final BasicBlock startBlock = findBlock(startFunction, 0x10024C2); final BasicBlock endBlock = findBlock(startFunction, 0x10026FB); final View view = PathFinder.createPath(m_notepad, startBlock, endBlock, null, null); assertEquals(9, view.getGraph().nodeCount()); assertEquals(11, view.getGraph().edgeCount()); final List<ViewEdge> edges = view.getGraph().getEdges(); final List<ViewNode> nodes = view.getGraph().getNodes(); assertEquals(EdgeType.JumpConditionalFalse, findEdge(edges, 0x10024C2, 0x1002523).getType()); assertEquals(EdgeType.JumpConditionalTrue, findEdge(edges, 0x10024C2, 0x1002539).getType()); assertEquals(EdgeType.JumpUnconditional, findEdge(edges, 0x100253F, 0x10026F9).getType()); assertEquals(Color.GREEN, findNode(nodes, 0x10024C2).getColor()); assertEquals(Color.YELLOW, findNode(nodes, 0x10026FB).getColor()); } @Test public void testPassingFunctionContinue() throws CouldntLoadDataException, PartialLoadException { // Tests pathfinding from one function to another function while passing one function // and having a target block that is NOT a RETURN block. // // What should happen here is that multiple paths are generated because the target // block is not necessarily hit the first time the function is entered. // 0x1004565 -> 0x1003C92 -> 0x100398D final Function startFunction = findFunction(m_notepad, 0x1004565); final BasicBlock startBlock = findBlock(startFunction, 0x1004629); final Function endFunction = findFunction(m_notepad, 0x100398D); final BasicBlock endBlock = findBlock(endFunction, 0x10039D1); final View view = PathFinder.createPath(m_notepad, startBlock, endBlock, null, null); assertEquals(37, view.getGraph().nodeCount()); assertEquals(64, view.getGraph().edgeCount()); } @Test public void testPassingFunctionReturn() throws CouldntLoadDataException, PartialLoadException { // Tests pathfinding from one function to another function while passing one function // and having a target block that is a RETURN block. // // What should happen here is that the pathfinding algorithm stops when it reaches // the RETURN node. That is consecutive calls to the target function should not // be part of the pathfinding result. // 0x1004565 -> 0x1003C92 -> 0x100398D final Function startFunction = findFunction(m_notepad, 0x1004565); final BasicBlock startBlock = findBlock(startFunction, 0x1004629); final Function endFunction = findFunction(m_notepad, 0x100398D); final BasicBlock endBlock = findBlock(endFunction, 0x10039D9); final View view = PathFinder.createPath(m_notepad, startBlock, endBlock, null, null); assertEquals(14, view.getGraph().nodeCount()); assertEquals(19, view.getGraph().edgeCount()); } @Test public void testRecursivePath() throws CouldntLoadDataException, PartialLoadException { // Tests pathfinding from a simple function to a simple function through // a recursive path final Function startFunction = findFunction(m_kernel32, 0x7C82E8B2); // GetVolumePathNameA final BasicBlock startBlock = findBlock(startFunction, 0x7C82E8B2); final Function endFunction = findFunction(m_kernel32, 0x7C8092B0); final BasicBlock endBlock = findBlock(endFunction, 0x7C8092B0); final View view = PathFinder.createPath(m_kernel32, startBlock, endBlock, null, null); assertEquals(1247, view.getGraph().nodeCount()); assertEquals(1988, view.getGraph().edgeCount()); } @Test public void testRecursiveTarget() throws CouldntLoadDataException, PartialLoadException { // Tests pathfinding from a simple function to a self-recursive function final Function startFunction = findFunction(m_kernel32, 0x7C866E7B); // SetCommConfig final BasicBlock startBlock = findBlock(startFunction, 0x7C866EF3); final Function endFunction = findFunction(m_kernel32, 0x7C865E16); // SetCommState final BasicBlock endBlock = findBlock(endFunction, 0x7C866106); final View view = PathFinder.createPath(m_kernel32, startBlock, endBlock, null, null); assertEquals(2 /** calling function **/ + 66 /** called function **/ + 3 /** split blocks **/ , view.getGraph().nodeCount()); assertEquals(99 /** called function **/ + 1 /** calling target function **/ + 3 + 3 /** recursive calls and returns **/ , view.getGraph().edgeCount()); } @Test public void testRegularFunction() throws CouldntLoadDataException, PartialLoadException { // Tests pathfinding between two simple functions // 0x1004565 // 0x1003CD7 final Function startFunction = findFunction(m_notepad, 0x1004565); final BasicBlock startBlock = findBlock(startFunction, 0x1004629); final Function endFunction = findFunction(m_notepad, 0x1003C92); final BasicBlock endBlock = findBlock(endFunction, 0x1003CD7); final View view = PathFinder.createPath(m_notepad, startBlock, endBlock, null, null); assertEquals(7, view.getGraph().nodeCount()); assertEquals(8, view.getGraph().edgeCount()); } @Test public void testToImportedFunction() throws CouldntLoadDataException, PartialLoadException { // Tests from the beginning of a function to an imported function final Function startFunction = findFunction(m_notepad, 0x0100398D); final BasicBlock startBlock = findBlock(startFunction, 0x100398D); final Function endFunction = findFunction(m_notepad, 0x1001000); endFunction.load(); final View view = PathFinder.createPath(m_notepad, startBlock, null, null, endFunction); assertEquals(3, view.getGraph().nodeCount()); assertEquals(2, view.getGraph().edgeCount()); } }