├── Module.manifest ├── build.gradle ├── data ├── 3vl-test.bin ├── ARM-LE-32-v8-Default.bin ├── README.txt ├── ia.diff ├── vm_example_password_is_infected.zip └── wbDES.exe ├── extension.properties ├── ghidra_scripts ├── Example1TF.java ├── Example2ARM.java ├── Example3VM.java ├── HackLu2009.java ├── README.txt └── WyseurWBDES.java └── src ├── main ├── help │ └── help │ │ ├── TOC_Source.xml │ │ ├── shared │ │ └── Frontpage.css │ │ └── topics │ │ └── programanalysislibrary │ │ └── help.html ├── java │ └── ghidra │ │ └── pal │ │ ├── absint │ │ └── tvl │ │ │ ├── TVLAbstractGhidraState.java │ │ │ ├── TVLAbstractGhidraStateFactory.java │ │ │ ├── TVLAbstractGhidraStateUtil.java │ │ │ ├── TVLAbstractInterpretBlock.java │ │ │ ├── TVLAbstractInterpretCFG.java │ │ │ ├── TVLAbstractInterpretCFGStateBundle.java │ │ │ ├── TVLAbstractInterpretMultiple.java │ │ │ ├── TVLAbstractInterpreter.java │ │ │ ├── TVLAbstractMemory.java │ │ │ ├── TVLAnalysisOutputOptions.java │ │ │ ├── TVLBitVector.java │ │ │ ├── TVLBitVectorUtil.java │ │ │ ├── TVLHighLevelInterface.java │ │ │ ├── TVLPcodeDatabaseTransformer.java │ │ │ └── TVLPcodeTransformer.java │ │ ├── cfg │ │ ├── CFG.java │ │ ├── CFGBuilder.java │ │ ├── CFGBuilderBundle.java │ │ ├── CFGEdge.java │ │ ├── CFGEdgeType.java │ │ ├── CFGExplorationTerminator.java │ │ ├── CFGFactory.java │ │ ├── CFGPendingEdge.java │ │ ├── CFGPointTerminator.java │ │ ├── CFGPriorityQueue.java │ │ ├── CFGVertex.java │ │ ├── CFGVertexDetailProvider.java │ │ ├── CacheInstructions.java │ │ ├── CacheInstructionsImpl.java │ │ ├── InstructionCache.java │ │ ├── InstructionDetailProvider.java │ │ ├── InstructionDetailProviderUtil.java │ │ ├── PcodeOpProvider.java │ │ ├── PseudoInstructionCache.java │ │ └── PseudoInstructionDetailProvider.java │ │ ├── generic │ │ ├── BitVector.java │ │ ├── DenseBitVector.java │ │ ├── PcodeDisasmUtil.java │ │ ├── PcodeOpVisitor.java │ │ ├── SparseBitVector.java │ │ └── VisitorUnimplementedException.java │ │ ├── math │ │ └── OlmSeidlMatrix.java │ │ ├── parsers │ │ ├── DCAFilter.java │ │ └── dcafilter │ │ │ └── grammar │ │ │ ├── BoolExpr.java │ │ │ ├── DCAFilter.g │ │ │ ├── DCAFilter.tokens │ │ │ ├── DCAFilterLexer.java │ │ │ ├── DCAFilterParser.java │ │ │ ├── DCAFilterWalker.g │ │ │ ├── DCAFilterWalker.java │ │ │ └── DCAFilterWalker.tokens │ │ ├── util │ │ ├── ByteArrayBitEnumerator.java │ │ ├── Colorizer.java │ │ ├── IntegerBitEnumerator.java │ │ ├── JavaUtil.java │ │ ├── NoDuplicatesPriorityQueue.java │ │ ├── Pair.java │ │ └── Printer.java │ │ └── wbc │ │ ├── CPABundle.java │ │ ├── CryptoBitVector.java │ │ ├── DPABundle.java │ │ ├── PABundle.java │ │ ├── PowerAnalysis.java │ │ ├── PowerAnalysisFactory.java │ │ ├── Score.java │ │ ├── TraceAggregator.java │ │ ├── aes │ │ ├── AES.java │ │ ├── AESDecAllPowerAnalysis.java │ │ ├── AESDecBytePowerAnalysis.java │ │ ├── AESEncAllPowerAnalysis.java │ │ ├── AESEncBytePowerAnalysis.java │ │ ├── AESGuess.java │ │ └── AESPowerAnalysis.java │ │ ├── dca │ │ └── DCAFilterContext.java │ │ └── des │ │ ├── DES.java │ │ ├── DESAllPowerAnalysis.java │ │ ├── DESGroupPowerAnalysis.java │ │ ├── DESGuess.java │ │ └── DESPowerAnalysis.java └── resources │ └── images │ └── README.txt └── test └── java ├── README.test.txt └── ghidra └── pal └── TVLAbstractInterpreterTest.java /Module.manifest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RolfRolles/GhidraPAL/749ae9546ebf3e987aa97dfd66a42c35cad0046d/Module.manifest -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Builds a Ghidra Extension for a given Ghidra installation. 2 | // 3 | // An absolute path to the Ghidra installation directory must be supplied either by setting the 4 | // GHIDRA_INSTALL_DIR environment variable or Gradle project property: 5 | // 6 | // > export GHIDRA_INSTALL_DIR= 7 | // > gradle 8 | // 9 | // or 10 | // 11 | // > gradle -PGHIDRA_INSTALL_DIR= 12 | // 13 | // Gradle should be invoked from the directory of the project to build. Please see the 14 | // application.gradle.version property in /Ghidra/application.properties 15 | // for the correction version of Gradle to use for the Ghidra installation you specify. 16 | 17 | //----------------------START "DO NOT MODIFY" SECTION------------------------------ 18 | def ghidraInstallDir 19 | 20 | if (System.env.GHIDRA_INSTALL_DIR) { 21 | ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR 22 | } 23 | else if (project.hasProperty("GHIDRA_INSTALL_DIR")) { 24 | ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR") 25 | } 26 | 27 | if (ghidraInstallDir) { 28 | apply from: new File(ghidraInstallDir).getCanonicalPath() + "/support/buildExtension.gradle" 29 | } 30 | else { 31 | throw new GradleException("GHIDRA_INSTALL_DIR is not defined!") 32 | } 33 | //----------------------END "DO NOT MODIFY" SECTION------------------------------- 34 | -------------------------------------------------------------------------------- /data/3vl-test.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RolfRolles/GhidraPAL/749ae9546ebf3e987aa97dfd66a42c35cad0046d/data/3vl-test.bin -------------------------------------------------------------------------------- /data/ARM-LE-32-v8-Default.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RolfRolles/GhidraPAL/749ae9546ebf3e987aa97dfd66a42c35cad0046d/data/ARM-LE-32-v8-Default.bin -------------------------------------------------------------------------------- /data/README.txt: -------------------------------------------------------------------------------- 1 | The "data" directory is intended to hold data files that will be used by this module and will 2 | not end up in the .jar file, but will be present in the zip or tar file. Typically, data 3 | files are placed here rather than in the resources directory if the user may need to edit them. 4 | 5 | An optional data/languages directory can exist for the purpose of containing various Sleigh language 6 | specification files and importer opinion files. 7 | 8 | The data/build.xml is used for building the contents of the data/languages directory. 9 | 10 | The skel language definition has been commented-out within the skel.ldefs file so that the 11 | skeleton language does not show-up within Ghidra. 12 | 13 | See the Sleigh language documentation (docs/languages/sleigh.htm or sleigh.pdf) for details 14 | on Sleigh language specification syntax. 15 | 16 | -------------------------------------------------------------------------------- /data/ia.diff: -------------------------------------------------------------------------------- 1 | 1897c1897,1908 2 | < # PF, AF not implemented 3 | --- 4 | > # AF not implemented 5 | > # Rolf: changes here 6 | > local ext1:8 = zext(result); 7 | > local ext2 = ext1 >> 4; 8 | > local ext3 = ext1 ^ ext2; 9 | > local ext4 = ext3 >> 2; 10 | > local ext5 = ext3 ^ ext4; 11 | > local ext6 = ext5 >> 1; 12 | > local ext7 = ext5 ^ ext6; 13 | > local ext8:8 = 1; 14 | > local ext9 = ext7 & ext8; 15 | > PF = (ext9 == 0); 16 | 1909c1920,1933 17 | < # PF, AF not implemented 18 | --- 19 | > 20 | > # Rolf: changes here 21 | > local ext1:8 = zext(result); 22 | > local ext2 = ext1 >> 4; 23 | > local ext3 = ext1 ^ ext2; 24 | > local ext4 = ext3 >> 2; 25 | > local ext5 = ext3 ^ ext4; 26 | > local ext6 = ext5 >> 1; 27 | > local ext7 = ext5 ^ ext6; 28 | > local ext8:8 = 1; 29 | > local ext9 = ext7 & ext8; 30 | > local newPF = (ext9 == 0); 31 | > PF = (!notzero & PF) | (notzero & newPF); 32 | > # AF not implemented 33 | -------------------------------------------------------------------------------- /data/vm_example_password_is_infected.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RolfRolles/GhidraPAL/749ae9546ebf3e987aa97dfd66a42c35cad0046d/data/vm_example_password_is_infected.zip -------------------------------------------------------------------------------- /data/wbDES.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RolfRolles/GhidraPAL/749ae9546ebf3e987aa97dfd66a42c35cad0046d/data/wbDES.exe -------------------------------------------------------------------------------- /extension.properties: -------------------------------------------------------------------------------- 1 | name=@extname@ 2 | description=Ghidra Program Analysis Library 3 | author=Rolf Rolles 4 | createdOn=April 17th, 2019 5 | version=@extversion@ 6 | -------------------------------------------------------------------------------- /ghidra_scripts/Example1TF.java: -------------------------------------------------------------------------------- 1 | //Example three-valued analysis for 3vl-test.bin 2 | //@author Rolf Rolles 3 | //@category Deobfuscation 4 | //@keybinding 5 | //@menupath 6 | //@toolbar 7 | 8 | import java.util.List; 9 | import java.util.Arrays; 10 | import java.util.ArrayList; 11 | 12 | import ghidra.app.script.GhidraScript; 13 | import ghidra.app.services.ConsoleService; 14 | import ghidra.app.plugin.core.colorizer.ColorizingService; 15 | import ghidra.framework.plugintool.PluginTool; 16 | import ghidra.program.model.address.AddressSpace; 17 | import ghidra.program.model.address.Address; 18 | 19 | import ghidra.pal.util.Pair; 20 | import ghidra.pal.util.Printer; 21 | import ghidra.pal.util.Colorizer; 22 | import ghidra.pal.absint.tvl.TVLAbstractGhidraState; 23 | import ghidra.pal.absint.tvl.TVLAbstractGhidraStateFactory; 24 | import ghidra.pal.absint.tvl.TVLHighLevelInterface; 25 | import ghidra.pal.absint.tvl.TVLAnalysisOutputOptions; 26 | 27 | public class Example1TF extends GhidraScript { 28 | 29 | // If !shouldSetTF, TF is initialized to 1/2. 30 | // Otherwise, TF is initialized to TFValue. 31 | void RunExample(boolean shouldSetTF, boolean TFValue) throws Exception { 32 | // Create 4 input states with random ESP values and no fixed values 33 | List randVars = new ArrayList(Arrays.asList("ESP")); 34 | List> fixedVars = null; 35 | 36 | // Set TF to the specified value (true:1, false:0), if shouldSetTF 37 | if(shouldSetTF) { 38 | long lTFVal = TFValue ? 1L : 0L; 39 | fixedVars = new ArrayList>(Arrays.asList(new Pair("TF", lTFVal))); 40 | printf("Example: TF set to %x\n", lTFVal); 41 | } 42 | 43 | // If !shouldSetTF, don't initialize TF (i.e., it's assumed to be 1/2) 44 | else 45 | printf("Example: TF not set\n"); 46 | 47 | // Create a list of states from the information above 48 | List states = TVLAbstractGhidraStateFactory.MakeInputStatesRandInit(currentProgram, 4, randVars, fixedVars); 49 | 50 | // Get Address objects for fixed locations 51 | AddressSpace defaultAS = currentProgram.getAddressFactory().getDefaultAddressSpace(); 52 | Address startEa = defaultAS.getAddress(0x00); 53 | Address endEa = defaultAS.getAddress(0x1C); 54 | 55 | // Perform the analysis, print resolved branch targets 56 | // You can experiment with changing the final parameter to: 57 | // * TVLAnalysisOutputOptions.ResolvedBranchPrints 58 | // * TVLAnalysisOutputOptions.ValueComments 59 | // * TVLAnalysisOutputOptions.PcodeComments 60 | TVLHighLevelInterface.AnalyzeRange(currentProgram, startEa, endEa, true, states, TVLAnalysisOutputOptions.ResolvedBranchPrints); 61 | } 62 | 63 | // This function runs the one above with the specified value of TF. 64 | void RunExampleSetTF(boolean TFValue) throws Exception { 65 | RunExample(true, TFValue); 66 | } 67 | 68 | public void run() throws Exception { 69 | // Initialize ghidra.pal 70 | PluginTool tool = state.getTool(); 71 | Printer.Set(tool.getService(ConsoleService.class)); 72 | Printer.SetFileOutputPath("c:\\temp\\ghidra-debug.txt"); 73 | Colorizer.Set(tool.getService(ColorizingService.class)); 74 | 75 | // 76 | // Run the examples: 77 | // 78 | 79 | // TF = 0 (prints "(0000001c,0): conditional branch always not taken") 80 | RunExampleSetTF(false); 81 | 82 | // TF = 1 (prints "(0000001c,0): conditional branch always taken") 83 | RunExampleSetTF(true); 84 | 85 | // TF = 1/2 (prints "0000001c: could not resolve") 86 | RunExample(false, false); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /ghidra_scripts/Example2ARM.java: -------------------------------------------------------------------------------- 1 | //Example three-valued analysis for ARM-LE-32-v8-Default.bin 2 | //@author Rolf Rolles 3 | //@category Deobfuscation 4 | //@keybinding 5 | //@menupath 6 | //@toolbar 7 | 8 | import java.util.List; 9 | import java.util.Arrays; 10 | import java.util.ArrayList; 11 | 12 | import ghidra.app.script.GhidraScript; 13 | import ghidra.app.services.ConsoleService; 14 | import ghidra.app.plugin.core.colorizer.ColorizingService; 15 | import ghidra.framework.plugintool.PluginTool; 16 | import ghidra.program.model.address.AddressSpace; 17 | import ghidra.program.model.address.Address; 18 | 19 | import ghidra.pal.util.Printer; 20 | import ghidra.pal.util.Colorizer; 21 | import ghidra.pal.absint.tvl.TVLAbstractGhidraState; 22 | import ghidra.pal.absint.tvl.TVLAbstractGhidraStateFactory; 23 | import ghidra.pal.absint.tvl.TVLHighLevelInterface; 24 | import ghidra.pal.absint.tvl.TVLAnalysisOutputOptions; 25 | 26 | public class Example2ARM extends GhidraScript { 27 | public void runWithAnalysisOptions(TVLAnalysisOutputOptions opts) throws Exception { 28 | // Specify that register "sp" needs a value, but we don't care which value 29 | List randVars = new ArrayList(Arrays.asList("sp")); 30 | // Create 6 input states with random sp values and no fixed values 31 | List states = TVLAbstractGhidraStateFactory.MakeInputStatesRandInit(currentProgram, 6, randVars, null); 32 | 33 | // Get Address objects for fixed locations 34 | AddressSpace defaultAS = currentProgram.getAddressFactory().getDefaultAddressSpace(); 35 | Address startEa = defaultAS.getAddress(0x00); 36 | Address endEa = defaultAS.getAddress(0x54); 37 | 38 | // Perform the analysis, print notifications about resolved branches 39 | TVLHighLevelInterface.AnalyzeRange(currentProgram, startEa, endEa, true, states, opts); 40 | } 41 | 42 | public void run() throws Exception { 43 | // Initialize the ghidra.pal library 44 | PluginTool tool = state.getTool(); 45 | Printer.Set(tool.getService(ConsoleService.class)); 46 | Printer.SetFileOutputPath("c:\\temp\\ghidra-debug.txt"); 47 | Colorizer.Set(tool.getService(ColorizingService.class)); 48 | 49 | // This option adds comments for any branches that it resolves. 50 | runWithAnalysisOptions(TVLAnalysisOutputOptions.ResolvedBranchComments); 51 | 52 | // This will add comments with the symbolic values of variables modified on a 53 | // given line (e.g. "101?0??0" for an 8-bit quantity that is written). 54 | // runWithAnalysisOptions(TVLAnalysisOutputOptions.ValueComments); 55 | 56 | // This will add comments when the PcodeOp objects have changed. 57 | // runWithAnalysisOptions(TVLAnalysisOutputOptions.PcodeComments); 58 | 59 | // This option prints out any branches that it resolves. 60 | // runWithAnalysisOptions(TVLAnalysisOutputOptions.ResolvedBranchPrints); 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ghidra_scripts/Example3VM.java: -------------------------------------------------------------------------------- 1 | //Example three-valued analysis for vm_example.zip 2 | //@author Rolf Rolles 3 | //@category Deobfuscation 4 | //@keybinding 5 | //@menupath 6 | //@toolbar 7 | 8 | import java.util.List; 9 | import java.util.Arrays; 10 | import java.util.ArrayList; 11 | 12 | import ghidra.app.script.GhidraScript; 13 | import ghidra.app.services.ConsoleService; 14 | import ghidra.app.plugin.core.colorizer.ColorizingService; 15 | import ghidra.framework.plugintool.PluginTool; 16 | import ghidra.program.model.address.AddressSpace; 17 | import ghidra.program.model.address.Address; 18 | 19 | import ghidra.pal.util.Printer; 20 | import ghidra.pal.util.Colorizer; 21 | import ghidra.pal.absint.tvl.TVLAbstractGhidraState; 22 | import ghidra.pal.absint.tvl.TVLAbstractGhidraStateFactory; 23 | import ghidra.pal.absint.tvl.TVLHighLevelInterface; 24 | import ghidra.pal.absint.tvl.TVLAnalysisOutputOptions; 25 | 26 | public class Example3VM extends GhidraScript { 27 | final long[] multiHeads = new long[]{ 28 | 0x100144fb, 29 | 0x10014007, 30 | 0x10015204, 31 | 0x100166d2, 32 | 0x10014c0b, 33 | 0x1001471c, 34 | 0x10017505, 35 | 0x10016734, 36 | 0x10014bda 37 | }; 38 | public void runExample(int i) throws Exception { 39 | if(i < 0 || i >= multiHeads.length) { 40 | printf("runExample(%d): invalid argument (must be 0 <= x < %d)\n", i, multiHeads.length); 41 | return; 42 | } 43 | // Create 4 input states with random ESP values and no fixed values 44 | List randVars = new ArrayList(Arrays.asList("ESP")); 45 | List states = TVLAbstractGhidraStateFactory.MakeInputStatesRandInit(currentProgram, 4, randVars, null); 46 | 47 | // Get Address objects for fixed locations 48 | AddressSpace defaultAS = currentProgram.getAddressFactory().getDefaultAddressSpace(); 49 | Address startEa = defaultAS.getAddress(multiHeads[i]); 50 | Address endEa = defaultAS.getAddress(0x10013956); 51 | 52 | // Perform the analysis, color unvisited vertices red 53 | TVLHighLevelInterface.AnalyzeCFGRegion(currentProgram, startEa, endEa, true, states, TVLAnalysisOutputOptions.CFGColorizeUnvisited); 54 | } 55 | 56 | public void run() throws Exception { 57 | PluginTool tool = state.getTool(); 58 | Printer.Set(tool.getService(ConsoleService.class)); 59 | Printer.SetFileOutputPath("c:\\temp\\ghidra-debug.txt"); 60 | Colorizer.Set(tool.getService(ColorizingService.class)); 61 | 62 | AddressSpace defaultAS = currentProgram.getAddressFactory().getDefaultAddressSpace(); 63 | // Create code for each of the heads above 64 | for(int i = 0; i < multiHeads.length; i++) 65 | disassemble(defaultAS.getAddress(multiHeads[i])); 66 | 67 | runExample(6); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /ghidra_scripts/HackLu2009.java: -------------------------------------------------------------------------------- 1 | //TODO write a description for this script 2 | //@author 3 | //@category WBC 4 | //@keybinding 5 | //@menupath 6 | //@toolbar 7 | 8 | import ghidra.app.script.GhidraScript; 9 | import ghidra.app.plugin.processors.sleigh.SleighLanguage; 10 | import ghidra.app.services.ConsoleService; 11 | import ghidra.framework.plugintool.PluginTool; 12 | import ghidra.pcode.emulate.BreakTableCallBack; 13 | import ghidra.pcode.emulate.Emulate; 14 | import ghidra.pcode.memstate.MemoryState; 15 | import ghidra.pcode.memstate.MemoryPageBank; 16 | import ghidra.pcode.memstate.MemoryFaultHandler; 17 | import ghidra.program.model.address.Address; 18 | import ghidra.program.model.address.AddressSpace; 19 | import ghidra.program.model.listing.Program; 20 | import ghidra.program.model.mem.Memory; 21 | import ghidra.program.model.mem.MemoryAccessException; 22 | import ghidra.program.model.pcode.VarnodeTranslator; 23 | 24 | import java.util.ArrayList; 25 | import java.util.HashSet; 26 | import java.util.List; 27 | import java.util.concurrent.ThreadLocalRandom; 28 | 29 | import ghidra.pal.util.Pair; 30 | import ghidra.pal.util.Printer; 31 | import ghidra.pal.wbc.TraceAggregator; 32 | import ghidra.pal.wbc.CryptoBitVector; 33 | import ghidra.pal.wbc.PowerAnalysisFactory; 34 | 35 | class MyMemFaultHandler implements MemoryFaultHandler { 36 | String variety; 37 | public MyMemFaultHandler(String s) { variety=s; } 38 | public boolean uninitializedRead(Address address, int size, byte[] buf, int bufOffset) 39 | { 40 | Printer.printf("%s: uninitializedRead(%s,%d,_,%x)\n", variety, address.toString(), size, bufOffset); 41 | return false; 42 | } 43 | public boolean unknownAddress(Address address, boolean write) 44 | { 45 | Printer.printf("%s: unknownAddress(%s,%b)\n", variety, address.toString(), write); 46 | return false; 47 | } 48 | } 49 | 50 | class AccruingMemFaultHandler implements MemoryFaultHandler { 51 | String variety; 52 | long ProgramBegin, ProgramEnd; 53 | Memory ProgramMem; 54 | public AccruingMemFaultHandler(String s, Memory m, long progBegin, long progEnd) { 55 | variety=s; 56 | ProgramMem = m; 57 | ProgramBegin = progBegin; 58 | ProgramEnd = progEnd; 59 | } 60 | public boolean uninitializedRead(Address address, int size, byte[] buf, int bufOffset) 61 | { 62 | //Printer.printf("%s: uninitializedRead(%s,%d,_,%x) %d\n", variety, address.toString(), size, bufOffset, buf.length); 63 | //return false; 64 | long a = address.getOffset(); 65 | if(a >= ProgramBegin && a <= ProgramEnd) { 66 | try { 67 | byte[] chunk = new byte[size]; 68 | ProgramMem.getBytes(address, chunk); 69 | System.arraycopy(chunk, 0, buf, bufOffset, size); 70 | } 71 | catch(MemoryAccessException e) { 72 | return false; 73 | } 74 | return true; 75 | } 76 | return false; 77 | } 78 | public boolean unknownAddress(Address address, boolean write) 79 | { 80 | Printer.printf("%s: unknownAddress(%s,%b)\n", variety, address.toString(), write); 81 | return false; 82 | } 83 | } 84 | 85 | class LoggingMemorizingMemoryBank extends MemoryPageBank { 86 | ArrayList Accesses = new ArrayList(); 87 | long StackBegin, StackEnd; 88 | 89 | public LoggingMemorizingMemoryBank(AddressSpace spc, boolean isBigEndian, int ps, MemoryFaultHandler faultHandler, long stackLow, long stackHigh) { 90 | super(spc,isBigEndian,ps,faultHandler); 91 | StackBegin = stackLow; 92 | StackEnd = stackHigh; 93 | } 94 | 95 | // Log the low byte of all addresses targeted by 1-byte reads 96 | public int getChunk(long addrOffset, int size, byte[] res, boolean stop) { 97 | int iRes = super.getChunk(addrOffset, size, res, stop); 98 | if(size == 1 && addrOffset >= 0x408108l) 99 | //if(size == 1 && !((addrOffset >= StackBegin && addrOffset <= StackEnd))) //{ 100 | // Commented-out code ensures that the address is on the stack 101 | //if(addrOffset >= StackBegin && addrOffset <= StackEnd) { 102 | // Accesses.add(new Pair(addrOffset,res[0])); 103 | Accesses.add((byte)(addrOffset&0xFFl)); 104 | //} 105 | return iRes; 106 | } 107 | 108 | public void setChunk(long offset, int size, byte[] val) { 109 | // Changed 110 | if(size == 1 && offset >= StackBegin && offset <= StackEnd) 111 | Accesses.add(val[0]); 112 | super.setChunk(offset, size, val); 113 | } 114 | } 115 | 116 | class EmulatorTraceGenerator { 117 | AddressSpace defaultSpace; 118 | LoggingMemorizingMemoryBank defaultMemoryBank; 119 | MemoryState ms; 120 | Emulate Emulator; 121 | Program CurrentProgram; 122 | 123 | public static final String[] Reg32Names = {"EAX","ECX","EDX","EBX","ESP","EBP","ESI","EDI"}; 124 | public static final long[] Reg32Values = {0x28abbcl,0x611856c0l,0x0l,0x0l,0x18000l,0x28ac08l,0x200283f0l,0x6119fe9fl}; // changed ESP 125 | public static final long ProgramBegin = 0x00400000l; 126 | public static final long ProgramEnd = 0x004167DCl; // changed 127 | public static final long StackBegin = 0x00010000l; // changed 128 | public static final long StackEnd = 0x00020000l; // changed 129 | public static final long InputBegin = 0x00018010l; // changed 130 | public static final long ExecBegin = 0x0040135Bl; // changed 131 | public static final long ExecEnd = 0x004013A2l; // changed 132 | 133 | void Init() { 134 | defaultMemoryBank.Accesses = new ArrayList(); 135 | SleighLanguage l = (SleighLanguage)CurrentProgram.getLanguage(); 136 | VarnodeTranslator vt = new VarnodeTranslator(CurrentProgram); 137 | 138 | for(int i = 0; i < Reg32Names.length; i++) 139 | ms.setValue(l.getRegister(Reg32Names[i]), Reg32Values[i]); 140 | } 141 | 142 | byte makeAscii(byte b) { 143 | byte masked = (byte)((int)b & 0xF); 144 | if(masked <= 9) 145 | return (byte)(0x30 + (int)masked); 146 | return (byte)(0x41 + ((int)masked-0xa)); 147 | } 148 | 149 | ArrayList execute(byte[] aesDecInput) { 150 | Address eaBeg = defaultSpace.getAddress(ExecBegin); 151 | Address eaEnd = defaultSpace.getAddress(ExecEnd); 152 | Init(); 153 | byte[] write = new byte[2]; 154 | for(int i = 0; i < 16; i++) { 155 | write[1] = makeAscii(aesDecInput[i]); 156 | write[0] = makeAscii((byte)((int)aesDecInput[i] >> 4)); 157 | defaultMemoryBank.setChunk(InputBegin+2*i, 2, write); 158 | } 159 | Emulator.setExecuteAddress(eaBeg); 160 | while(!eaEnd.equals(Emulator.getExecuteAddress())) { 161 | //Printer.printf("Emulating %s\n", Emulator.getExecuteAddress().toString()); 162 | Emulator.executeInstruction(true); 163 | } 164 | return defaultMemoryBank.Accesses; 165 | } 166 | 167 | public EmulatorTraceGenerator(Program currentProgram) 168 | { 169 | CurrentProgram = currentProgram; 170 | SleighLanguage l = (SleighLanguage)currentProgram.getLanguage(); 171 | 172 | // Initialize AddressSpace objects 173 | defaultSpace = currentProgram.getAddressFactory().getDefaultAddressSpace(); 174 | AddressSpace registerSpace = currentProgram.getAddressFactory().getRegisterSpace(); 175 | AddressSpace uniqueSpace = currentProgram.getAddressFactory().getUniqueSpace(); 176 | 177 | // Create MemoryPageBank objects for the address spaces 178 | boolean isBigEndian = l.isBigEndian(); 179 | 180 | MemoryFaultHandler acc = new AccruingMemFaultHandler("default", currentProgram.getMemory(), ProgramBegin, ProgramEnd); 181 | defaultMemoryBank = new LoggingMemorizingMemoryBank(defaultSpace, isBigEndian, 4096, acc, StackBegin, StackEnd); 182 | MemoryPageBank registerMemoryBank = new MemoryPageBank(registerSpace, false, 4096, new MyMemFaultHandler("register")); 183 | MemoryPageBank uniqueMemoryBank = new MemoryPageBank(uniqueSpace, false, 4096, new MyMemFaultHandler("unique")); 184 | 185 | // Create and initialize the MemoryState 186 | ms = new MemoryState(l); 187 | ms.setMemoryBank(registerMemoryBank); 188 | ms.setMemoryBank(defaultMemoryBank); 189 | 190 | // Initialize the BreakTable 191 | BreakTableCallBack bt = new BreakTableCallBack(l); 192 | 193 | // Create the emulator object 194 | Emulator = new Emulate(l, ms, bt); 195 | } 196 | } 197 | 198 | public class HackLu2009 extends GhidraScript { 199 | public static Byte[] box(byte[] byteArray) { 200 | Byte[] box = new Byte[byteArray.length]; 201 | for (int i = 0; i < box.length; i++) 202 | box[i] = byteArray[i]; 203 | return box; 204 | } 205 | Pair>, List> getSamples(int numSamples) { 206 | Printer.printf("Collecting %d samples\n", numSamples); 207 | List> samples = new ArrayList>(); 208 | List pts = new ArrayList(); 209 | EmulatorTraceGenerator et = new EmulatorTraceGenerator(currentProgram); 210 | et.Init(); 211 | for(int i = 0; i < numSamples; i++) { 212 | Printer.printf("Collecting sample %d\n", i); 213 | if(monitor.isCancelled()) 214 | return null; 215 | byte[] aesDecInput = new byte[16]; 216 | for(int j = 0; j < 16; j++) 217 | aesDecInput[j] = (byte)(ThreadLocalRandom.current().nextLong()); 218 | ArrayList sample = et.execute(aesDecInput); 219 | Printer.printf("Sample %d size is %d\n", i, sample.size()); 220 | samples.add(sample); 221 | pts.add(box(aesDecInput)); 222 | } 223 | return new Pair>, List>(samples, pts); 224 | } 225 | public void doCPA(int nSamples) { 226 | Pair>, List> samples = getSamples(nSamples); 227 | if(samples == null) 228 | return; 229 | List points = TraceAggregator.aggregate(samples.x); 230 | PowerAnalysisFactory.aesCPA(1,true).analyzeTrace(points,samples.y); 231 | } 232 | public void run() throws Exception { 233 | PluginTool tool = state.getTool(); 234 | // Initialize the Printer class, so that other classes can print 235 | // debug information. 236 | Printer.Set(tool.getService(ConsoleService.class)); 237 | Printer.SetFileOutputPath("c:\\temp\\ghidra-debug2.txt"); 238 | doCPA(100); 239 | } 240 | 241 | } 242 | -------------------------------------------------------------------------------- /ghidra_scripts/README.txt: -------------------------------------------------------------------------------- 1 | Java source directory to hold module-specific Ghidra scripts. 2 | -------------------------------------------------------------------------------- /ghidra_scripts/WyseurWBDES.java: -------------------------------------------------------------------------------- 1 | //Differential computation analysis / correlation power analysis for wbdes.exe, Wyseur's 2007 challenge 2 | //@author Rolf Rolles 3 | //@category WBC 4 | //@keybinding 5 | //@menupath 6 | //@toolbar 7 | 8 | import ghidra.app.script.GhidraScript; 9 | import ghidra.app.plugin.processors.sleigh.SleighLanguage; 10 | import ghidra.app.services.ConsoleService; 11 | import ghidra.framework.plugintool.PluginTool; 12 | import ghidra.pcode.emulate.BreakTableCallBack; 13 | import ghidra.pcode.emulate.Emulate; 14 | import ghidra.pcode.memstate.MemoryState; 15 | import ghidra.pcode.memstate.MemoryPageBank; 16 | import ghidra.pcode.memstate.MemoryFaultHandler; 17 | import ghidra.program.model.address.Address; 18 | import ghidra.program.model.address.AddressSpace; 19 | import ghidra.program.model.listing.Program; 20 | import ghidra.program.model.mem.Memory; 21 | import ghidra.program.model.mem.MemoryAccessException; 22 | import ghidra.program.model.pcode.VarnodeTranslator; 23 | 24 | import java.util.ArrayList; 25 | import java.util.HashSet; 26 | import java.util.List; 27 | import java.util.concurrent.ThreadLocalRandom; 28 | 29 | import ghidra.pal.util.Pair; 30 | import ghidra.pal.util.Printer; 31 | import ghidra.pal.wbc.TraceAggregator; 32 | import ghidra.pal.wbc.CryptoBitVector; 33 | import ghidra.pal.wbc.PowerAnalysisFactory; 34 | 35 | class MyMemFaultHandler implements MemoryFaultHandler { 36 | String variety; 37 | public MyMemFaultHandler(String s) { variety=s; } 38 | public boolean uninitializedRead(Address address, int size, byte[] buf, int bufOffset) 39 | { 40 | Printer.printf("%s: uninitializedRead​(%s,%d,_,%x)\n", variety, address.toString(), size, bufOffset); 41 | return false; 42 | } 43 | public boolean unknownAddress(Address address, boolean write) 44 | { 45 | Printer.printf("%s: unknownAddress(%s,%b)\n", variety, address.toString(), write); 46 | return false; 47 | } 48 | } 49 | 50 | class AccruingMemFaultHandler implements MemoryFaultHandler { 51 | String variety; 52 | long ProgramBegin, ProgramEnd; 53 | Memory ProgramMem; 54 | public AccruingMemFaultHandler(String s, Memory m, long progBegin, long progEnd) { 55 | variety=s; 56 | ProgramMem = m; 57 | ProgramBegin = progBegin; 58 | ProgramEnd = progEnd; 59 | } 60 | public boolean uninitializedRead(Address address, int size, byte[] buf, int bufOffset) 61 | { 62 | //Printer.printf("%s: uninitializedRead​(%s,%d,_,%x) %d\n", variety, address.toString(), size, bufOffset, buf.length); 63 | //return false; 64 | long a = address.getOffset(); 65 | if(a >= ProgramBegin && a <= ProgramEnd) { 66 | try { 67 | byte[] chunk = new byte[size]; 68 | ProgramMem.getBytes(address, chunk); 69 | System.arraycopy(chunk, 0, buf, bufOffset, size); 70 | } 71 | catch(MemoryAccessException e) { 72 | return false; 73 | } 74 | return true; 75 | } 76 | return false; 77 | } 78 | public boolean unknownAddress(Address address, boolean write) 79 | { 80 | Printer.printf("%s: unknownAddress(%s,%b)\n", variety, address.toString(), write); 81 | return false; 82 | } 83 | } 84 | 85 | class LoggingMemorizingMemoryBank extends MemoryPageBank { 86 | ArrayList Accesses = new ArrayList(); 87 | long StackBegin, StackEnd; 88 | 89 | public LoggingMemorizingMemoryBank(AddressSpace spc, boolean isBigEndian, int ps, MemoryFaultHandler faultHandler, long stackLow, long stackHigh) { 90 | super(spc,isBigEndian,ps,faultHandler); 91 | StackBegin = stackLow; 92 | StackEnd = stackHigh; 93 | } 94 | 95 | // Log the low byte of all addresses targeted by 1-byte reads 96 | public int getChunk(long addrOffset, int size, byte[] res, boolean stop) { 97 | int iRes = super.getChunk(addrOffset, size, res, stop); 98 | if(size == 1) { 99 | // Commented-out code ensures that the address is on the stack 100 | //if(addrOffset >= StackBegin && addrOffset <= StackEnd) { 101 | // Accesses.add(new Pair(addrOffset,res[0])); 102 | Accesses.add((byte)(addrOffset&0xFFl)); 103 | } 104 | return iRes; 105 | } 106 | 107 | public void setChunk(long offset, int size, byte[] val) { 108 | super.setChunk(offset, size, val); 109 | //if(offset >= StackBegin && offset <= StackEnd) { 110 | // for(int i = 0; i < size; i++) { 111 | // long j = i+offset; 112 | // if(j <= StackEnd) { 113 | // Accesses.add(new Pair(j,val[i])); 114 | // } 115 | // } 116 | //} 117 | if(size == 1) 118 | Accesses.add((byte)(offset&0xFFl)); 119 | } 120 | } 121 | 122 | class EmulatorTraceGenerator { 123 | AddressSpace defaultSpace; 124 | LoggingMemorizingMemoryBank defaultMemoryBank; 125 | MemoryState ms; 126 | Emulate Emulator; 127 | Program CurrentProgram; 128 | HashSet
PrintfAddrs; 129 | 130 | public static final long[] PrintfLocations = {0x04010a5l,0x0401193l,0x04011b9l,0x0401deel,0x0402372l,0x0402388l}; 131 | public static final String[] Reg32Names = {"EAX","ECX","EDX","EBX","ESP","EBP","ESI","EDI"}; 132 | public static final long[] Reg32Values = {0x28abbcl,0x611856c0l,0x0l,0x0l,0x28ab50l,0x28ac08l,0x200283f0l,0x6119fe9fl}; 133 | public static final long ProgramBegin = 0x00400000l; 134 | public static final long ProgramEnd = 0x005201ffl; 135 | public static final long StackBegin = 0x0028ab50l; 136 | public static final long StackEnd = 0x0028ABFCl; 137 | public static final long InputBegin = 0x0028ABE8l; 138 | public static final long ExecBegin = 0x004011C5l; 139 | public static final long ExecEnd = 0x00402381l; 140 | 141 | void Init() { 142 | defaultMemoryBank.Accesses = new ArrayList(); 143 | SleighLanguage l = (SleighLanguage)CurrentProgram.getLanguage(); 144 | VarnodeTranslator vt = new VarnodeTranslator(CurrentProgram); 145 | 146 | for(int i = 0; i < Reg32Names.length; i++) 147 | ms.setValue(l.getRegister(Reg32Names[i]), Reg32Values[i]); 148 | } 149 | 150 | ArrayList execute(long desInput) { 151 | Address eaBeg = defaultSpace.getAddress(ExecBegin); 152 | Address eaEnd = defaultSpace.getAddress(ExecEnd); 153 | Init(); 154 | byte[] desArr = new byte[8]; 155 | for(int i = 0; i < 8; i++) 156 | desArr[i] = (byte)((desInput >> (8*i)) & 0xFFl); 157 | defaultMemoryBank.setChunk(InputBegin, 8, desArr); 158 | Emulator.setExecuteAddress(eaBeg); 159 | while(!eaEnd.equals(Emulator.getExecuteAddress())) { 160 | if(PrintfAddrs.contains(Emulator.getExecuteAddress())) 161 | Emulator.setExecuteAddress(Emulator.getExecuteAddress().add(5L)); 162 | //Printer.printf("Emulating %s\n", Emulator.getExecuteAddress().toString()); 163 | Emulator.executeInstruction(true); 164 | } 165 | return defaultMemoryBank.Accesses; 166 | } 167 | 168 | public EmulatorTraceGenerator(Program currentProgram) 169 | { 170 | CurrentProgram = currentProgram; 171 | SleighLanguage l = (SleighLanguage)currentProgram.getLanguage(); 172 | 173 | // Initialize AddressSpace objects 174 | defaultSpace = currentProgram.getAddressFactory().getDefaultAddressSpace(); 175 | AddressSpace registerSpace = currentProgram.getAddressFactory().getRegisterSpace(); 176 | AddressSpace uniqueSpace = currentProgram.getAddressFactory().getUniqueSpace(); 177 | 178 | // Create MemoryPageBank objects for the address spaces 179 | boolean isBigEndian = l.isBigEndian(); 180 | 181 | MemoryFaultHandler acc = new AccruingMemFaultHandler("default", currentProgram.getMemory(), ProgramBegin, ProgramEnd); 182 | defaultMemoryBank = new LoggingMemorizingMemoryBank(defaultSpace, isBigEndian, 4096, acc, StackBegin, StackEnd); 183 | MemoryPageBank registerMemoryBank = new MemoryPageBank(registerSpace, false, 4096, new MyMemFaultHandler("register")); 184 | MemoryPageBank uniqueMemoryBank = new MemoryPageBank(uniqueSpace, false, 4096, new MyMemFaultHandler("unique")); 185 | 186 | // Create and initialize the MemoryState 187 | ms = new MemoryState(l); 188 | ms.setMemoryBank(registerMemoryBank); 189 | ms.setMemoryBank(defaultMemoryBank); 190 | 191 | // Initialize the BreakTable 192 | BreakTableCallBack bt = new BreakTableCallBack(l); 193 | 194 | // Create the emulator object 195 | Emulator = new Emulate(l, ms, bt); 196 | 197 | PrintfAddrs = new HashSet
(); 198 | for(long printfRef : PrintfLocations) 199 | PrintfAddrs.add(defaultSpace.getAddress(printfRef)); 200 | } 201 | } 202 | 203 | public class WyseurWBDES extends GhidraScript { 204 | 205 | public long bswap64(long desInput) { 206 | long ptReversed = 0l; 207 | for(int v = 0; v < 8; v++) 208 | ptReversed |= ((desInput >> (v*8)) & 0xFFl) << ((7-v)*8); 209 | return ptReversed; 210 | } 211 | 212 | Pair>, List> getSamples(int numSamples) { 213 | List> samples = new ArrayList>(); 214 | List pts = new ArrayList(); 215 | EmulatorTraceGenerator et = new EmulatorTraceGenerator(currentProgram); 216 | et.Init(); 217 | for(int i = 0; i < numSamples; i++) { 218 | Printer.printf("Collecting sample %d\n", i); 219 | if(monitor.isCancelled()) 220 | return null; 221 | long desInput = ThreadLocalRandom.current().nextLong(); 222 | samples.add(et.execute(desInput)); 223 | pts.add(bswap64(desInput)); 224 | } 225 | return new Pair>, List>(samples, pts); 226 | } 227 | 228 | public void doCPA(int nSamples) { 229 | Pair>, List> samples = getSamples(nSamples); 230 | if(samples == null) 231 | return; 232 | List points = TraceAggregator.aggregate(samples.x); 233 | PowerAnalysisFactory.desCPA().analyzeTrace(points,samples.y); 234 | } 235 | 236 | public void doDPA(int nSamplesPer, int nTimes) { 237 | List> allSamples = new ArrayList>(); 238 | List allPlaintexts = new ArrayList(); 239 | for(int i = 0; i < nTimes; i++) { 240 | Printer.printf("DPA(%d): collecting %d more samples\n", i, nSamplesPer); 241 | Pair>, List> samples = getSamples(nSamplesPer); 242 | if(samples == null) 243 | return; 244 | allSamples.addAll(samples.x); 245 | allPlaintexts.addAll(samples.y); 246 | List points = TraceAggregator.aggregate(allSamples); 247 | PowerAnalysisFactory.desDPA().analyzeTrace(points,allPlaintexts); 248 | } 249 | } 250 | 251 | public void run() throws Exception { 252 | if (currentProgram == null) { 253 | throw new NullPointerException("No program available, exit!\n"); 254 | } 255 | 256 | PluginTool tool = state.getTool(); 257 | // Initialize the Printer class, so that other classes can print 258 | // debug information. 259 | Printer.Set(tool.getService(ConsoleService.class)); 260 | Printer.SetFileOutputPath("c:\\temp\\ghidra-debug2.txt"); 261 | 262 | doCPA(20); 263 | 264 | doDPA(20, 100); 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /src/main/help/help/TOC_Source.xml: -------------------------------------------------------------------------------- 1 | 2 | 49 | 50 | 51 | 52 | 57 | 58 | -------------------------------------------------------------------------------- /src/main/help/help/shared/Frontpage.css: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | WARNING! 18 | This file is copied to all help directories. If you change this file, you must copy it 19 | to each src/main/help/help/shared directory. 20 | 21 | 22 | Java Help Note: JavaHelp does not accept sizes (like in 'margin-top') in anything but 23 | px (pixel) or with no type marking. 24 | 25 | */ 26 | 27 | body { margin-bottom: 50px; margin-left: 10px; margin-right: 10px; margin-top: 10px; } /* some padding to improve readability */ 28 | li { font-family:times new roman; font-size:14pt; } 29 | h1 { color:#000080; font-family:times new roman; font-size:36pt; font-style:italic; font-weight:bold; text-align:center; } 30 | h2 { margin: 10px; margin-top: 20px; color:#984c4c; font-family:times new roman; font-size:18pt; font-weight:bold; } 31 | h3 { margin-left: 10px; margin-top: 20px; color:#0000ff; font-family:times new roman; font-size:14pt; font-weight:bold; } 32 | h4 { margin-left: 10px; margin-top: 20px; font-family:times new roman; font-size:14pt; font-style:italic; } 33 | 34 | /* 35 | P tag code. Most of the help files nest P tags inside of blockquote tags (the was the 36 | way it had been done in the beginning). The net effect is that the text is indented. In 37 | modern HTML we would use CSS to do this. We need to support the Ghidra P tags, nested in 38 | blockquote tags, as well as naked P tags. The following two lines accomplish this. Note 39 | that the 'blockquote p' definition will inherit from the first 'p' definition. 40 | */ 41 | p { margin-left: 40px; font-family:times new roman; font-size:14pt; } 42 | blockquote p { margin-left: 10px; } 43 | 44 | p.providedbyplugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px } 45 | p.ProvidedByPlugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px } 46 | p.relatedtopic { color:#800080; margin-left: 10px; font-size:14pt; } 47 | p.RelatedTopic { color:#800080; margin-left: 10px; font-size:14pt; } 48 | 49 | /* 50 | We wish for a tables to have space between it and the preceding element, so that text 51 | is not too close to the top of the table. Also, nest the table a bit so that it is clear 52 | the table relates to the preceding text. 53 | */ 54 | table { margin-left: 20px; margin-top: 10px; width: 80%;} 55 | td { font-family:times new roman; font-size:14pt; vertical-align: top; } 56 | th { font-family:times new roman; font-size:14pt; font-weight:bold; background-color: #EDF3FE; } 57 | 58 | code { color: black; font-family: courier new; font-size: 14pt; } 59 | -------------------------------------------------------------------------------- /src/main/help/help/topics/programanalysislibrary/help.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | Skeleton Help File for a Module 13 | 14 | 15 | 16 | 17 |

Skeleton Help File for a Module

18 | 19 |

This is a simple skeleton help topic. For a better description of what should and should not 20 | go in here, see the "sample" Ghidra extension in the Extensions/Ghidra directory, or see your 21 | favorite help topic. In general, language modules do not have their own help topics.

22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/absint/tvl/TVLAbstractGhidraState.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.absint.tvl; 2 | 3 | import java.util.HashMap; 4 | 5 | import ghidra.pal.util.Printer; 6 | import ghidra.program.model.pcode.Varnode; 7 | 8 | // This class holds an abstract machine state: 9 | // * Register Varnodes 10 | // * Unique Varnodes 11 | // * A map from memory object id to its TVLAbstractMemory object 12 | public class TVLAbstractGhidraState { 13 | public TVLAbstractMemory Registers; 14 | public TVLAbstractMemory Uniques; 15 | public HashMap Memories; 16 | public boolean bigEndian; 17 | 18 | public TVLAbstractGhidraState(boolean isBigEndian) 19 | { 20 | Registers = new TVLAbstractMemory(isBigEndian); 21 | Uniques = new TVLAbstractMemory(isBigEndian); 22 | Memories = new HashMap<>(); 23 | bigEndian = isBigEndian; 24 | } 25 | 26 | // Reset all state 27 | public void clear() 28 | { 29 | Registers.clear(); 30 | Uniques.clear(); 31 | Memories.clear(); 32 | } 33 | 34 | // Deep copy 35 | public TVLAbstractGhidraState clone() 36 | { 37 | TVLAbstractGhidraState r = new TVLAbstractGhidraState(bigEndian); 38 | r.Registers = Registers.clone(); 39 | r.Uniques = Uniques.clone(); 40 | HashMap newMemories = new HashMap<>(); 41 | for(HashMap.Entry entry : Memories.entrySet()) 42 | newMemories.put(entry.getKey(), entry.getValue().clone()); 43 | r.Memories = newMemories; 44 | return r; 45 | } 46 | 47 | // Reset only the unique values. The logic here is that the unique values 48 | // should only be defined and used in the scope of a single instruction's 49 | // pcode, and therefore don't need to be tracked between instructions. 50 | // Thus you can save time and memory by clearing them between instructions. 51 | public void ClearUniques() 52 | { 53 | Uniques.clear(); 54 | } 55 | 56 | // Associate a varnode with a three-valued bitvector. 57 | public void Associate(Varnode dest, TVLBitVector bv) 58 | { 59 | if(dest.isRegister()) 60 | { 61 | // Printer.println("Associate(): "+dest.toString()+" -> "+bv.toString()); 62 | Registers.StoreWholeQuantity(dest, bv); 63 | } 64 | else if(dest.isUnique()) 65 | Uniques.StoreWholeQuantity(dest,bv); 66 | else 67 | { 68 | Printer.println("Associate(): Unknown destination "+dest.toString()); 69 | // Should throw an exception here... 70 | } 71 | } 72 | 73 | // Store a value bv into memory mem at address addr. 74 | public void Store(Varnode mem, long addr, TVLBitVector bv) 75 | { 76 | TVLAbstractMemory am; 77 | 78 | // Use the "offset" of the memory Varnode as an identifier. 79 | // I found it was necessary to do this instead of using the Varnode 80 | // itself as an index... maybe this is related to that Varnode equality 81 | // bug I saw on the github issues page... 82 | long memOffset = mem.getOffset(); 83 | if(Memories.containsKey(memOffset)) 84 | am = Memories.get(memOffset); 85 | else 86 | { 87 | am = new TVLAbstractMemory(bigEndian); 88 | Memories.put(memOffset, am); 89 | } 90 | am.StoreWholeQuantity(addr, bv); 91 | } 92 | 93 | // Retrieve the value of a register, memory, or constant. 94 | public TVLBitVector Lookup(Varnode what) 95 | { 96 | if(what.isConstant()) 97 | return new TVLBitVector(new GhidraSizeAdapter(what.getSize()), what.getOffset()); 98 | if(what.isRegister()) 99 | return Registers.LookupWholeQuantity(what); 100 | if(what.isUnique()) 101 | return Uniques.LookupWholeQuantity(what); 102 | // If this happens, read the documentation 103 | // Should throw an exception here 104 | Printer.println("Lookup(): Unknown source "+what.toString()); 105 | return new TVLBitVector(new GhidraSizeAdapter(what.getSize())); 106 | } 107 | 108 | // Load a value from memory. 109 | public TVLBitVector Load(Varnode mem, long addr, int size) 110 | { 111 | long memOffset = mem.getOffset(); 112 | if(!Memories.containsKey(memOffset)) 113 | return new TVLBitVector(size); 114 | return Memories.get(memOffset).LookupWholeQuantity(addr, size); 115 | } 116 | 117 | // Remove all information about an entire memory space -- this should 118 | // happen upon writes to unknown locations. 119 | public void MakeMemoryTop(Varnode mem) 120 | { 121 | Memories.remove(mem.getOffset()); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/absint/tvl/TVLAbstractGhidraStateFactory.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.absint.tvl; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.concurrent.ThreadLocalRandom; 6 | 7 | import ghidra.pal.util.Pair; 8 | import ghidra.program.model.lang.Language; 9 | import ghidra.program.model.lang.Register; 10 | import ghidra.program.model.listing.Program; 11 | import ghidra.program.model.pcode.Varnode; 12 | import ghidra.program.model.pcode.VarnodeTranslator; 13 | 14 | // This class is meant to wrap up common usage scenarios for creating multiple 15 | // TVLGhidraAbstractState objects. Basically, the user may specify registers 16 | // that should be initialized, in two flavors: 17 | // 1) initialized to some value, but not any specific one (randomly-generated) 18 | // 2) initialized to a fixed value 19 | public class TVLAbstractGhidraStateFactory { 20 | 21 | // Make initial states when some variables should be randomly initialized. 22 | // Actually generates twice as many states as specified by numInputStates. 23 | public static final List MakeInputStatesRandInit( 24 | Program currentProgram, 25 | int numInputStates, 26 | List randInitVars, 27 | List> fixedInitVars) throws Exception 28 | { 29 | // Get the translator for strings -> Varnode 30 | Language l = currentProgram.getLanguage(); 31 | VarnodeTranslator vt = new VarnodeTranslator(currentProgram); 32 | List inputStates = new ArrayList(); 33 | 34 | // For the variables that should be randomly initialized 35 | for(int i = 0; i < numInputStates; i++) { 36 | 37 | // Create two states, one for the random value, the other for its 38 | // inverse. 39 | TVLAbstractGhidraState stateRegular = new TVLAbstractGhidraState(l.isBigEndian()); 40 | TVLAbstractGhidraState stateInverse = new TVLAbstractGhidraState(l.isBigEndian()); 41 | 42 | // For each specified variable... 43 | for(String strRvar : randInitVars) { 44 | 45 | // Get its Varnode, or throw an exception if the user-specified 46 | // string did not name a designated varnode. 47 | Register rReg = l.getRegister(strRvar); 48 | if(rReg == null) 49 | throw new IllegalArgumentException(String.format("\"%s\": cannot retrieve corresponding Register object", strRvar)); 50 | Varnode vReg = vt.getVarnode(rReg); 51 | 52 | // Initialize one state with the random value, the other with 53 | // its inverse 54 | long regVal = ThreadLocalRandom.current().nextLong(); 55 | stateRegular.Associate(vReg, new TVLBitVector(new GhidraSizeAdapter(vReg.getSize()), regVal)); 56 | stateInverse.Associate(vReg, new TVLBitVector(new GhidraSizeAdapter(vReg.getSize()), ~regVal)); 57 | } 58 | // Apply any specified fixed values. 59 | inputStates.add(ApplyFixedVars(currentProgram, stateRegular, fixedInitVars)); 60 | inputStates.add(ApplyFixedVars(currentProgram, stateInverse, fixedInitVars)); 61 | } 62 | return inputStates; 63 | } 64 | 65 | // Create input states with fixed values for specified variables 66 | public static final TVLAbstractGhidraState ApplyFixedVars( 67 | Program currentProgram, 68 | TVLAbstractGhidraState stateIn, 69 | List> fixedInitVars) throws Exception { 70 | 71 | // If no fixed variables, return the state as-is. 72 | if(fixedInitVars == null) 73 | return stateIn; 74 | 75 | // Get the translator for strings -> Varnode 76 | Language l = currentProgram.getLanguage(); 77 | VarnodeTranslator vt = new VarnodeTranslator(currentProgram); 78 | 79 | // For each specified variable... 80 | for(Pair fi : fixedInitVars) { 81 | // Get its Varnode, or throw an exception if the user-specified 82 | // string did not name a designated varnode. 83 | Register rReg = l.getRegister(fi.x); 84 | if(rReg == null) 85 | throw new IllegalArgumentException(String.format("\"%s\": cannot retrieve corresponding Register object", fi.x)); 86 | Varnode vReg = vt.getVarnode(rReg); 87 | 88 | // Fix the value of the varnode to the specified one 89 | stateIn.Associate(vReg, new TVLBitVector(new GhidraSizeAdapter(vReg.getSize()), fi.y)); 90 | } 91 | return stateIn; 92 | } 93 | 94 | // Create an input state with only certain values fixed. 95 | public static final TVLAbstractGhidraState MakeInputStateFixedInit( 96 | Program currentProgram, 97 | List> fixedInitVars) throws Exception 98 | { 99 | Language l = currentProgram.getLanguage(); 100 | return ApplyFixedVars(currentProgram, new TVLAbstractGhidraState(l.isBigEndian()), fixedInitVars); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/absint/tvl/TVLAbstractGhidraStateUtil.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.absint.tvl; 2 | 3 | import java.util.HashMap; 4 | 5 | // Implements Join on the level of AbstractMemory objects, memory HashMaps, 6 | // and entire state objects. 7 | public final class TVLAbstractGhidraStateUtil { 8 | private TVLAbstractGhidraStateUtil() {} 9 | 10 | // Join two individual TVLAbstractMemory objects. 11 | public static TVLAbstractMemory Join(TVLAbstractMemory lhs, TVLAbstractMemory rhs) { 12 | TVLAbstractMemory smaller, larger; 13 | 14 | // Optimization: start with the smaller memory. 15 | if(lhs.Contents.size() <= rhs.Contents.size()) { 16 | smaller = lhs; 17 | larger = rhs; 18 | } 19 | else { 20 | smaller = rhs; 21 | larger = lhs; 22 | } 23 | // Join together the individual entities. 24 | TVLAbstractMemory out = new TVLAbstractMemory(lhs.bigEndian); 25 | for(HashMap.Entry entry : smaller.Contents.entrySet()){ 26 | // If the other map has an entity with the same key... 27 | if(larger.Contents.containsKey(entry.getKey())) { 28 | TVLBitVector other = larger.Contents.get(entry.getKey()); 29 | TVLBitVector thisbv = entry.getValue(); 30 | TVLBitVector joined = TVLBitVectorUtil.Join(other,thisbv); 31 | 32 | // Only add them to the output if non-Top. 33 | if(!joined.isTop()) 34 | out.Contents.put(entry.getKey(), joined); 35 | } 36 | } 37 | return out; 38 | } 39 | 40 | public static HashMap Join(HashMap lhs, HashMap rhs) { 41 | HashMap smaller, larger; 42 | 43 | // Optimization: start with the smaller HashMap. 44 | if(lhs.size() <= rhs.size()) { 45 | smaller = lhs; 46 | larger = rhs; 47 | } 48 | else { 49 | smaller = rhs; 50 | larger = lhs; 51 | } 52 | 53 | // Join together the values in the map by key. 54 | HashMap memOut = new HashMap(); 55 | for(HashMap.Entry entry : smaller.entrySet()) { 56 | // If the other map has an entity with the same key... 57 | if(larger.containsKey(entry.getKey())) { 58 | TVLAbstractMemory other = larger.get(entry.getKey()); 59 | TVLAbstractMemory thismem = entry.getValue(); 60 | TVLAbstractMemory joined = Join(other,thismem); 61 | // Join them with the function above and add to result. 62 | memOut.put(entry.getKey(), joined); 63 | } 64 | } 65 | return memOut; 66 | } 67 | 68 | // Join two entire state objects. 69 | public static TVLAbstractGhidraState Join(TVLAbstractGhidraState lhs, TVLAbstractGhidraState rhs) { 70 | TVLAbstractGhidraState out = new TVLAbstractGhidraState(lhs.bigEndian); 71 | out.Registers = Join(lhs.Registers, rhs.Registers); 72 | out.Uniques = Join(lhs.Uniques, rhs.Uniques); 73 | out.Memories = Join(lhs.Memories, rhs.Memories); 74 | return out; 75 | } 76 | 77 | // Join two individual TVLAbstractMemory objects. 78 | public static boolean isEqualTo(TVLAbstractMemory lhs, TVLAbstractMemory rhs) { 79 | TVLAbstractMemory smaller, larger; 80 | 81 | // Optimization: start with the smaller memory. 82 | if(lhs.Contents.size() <= rhs.Contents.size()) { 83 | smaller = lhs; 84 | larger = rhs; 85 | } 86 | else { 87 | smaller = rhs; 88 | larger = lhs; 89 | } 90 | 91 | for(HashMap.Entry entry : smaller.Contents.entrySet()){ 92 | // If the other map has an entity with the same key... 93 | if(larger.Contents.containsKey(entry.getKey())) { 94 | TVLBitVector other = larger.Contents.get(entry.getKey()); 95 | TVLBitVector thisbv = entry.getValue(); 96 | if(!TVLBitVectorUtil.isEqualTo(other, thisbv)) { 97 | return false; 98 | } 99 | } 100 | else if(!entry.getValue().isTop()) { 101 | return false; 102 | } 103 | } 104 | for(HashMap.Entry entry : larger.Contents.entrySet()){ 105 | // If the other map has an entity with the same key... 106 | if(smaller.Contents.containsKey(entry.getKey())) 107 | continue; 108 | else if(!entry.getValue().isTop()) { 109 | return false; 110 | } 111 | } 112 | return true; 113 | } 114 | 115 | // Join two individual TVLAbstractMemory objects. 116 | public static boolean isEqualTo(HashMap lhs, HashMap rhs) { 117 | HashMap smaller, larger; 118 | 119 | // Optimization: start with the smaller HashMap. 120 | if(lhs.size() <= rhs.size()) { 121 | smaller = lhs; 122 | larger = rhs; 123 | } 124 | else { 125 | smaller = rhs; 126 | larger = lhs; 127 | } 128 | 129 | for(HashMap.Entry entry : smaller.entrySet()){ 130 | // If the other map has an entity with the same key... 131 | if(larger.containsKey(entry.getKey())) { 132 | TVLAbstractMemory other = larger.get(entry.getKey()); 133 | TVLAbstractMemory thisbv = entry.getValue(); 134 | if(!isEqualTo(other, thisbv)) { 135 | return false; 136 | } 137 | } 138 | else if(!entry.getValue().isTop()) { 139 | return false; 140 | } 141 | } 142 | for(HashMap.Entry entry : larger.entrySet()){ 143 | // If the other map has an entity with the same key... 144 | if(smaller.containsKey(entry.getKey())) 145 | continue; 146 | else if(!entry.getValue().isTop()) { 147 | return false; 148 | } 149 | } 150 | return true; 151 | } 152 | 153 | // Join two entire state objects. 154 | public static boolean isEqualTo(TVLAbstractGhidraState lhs, TVLAbstractGhidraState rhs) { 155 | if(!isEqualTo(lhs.Registers, rhs.Registers)) 156 | return false; 157 | if(!isEqualTo(lhs.Uniques, rhs.Uniques)) 158 | return false; 159 | if(!isEqualTo(lhs.Memories, rhs.Memories)) 160 | return false; 161 | return true; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/absint/tvl/TVLAbstractInterpretBlock.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.absint.tvl; 2 | 3 | import ghidra.program.model.listing.Instruction; 4 | import ghidra.program.model.listing.Program; 5 | import ghidra.program.model.pcode.PcodeOp; 6 | import ghidra.pal.generic.VisitorUnimplementedException; 7 | 8 | // The base TVLAbstractInterpreter doesn't define the branch operators. This 9 | // class implements them. 10 | public class TVLAbstractInterpretBlock extends TVLAbstractInterpreter { 11 | 12 | // This variable holds the 3-valued evaluation for the last conditional 13 | // branch encountered. 14 | public TVLBitVector LastBranchCondition; 15 | 16 | // This holds the 3-valued evaluation for the last indirect branch. 17 | public TVLBitVector LastIndirectBranchDestination; 18 | public TVLAbstractInterpretBlock(Program p) { 19 | super(p.getLanguage().isBigEndian()); 20 | } 21 | 22 | // Before each PcodeOp, clear the branch state variables. 23 | public void VisitorBefore(Instruction instr, PcodeOp pcode) { 24 | LastBranchCondition = null; 25 | LastIndirectBranchDestination = null; 26 | } 27 | 28 | // Update the last indirect branch destination. 29 | public void visit_BRANCHIND(Instruction instr, PcodeOp pcode) throws VisitorUnimplementedException { 30 | TVLBitVector addr = visit_Varnode(instr,pcode,pcode.getInput(0)); 31 | LastIndirectBranchDestination = addr; 32 | } 33 | 34 | public void visit_RETURN(Instruction instr, PcodeOp pcode) throws VisitorUnimplementedException { 35 | visit_BRANCHIND(instr, pcode); 36 | } 37 | 38 | // Update the last conditional branch evaluation. 39 | public void visit_CBRANCH(Instruction instr, PcodeOp pcode) throws VisitorUnimplementedException { 40 | TVLBitVector condition = visit_Varnode(instr,pcode,pcode.getInput(1)); 41 | LastBranchCondition = condition; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/absint/tvl/TVLAbstractInterpretCFG.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.absint.tvl; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.List; 6 | import java.util.Iterator; 7 | 8 | import ghidra.program.model.address.Address; 9 | import ghidra.program.model.listing.Program; 10 | import ghidra.program.model.pcode.PcodeOp; 11 | import ghidra.pal.util.JavaUtil; 12 | import ghidra.pal.util.Pair; 13 | import ghidra.pal.util.Printer; 14 | import ghidra.pal.cfg.CFGEdge; 15 | import ghidra.pal.cfg.CFGEdgeType; 16 | import ghidra.pal.cfg.CFGVertex; 17 | import ghidra.pal.cfg.CFGPriorityQueue; 18 | import ghidra.pal.cfg.CFG; 19 | 20 | public class TVLAbstractInterpretCFG { 21 | Program currentProgram; 22 | public TVLAbstractInterpretCFGStateBundle State; 23 | boolean debug; 24 | public TVLAbstractInterpretCFG(Program cp) { 25 | currentProgram = cp; 26 | debug = false; 27 | State = new TVLAbstractInterpretCFGStateBundle(cp); 28 | } 29 | public TVLAbstractInterpretCFG(Program cp, boolean dbg) { 30 | currentProgram = cp; 31 | debug = dbg; 32 | State = new TVLAbstractInterpretCFGStateBundle(cp); 33 | } 34 | public void setDebug(boolean d) { 35 | debug = d; 36 | } 37 | 38 | void DebugPrint(String format, Object... args) { 39 | if(debug) 40 | Printer.printf(format, args); 41 | } 42 | 43 | // Apply the TVLAbstractInterpretBlock visitor to each PcodeOp on vertex v. 44 | // v: the vertex 45 | // stateIn: the abstract state on input 46 | // Return value: a list of edges that should be explored next, as well as 47 | // the abstract state at the end of the block after abstract interpretation. 48 | // Preconditions: the State object should have been initialized 49 | public Pair,PcodeOp>>, TVLAbstractGhidraState> DoBlock( 50 | CFGVertex,PcodeOp> v, 51 | TVLAbstractGhidraState stateIn) throws Exception 52 | { 53 | 54 | // Initialize the block-level abstract interpreter. 55 | State.pb.AbstractState = stateIn; 56 | 57 | // Abstract interpret each statement on the block. 58 | List,PcodeOp>> pcodeLocOpList = v.getEntities(); 59 | Iterator,PcodeOp>> pcIt = pcodeLocOpList.iterator(); 60 | 61 | Pair,PcodeOp> lastEntity = null; 62 | while(pcIt.hasNext()) { 63 | Pair,PcodeOp> p = pcIt.next(); 64 | // Printer.printf("%s %s\n", p.x, p.y); 65 | // This null is here because I had my visitor methods take more 66 | // arguments than were truly necessary. It wasn't a big deal for 67 | // how I was using the class at the time... but it is now. 68 | State.pb.visit(null, p.y); 69 | lastEntity = p; 70 | } 71 | 72 | // validTargets is one component of the return value; it specifies the 73 | // outgoing edges that weren't eliminated by the analysis. 74 | List,PcodeOp>> validTargets = new ArrayList,PcodeOp>>(); 75 | 76 | // outEdges: list of all outgoing edges from the block in question. 77 | Collection,PcodeOp>> outEdges = State.cfg.getOutEdges(v); 78 | 79 | // Try to prune the outgoing edges, if this was a conditional branch 80 | // and we discovered that only one of the edges would be taken under 81 | // the current input state. 82 | if(lastEntity != null && lastEntity.y != null) { 83 | 84 | switch(lastEntity.y.getOpcode()) { 85 | // If this was a conditional branch, determine if the branch 86 | // condition was determined to always evaluate to a constant 87 | // under the given input state. 88 | case PcodeOp.CBRANCH: 89 | TVLBitVector branchCondition = State.pb.LastBranchCondition; 90 | 91 | // This shouldn't happen... the last PcodeOp was a CBRANCH, 92 | // which should have resulted in pb.LastBranchCondition 93 | // being initialized (because the abstract interpretation 94 | // code for that case always sets the variable). 95 | if(branchCondition == null) { 96 | DebugPrint("%s: abstract interpreter did not record branch condition?\n", v.getLocator().toString()); 97 | validTargets.addAll(outEdges); 98 | } 99 | 100 | // This should always happen. 101 | else { 102 | // See if the branch condition evaluated to a constant. 103 | Pair p = branchCondition.GetConstantValue(); 104 | if(p != null) { 105 | boolean wasTaken = JavaUtil.CompareLongs(p.y,1L); 106 | DebugPrint("%s: resolved opaque predicate! always %staken\n", lastEntity.x.toString(), wasTaken ? "" : "not "); 107 | CFGEdgeType desired = wasTaken ? CFGEdgeType.COND_TAKEN : CFGEdgeType.COND_NOTTAKEN; 108 | 109 | // Only use the taken/not taken edge, depending on 110 | // how the branch condition evaluated. 111 | Iterator,PcodeOp>> eit = outEdges.iterator(); 112 | while(eit.hasNext()) { 113 | CFGEdge,PcodeOp> edge = eit.next(); 114 | if(edge.getEdgeType().equals(desired)) 115 | validTargets.add(edge); 116 | } 117 | } 118 | 119 | // If it didn't evaluate to a constant, use both edges. 120 | else { 121 | DebugPrint("%s: could not resolve!\n", lastEntity.x.toString()); 122 | validTargets.addAll(outEdges); 123 | } 124 | } 125 | break; 126 | 127 | // I could technically handle these also by looking at 128 | // pb.LastIndirectBranchDestination. 129 | case PcodeOp.BRANCHIND: 130 | case PcodeOp.RETURN: 131 | validTargets.addAll(outEdges); 132 | break; 133 | 134 | // For any other case, use all outgoing edges. 135 | default: 136 | validTargets.addAll(outEdges); 137 | break; 138 | } 139 | } 140 | // Return the list of possible targets, as well as the state at the end 141 | // of abstract interpretation. 142 | return new Pair,PcodeOp>>, TVLAbstractGhidraState>(validTargets, State.pb.AbstractState); 143 | } 144 | 145 | public void DoCFG(CFG,PcodeOp> cfg, TVLAbstractGhidraState rootInputState) throws Exception { 146 | // This stuff should be moved outside of this function. 147 | State.Init(cfg, rootInputState); 148 | DoCFGInner(); 149 | } 150 | 151 | void DoCFGInner() throws Exception { 152 | 153 | // Allocate the worklist (reverse post-order priority queue). 154 | CFGPriorityQueue,PcodeOp> jpq = new CFGPriorityQueue,PcodeOp>(State.cfg, true); 155 | 156 | // Add all initial vertices to the worklist. 157 | Iterator,PcodeOp>> ivIt = State.cfg.getInitialVertices().iterator(); 158 | while(ivIt.hasNext()) { 159 | CFGVertex,PcodeOp> curr = ivIt.next(); 160 | DebugPrint("%s: initial vertex\n", curr.getLocator().toString()); 161 | jpq.PQ.add(curr); 162 | } 163 | 164 | // The worklist algorithm itself. 165 | int numIterations = 0; 166 | while(!jpq.PQ.isEmpty()) { 167 | // De-queue the current vertex. 168 | numIterations++; 169 | CFGVertex,PcodeOp> currVertex = jpq.PQ.poll(); 170 | Pair currLocator = currVertex.getLocator(); 171 | DebugPrint("Iteration %d: vertex at %s\n", numIterations, currLocator.toString()); 172 | 173 | // Form the initial state for the current vertex. 174 | TVLAbstractGhidraState joined = State.getInputState(currVertex); 175 | 176 | // Now, abstract interpret the block in the given input state. 177 | Pair,PcodeOp>>, TVLAbstractGhidraState> output = DoBlock(currVertex, joined); 178 | 179 | // If the output state did not change vs. last iteration, skip. 180 | if(!State.updateOutputState(currVertex, output.y)) 181 | continue; 182 | 183 | // Otherwise, add all targeted children to the worklist. 184 | List,PcodeOp>> newTargets = output.x; 185 | Iterator,PcodeOp>> it = newTargets.iterator(); 186 | while(it.hasNext()) { 187 | CFGEdge,PcodeOp> edgeNext = it.next(); 188 | CFGVertex,PcodeOp> vertNext = edgeNext.getEnd(); 189 | jpq.PQ.add(vertNext); 190 | } 191 | } 192 | // Done. 193 | DebugPrint("%s: %d iterations to fixedpoint\n", State.cfg.getBeginAddr().toString(), numIterations); 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/absint/tvl/TVLAbstractInterpretCFGStateBundle.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.absint.tvl; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | 9 | import ghidra.pal.cfg.CFG; 10 | import ghidra.pal.cfg.CFGEdge; 11 | import ghidra.pal.cfg.CFGVertex; 12 | import ghidra.pal.util.Pair; 13 | import ghidra.pal.util.Printer; 14 | import ghidra.program.model.address.Address; 15 | import ghidra.program.model.listing.Program; 16 | import ghidra.program.model.pcode.PcodeOp; 17 | 18 | // This class holds the state used for performing the abstract interpretation 19 | // upon a CFG. Thus, we decouple the state data from the code that computes it, 20 | // to simplify code that consumes the analysis results. 21 | public class TVLAbstractInterpretCFGStateBundle { 22 | Program currentProgram; 23 | TVLAbstractGhidraState rootInputState; 24 | HashSet,PcodeOp>> initialVerticesSet; 25 | HashMap,PcodeOp>, TVLAbstractGhidraState> outputStates; 26 | boolean debug; 27 | 28 | public TVLAbstractInterpretBlock pb; 29 | public CFG,PcodeOp> cfg; 30 | public TVLAbstractInterpretCFGStateBundle(Program p) { 31 | currentProgram = p; 32 | pb = new TVLAbstractInterpretBlock(currentProgram); 33 | outputStates = new HashMap,PcodeOp>, TVLAbstractGhidraState>(); 34 | debug = false; 35 | } 36 | public void Init(CFG,PcodeOp> g, TVLAbstractGhidraState initialState) { 37 | cfg = g; 38 | rootInputState = initialState; 39 | 40 | // Store the initial vertices as a HashSet for fast lookups. 41 | initialVerticesSet = new HashSet,PcodeOp>>(); 42 | initialVerticesSet.addAll(g.getInitialVertices()); 43 | } 44 | public void setDebug(boolean d) { 45 | debug = d; 46 | } 47 | void DebugPrint(String format, Object... args) { 48 | if(debug) 49 | Printer.printf(format, args); 50 | } 51 | 52 | // Get the input state for a given vertex. If it's an initial vertex, clone 53 | // the cached one. Otherwise, form it by merging the output states from its 54 | // predecessors. 55 | public TVLAbstractGhidraState getInputState(CFGVertex,PcodeOp> currVertex) { 56 | Pair currLocator = currVertex.getLocator(); 57 | TVLAbstractGhidraState joined = null; 58 | 59 | // If it was an initial vertex, clone the root state. 60 | if(initialVerticesSet.contains(currVertex)) { 61 | DebugPrint("%s: was initial vertex, using saved input state\n", currLocator.toString()); 62 | return rootInputState.clone(); 63 | } 64 | // Otherwise, merge outgoing states from incoming edges. 65 | DebugPrint("%s: was not initial vertex, joining output states from successors\n", currLocator.toString()); 66 | 67 | // Iterate through all incoming edges. 68 | Iterator,PcodeOp>> itInEdges = cfg.getInEdges(currVertex).iterator(); 69 | while(itInEdges.hasNext()) { 70 | CFGEdge,PcodeOp> e = itInEdges.next(); 71 | CFGVertex,PcodeOp> inVertex = e.getStart(); 72 | 73 | // If we have cached data for the output of an incoming vertex... 74 | if(outputStates.containsKey(inVertex)) { 75 | DebugPrint("%s: incoming vertex %s had cached state\n", currLocator.toString(), inVertex.getLocator().toString()); 76 | TVLAbstractGhidraState inVertexState = outputStates.get(inVertex); 77 | // If this is the first incoming vertex with state, replace 78 | // the null pointer with a clone of the state. 79 | if(joined == null) { 80 | DebugPrint("\tPrevious state was null, replacing\n"); 81 | joined = inVertexState.clone(); 82 | } 83 | // Otherwise, join the existing state with this one. 84 | else { 85 | DebugPrint("\tHad previous state\n"); 86 | joined = TVLAbstractGhidraStateUtil.Join(joined, inVertexState); 87 | } 88 | } 89 | 90 | // If we didn't have cached data, skip it. 91 | else { 92 | DebugPrint("%s: incoming vertex %s did not have cached state\n", currLocator.toString(), inVertex.getLocator().toString()); 93 | } 94 | } 95 | // If no incoming vertex had cached data, create a Top state. 96 | if(joined == null) { 97 | DebugPrint("%s: no cached incoming vertex states, using top\n", currLocator.toString()); 98 | return new TVLAbstractGhidraState(currentProgram.getLanguage().isBigEndian()); 99 | } 100 | // Return the joined state. 101 | return joined; 102 | } 103 | 104 | // Associate the output state with the vertex. If it's the same as the 105 | // previous output state, there's no need to process the children again. 106 | public boolean updateOutputState(CFGVertex,PcodeOp> currVertex, TVLAbstractGhidraState out) { 107 | // If we've seen this block before, and the output state hasn't 108 | // changed, there's no need to process the children. 109 | if(outputStates.containsKey(currVertex)) { 110 | TVLAbstractGhidraState saved = outputStates.get(currVertex); 111 | if(TVLAbstractGhidraStateUtil.isEqualTo(saved,out)) 112 | return false; 113 | } 114 | // If we haven't seen it before, or if the output changed, update 115 | // the output state. 116 | outputStates.put(currVertex, out); 117 | return true; 118 | } 119 | 120 | // After analysis, get a list of unvisited vertices, i.e., those targeted 121 | // solely by opaque predicates. 122 | public List,PcodeOp>> getUnvisited() { 123 | List,PcodeOp>> out = new ArrayList,PcodeOp>>(); 124 | Iterator,PcodeOp>> itVert = cfg.getVertices().iterator(); 125 | int numUnvisited = 0; 126 | while(itVert.hasNext()) { 127 | CFGVertex,PcodeOp> currVertex = itVert.next(); 128 | if(!outputStates.containsKey(currVertex)) { 129 | DebugPrint("%s: vertex was not visited\n", currVertex.getLocator().toString()); 130 | numUnvisited++; 131 | out.add(currVertex); 132 | } 133 | } 134 | DebugPrint("%s: %d vertices unvisited due to opaque predicates\n", cfg.getBeginAddr().toString(), numUnvisited); 135 | return out; 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/absint/tvl/TVLAbstractMemory.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.absint.tvl; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.LinkedList; 6 | 7 | import ghidra.program.model.pcode.Varnode; 8 | 9 | // The trivial memory model. Writes to locations that are not fully constant 10 | //result in an all-top memory (though the creation of the all-top memory takes 11 | //place outside of this class). 12 | public class TVLAbstractMemory { 13 | 14 | // Memory is just a hash map from addresses to 8-bit bitvectors. 15 | public HashMap Contents; 16 | public boolean bigEndian; 17 | public TVLAbstractMemory(boolean isBigEndian) { 18 | Contents = new HashMap<>(); 19 | bigEndian = isBigEndian; 20 | } 21 | 22 | // Debugging. 23 | void Dump(String str) 24 | { 25 | //Printer.println("Dump(): "+str); 26 | //for (HashMap.Entry entry : Contents.entrySet()) 27 | // Printer.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); 28 | } 29 | 30 | public TVLAbstractMemory clone() 31 | { 32 | @SuppressWarnings("unchecked") 33 | HashMap newContents = (HashMap)Contents.clone(); 34 | TVLAbstractMemory newMemory = new TVLAbstractMemory(bigEndian); 35 | newMemory.Contents = newContents; 36 | return newMemory; 37 | } 38 | 39 | // Store a byte to the specified location. 40 | void Store(long addr, TVLBitVector bv) 41 | { 42 | Contents.put(addr,bv); 43 | } 44 | 45 | // Return a new memory, entirely unknown. 46 | TVLAbstractMemory Top() 47 | { 48 | return new TVLAbstractMemory(bigEndian); 49 | } 50 | 51 | // Store a multi-byte quantity into memory and return a new one. Could 52 | // improve by only duplicating once, or by using an applicative dictionary. 53 | void StoreWholeQuantity(long addr, TVLBitVector bv) 54 | { 55 | byte[] bvArr = bv.Value(); 56 | int bvSize = bv.Size(); 57 | for(int i = 0; i < bvSize; i += 8) 58 | { 59 | byte[] subArr; 60 | if(bigEndian) 61 | subArr = Arrays.copyOfRange(bvArr, bvSize-(i+8), bvSize-i); 62 | else 63 | subArr = Arrays.copyOfRange(bvArr, i, i+8); 64 | Store(addr, new TVLBitVector(subArr)); 65 | addr += 1; 66 | } 67 | //Dump("StoreWholeQuantity(): "+addr+" "+bv); 68 | } 69 | 70 | void StoreWholeQuantity(Varnode dest, TVLBitVector bv) 71 | { 72 | StoreWholeQuantity(dest.getOffset(), bv); 73 | } 74 | 75 | // Load one byte, or return top if the address was unmapped. 76 | TVLBitVector Lookup(long addr) 77 | { 78 | //Dump("Lookup(): "+addr); 79 | if(Contents.containsKey(addr)) 80 | return Contents.get(addr); 81 | TVLBitVector bv = new TVLBitVector(8); 82 | return bv; 83 | } 84 | 85 | // Load a multi-byte quantity, where the size is specified in bits. 86 | TVLBitVector LookupWholeQuantity(long addr, int size) 87 | { 88 | // Perform each of the lookups. 89 | LinkedList list = new LinkedList(); 90 | for(int i = 0; i < size; i += 8) 91 | { 92 | TVLBitVector val = Lookup(addr); 93 | if(bigEndian) 94 | list.addFirst(val); 95 | else 96 | list.addLast(val); 97 | addr += 1; 98 | } 99 | byte[] arr = new byte[size]; 100 | 101 | // Store them into one large bitvector, in a little-endian fashion. 102 | int i = 0; 103 | while(!list.isEmpty()) 104 | { 105 | 106 | TVLBitVector current = list.remove(); 107 | System.arraycopy(current.Value(), 0, arr, i*8, 8); 108 | i++; 109 | } 110 | return new TVLBitVector(arr); 111 | } 112 | 113 | // Load a multi-byte quantity, where the size is specified as a number of 114 | // bytes in a wrapper. 115 | TVLBitVector LookupWholeQuantity(long addr, GhidraSizeAdapter gsa) 116 | { 117 | return LookupWholeQuantity(addr, gsa.sz*8); 118 | } 119 | 120 | // Load a multi-byte quantity, where the size is specified as a number of 121 | // bytes in a wrapper. 122 | TVLBitVector LookupWholeQuantity(Varnode src) 123 | { 124 | return LookupWholeQuantity(src.getOffset(), src.getSize() * 8); 125 | } 126 | 127 | void clear() 128 | { 129 | Contents.clear(); 130 | } 131 | 132 | public boolean isTop() { 133 | for(HashMap.Entry entry : Contents.entrySet()) 134 | if(!entry.getValue().isTop()) 135 | return false; 136 | clear(); 137 | return true; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/absint/tvl/TVLAnalysisOutputOptions.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.absint.tvl; 2 | 3 | public enum TVLAnalysisOutputOptions { 4 | PcodeComments, 5 | ValueComments, 6 | CFGColorizeUnvisited, 7 | ResolvedBranchComments, 8 | ResolvedBranchPrints 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/absint/tvl/TVLBitVector.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.absint.tvl; 2 | 3 | import ghidra.pal.util.Pair; 4 | 5 | //This class mostly came about from the fact that this code was originally a 6 | //very literal port from the OCaml. My OCaml framework had boolean data types 7 | //that were a single bit apiece. In Ghidra, everything seems to be 8 | //byte-granularity (similar to Hex-Rays microcode). So I wrote this analysis 9 | //allowing objects of arbitrary number of bits, whereas the Ghidra data types 10 | //are coarser. Unless there's a reason bit-granularity is useful, I should 11 | //probably re-design the architecture to work upon bytes. 12 | // 13 | //Anyway, this class just exists to signify in method signatures that bit 14 | //sizes are being specified, versus Ghidra's byte size specifications. For 15 | //proper type-safety, I should also have a BitSizeAdapter class. 16 | class GhidraSizeAdapter { 17 | public int sz; 18 | public GhidraSizeAdapter(int s) { sz = s; } 19 | } 20 | 21 | //This class implements aggregates of an arbitrary number of three-valued 22 | //quantities. I can think of many ways that I might refine this in a later 23 | //implementation: 24 | //* Use 2 bits apiece for a given 3-valued bit, rather than an entire byte. 25 | //* Use a byte-granularity by default, and implement larger quantities as 26 | //aggregates ("ByteVectors" instead of "BitVectors"). This allows a single 27 | //16-bit quantity to represent a byte. I should provide seamless access to 28 | //the elements across different bytes in this case. 29 | //* Use four values instead of three, basically Powerset({0,1}), where the 30 | //elements are: 31 | //* {0,1} (equivalent to the existing 1/2) 32 | //* {0} (equivalent to the existing 0) 33 | //* {1} (equivalent to the existing 1) 34 | //* {} (new -- signifying uninitialized) 35 | //The advantage of this is being more mathematically compatible with the 36 | //theoretical framework of abstract interpretation, in particular, the 37 | //existence of bottom elements. As for why I didn't code it that way in the 38 | //first place, again, this is a more-or-less literal port of the OCaml 39 | //version, and I know a lot more about abstract interpretation now than when 40 | //I originally created this analysis nine years ago. 41 | public class TVLBitVector { 42 | // Constants dictating the three possibilities 43 | public static final byte TVL_0 = 0; 44 | public static final byte TVL_HALF = 1; 45 | public static final byte TVL_1 = 2; 46 | 47 | // The raw array of 3-valued quantities. 48 | byte AbsValue[]; 49 | 50 | // These methods are just a reflection of my lack of understanding of the 51 | // Java philosophy of best practices of object-oriented design. It's a sort 52 | // of schizophrenic mixture of encapsulation-and-data-hiding-but-not-really. 53 | public int Size() { return AbsValue.length; } 54 | public byte[] Value() { return AbsValue; } 55 | 56 | // If there are no 1/2 bits, and the constant fits in a long, get the value 57 | // and bit size. 58 | public Pair GetConstantValue() 59 | { 60 | if(AbsValue.length > 64) 61 | return null; 62 | 63 | long val = 0; 64 | for(int i = 0; i < AbsValue.length; i++) { 65 | if(AbsValue[i] == TVL_HALF) 66 | return null; 67 | if(AbsValue[i] == TVL_1) 68 | val |= 1 << i; 69 | } 70 | return new Pair(AbsValue.length,val); 71 | } 72 | 73 | // Set every bit to 1/2. 74 | void MakeTop() 75 | { 76 | for(int i = 0; i < AbsValue.length; i++) 77 | AbsValue[i] = TVL_HALF; 78 | } 79 | 80 | static final char[] Representation = { '0', '?', '1' }; 81 | 82 | // Print the bit-vector as a series of bytes, with "?" used for 1/2 bits. 83 | @Override 84 | public String toString() 85 | { 86 | String s = ""; 87 | for(int i = AbsValue.length-1; i >= 0; i--) 88 | s += Representation[AbsValue[i]]; 89 | return s; 90 | } 91 | 92 | // Below here are the constructors and initializers. 93 | 94 | // sz: number of bits. Initialize all to 1/2. 95 | public TVLBitVector(int sz) 96 | { 97 | AbsValue = new byte[sz]; 98 | MakeTop(); 99 | } 100 | 101 | // gsa: container of a number of bytes. Initialize all to 1/2. 102 | public TVLBitVector(GhidraSizeAdapter gsa) 103 | { 104 | AbsValue = new byte[gsa.sz*8]; 105 | MakeTop(); 106 | } 107 | 108 | // Helper method to initialize a bitvector given a constant value. 109 | void InitializeFromConstant(int sz, long value) 110 | { 111 | AbsValue = new byte[sz]; 112 | for (int i = 0; i < sz; i++) 113 | AbsValue[i] = ((value >> i) & 1) == 0 ? TVL_0 : TVL_1; 114 | } 115 | 116 | // sz: number of bits. value: constant. 117 | public TVLBitVector(int sz, long value) 118 | { 119 | InitializeFromConstant(sz,value); 120 | } 121 | 122 | // gsa: container of a number of bytes. value: constant. 123 | public TVLBitVector(GhidraSizeAdapter gsa, long value) 124 | { 125 | InitializeFromConstant(gsa.sz*8,value); 126 | } 127 | 128 | // Arr: an existing array of three-valued bits. 129 | public TVLBitVector(byte[] Arr) 130 | { 131 | AbsValue = Arr; 132 | } 133 | 134 | // Copy this object. 135 | public TVLBitVector clone() 136 | { 137 | return new TVLBitVector(AbsValue.clone()); 138 | } 139 | 140 | public byte GetSign() 141 | { 142 | return AbsValue[AbsValue.length-1]; 143 | } 144 | 145 | public boolean isTop() { 146 | for(int i = 0; i < AbsValue.length; i++) 147 | if(AbsValue[i] != TVL_HALF) 148 | return false; 149 | return true; 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/absint/tvl/TVLHighLevelInterface.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.absint.tvl; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import ghidra.pal.cfg.CFG; 7 | import ghidra.pal.cfg.CFGFactory; 8 | import ghidra.pal.generic.PcodeDisasmUtil; 9 | import ghidra.pal.generic.VisitorUnimplementedException; 10 | import ghidra.pal.util.Pair; 11 | import ghidra.program.model.address.Address; 12 | import ghidra.program.model.listing.Program; 13 | import ghidra.program.model.pcode.PcodeOp; 14 | 15 | // This is a "factory", if you will, generating analysis results. 16 | // Basically, there are three different ways to specify regions of code: 17 | // 1) Request list of instructions between two addresses 18 | // 2) Request CFG with naturally-terminating control flow, by address 19 | // 3) Request CFG "within" two addresses 20 | // The user can also specify how to obtain the Instructions: 21 | // 1) Only use instructions defined in the database 22 | // 2) Use "pseudo" instructions by disassembling raw bytes, ignoring the DB 23 | // The user must also specify one or more input states. 24 | // The user can specify how the analysis results should be handled. See 25 | // the enum in TVLAnalysisOutputOptions. 26 | public class TVLHighLevelInterface { 27 | 28 | // 29 | // CFG by address (no terminating address) 30 | // 31 | public static CFG,PcodeOp> AnalyzeCFG(Program p, Address ea, boolean usePseudo, TVLAbstractGhidraState state, TVLAnalysisOutputOptions opt) throws Exception, VisitorUnimplementedException { 32 | return AnalyzeCFG(p, ea, usePseudo, Arrays.asList(state), opt); 33 | } 34 | 35 | public static CFG,PcodeOp> AnalyzeCFG(Program p, Address ea, boolean usePseudo, List states, TVLAnalysisOutputOptions opt) throws Exception, VisitorUnimplementedException { 36 | CFG,PcodeOp> cfg = CFGFactory.GetCFG(p, ea, null, usePseudo); 37 | TVLAbstractInterpretMultiple tam = new TVLAbstractInterpretMultiple(p); 38 | tam.DoCFG(cfg, states, opt); 39 | return cfg; 40 | } 41 | 42 | // 43 | // CFG by region (start and end address) 44 | // 45 | public static CFG,PcodeOp> AnalyzeCFGRegion(Program p, Address startEa, Address endEa, boolean usePseudo, TVLAbstractGhidraState state, TVLAnalysisOutputOptions opt) throws Exception, VisitorUnimplementedException { 46 | return AnalyzeCFGRegion(p, startEa, endEa, usePseudo, Arrays.asList(state), opt); 47 | } 48 | public static CFG,PcodeOp> AnalyzeCFGRegion(Program p, Address startEa, Address endEa, boolean usePseudo, List states, TVLAnalysisOutputOptions opt) throws Exception, VisitorUnimplementedException { 49 | CFG,PcodeOp> cfg = CFGFactory.GetCFG(p, startEa, endEa, usePseudo); 50 | TVLAbstractInterpretMultiple tam = new TVLAbstractInterpretMultiple(p); 51 | tam.DoCFG(cfg, states, opt); 52 | return cfg; 53 | } 54 | 55 | // 56 | // Flat list of instructions by start and end address 57 | // 58 | public static List,PcodeOp>> AnalyzeRange(Program p, Address startEa, Address endEa, boolean usePseudo, TVLAbstractGhidraState state, TVLAnalysisOutputOptions opt) throws Exception, VisitorUnimplementedException { 59 | return AnalyzeRange(p, startEa, endEa, usePseudo, Arrays.asList(state), opt); 60 | } 61 | public static List,PcodeOp>> AnalyzeRange(Program p, Address startEa, Address endEa, boolean usePseudo, List states, TVLAnalysisOutputOptions opt) throws Exception, VisitorUnimplementedException { 62 | List,PcodeOp>> entities = PcodeDisasmUtil.GetRange(p, usePseudo, startEa, endEa); 63 | TVLAbstractInterpretMultiple tam = new TVLAbstractInterpretMultiple(p); 64 | return tam.DoEntityList(entities, states, opt); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/absint/tvl/TVLPcodeDatabaseTransformer.java: -------------------------------------------------------------------------------- 1 | // Turns out you can't persistently modify pcode. I'll keep the code around in 2 | // case I decide to re-use the mechanisms for modifying pcode objects, as 3 | // opposed to how TVLPcodeTransformer generates new objects. 4 | 5 | /* 6 | package ghidra.pal.absint.tvl; 7 | 8 | import ghidra.pal.util.Pair; 9 | import ghidra.pal.util.Printer; 10 | import ghidra.program.model.address.Address; 11 | import ghidra.program.model.listing.Instruction; 12 | import ghidra.program.model.listing.Listing; 13 | import ghidra.program.model.listing.Program; 14 | import ghidra.program.model.pcode.PcodeOp; 15 | import ghidra.program.model.pcode.Varnode; 16 | 17 | public class TVLPcodeDatabaseTransformer extends TVLPcodeTransformer { 18 | Listing currentListing; 19 | public TVLPcodeDatabaseTransformer(Program cp) { 20 | super(cp); 21 | currentListing = cp.getListing(); 22 | } 23 | public PcodeOp disassembleGetDBPcode(Pair ea) { 24 | Instruction i = currentListing.getInstructionAt(ea.x); 25 | PcodeOp[] pcode = i.getPcode(); 26 | PcodeOp curr = pcode[ea.y]; 27 | return curr; 28 | } 29 | 30 | protected PcodeOp MakeCopy(Pair,PcodeOp> p, Varnode outVal) { 31 | Printer.printf("%s: %s could turn into constant COPY %s\n", p.x.toString(), p.y.toString(), outVal.toString()); 32 | PcodeOp curr = disassembleGetDBPcode(p.x); 33 | curr.setOpcode(PcodeOp.COPY); 34 | curr.setInput(outVal, 0); 35 | while(curr.getNumInputs() > 1) 36 | curr.removeInput(1); 37 | return null; 38 | } 39 | 40 | protected PcodeOp changeUnaryOp(Pair,PcodeOp> p, Varnode newVar) { 41 | PcodeOp curr = disassembleGetDBPcode(p.x); 42 | curr.setInput(newVar, 0); 43 | return null; 44 | } 45 | protected PcodeOp changeBinaryOp(Pair,PcodeOp> p, Varnode newIn1, Varnode newIn2) { 46 | if(newIn1 != null || newIn2 != null) { 47 | PcodeOp curr = disassembleGetDBPcode(p.x); 48 | if(newIn1 != null) 49 | curr.setInput(newIn1, 0); 50 | if(newIn2 != null) 51 | curr.setInput(newIn2, 0); 52 | return null; 53 | } 54 | return null; 55 | } 56 | protected PcodeOp changeLoad(Pair,PcodeOp> p, Varnode newIn1) { 57 | PcodeOp curr = disassembleGetDBPcode(p.x); 58 | curr.setInput(newIn1, 1); 59 | return null; 60 | } 61 | protected PcodeOp changeStore(Pair,PcodeOp> p, Varnode newIn1, Varnode newIn2) { 62 | if(newIn1 != null || newIn2 != null) { 63 | PcodeOp curr = disassembleGetDBPcode(p.x); 64 | if(newIn1 != null) 65 | curr.setInput(newIn1, 1); 66 | if(newIn2 != null) 67 | curr.setInput(newIn2, 2); 68 | return null; 69 | } 70 | return null; 71 | } 72 | protected PcodeOp changeBranchInd(Pair,PcodeOp> p, Varnode newDest) { 73 | return null; 74 | } 75 | protected PcodeOp changeCBranch(Pair,PcodeOp> p, Varnode newIn1) { 76 | PcodeOp curr = disassembleGetDBPcode(p.x); 77 | curr.setInput(newIn1, 1); 78 | return null; 79 | } 80 | } 81 | */ 82 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/CFG.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import java.lang.Iterable; 4 | import java.util.ArrayList; 5 | import java.util.Collection; 6 | import java.util.HashMap; 7 | import java.util.Iterator; 8 | import java.util.List; 9 | import ghidra.graph.GDirectedGraph; 10 | import ghidra.pal.util.Pair; 11 | import ghidra.pal.util.Printer; 12 | import ghidra.program.model.listing.Program; 13 | 14 | // This class implements the GDirectedGraph interface as defined by Ghidra, 15 | // using the CFGVertex and CFGEdge classes. By implementing this interface, we 16 | // get access to the existing graph utility methods inside of Ghidra. Also, 17 | // hopefully in the future, the Ghidra developers will include a graph drawing 18 | // component that works upon GDirectedGraph objects, at which point we will be 19 | // able to freely (or cheaply) make use of that functionality, rather than 20 | // having to adapt or rewrite this component. 21 | // 22 | // And, anyway, Ghidra's GDirectedGraph interface is pretty good. I see no 23 | // reason not to use it. 24 | // 25 | // I haven't bothered to comment most of this because the methods are already 26 | // documented in GDirectedGraph.java, and there are no surprises in the 27 | // implementation. I have marked the methods that are not strictly required 28 | // by the interface. 29 | public class CFG implements GDirectedGraph, CFGEdge> { 30 | A BeginAddr; 31 | HashMap> AddressToVertex; 32 | HashMap, ArrayList>> EdgesBySource; 33 | HashMap, ArrayList>> EdgesByDest; 34 | 35 | public CFG(A eaBeg) { 36 | BeginAddr = eaBeg; 37 | AddressToVertex = new HashMap>(); 38 | EdgesBySource = new HashMap, ArrayList>>(); 39 | EdgesByDest = new HashMap, ArrayList>>(); 40 | } 41 | 42 | // Non-interface method 43 | public A getBeginAddr() { 44 | return BeginAddr; 45 | } 46 | 47 | // Non-interface method 48 | public CFGVertex lookupVertex(A ea) { 49 | if(AddressToVertex.containsKey(ea)) 50 | return AddressToVertex.get(ea); 51 | return null; 52 | } 53 | 54 | // Non-interface method 55 | public boolean hasVertex(A ea) { 56 | return lookupVertex(ea) != null; 57 | } 58 | 59 | // Non-interface method 60 | private ArrayList> ensureSourceMapEntry(CFGVertex v) { 61 | if(EdgesBySource.containsKey(v)) 62 | return EdgesBySource.get(v); 63 | ArrayList> srcList = new ArrayList>(); 64 | EdgesBySource.put(v,srcList); 65 | return srcList; 66 | } 67 | 68 | // Non-interface method 69 | private ArrayList> ensureDestMapEntry(CFGVertex v) { 70 | if(EdgesByDest.containsKey(v)) 71 | return EdgesByDest.get(v); 72 | ArrayList> dstList = new ArrayList>(); 73 | EdgesByDest.put(v,dstList); 74 | return dstList; 75 | } 76 | 77 | // Non-interface method. Get all vertices with no incoming edges. 78 | public ArrayList> getInitialVertices() { 79 | ArrayList> initialList = new ArrayList>(); 80 | Collection> allVertices = getVertices(); 81 | Iterator> it = allVertices.iterator(); 82 | while(it.hasNext()) { 83 | CFGVertex v = it.next(); 84 | ArrayList> inEdges = ensureDestMapEntry(v); 85 | //Printer.printf("getInitialVertices(%s): had %d incoming vertices\n", v.toString(), inEdges.size()); 86 | if(inEdges.isEmpty()) 87 | initialList.add(v); 88 | } 89 | return initialList; 90 | } 91 | 92 | // Non-interface method. Get all vertices with no outgoing edges. 93 | public ArrayList> getTerminalVertices() { 94 | ArrayList> terminalList = new ArrayList>(); 95 | Collection> allVertices = getVertices(); 96 | Iterator> it = allVertices.iterator(); 97 | while(it.hasNext()) { 98 | CFGVertex v = it.next(); 99 | ArrayList> outEdges = ensureSourceMapEntry(v); 100 | if(outEdges.isEmpty()) 101 | terminalList.add(v); 102 | } 103 | return terminalList; 104 | } 105 | 106 | public boolean addVertex(CFGVertex v) { 107 | if(EdgesBySource.containsKey(v) || EdgesByDest.containsKey(v)) 108 | return false; 109 | ensureSourceMapEntry(v); 110 | ensureDestMapEntry(v); 111 | AddressToVertex.put(v.getLocator(), v); 112 | return true; 113 | } 114 | 115 | public boolean removeVertex(CFGVertex v) { 116 | boolean bRet = false; 117 | if(EdgesBySource.containsKey(v)) { 118 | EdgesBySource.remove(v); 119 | bRet = true; 120 | } 121 | if(EdgesByDest.containsKey(v)) { 122 | EdgesByDest.remove(v); 123 | bRet = true; 124 | } 125 | AddressToVertex.remove(v.getLocator()); 126 | return bRet; 127 | } 128 | public void removeVertices(Iterable> vertices) { 129 | Iterator> it = vertices.iterator(); 130 | while(it.hasNext()) { 131 | CFGVertex curr = it.next(); 132 | removeVertex(curr); 133 | } 134 | } 135 | public void addEdge(CFGEdge e) { 136 | ensureSourceMapEntry(e.getStart()).add(e); 137 | ensureDestMapEntry(e.getEnd()).add(e); 138 | } 139 | public boolean removeEdge(CFGEdge e) { 140 | boolean bRet = false; 141 | bRet |= ensureSourceMapEntry(e.getStart()).remove(e); 142 | bRet |= ensureDestMapEntry(e.getEnd()).remove(e); 143 | return bRet; 144 | } 145 | public void removeEdges(Iterable> edges) { 146 | Iterator> it = edges.iterator(); 147 | while(it.hasNext()) 148 | removeEdge(it.next()); 149 | } 150 | public CFGEdge findEdge(CFGVertex start, CFGVertex end) { 151 | ArrayList> srcEdges = ensureSourceMapEntry(start); 152 | Iterator> it = srcEdges.iterator(); 153 | while(it.hasNext()) { 154 | CFGEdge e = it.next(); 155 | if(e.getEnd() == end) 156 | return e; 157 | } 158 | return null; 159 | } 160 | public Collection> getVertices() { 161 | return EdgesBySource.keySet(); 162 | } 163 | public Collection> getEdges() { 164 | ArrayList> outList = new ArrayList>(); 165 | for(HashMap.Entry, ArrayList>> entry : EdgesBySource.entrySet()) 166 | outList.addAll(entry.getValue()); 167 | return outList; 168 | } 169 | public boolean containsVertex(CFGVertex v) { 170 | return EdgesBySource.containsKey(v); 171 | } 172 | public boolean containsEdge(CFGEdge e) { 173 | CFGVertex src = e.getStart(); 174 | if(!EdgesBySource.containsKey(src)) 175 | return false; 176 | ArrayList> list = EdgesBySource.get(src); 177 | return list.contains(e); 178 | } 179 | public boolean containsEdge(CFGVertex from, CFGVertex to) { 180 | if(!EdgesBySource.containsKey(from)) 181 | return false; 182 | Iterator> it = EdgesBySource.get(from).iterator(); 183 | while(it.hasNext()) { 184 | if(it.next().getEnd() == to) 185 | return true; 186 | } 187 | return false; 188 | } 189 | public boolean isEmpty() { 190 | return EdgesBySource.isEmpty(); 191 | } 192 | public int getVertexCount() { 193 | return EdgesBySource.size(); 194 | } 195 | public int getEdgeCount() { 196 | return getEdges().size(); 197 | } 198 | @SuppressWarnings("unchecked") 199 | public Collection> getInEdges(CFGVertex v) { 200 | return (Collection>)ensureDestMapEntry(v).clone(); 201 | } 202 | @SuppressWarnings("unchecked") 203 | public Collection> getOutEdges(CFGVertex v) { 204 | return (Collection>)ensureSourceMapEntry(v).clone(); 205 | } 206 | @SuppressWarnings("unchecked") 207 | public CFG copy() { 208 | CFG n = new CFG(BeginAddr); 209 | n.AddressToVertex = (HashMap>)this.AddressToVertex.clone(); 210 | n.EdgesBySource = (HashMap, ArrayList>>)this.EdgesBySource.clone(); 211 | n.EdgesByDest = (HashMap, ArrayList>>)this.EdgesByDest.clone(); 212 | return n; 213 | } 214 | public CFG emptyCopy() { 215 | return new CFG(BeginAddr); 216 | } 217 | 218 | // Non-interface method 219 | public void PrintGraph(Program currentProgram) throws Exception { 220 | Collection> vertices = getVertices(); 221 | Iterator> it = vertices.iterator(); 222 | while(it.hasNext()) { 223 | CFGVertex v = it.next(); 224 | Printer.printf("%s: block head\n", v.getLocator()); 225 | List> entities = v.getEntities(); 226 | if(entities != null) { 227 | Iterator> adit = entities.iterator(); 228 | while(adit.hasNext()) { 229 | Pair p = adit.next(); 230 | Printer.printf("\t%s %s\n", p.x.toString(), p.y.toString()); 231 | } 232 | } 233 | } 234 | Collection> edges = getEdges(); 235 | Iterator> eit = edges.iterator(); 236 | while(eit.hasNext()) { 237 | CFGEdge edge = eit.next(); 238 | CFGVertex start = edge.getStart(); 239 | CFGVertex end = edge.getEnd(); 240 | Printer.printf("\t%s->%s (%s)\n", start.getLocator(), end.getLocator(), edge.getEdgeType()); 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/CFGBuilder.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.HashSet; 6 | import java.util.Collection; 7 | import java.util.List; 8 | 9 | import ghidra.pal.util.Printer; 10 | import ghidra.pal.util.Pair; 11 | 12 | 13 | public class CFGBuilder { 14 | CFGBuilderBundle State; 15 | CFGVertexDetailProvider VertexDetailProvider; 16 | CFGExplorationTerminator Terminator; 17 | 18 | // detailProvider: analyzes entities of type T (Instruction, 19 | // PseudoInstruction, PcodeOp, PcodeOpRaw) found at location type A 20 | // (Address, Pair for PcodeOp/PcodeOpRaw). 21 | // terminator: tells whether to continue following flow at location type A 22 | public CFGBuilder(CFGVertexDetailProvider detailProvider, CFGExplorationTerminator terminator) { 23 | VertexDetailProvider = detailProvider; 24 | Terminator = terminator; 25 | State = new CFGBuilderBundle(); 26 | } 27 | 28 | // Invoke the termination checker upon the specified address. For the 29 | // ordinary case, there will be no terminator, so this will always return 30 | // false. 31 | boolean shouldTerminateAt(A loc) { 32 | return Terminator != null && Terminator.shouldTerminateAt(loc); 33 | } 34 | 35 | // We create edges after all vertices have been discovered. This simplifies 36 | // the bookkeeping. 37 | void ApplyDeferredEdges(CFG graph, List> DeferredEdges) { 38 | Iterator> it = DeferredEdges.iterator(); 39 | 40 | // Vertices might be missing from the graph due to the search having 41 | // been terminated by the function above. Only add edges to present 42 | // pairs of vertices. 43 | while(it.hasNext()) { 44 | CFGPendingEdge pe = it.next(); 45 | if(shouldTerminateAt(pe.dst)) 46 | continue; 47 | CFGVertex dst = graph.lookupVertex(pe.dst); 48 | // This should only happen if the shouldTermiateAt() check above 49 | // failed, though it would have contiued in that case, thus, be 50 | // noisy about it. 51 | if(dst == null) { 52 | Printer.printf("%s: graph did not contain destination vertex (source %s)\n", pe.dst.toString(), pe.src.toString()); 53 | continue; 54 | } 55 | // This shouldn't happen -- terminated addresses shouldn't be the 56 | // source of edges, since if they were terminated, their outgoing 57 | // flows should not have been processed, thus, be noisy. 58 | if(shouldTerminateAt(pe.src)) { 59 | Printer.printf("%s: graph did not contain source vertex (dest %s)\n", pe.src.toString(), pe.dst.toString()); 60 | continue; 61 | } 62 | CFGVertex src = graph.lookupVertex(pe.src); 63 | // This shouldn't happen, so be noisy about it. 64 | if(src == null) { 65 | Printer.printf("%s: graph did not contain source vertex (dest %s)\n", pe.src.toString(), pe.dst.toString()); 66 | continue; 67 | } 68 | graph.addEdge(new CFGEdge(src, dst, pe.t)); 69 | } 70 | } 71 | 72 | // A "singleton" CFG is one in which each vertex corresponds to one entity 73 | // (Instruction, PcodeOp, etc.). That's opposed to a "merged" CFG, in which 74 | // basic blocks contain lists of entities. 75 | public CFG CreateSingletonCFG(A handlerEa) throws Exception 76 | { 77 | CFG graph = new CFG(handlerEa); 78 | 79 | // Algorithm state: 80 | // * Location worklist 81 | // * Set of heads 82 | // * Edges to be applied 83 | State = new CFGBuilderBundle(); 84 | State.Heads.add(handlerEa); 85 | 86 | // Addresses to skip because disassembly failed 87 | HashSet Skip = new HashSet(); 88 | 89 | // Bootstrap worklist algorithm 90 | State.LocationWorkList.add(handlerEa); 91 | while(!State.LocationWorkList.isEmpty()) 92 | { 93 | // Get current location 94 | A curr = State.LocationWorkList.remove(0); 95 | 96 | // If we've seen it, or disassembly previously failed, or the 97 | // terminator says to stop, skip this location. 98 | if(graph.hasVertex(curr) || Skip.contains(curr) || shouldTerminateAt(curr)) 99 | continue; 100 | 101 | // Call the detail provider to analyze the flows from the entity 102 | // and update the State object accordingly. 103 | T instr = VertexDetailProvider.provide(curr, State); 104 | 105 | // If the detail provider returned NULL, remark and record. 106 | if(instr == null) { 107 | Skip.add(curr); 108 | Printer.printf("%s: instruction was null\n", curr.toString()); 109 | continue; 110 | } 111 | 112 | // Otherwise, allocate a list for the pair 113 | // () for the CFGVertex, and add the pair to the list. 114 | List> l = new ArrayList>(); 115 | l.add(new Pair(curr,instr)); 116 | 117 | // Create and add the vertex. 118 | CFGVertex currVertex = new CFGVertex(curr, l); 119 | graph.addVertex(currVertex); 120 | } 121 | 122 | // After worklist termination, add the edges. 123 | ApplyDeferredEdges(graph, State.DeferredEdges); 124 | 125 | // Add additional Heads to the state information for any vertex with 126 | // more than one predecessor. 127 | Collection> vertices = graph.getVertices(); 128 | Iterator> it = vertices.iterator(); 129 | while(it.hasNext()) { 130 | CFGVertex destVert = it.next(); 131 | Collection> inEdges = graph.getInEdges(destVert); 132 | if(inEdges.size() > 1) 133 | State.Heads.add(destVert.getLocator()); 134 | } 135 | return graph; 136 | } 137 | // Create a "merged" CFG, in which there are multiple entities per vertex 138 | // (as in, basic blocks). 139 | public CFG CreateMergedCFG(A handlerEa) throws Exception 140 | { 141 | // Create the singleton graph first. Use it and the resulting state to 142 | // merge together chains of vertices , where: 143 | // !Heads.contains(y): y is not a designed block head 144 | // |npred(x)|==|nsucc(y)|==1: x one successor, y one predecessor 145 | // pred(y) == x && succ(x) == y: x and y only have edges between them 146 | CFG singletonCFG = CreateSingletonCFG(handlerEa); 147 | CFG mergedCFG = new CFG(handlerEa); 148 | 149 | // Allocate list for edges in merged graph. 150 | List> DeferredEdges = new ArrayList>(); 151 | 152 | Iterator it = State.Heads.iterator(); 153 | // Iterate through all of the heads in the singleton CFG. 154 | while(it.hasNext()) { 155 | A blockHeadEa = it.next(); 156 | 157 | // If we can't find the head by its location, this is bad. 158 | CFGVertex blockVertex = singletonCFG.lookupVertex(blockHeadEa); 159 | if(blockVertex == null) { 160 | Printer.printf("Block %s: could not find head vertex\n", blockHeadEa.toString()); 161 | continue; 162 | } 163 | 164 | // Iterate through blockVertex and its successors. 165 | CFGVertex currVertex = blockVertex; 166 | List> blockEnts = new ArrayList>(); 167 | Collection> outEdges; 168 | while(true) { 169 | outEdges = singletonCFG.getOutEdges(currVertex); 170 | 171 | // If |nsucc(x)| != 1, can't merge. 172 | int numOutEdges = outEdges.size(); 173 | blockEnts.addAll(currVertex.getEntities()); 174 | if(numOutEdges != 1) 175 | break; 176 | 177 | // If y is a head, don't add an edge. 178 | CFGEdge e = outEdges.iterator().next(); 179 | CFGVertex nextVertex = e.getEnd(); 180 | if(State.Heads.contains(nextVertex.getLocator())) 181 | break; 182 | 183 | // I decided not to forcibly create heads at the targets of 184 | // unconditional branches. For unobfuscated code, if there's a 185 | // jump instruction, the target really ought to have multiple 186 | // incoming references. For obfuscated code, we want to ignore 187 | // unconditional jumps, so we don't mind coalescing them into 188 | // a single block. So, just ignore unconditional edges. 189 | // if(e.getEdgeType() == CFGEdgeType.UNCONDITIONAL) { 190 | // } 191 | currVertex = nextVertex; 192 | } 193 | Iterator> edit = outEdges.iterator(); 194 | 195 | // Add all outgoing edges to the merged CFG's deferred list. 196 | while(edit.hasNext()) { 197 | CFGEdge e = edit.next(); 198 | DeferredEdges.add(new CFGPendingEdge(blockHeadEa, e.getEnd().getLocator(), e.getEdgeType())); 199 | } 200 | 201 | // Create a new vertex with the list of all the 202 | // () pairs. 203 | mergedCFG.addVertex(new CFGVertex(blockHeadEa, blockEnts)); 204 | } 205 | 206 | // Apply the edges to the CFG. 207 | ApplyDeferredEdges(mergedCFG, DeferredEdges); 208 | return mergedCFG; 209 | } 210 | } 211 | 212 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/CFGBuilderBundle.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashSet; 5 | import java.util.List; 6 | 7 | // The state bundle for the CFG worklist algorithm. The CFGVertexDetailProvider 8 | // objects modify this object to add new locations to the worklist, add pending 9 | // edges, and specify that locations mark block heads. 10 | public class CFGBuilderBundle { 11 | public List LocationWorkList; 12 | public List> DeferredEdges; 13 | public HashSet Heads; 14 | 15 | public CFGBuilderBundle() { 16 | LocationWorkList = new ArrayList(); 17 | DeferredEdges = new ArrayList>(); 18 | Heads = new HashSet(); 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/CFGEdge.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import ghidra.graph.DefaultGEdge; 4 | 5 | // Straightforward implementation of DefaultGEdge. 6 | public class CFGEdge extends DefaultGEdge> { 7 | CFGEdgeType EdgeType; 8 | public CFGEdge(CFGVertex start, CFGVertex end, CFGEdgeType t) { 9 | super(start,end); 10 | EdgeType = t; 11 | } 12 | public CFGEdgeType getEdgeType() { 13 | return EdgeType; 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/CFGEdgeType.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | // Type of a CFG edge. Made public so as to be accessible outside of the 4 | // package. 5 | public enum CFGEdgeType { 6 | FALLTHROUGH, 7 | UNCONDITIONAL, 8 | COND_TAKEN, 9 | COND_NOTTAKEN, 10 | NWAY; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/CFGExplorationTerminator.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | // The recursive traversal algorithm for control flow graph discovery is 4 | // largely generic, working unmodified across architectures and across a 5 | // swath of declarative programming languages. (There are a few caveats to that 6 | // statement, such as architectures with branch delay slots, and high-level 7 | // constructs such as exception handling code.) Basically, given a starting 8 | // address, the recursive traversal CFG building code explores all 9 | // intraprocedural control flows, terminating upon return instructions, 10 | // unresolvable indirect branches, or calls to functions that do not return. 11 | // 12 | // However, obfuscated code might call for different exploration strategies. 13 | // For instance, for virtualization obfuscators, there might be a common 14 | // location to which all VM instruction handlers ultimately branch. To avoid 15 | // duplicating this code in the control flow graphs for each handler, we can 16 | // tell the CFG building code to stop exploring once this location is reached. 17 | // 18 | // Thus, this interface abstracts the action of inspecting a program location 19 | // and telling the CFG discovery algorithm "yes" or "no" to proceed. 20 | public interface CFGExplorationTerminator { 21 | public boolean shouldTerminateAt(A location); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/CFGFactory.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import ghidra.app.util.PseudoInstruction; 4 | import ghidra.program.model.listing.Program; 5 | import ghidra.program.model.address.Address; 6 | import ghidra.program.model.listing.Instruction; 7 | import ghidra.program.model.pcode.PcodeOp; 8 | import ghidra.pal.util.Pair; 9 | 10 | 11 | // This factory class can build control flow graphs in a variety of 12 | // circumstances: 13 | // * The entity type might be Instruction, PseudoInstruction, PcodeOp, PcodeOpRaw 14 | // * Should we use Instruction objects (from the DB), or PseudoInstruction 15 | // objects (independently from the DB)? 16 | // * Should the graphs be Singletons (one entity per vertex) or block graphs 17 | // (singletons merged by control flow)? 18 | // * The user can configure how the CFG algorithm's termination conditions 19 | // behave. For function CFGs, do nothing. For special circumstances 20 | // (designated end location, or more generic), allow the user to specify. 21 | public final class CFGFactory { 22 | protected static CFGBuilder GetPseudoCFGBuilder(Program currentProgram, CFGExplorationTerminator
term) { 23 | return new CFGBuilder(new PseudoInstructionDetailProvider(currentProgram), term); 24 | } 25 | 26 | public static CFG GetPseudoCFG(Program currentProgram, Address startEa, CFGExplorationTerminator
term) throws Exception { 27 | CFGBuilder b = GetPseudoCFGBuilder(currentProgram, term); 28 | return b.CreateMergedCFG(startEa); 29 | } 30 | 31 | public static CFG GetPseudoCFG(Program currentProgram, Address startEa) throws Exception { 32 | return GetPseudoCFG(currentProgram, startEa, (CFGExplorationTerminator
)null); 33 | } 34 | 35 | public static CFG GetPseudoCFG(Program currentProgram, Address startEa, Address endEa) throws Exception { 36 | CFGExplorationTerminator
term = new CFGPointTerminator
(endEa); 37 | return GetPseudoCFG(currentProgram, startEa, term); 38 | } 39 | 40 | public static CFG GetSingletonPseudoCFG(Program currentProgram, Address startEa, CFGExplorationTerminator
term) throws Exception { 41 | CFGBuilder b = GetPseudoCFGBuilder(currentProgram, term); 42 | return b.CreateSingletonCFG(startEa); 43 | } 44 | 45 | public static CFG GetSingletonPseudoCFG(Program currentProgram, Address startEa) throws Exception { 46 | return GetSingletonPseudoCFG(currentProgram, startEa, (CFGExplorationTerminator
)null); 47 | } 48 | 49 | public static CFG GetSingletonPseudoCFG(Program currentProgram, Address startEa, Address endEa) throws Exception { 50 | CFGExplorationTerminator
term = new CFGPointTerminator
(endEa); 51 | return GetSingletonPseudoCFG(currentProgram, startEa, term); 52 | } 53 | 54 | protected static CFGBuilder GetCFGBuilder(Program currentProgram, CFGExplorationTerminator
term) throws Exception { 55 | return new CFGBuilder(new InstructionDetailProvider(currentProgram), term); 56 | } 57 | 58 | public static CFG GetCFG(Program currentProgram, Address startEa, CFGExplorationTerminator
term) throws Exception { 59 | CFGBuilder b = GetCFGBuilder(currentProgram, term); 60 | return b.CreateMergedCFG(startEa); 61 | } 62 | 63 | public static CFG GetCFG(Program currentProgram, Address startEa) throws Exception { 64 | return GetCFG(currentProgram, startEa, (CFGExplorationTerminator
)null); 65 | } 66 | 67 | public static CFG GetCFG(Program currentProgram, Address startEa, Address endEa) throws Exception { 68 | CFGExplorationTerminator
term = new CFGPointTerminator
(endEa); 69 | return GetCFG(currentProgram, startEa, term); 70 | } 71 | 72 | // Allows endEa to be null, in which case it does not create a terminator 73 | public static CFG,PcodeOp> GetCFG(Program p, Address startEa, Address endEa, boolean usePseudo) throws Exception { 74 | if(usePseudo) { 75 | if(endEa == null) 76 | return GetPcodePseudoCFG(p, startEa); 77 | return GetPcodePseudoCFG(p, startEa, endEa); 78 | } 79 | if(endEa == null) 80 | return GetPcodeCFG(p, startEa); 81 | return GetPcodeCFG(p, startEa, endEa); 82 | } 83 | 84 | public static CFG GetSingletonCFG(Program currentProgram, Address startEa, CFGExplorationTerminator
term) throws Exception { 85 | CFGBuilder b = GetCFGBuilder(currentProgram, term); 86 | return b.CreateSingletonCFG(startEa); 87 | } 88 | 89 | public static CFG GetSingletonCFG(Program currentProgram, Address startEa) throws Exception { 90 | return GetSingletonCFG(currentProgram, startEa, (CFGExplorationTerminator
)null); 91 | } 92 | 93 | public static CFG GetSingletonCFG(Program currentProgram, Address startEa, Address endEa) throws Exception { 94 | CFGExplorationTerminator
term = new CFGPointTerminator
(endEa); 95 | return GetSingletonCFG(currentProgram, startEa, term); 96 | } 97 | 98 | protected static CFGBuilder,PcodeOp> GetPcodePseudoCFGBuilder(Program currentProgram, CFGExplorationTerminator> term) { 99 | PseudoInstructionCache pic = new PseudoInstructionCache(currentProgram); 100 | return new CFGBuilder,PcodeOp>(new PcodeOpProvider(currentProgram, pic), term); 101 | } 102 | 103 | public static CFG,PcodeOp> GetPcodePseudoCFG(Program currentProgram, Address startEa, CFGExplorationTerminator> term) throws Exception { 104 | CFGBuilder,PcodeOp> b = GetPcodePseudoCFGBuilder(currentProgram, term); 105 | Pair startLoc = new Pair(startEa, 0); 106 | return b.CreateMergedCFG(startLoc); 107 | } 108 | 109 | public static CFG,PcodeOp> GetPcodePseudoCFG(Program currentProgram, Address startEa) throws Exception { 110 | return GetPcodePseudoCFG(currentProgram, startEa, (CFGExplorationTerminator>)null); 111 | } 112 | 113 | public static CFG,PcodeOp> GetPcodePseudoCFG(Program currentProgram, Address startEa, Address endEa) throws Exception { 114 | Pair endLoc = new Pair(endEa, 0); 115 | CFGExplorationTerminator> term = new CFGPointTerminator>(endLoc); 116 | return GetPcodePseudoCFG(currentProgram, startEa, term); 117 | } 118 | 119 | public static CFG,PcodeOp> GetSingletonPcodePseudoCFG(Program currentProgram, Address startEa, CFGExplorationTerminator> term) throws Exception { 120 | CFGBuilder,PcodeOp> b = GetPcodePseudoCFGBuilder(currentProgram, term); 121 | Pair startLoc = new Pair(startEa, 0); 122 | return b.CreateSingletonCFG(startLoc); 123 | } 124 | 125 | public static CFG,PcodeOp> GetSingletonPcodePseudoCFG(Program currentProgram, Address startEa) throws Exception { 126 | return GetSingletonPcodePseudoCFG(currentProgram, startEa, (CFGExplorationTerminator>)null); 127 | } 128 | 129 | public static CFG,PcodeOp> GetSingletonPcodePseudoCFG(Program currentProgram, Address startEa, Address endEa) throws Exception { 130 | Pair endLoc = new Pair(endEa, 0); 131 | CFGExplorationTerminator> term = new CFGPointTerminator>(endLoc); 132 | return GetSingletonPcodePseudoCFG(currentProgram, startEa, term); 133 | } 134 | 135 | protected static CFGBuilder,PcodeOp> GetPcodeCFGBuilder(Program currentProgram, CFGExplorationTerminator> term) { 136 | InstructionCache pic = new InstructionCache(currentProgram); 137 | return new CFGBuilder,PcodeOp>(new PcodeOpProvider(currentProgram, pic), term); 138 | } 139 | 140 | public static CFG,PcodeOp> GetPcodeCFG(Program currentProgram, Address startEa, CFGExplorationTerminator> term) throws Exception { 141 | CFGBuilder,PcodeOp> b = GetPcodeCFGBuilder(currentProgram, term); 142 | Pair startLoc = new Pair(startEa, 0); 143 | return b.CreateMergedCFG(startLoc); 144 | } 145 | 146 | public static CFG,PcodeOp> GetPcodeCFG(Program currentProgram, Address startEa) throws Exception { 147 | return GetPcodeCFG(currentProgram, startEa, (CFGExplorationTerminator>)null); 148 | } 149 | 150 | public static CFG,PcodeOp> GetPcodeCFG(Program currentProgram, Address startEa, Address endEa) throws Exception { 151 | Pair endLoc = new Pair(endEa, 0); 152 | CFGExplorationTerminator> term = new CFGPointTerminator>(endLoc); 153 | return GetPcodeCFG(currentProgram, startEa, term); 154 | } 155 | 156 | public static CFG,PcodeOp> GetSingletonPcodeCFG(Program currentProgram, Address startEa, CFGExplorationTerminator> term) throws Exception { 157 | CFGBuilder,PcodeOp> b = GetPcodeCFGBuilder(currentProgram, term); 158 | Pair startLoc = new Pair(startEa, 0); 159 | return b.CreateSingletonCFG(startLoc); 160 | } 161 | 162 | public static CFG,PcodeOp> GetSingletonPcodeCFG(Program currentProgram, Address startEa) throws Exception { 163 | return GetSingletonPcodeCFG(currentProgram, startEa, (CFGExplorationTerminator>)null); 164 | } 165 | 166 | public static CFG,PcodeOp> GetSingletonPcodeCFG(Program currentProgram, Address startEa, Address endEa) throws Exception { 167 | Pair endLoc = new Pair(endEa, 0); 168 | CFGExplorationTerminator> term = new CFGPointTerminator>(endLoc); 169 | return GetSingletonPcodeCFG(currentProgram, startEa, term); 170 | } 171 | 172 | } 173 | 174 | // import ghidra.pal.CFGFactory; 175 | // Printer.Set(tool.getService(ConsoleService.class)); 176 | // Printer.SetFileOutputPath("c:\\temp\\ghidra-debug.txt"); 177 | // AddressSpace defaultAS = currentProgram.getAddressFactory().getDefaultAddressSpace(); 178 | // Address startEa = defaultAS.getAddress(0x10015204); 179 | // Address endEa = defaultAS.getAddress(0x10013956); 180 | // CFGFactory.GetPCodeCFG(currentProgram, startEa, endEa).PrintGraph(currentProgram); -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/CFGPendingEdge.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | // A class representing an edge that is yet to be added to a CFG. 4 | // Edges are specified by location types and edge types. 5 | public class CFGPendingEdge { 6 | public A src; 7 | public A dst; 8 | public CFGEdgeType t; 9 | public CFGPendingEdge(A s, A d, CFGEdgeType ty) { 10 | src = s; 11 | dst = d; 12 | t = ty; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/CFGPointTerminator.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import java.util.Collection; 4 | import java.util.HashSet; 5 | 6 | // For scenarios where you want an ordinary graph built, but want flow to stop 7 | // upon reading one or more specified locations, use this class and pass the 8 | // location(s) to the constructor. 9 | public class CFGPointTerminator implements CFGExplorationTerminator { 10 | HashSet TermPoints; 11 | 12 | public CFGPointTerminator(A singleLoc) { 13 | TermPoints = new HashSet(); 14 | TermPoints.add(singleLoc); 15 | } 16 | 17 | public CFGPointTerminator(Collection locCollection) { 18 | TermPoints = new HashSet(); 19 | TermPoints.addAll(locCollection); 20 | } 21 | 22 | public boolean shouldTerminateAt(A loc) { 23 | return TermPoints.contains(loc); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/CFGPriorityQueue.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import java.util.Iterator; 4 | import java.util.List; 5 | import java.util.Comparator; 6 | import java.util.HashMap; 7 | import ghidra.graph.algo.DepthFirstSorter; 8 | import ghidra.pal.util.NoDuplicatesPriorityQueue; 9 | 10 | // Use for data flow analysis-style worklist algorithms. We order the vertices 11 | // by reverse post-order for forward-style problems, or preorder for backwards 12 | // problems. 13 | public class CFGPriorityQueue { 14 | class CFGVertexForwardComparator implements Comparator> { 15 | // Reverse post-order mapping for vertices. 16 | HashMap VertexReversePostOrder; 17 | 18 | // Construct the ordering from the graph. 19 | public CFGVertexForwardComparator(CFG g) { 20 | VertexReversePostOrder = new HashMap(); 21 | 22 | // Get the postorder list from Ghidra. 23 | List> jvpre = DepthFirstSorter.postOrder(g); 24 | 25 | // First vertex has largest number; descends subsequently. 26 | int i = jvpre.size() - 1; 27 | 28 | Iterator> it = jvpre.iterator(); 29 | while(it.hasNext()) { 30 | CFGVertex curr = it.next(); 31 | // Printer.printf("Vertex %s: ordering number %d\n", curr.getLocator().toString(), i); 32 | VertexReversePostOrder.put(curr.getLocator(), i--); 33 | } 34 | } 35 | @Override 36 | public int compare(CFGVertex lhs, CFGVertex rhs) { 37 | return VertexReversePostOrder.get(lhs.getLocator()) - VertexReversePostOrder.get(rhs.getLocator()); 38 | } 39 | } 40 | 41 | class CFGVertexBackwardComparator implements Comparator> { 42 | // Reverse post-order mapping for vertices. 43 | HashMap VertexPreOrder; 44 | 45 | // Construct the ordering from the graph. 46 | public CFGVertexBackwardComparator(CFG g) { 47 | VertexPreOrder = new HashMap(); 48 | 49 | // Get the preorder list from Ghidra. 50 | List> jvpre = DepthFirstSorter.preOrder(g); 51 | 52 | // First vertex has smallest number; ascends subsequently. 53 | int i = 0; 54 | 55 | Iterator> it = jvpre.iterator(); 56 | while(it.hasNext()) { 57 | CFGVertex curr = it.next(); 58 | // Printer.printf("Vertex %s: ordering number %d\n", curr.getLocator().toString(), i); 59 | VertexPreOrder.put(curr.getLocator(), i--); 60 | } 61 | } 62 | @Override 63 | public int compare(CFGVertex lhs, CFGVertex rhs) { 64 | return VertexPreOrder.get(lhs.getLocator()) - VertexPreOrder.get(rhs.getLocator()); 65 | } 66 | } 67 | // The priority queue itself. 68 | public NoDuplicatesPriorityQueue> PQ; 69 | 70 | // Constructor dictates forward or backward ordering. 71 | public CFGPriorityQueue(CFG g, boolean forward) { 72 | Comparator> comp; 73 | if(forward) 74 | comp = new CFGVertexForwardComparator(g); 75 | else 76 | comp = new CFGVertexBackwardComparator(g); 77 | PQ = new NoDuplicatesPriorityQueue>(g.getVertexCount(), comp); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/CFGVertex.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import java.util.List; 4 | import ghidra.graph.GVertex; 5 | import ghidra.pal.util.Pair; 6 | 7 | // CFGVertex has a straightfoward implementation. 8 | public class CFGVertex implements GVertex { 9 | A Locator; 10 | List> Entities; 11 | public CFGVertex(A Location, List> Ents) { 12 | Locator = Location; 13 | Entities = Ents; 14 | } 15 | public int hashCode() { 16 | return Locator.hashCode(); 17 | } 18 | public A getLocator() { 19 | return Locator; 20 | } 21 | public List> getEntities() { 22 | return Entities; 23 | } 24 | 25 | public void setEntities(List> ents) { 26 | Entities = ents; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/CFGVertexDetailProvider.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | // Generic interface used by the CFG construction algorithm. Type parameters: 4 | // A, location: Address, or Pair 5 | // T, entity: Instruction, PseudoInstruction, PcodeOp, PcodeOpRaw 6 | public interface CFGVertexDetailProvider { 7 | // 1) Disassemble the entity at the location. 8 | // 2) Modify the State object to indicate outgoing flow information. 9 | // 3) Return the entity from step 1. 10 | public T provide(A a, CFGBuilderBundle State); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/CacheInstructions.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import ghidra.program.model.address.Address; 4 | import ghidra.program.model.listing.Instruction; 5 | import ghidra.program.model.listing.Program; 6 | 7 | // The CFGVertexDetailProvider interface classes use this to cache the 8 | // disassemblies from specified addresses. 9 | public interface CacheInstructions { 10 | // Initialize the cache, given a reference to the current program. 11 | public void init(Program p); 12 | 13 | // Get a cached or new disassembly for a given address. 14 | public T getInstruction(Address ea); 15 | 16 | // Get a new disassembly for a given address. 17 | public T disassembleNew(Address ea); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/CacheInstructionsImpl.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import java.util.HashMap; 4 | 5 | import ghidra.program.model.address.Address; 6 | import ghidra.program.model.listing.Instruction; 7 | 8 | // I don't know if this is good Java programming practice. Since the 9 | // implementations for different instruction types use common code, but I can't 10 | // declare concrete functions in an interface, I made this "-Impl" class to 11 | // put the common code. But maybe I should have just made the whole thing an 12 | // abstract class and not bothered with an interface? 13 | public abstract class CacheInstructionsImpl implements CacheInstructions{ 14 | HashMap DisasmCache; 15 | public T getInstruction(Address ea) { 16 | if(!DisasmCache.containsKey(ea)) { 17 | T ret = disassembleNew(ea); 18 | if(ret != null) 19 | DisasmCache.put(ea, ret); 20 | return ret; 21 | } 22 | return DisasmCache.get(ea); 23 | } 24 | public CacheInstructionsImpl() { 25 | DisasmCache = new HashMap(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/InstructionCache.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import ghidra.program.model.address.Address; 4 | import ghidra.program.model.listing.Instruction; 5 | import ghidra.program.model.listing.Listing; 6 | import ghidra.program.model.listing.Program; 7 | 8 | // Implements the caching functionality for Instruction types. 9 | public class InstructionCache extends CacheInstructionsImpl { 10 | Listing currentListing; 11 | public void init (Program p) { 12 | currentListing = p.getListing(); 13 | } 14 | public InstructionCache(Program p) { 15 | super(); 16 | init(p); 17 | } 18 | public Instruction disassembleNew(Address ea) { 19 | return currentListing.getInstructionAt(ea); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/InstructionDetailProvider.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import ghidra.program.model.address.Address; 4 | import ghidra.program.model.listing.Instruction; 5 | import ghidra.program.model.listing.Listing; 6 | import ghidra.program.model.listing.Program; 7 | 8 | // Provides details for Instruction objects. As most of the implementation is 9 | // shared for PseudoInstruction objects, the meat is in a common class 10 | // InstructionDetailProviderUtil. 11 | public class InstructionDetailProvider implements CFGVertexDetailProvider { 12 | Listing currentListing; 13 | public InstructionDetailProvider(Program p) { 14 | currentListing = p.getListing(); 15 | } 16 | public Instruction provide(Address curr, CFGBuilderBundle
State) { 17 | Instruction instr = currentListing.getInstructionAt(curr); 18 | if(instr == null) 19 | return null; 20 | InstructionDetailProviderUtil.addFlows(instr, curr, State); 21 | return instr; 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/InstructionDetailProviderUtil.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import ghidra.program.model.address.Address; 4 | import ghidra.program.model.listing.Instruction; 5 | import ghidra.program.model.symbol.FlowType; 6 | 7 | // Utility class, common between Instruction and PseudoInstruction vertex 8 | // entities, to retrieve the flow information from a given Instruction and add 9 | // it to the State object. 10 | public final class InstructionDetailProviderUtil { 11 | public static void addFlows(Instruction instr, Address curr, CFGBuilderBundle
State) { 12 | FlowType ft = instr.getFlowType(); 13 | 14 | // If it's a jump, be more specific about the type... 15 | if(ft.isJump()) { 16 | Address[] flows = instr.getFlows(); 17 | 18 | // For conditional jumps, add both targets as heads. 19 | if(ft.isConditional()) { 20 | Address takenEa = flows[0]; 21 | Address notTakenEa = instr.getFallThrough(); 22 | State.DeferredEdges.add(new CFGPendingEdge
(curr, takenEa, CFGEdgeType.COND_TAKEN)); 23 | State.DeferredEdges.add(new CFGPendingEdge
(curr, notTakenEa, CFGEdgeType.COND_NOTTAKEN)); 24 | State.LocationWorkList.add(takenEa); 25 | State.LocationWorkList.add(notTakenEa); 26 | State.Heads.add(takenEa); 27 | State.Heads.add(notTakenEa); 28 | } 29 | 30 | // For unconditional jumps, don't add them as heads. 31 | else if (ft.isUnConditional()) { 32 | Address takenEa = flows[0]; 33 | State.DeferredEdges.add(new CFGPendingEdge
(curr, takenEa, CFGEdgeType.UNCONDITIONAL)); 34 | State.LocationWorkList.add(takenEa); 35 | } 36 | 37 | // For computed jumps like switch statements, use the stored flows 38 | // information to add edges. 39 | else if (ft.isComputed()) { 40 | for(int i = 0; i < flows.length; i++) { 41 | State.LocationWorkList.add(flows[i]); 42 | State.DeferredEdges.add(new CFGPendingEdge
(curr, flows[i], CFGEdgeType.NWAY)); 43 | State.Heads.add(flows[i]); 44 | } 45 | } 46 | } 47 | 48 | // If it wasn't a jump, see if it has a fallthrough and add that. 49 | else if(ft.hasFallthrough()) { 50 | Address fallThrough = instr.getFallThrough(); 51 | State.LocationWorkList.add(fallThrough); 52 | State.DeferredEdges.add(new CFGPendingEdge
(curr, fallThrough, CFGEdgeType.FALLTHROUGH)); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/PcodeOpProvider.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import ghidra.pcode.error.LowlevelError; 4 | import ghidra.pcode.opbehavior.BinaryOpBehavior; 5 | import ghidra.pcode.opbehavior.OpBehavior; 6 | import ghidra.pcode.opbehavior.UnaryOpBehavior; 7 | import ghidra.pcode.pcoderaw.PcodeOpRaw; 8 | import ghidra.program.model.address.Address; 9 | import ghidra.program.model.listing.Instruction; 10 | import ghidra.program.model.listing.Program; 11 | import ghidra.program.model.pcode.PcodeOp; 12 | import ghidra.pal.util.Pair; 13 | 14 | // This class analyzes PcodeOp objects and adds flows to the CFG state object 15 | // throughout CFG construction. 16 | public class PcodeOpProvider implements CFGVertexDetailProvider,PcodeOp> { 17 | CacheInstructions Cache; 18 | 19 | public PcodeOpProvider(Program p, CacheInstructions cache) { 20 | Cache = cache; 21 | Cache.init(p); 22 | } 23 | 24 | // Common method to compute the location for the fall-through. 25 | Pair getFallthruLoc(Pair curLoc, Instruction insn) { 26 | int pcodeLen = insn.getPcode().length; 27 | int insnLen = insn.getLength(); 28 | int nextLoc = curLoc.y+1; 29 | // If we've reached the end of the pcode for this Instruction, move on 30 | // to the next one. 31 | if(nextLoc >= pcodeLen) 32 | return new Pair(curLoc.x.addWrap(insnLen),0); 33 | // Otherwise, increment the pcode index and return a new location. 34 | return new Pair(curLoc.x, nextLoc); 35 | } 36 | 37 | // Common method to compute the location for the taken destination. 38 | Pair getBranchTakenLoc(Pair curLoc, Instruction insn) { 39 | int insnLen = insn.getLength(); 40 | PcodeOp[] pcode = insn.getPcode(); 41 | PcodeOp currPcode = pcode[curLoc.y]; 42 | 43 | // I adapted this from Ghidra's Emulate.java. 44 | Address destaddr = currPcode.getInput(0).getAddress(); 45 | int current_op = curLoc.y; 46 | 47 | // If the destination is in the "constant space", presumably this means 48 | // it's within a single Instruction's Pcode block. 49 | if (destaddr.getAddressSpace().isConstantSpace()) { 50 | 51 | // Compute the destination as an index within the PcodeOp array. 52 | long id = destaddr.getOffset(); 53 | id = id + current_op; 54 | current_op = (int) id; 55 | 56 | // If the destination is the last PcodeOp index, round it up to the 57 | // next Instruction according to logic ripped from Emulate.java. 58 | if (current_op == pcode.length) 59 | return new Pair(curLoc.x.addWrap(insnLen),0); 60 | 61 | // If a negative displacement, or outside of the Pcode, die. Again, 62 | // logic stolen from Emulate. 63 | else if ((current_op < 0) || (current_op >= pcode.length)) 64 | throw new LowlevelError("Bad intra-instruction branch"); 65 | 66 | // Else, if the destination is within the PcodeOp block, return 67 | // that location. 68 | else 69 | return new Pair(curLoc.x, current_op); 70 | } 71 | // Otherwise, if the destination address is an address, return the 72 | // location of its first PcodeOp. 73 | return new Pair(destaddr,0); 74 | } 75 | 76 | // Adds flows for an unconditional branch. 77 | void branch(Pair addr, Instruction insn, CFGBuilderBundle> State) { 78 | Pair destLoc = getBranchTakenLoc(addr, insn); 79 | State.DeferredEdges.add(new CFGPendingEdge>(addr, destLoc, CFGEdgeType.UNCONDITIONAL)); 80 | State.LocationWorkList.add(destLoc); 81 | //State.Heads.add(destLoc); 82 | } 83 | 84 | // Adds flows for an op that merely falls through. 85 | void fallthruOp(Pair curLoc, Instruction insn, CFGBuilderBundle> State) { 86 | Pair ftLoc = getFallthruLoc(curLoc, insn); 87 | State.LocationWorkList.add(ftLoc); 88 | State.DeferredEdges.add(new CFGPendingEdge>(curLoc, ftLoc, CFGEdgeType.FALLTHROUGH)); 89 | } 90 | 91 | // Add flows to both sides of a conditional branch. 92 | void conditionalBranch(Pair curLoc, Instruction insn, CFGBuilderBundle> State) { 93 | Pair ftLoc = getFallthruLoc(curLoc, insn); 94 | Pair destLoc = getBranchTakenLoc(curLoc, insn); 95 | State.DeferredEdges.add(new CFGPendingEdge>(curLoc, destLoc, CFGEdgeType.COND_TAKEN)); 96 | State.DeferredEdges.add(new CFGPendingEdge>(curLoc, ftLoc, CFGEdgeType.COND_NOTTAKEN)); 97 | State.LocationWorkList.add(destLoc); 98 | State.LocationWorkList.add(ftLoc); 99 | State.Heads.add(destLoc); 100 | State.Heads.add(ftLoc); 101 | } 102 | 103 | // Inspect the PcodeOp and add flows based on its type, using the methods 104 | // above. 105 | public PcodeOp provide(Pair addr, CFGBuilderBundle> State) { 106 | 107 | // Get the instruction from the cache (or disassemble afresh). Failed? 108 | // Return null. 109 | T instr = Cache.getInstruction(addr.x); 110 | if(instr == null) 111 | return null; 112 | 113 | // If the location is not within the PcodeOp block, return null. 114 | PcodeOp[] pcode = instr.getPcode(); 115 | if(addr.y > pcode.length) 116 | return null; 117 | 118 | // Get the PcodeOp, its Raw counterpart, and behavior. 119 | PcodeOp currPcode = pcode[addr.y]; 120 | PcodeOpRaw raw = new PcodeOpRaw(currPcode); 121 | OpBehavior behave = raw.getBehavior(); 122 | 123 | // Unary and binary operators are fallthroughs. 124 | if((behave instanceof UnaryOpBehavior) || (behave instanceof BinaryOpBehavior)) { 125 | fallthruOp(addr, instr, State); 126 | } 127 | else { 128 | 129 | // Switch over other behavior types. 130 | switch (behave.getOpCode()) { 131 | 132 | // These just fall through. 133 | case PcodeOp.LOAD: 134 | case PcodeOp.STORE: 135 | case PcodeOp.MULTIEQUAL: 136 | case PcodeOp.INDIRECT: 137 | case PcodeOp.CALLOTHER: 138 | case PcodeOp.CALL: 139 | case PcodeOp.CALLIND: 140 | fallthruOp(addr, instr, State); 141 | break; 142 | 143 | // Handle branches as above. 144 | case PcodeOp.BRANCH: 145 | branch(addr, instr, State); 146 | break; 147 | // Handle conditional branches as above. 148 | case PcodeOp.CBRANCH: 149 | conditionalBranch(addr, instr, State); 150 | break; 151 | // For indirect branches, see if the Instruction object has 152 | // flows, and use those if so. I don't know if I'm doing the 153 | // right thing here. 154 | case PcodeOp.BRANCHIND: 155 | Address[] flows = instr.getFlows(); 156 | for(int i = 0; i < flows.length; i++) { 157 | Pair p = new Pair(flows[i],0); 158 | State.LocationWorkList.add(p); 159 | State.DeferredEdges.add(new CFGPendingEdge>(addr, p, CFGEdgeType.NWAY)); 160 | State.Heads.add(p); 161 | } 162 | break; 163 | // For return operations, don't add any flows. 164 | case PcodeOp.RETURN: 165 | break; 166 | // Should have been covered by the binary/unary ops above 167 | default: 168 | break; 169 | } 170 | } 171 | // Finally, after adding flows, return the PcodeOp. 172 | return currPcode; 173 | } 174 | } 175 | 176 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/PseudoInstructionCache.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import ghidra.app.util.PseudoDisassembler; 4 | import ghidra.app.util.PseudoInstruction; 5 | import ghidra.program.model.address.Address; 6 | import ghidra.program.model.lang.InsufficientBytesException; 7 | import ghidra.program.model.lang.UnknownContextException; 8 | import ghidra.program.model.lang.UnknownInstructionException; 9 | import ghidra.program.model.listing.Program; 10 | 11 | //Implements the caching functionality for PseudoInstruction types. 12 | public class PseudoInstructionCache extends CacheInstructionsImpl { 13 | PseudoDisassembler pdis; 14 | 15 | public void init(Program p) { 16 | pdis = new PseudoDisassembler(p); 17 | } 18 | public PseudoInstructionCache(Program p) { 19 | super(); 20 | init(p); 21 | } 22 | 23 | public PseudoInstruction disassembleNew(Address ea) 24 | { 25 | try { 26 | return pdis.disassemble(ea); 27 | } catch (InsufficientBytesException e) { 28 | return null; 29 | } catch (UnknownInstructionException e) { 30 | return null; 31 | } catch (UnknownContextException e) { 32 | return null; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/cfg/PseudoInstructionDetailProvider.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.cfg; 2 | 3 | import ghidra.app.util.PseudoDisassembler; 4 | import ghidra.app.util.PseudoInstruction; 5 | import ghidra.program.model.address.Address; 6 | import ghidra.program.model.lang.InsufficientBytesException; 7 | import ghidra.program.model.lang.UnknownContextException; 8 | import ghidra.program.model.lang.UnknownInstructionException; 9 | import ghidra.program.model.listing.Program; 10 | 11 | // Provides details for PseudoInstruction objects. As most of the implementation 12 | // is shared for Instruction objects, the meat is in a common class 13 | // InstructionDetailProviderUtil. 14 | public class PseudoInstructionDetailProvider implements CFGVertexDetailProvider { 15 | PseudoDisassembler pdis; 16 | public PseudoInstructionDetailProvider(Program currentProgram) { 17 | pdis = new PseudoDisassembler(currentProgram); 18 | } 19 | public PseudoInstruction provide(Address curr, CFGBuilderBundle
State) { 20 | PseudoInstruction instr; 21 | try { 22 | instr = pdis.disassemble(curr); 23 | } catch (InsufficientBytesException e) { 24 | return null; 25 | } catch (UnknownInstructionException e) { 26 | return null; 27 | } catch (UnknownContextException e) { 28 | return null; 29 | } 30 | if(instr == null) 31 | return null; 32 | InstructionDetailProviderUtil.addFlows(instr, curr, State); 33 | return instr; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/generic/BitVector.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.generic; 2 | 3 | public interface BitVector { 4 | public boolean getBit(long which); 5 | public void assignBit(long which, boolean how); 6 | public void setBit(long which); 7 | public void clearBit(long which); 8 | public void assignBits(long which, int num, boolean how); 9 | public void setBits(long which, int num); 10 | public void clearBits(long which, int num); 11 | public boolean anySet(long which, int num); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/generic/DenseBitVector.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.generic; 2 | 3 | import java.util.Arrays; 4 | 5 | import ghidra.pal.util.JavaUtil; 6 | import ghidra.pal.util.Pair; 7 | 8 | public class DenseBitVector implements BitVector { 9 | protected long[] Bits; 10 | protected long Length; 11 | protected Pair getPosition(long numBits) { 12 | long numBytes = numBits / 64L; 13 | long numRemainderBits = numBits % 64L; 14 | return new Pair(numBytes,numRemainderBits); 15 | } 16 | 17 | public DenseBitVector(long numBits) { 18 | Length = numBits; 19 | Pair numLongs = getPosition(numBits); 20 | Bits = new long[(int) (numLongs.x + (JavaUtil.CompareLongs(numLongs.y, 0L) ? 0 : 1))]; 21 | } 22 | 23 | public long getLength() { return Length; } 24 | 25 | protected DenseBitVector() { 26 | } 27 | 28 | @Override 29 | public boolean equals(Object o) { 30 | if(this == o) 31 | return true; 32 | 33 | if(!(o instanceof DenseBitVector)) 34 | return false; 35 | 36 | DenseBitVector other = (DenseBitVector)o; 37 | if(other.Length != Length) 38 | return false; 39 | 40 | Pair numLongs = getPosition(Length); 41 | for(int i = 0; i < numLongs.x.intValue(); i++) 42 | if(other.Bits[i] != Bits[i]) 43 | return false; 44 | 45 | if(numLongs.y == 0) 46 | return true; 47 | 48 | int mask = (1 << numLongs.y) - 1; 49 | return (other.Bits[numLongs.x.intValue()] & mask) == (Bits[numLongs.x.intValue()] & mask); 50 | } 51 | 52 | public DenseBitVector clone() { 53 | DenseBitVector bv = new DenseBitVector(); 54 | bv.Bits = Arrays.copyOf(Bits, (int)Length); 55 | bv.Length = Length; 56 | return bv; 57 | } 58 | 59 | public boolean getBit(long which) { 60 | assert(0 <= which && which < Length); 61 | Pair pos = getPosition(which); 62 | return ((Bits[pos.x.intValue()] >> pos.y) & 1L) != 0; 63 | } 64 | 65 | public void assignBit(long which, boolean how) { 66 | assert(0 <= which && which < Length); 67 | Pair pos = getPosition(which); 68 | long relevant = Bits[pos.x.intValue()]; 69 | long mask = 1L << pos.y; 70 | relevant &= ~mask; 71 | long lHow = how ? 1L << pos.y : 0L; 72 | Bits[pos.x.intValue()] = relevant | lHow; 73 | } 74 | 75 | public void setBit(long which) { 76 | assignBit(which, true); 77 | } 78 | 79 | public void clearBit(long which) { 80 | assignBit(which, false); 81 | } 82 | 83 | public void assignBits(long which, int num, boolean how) { 84 | for(long i = 0L; i < num; i++) 85 | assignBit(which+i,how); 86 | } 87 | 88 | public void setBits(long which, int num) { 89 | assignBits(which, num, true); 90 | } 91 | 92 | public void clearBits(long which, int num) { 93 | assignBits(which, num, false); 94 | } 95 | public boolean anySet(long which, int num) { 96 | for(long i = 0; i < num; i++) 97 | if(getBit(which+i)) 98 | return true; 99 | return false; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/generic/PcodeDisasmUtil.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.generic; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import ghidra.pal.cfg.CacheInstructions; 7 | import ghidra.pal.cfg.InstructionCache; 8 | import ghidra.pal.cfg.PseudoInstructionCache; 9 | import ghidra.pal.util.Pair; 10 | import ghidra.program.model.address.Address; 11 | import ghidra.program.model.listing.Instruction; 12 | import ghidra.program.model.listing.Program; 13 | import ghidra.program.model.pcode.PcodeOp; 14 | 15 | public class PcodeDisasmUtil { 16 | public static final List,PcodeOp>> GetRange(Program p, boolean usePseudo, Address startEa, Address endEa) { 17 | CacheInstructions pic; 18 | if(usePseudo) 19 | pic = new PseudoInstructionCache(p); 20 | else 21 | pic = new InstructionCache(p); 22 | List,PcodeOp>> entities = new ArrayList,PcodeOp>>(); 23 | Address currEa = startEa; 24 | while(currEa.getOffset() <= endEa.getOffset()) { 25 | Instruction iCurr = pic.getInstruction(currEa); 26 | PcodeOp[] pcode = iCurr.getPcode(); 27 | for(int i = 0; i < pcode.length; i++) 28 | entities.add(new Pair,PcodeOp>(new Pair(currEa,i),pcode[i])); 29 | currEa = currEa.add(iCurr.getLength()); 30 | } 31 | return entities; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/generic/SparseBitVector.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.generic; 2 | 3 | import java.util.HashMap; 4 | 5 | import ghidra.pal.util.Printer; 6 | 7 | // I don't know enough Java development to know when to prefer an abstract 8 | // class versus an interface. It's clear that only three methods differ between 9 | // this and DenseBitVector. 10 | public class SparseBitVector implements BitVector { 11 | boolean Default; 12 | HashMap Map; 13 | public SparseBitVector(boolean defaultValue) { 14 | Map = new HashMap(); 15 | Default = defaultValue; 16 | } 17 | 18 | public void dump() { 19 | Printer.printf("Begin\n"); 20 | for(HashMap.Entry ent : Map.entrySet()) 21 | Printer.printf("%x->%s\n", ent.getKey(), ent.getValue()); 22 | } 23 | 24 | // Should probably move that into the interface 25 | public void clear() { 26 | Map.clear(); 27 | } 28 | 29 | public boolean equals(Object o) { 30 | if(this == o) 31 | return true; 32 | 33 | if(!(o instanceof SparseBitVector)) 34 | return false; 35 | 36 | SparseBitVector other = (SparseBitVector)o; 37 | if(Default != other.Default) 38 | return false; 39 | 40 | for(HashMap.Entry ent : Map.entrySet()) { 41 | boolean value = ent.getValue(); 42 | boolean wasDefault = value == Default; 43 | if(other.Map.containsKey(ent.getKey())) { 44 | boolean ovalue = other.Map.get(ent.getKey()); 45 | if(ovalue != value) 46 | return false; 47 | } 48 | else if (!wasDefault) 49 | return false; 50 | } 51 | for(HashMap.Entry ent : other.Map.entrySet()) { 52 | if(Map.containsKey(ent.getKey())) 53 | continue; 54 | boolean ovalue = ent.getValue(); 55 | if(ovalue != other.Default) 56 | return false; 57 | } 58 | return true; 59 | } 60 | 61 | public boolean getBit(long which) { 62 | if(Map.containsKey(which)) 63 | return Map.get(which); 64 | return Default; 65 | } 66 | public void assignBit(long which, boolean how) { 67 | if(how == Default) 68 | Map.remove(which); 69 | else 70 | Map.put(which, how); 71 | } 72 | public void setBit(long which) { 73 | assignBit(which, true); 74 | } 75 | public void clearBit(long which) { 76 | assignBit(which, false); 77 | } 78 | public void assignBits(long which, int num, boolean how) { 79 | assert(num >= 0L); 80 | for(long i = 0L; i < num; i++) 81 | assignBit(which+i,how); 82 | } 83 | public void setBits(long which, int num) { 84 | assignBits(which,num,true); 85 | } 86 | public void clearBits(long which, int num) { 87 | assignBits(which,num,false); 88 | } 89 | public boolean anySet(long which, int num) { 90 | for(long i = 0; i < num; i++) 91 | if(getBit(which+i)) 92 | return true; 93 | return false; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/generic/VisitorUnimplementedException.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.generic; 2 | 3 | // This exception is thrown when the visitor doesn't implement a particular 4 | // variety of PcodeOp or Varnode. 5 | public class VisitorUnimplementedException extends Exception { 6 | public VisitorUnimplementedException(String errorMessage) { 7 | super(errorMessage); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/parsers/DCAFilter.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.parsers; 2 | 3 | import java.util.HashMap; 4 | import org.antlr.runtime.*; 5 | import org.antlr.runtime.tree.CommonTree; 6 | import org.antlr.runtime.tree.CommonTreeNodeStream; 7 | 8 | import ghidra.pal.parsers.dcafilter.grammar.*; 9 | 10 | 11 | public class DCAFilter { 12 | public BoolExpr parse (String source) { 13 | //CharStream input = CharStreams.fromString("(InsnEa >= 0x40000) && (InsnEa <= 0x500000) && IsWrite && AccessSize == 4"); 14 | DCAFilterLexer lexer = new DCAFilterLexer(new ANTLRStringStream(source)); 15 | CommonTokenStream tokens = new CommonTokenStream(lexer); 16 | DCAFilterParser parser = new DCAFilterParser(tokens); 17 | try { 18 | DCAFilterParser.start_rule_return returnValue = parser.start_rule(); 19 | CommonTree tree = (CommonTree)returnValue.getTree(); 20 | CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree); 21 | DCAFilterWalker walker = new DCAFilterWalker(nodes); 22 | return walker.start_rule(); 23 | } catch (RecognitionException e) { 24 | throw new IllegalStateException("Recognition exception is never thrown, only declared."); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/parsers/dcafilter/grammar/BoolExpr.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.parsers.dcafilter.grammar; 2 | 3 | import ghidra.pal.wbc.dca.DCAFilterContext; 4 | 5 | public abstract class BoolExpr { 6 | abstract public boolean evaluate(DCAFilterContext c); 7 | } 8 | 9 | class BoolConst extends BoolExpr { 10 | boolean val; 11 | public BoolConst(boolean v) { 12 | val = v; 13 | } 14 | @Override 15 | public boolean evaluate(DCAFilterContext c) { 16 | return val; 17 | } 18 | } 19 | 20 | abstract class BoolExpr2 extends BoolExpr { 21 | final BoolExpr lhs, rhs; 22 | public BoolExpr2(BoolExpr l, BoolExpr r) { 23 | lhs = l; 24 | rhs = r; 25 | } 26 | } 27 | 28 | class AndExpr extends BoolExpr2 { 29 | public AndExpr(BoolExpr l, BoolExpr r) { 30 | super(l,r); 31 | } 32 | @Override 33 | public boolean evaluate(DCAFilterContext c) { 34 | if(lhs.evaluate(c)) 35 | return rhs.evaluate(c); 36 | return false; 37 | } 38 | } 39 | 40 | class OrExpr extends BoolExpr2 { 41 | public OrExpr(BoolExpr l, BoolExpr r) { 42 | super(l,r); 43 | } 44 | @Override 45 | public boolean evaluate(DCAFilterContext c) { 46 | if(!lhs.evaluate(c)) 47 | return rhs.evaluate(c); 48 | return true; 49 | } 50 | } 51 | 52 | class NotExpr extends BoolExpr { 53 | final BoolExpr child; 54 | public NotExpr(BoolExpr l) { 55 | child = l; 56 | } 57 | @Override 58 | public boolean evaluate(DCAFilterContext c) { 59 | return !child.evaluate(c); 60 | } 61 | } 62 | 63 | class IsReadVar extends BoolExpr { 64 | @Override 65 | public boolean evaluate(DCAFilterContext c) { 66 | return c.IsRead; 67 | } 68 | } 69 | 70 | class IsWriteVar extends BoolExpr { 71 | @Override 72 | public boolean evaluate(DCAFilterContext c) { 73 | return c.IsWrite; 74 | } 75 | } 76 | 77 | abstract class LongExpr { 78 | abstract public long evaluate(DCAFilterContext c); 79 | } 80 | 81 | class AccessSizeVar extends LongExpr { 82 | @Override 83 | public long evaluate(DCAFilterContext c) { 84 | return c.AccessSize; 85 | } 86 | } 87 | 88 | class AccessEaVar extends LongExpr { 89 | @Override 90 | public long evaluate(DCAFilterContext c) { 91 | return c.AccessEa; 92 | } 93 | } 94 | 95 | class InsnEaVar extends LongExpr { 96 | @Override 97 | public long evaluate(DCAFilterContext c) { 98 | return c.InsnEa; 99 | } 100 | } 101 | 102 | class LongConstant extends LongExpr { 103 | long val; 104 | public LongConstant(String v, int radix) { 105 | if(radix == 16) 106 | val = Long.decode(v); 107 | else if (radix == 10) 108 | val = Integer.parseInt(v); 109 | else 110 | throw new IllegalArgumentException(String.format("LongConstant(%s,%d)", v, radix)); 111 | } 112 | 113 | @Override 114 | public long evaluate(DCAFilterContext c) { 115 | return val; 116 | } 117 | } 118 | 119 | abstract class IntCompare extends BoolExpr { 120 | LongExpr lhs; 121 | LongExpr rhs; 122 | public IntCompare(LongExpr l, LongExpr r) { 123 | lhs = l; 124 | rhs = r; 125 | } 126 | } 127 | 128 | class IntEQ extends IntCompare { 129 | public IntEQ(LongExpr l, LongExpr r) { 130 | super(l,r); 131 | } 132 | @Override 133 | public boolean evaluate(DCAFilterContext c) { 134 | return Long.compareUnsigned(lhs.evaluate(c), rhs.evaluate(c)) == 0; 135 | } 136 | } 137 | 138 | class IntNE extends IntCompare { 139 | public IntNE(LongExpr l, LongExpr r) { 140 | super(l,r); 141 | } 142 | @Override 143 | public boolean evaluate(DCAFilterContext c) { 144 | return Long.compareUnsigned(lhs.evaluate(c), rhs.evaluate(c)) != 0; 145 | } 146 | } 147 | 148 | class IntGT extends IntCompare { 149 | public IntGT(LongExpr l, LongExpr r) { 150 | super(l,r); 151 | } 152 | @Override 153 | public boolean evaluate(DCAFilterContext c) { 154 | return Long.compareUnsigned(lhs.evaluate(c), rhs.evaluate(c)) > 0; 155 | } 156 | } 157 | 158 | class IntGE extends IntCompare { 159 | public IntGE(LongExpr l, LongExpr r) { 160 | super(l,r); 161 | } 162 | @Override 163 | public boolean evaluate(DCAFilterContext c) { 164 | return Long.compareUnsigned(lhs.evaluate(c), rhs.evaluate(c)) >= 0; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/parsers/dcafilter/grammar/DCAFilter.g: -------------------------------------------------------------------------------- 1 | grammar DCAFilter; 2 | options {output=AST;} 3 | 4 | tokens { 5 | Constant; 6 | Intvar; 7 | } 8 | 9 | start_rule 10 | : boolExpr EOF -> boolExpr; 11 | 12 | ISWRITE : 'IsWrite'; 13 | ISREAD : 'IsRead'; 14 | 15 | boolExpr : boolExprOr; 16 | 17 | boolExprOr 18 | : boolExprAnd (OR^ boolExprAnd)*; 19 | 20 | boolExprAnd 21 | : boolExprNot (AND^ boolExprNot)*; 22 | 23 | boolExprNot 24 | : NOT^ boolExprTerm 25 | | boolExprTerm; 26 | 27 | boolExprTerm 28 | : '(' boolExprOr ')' -> boolExprOr 29 | | TRUE 30 | | FALSE 31 | | ISWRITE 32 | | ISREAD 33 | | comparison 34 | ; 35 | 36 | constant : HEX | DEC; 37 | 38 | EQ : '=='; 39 | NE : '!='; 40 | GE : '>='; 41 | GT : '>'; 42 | LE : '<='; 43 | LT : '<'; 44 | 45 | relop : EQ | NE | GE | GT | LE | LT; 46 | 47 | Accsize : 'AccessSize'; 48 | Insea : 'InsnEa'; 49 | Accea : 'AccessEa'; 50 | 51 | intvar : Accsize | Accea | Insea; 52 | 53 | compop 54 | : constant 55 | | intvar; 56 | 57 | comparison: compop relop^ compop; 58 | 59 | TRUE : 'true'; 60 | FALSE: 'false'; 61 | AND: '&&'; 62 | OR: '||'; 63 | NOT: '!'; 64 | WS : (' ' | '\t' | '\r' | '\n' )* {$channel=HIDDEN;}; 65 | HEX : '0x' ('a'..'f' | 'A'..'F' | '0'..'9')+; 66 | DEC : ('0'..'9')+; 67 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/parsers/dcafilter/grammar/DCAFilter.tokens: -------------------------------------------------------------------------------- 1 | T__25=25 2 | T__26=26 3 | AND=4 4 | Accea=5 5 | Accsize=6 6 | Constant=7 7 | DEC=8 8 | EQ=9 9 | FALSE=10 10 | GE=11 11 | GT=12 12 | HEX=13 13 | ISREAD=14 14 | ISWRITE=15 15 | Insea=16 16 | Intvar=17 17 | LE=18 18 | LT=19 19 | NE=20 20 | NOT=21 21 | OR=22 22 | TRUE=23 23 | WS=24 24 | '('=25 25 | ')'=26 26 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/parsers/dcafilter/grammar/DCAFilterWalker.g: -------------------------------------------------------------------------------- 1 | tree grammar DCAFilterWalker; 2 | 3 | options { 4 | tokenVocab=DCAFilter; 5 | ASTLabelType=CommonTree; 6 | } 7 | 8 | start_rule returns [BoolExpr e] 9 | : boolExpr {e = $boolExpr.e;} 10 | ; 11 | 12 | boolExpr returns [BoolExpr e] 13 | : ^(OR a=boolExpr b=boolExpr) {e = new OrExpr($a.e,$b.e);} 14 | | ^(AND a=boolExpr b=boolExpr) {e = new AndExpr($a.e,$b.e);} 15 | | ^(NOT a=boolExpr) {e = new NotExpr($a.e);} 16 | | TRUE {e = new BoolConst(true);} 17 | | FALSE {e = new BoolConst(false);} 18 | | ISREAD {e = new IsReadVar();} 19 | | ISWRITE {e = new IsWriteVar();} 20 | | comparison {e = $comparison.e;} 21 | ; 22 | 23 | intvar returns [LongExpr e] 24 | : Accsize {e = new AccessSizeVar();} 25 | | Accea {e = new AccessEaVar();} 26 | | Insea {e = new InsnEaVar();} 27 | ; 28 | 29 | constant returns [LongExpr e] 30 | : HEX {e = new LongConstant($HEX.text,16);} 31 | | DEC {e = new LongConstant($DEC.text,10);} 32 | ; 33 | 34 | compop returns [LongExpr e] 35 | : constant {e = $constant.e;} 36 | | intvar {e = $intvar.e;} 37 | ; 38 | comparison returns [BoolExpr e] 39 | : ^(EQ a=compop b=compop) {e = new IntEQ($a.e,$b.e); } 40 | | ^(NE a=compop b=compop) {e = new IntNE($a.e,$b.e); } 41 | | ^(GT a=compop b=compop) {e = new IntGT($a.e,$b.e); } 42 | | ^(GE a=compop b=compop) {e = new IntGE($a.e,$b.e); } 43 | | ^(LT a=compop b=compop) {e = new IntGE($b.e,$a.e); } 44 | | ^(LE a=compop b=compop) {e = new IntGT($b.e,$a.e); } 45 | ; 46 | 47 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/parsers/dcafilter/grammar/DCAFilterWalker.tokens: -------------------------------------------------------------------------------- 1 | T__25=25 2 | T__26=26 3 | AND=4 4 | Accea=5 5 | Accsize=6 6 | Constant=7 7 | DEC=8 8 | EQ=9 9 | FALSE=10 10 | GE=11 11 | GT=12 12 | HEX=13 13 | ISREAD=14 14 | ISWRITE=15 15 | Insea=16 16 | Intvar=17 17 | LE=18 18 | LT=19 19 | NE=20 20 | NOT=21 21 | OR=22 22 | TRUE=23 23 | WS=24 24 | '('=25 25 | ')'=26 26 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/util/ByteArrayBitEnumerator.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.util; 2 | 3 | import java.util.Iterator; 4 | 5 | public class ByteArrayBitEnumerator implements Iterable> { 6 | final byte[] byteArr; 7 | public ByteArrayBitEnumerator(byte[] arr) { 8 | byteArr = arr; 9 | } 10 | public Iterator> iterator() { 11 | return new ByteArrayBitEnumeratorIterator(byteArr); 12 | } 13 | class ByteArrayBitEnumeratorIterator implements Iterator> { 14 | final byte[] source; 15 | protected int byteIdx; 16 | protected int bitIdx; 17 | public ByteArrayBitEnumeratorIterator(byte[] arr) { 18 | source = arr; 19 | byteIdx = 0; 20 | bitIdx = 0; 21 | } 22 | public boolean hasNext() { 23 | return byteIdx != source.length; 24 | } 25 | public Pair next() { 26 | int bit = (source[byteIdx] >> bitIdx) & 1; 27 | int pos = byteIdx*8+bitIdx; 28 | if(++bitIdx == 8) { 29 | bitIdx = 0; 30 | byteIdx += 1; 31 | } 32 | return new Pair(pos,bit); 33 | } 34 | public void remove() { 35 | throw new UnsupportedOperationException(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/util/Colorizer.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.util; 2 | 3 | import java.awt.Color; 4 | import ghidra.program.model.address.Address; 5 | import ghidra.app.plugin.core.colorizer.ColorizingService; 6 | 7 | // This is here so that classes outside of the GhidraScript-derivative can 8 | // manipulate colors in the code browser. Classes derived from GhidraScript 9 | // inherit color-manipulating methods by default, but classes outside of that 10 | // need to access the ColorizingService object. Basically we just set the 11 | // ColorizingService variable from the GhidraScript-derivative, and then we can 12 | // manipulate colors from other classes. 13 | public final class Colorizer { 14 | private Colorizer() {} 15 | static private ColorizingService col; 16 | static public void Set(ColorizingService c) { col = c; } 17 | static public void setBackgroundColor(Address a, Color c) { col.setBackgroundColor(a, a, c); } 18 | static public void clearBackgroundcolor(Address a) { col.clearBackgroundColor(a, a); } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/util/IntegerBitEnumerator.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.util; 2 | 3 | import java.util.Iterator; 4 | 5 | public class IntegerBitEnumerator implements Iterable> { 6 | final long source; 7 | final int nBits; 8 | public IntegerBitEnumerator(int s, int b) { 9 | source = s; 10 | nBits = b; 11 | } 12 | public IntegerBitEnumerator(long s, int b) { 13 | source = s; 14 | nBits = b; 15 | } 16 | public Iterator> iterator() { 17 | return new IntegerBitEnumeratorIterator(source,nBits); 18 | } 19 | class IntegerBitEnumeratorIterator implements Iterator> { 20 | final long isource; 21 | final int inBits; 22 | protected int idx; 23 | public IntegerBitEnumeratorIterator(int s, int b) { 24 | isource = s; 25 | inBits = b; 26 | idx = 0; 27 | } 28 | public IntegerBitEnumeratorIterator(long s, int b) { 29 | isource = s; 30 | inBits = b; 31 | idx = 0; 32 | } 33 | public boolean hasNext() { 34 | return idx != inBits; 35 | } 36 | public Pair next() { 37 | return new Pair(idx,(int)(isource>>idx++)&1); 38 | } 39 | public void remove() { 40 | throw new UnsupportedOperationException(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/util/JavaUtil.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.util; 2 | 3 | public final class JavaUtil { 4 | // What the hell is this? Java was returning true for "cval.y!=res.x" 5 | // when the values were identical. StackExchange suggested it was an 6 | // int/long issue. I don't like that very much. 7 | static public boolean CompareLongs(long l1, long l2) 8 | { 9 | return new Long(l1).equals(new Long(l2)); 10 | } 11 | public static byte[] hexStringToByteArray(String s) { 12 | byte[] b = new byte[s.length() / 2]; 13 | for (int i = 0; i < b.length; i++) { 14 | int index = i * 2; 15 | int v = Integer.parseInt(s.substring(index, index + 2), 16); 16 | b[i] = (byte) v; 17 | } 18 | return b; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/util/NoDuplicatesPriorityQueue.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.util; 2 | 3 | import java.util.Comparator; 4 | import java.util.PriorityQueue; 5 | 6 | // Idea taken from StackExchange and implemented hastily. Basically, we don't 7 | // want any duplicates in our priority queue, so check for existence when 8 | // adding elements to the queue. 9 | public class NoDuplicatesPriorityQueue extends PriorityQueue 10 | { 11 | @Override 12 | public boolean offer(E e) 13 | { 14 | boolean isAdded = false; 15 | if(!super.contains(e)) 16 | isAdded = super.offer(e); 17 | return isAdded; 18 | } 19 | @Override 20 | public boolean add(E e) 21 | { 22 | boolean isAdded = false; 23 | if(!super.contains(e)) 24 | isAdded = super.add(e); 25 | return isAdded; 26 | } 27 | public NoDuplicatesPriorityQueue(int initialCapacity, Comparator comparator) { 28 | super(initialCapacity, comparator); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/util/Pair.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.util; 2 | 3 | //Does Java really not ship with generic pairs or tuples? That seems like an 4 | //oversight on their behalf. 5 | public class Pair { 6 | public final X x; 7 | public final Y y; 8 | public Pair(X x, Y y) { 9 | this.x = x; 10 | this.y = y; 11 | } 12 | @Override 13 | public boolean equals(Object o) { 14 | if(!(o instanceof Pair)) 15 | return false; 16 | @SuppressWarnings("unchecked") 17 | Pair other = (Pair)o; 18 | if(this.x.getClass() != other.x.getClass()) 19 | return false; 20 | if(this.y.getClass() != other.y.getClass()) 21 | return false; 22 | return this.x.equals(other.x) && this.y.equals(other.y); 23 | } 24 | @Override 25 | public int hashCode() { 26 | final int prime = 31; 27 | int result = 1; 28 | result = prime * result + ((this.x == null) ? 0 : this.x.hashCode()); 29 | result = prime * result + ((this.y == null) ? 0 : this.y.hashCode()); 30 | return result; 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return "(" + x.toString() + "," + y.toString() + ")"; 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/util/Printer.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.util; 2 | 3 | import java.io.PrintStream; 4 | import java.io.FileOutputStream; 5 | import ghidra.app.services.ConsoleService; 6 | 7 | //This is here so that classes outside of the GhidraScript-derivative can 8 | //print to the console. Those classes inherit the println() method, but 9 | //classes outside of that need to access the ConsoleService object. Basically 10 | //we just set the ConsoleService variable from the GhidraScript-derivative, 11 | //and then we can call Printer.println() from other classes. 12 | public final class Printer { 13 | private Printer() {} 14 | static private ConsoleService con; 15 | static public void Set(ConsoleService c) { con = c; } 16 | static public void println(String s) { 17 | con.println(s); 18 | if(pout != null) 19 | pout.println(s); 20 | } 21 | static public void printf(String format, Object... args) { 22 | con.print(String.format(format, args)); 23 | if(pout != null) 24 | pout.format(format, args); 25 | } 26 | static private FileOutputStream fout; 27 | static private PrintStream pout; 28 | static public void SetFileOutputPath(String path) { 29 | if(fout != null) { 30 | try { 31 | fout.close(); 32 | } catch (Exception e) { } 33 | fout = null; 34 | } 35 | if(pout != null) { 36 | pout.close(); 37 | pout = null; 38 | } 39 | try { 40 | fout=new FileOutputStream(path); 41 | } 42 | catch (Exception e) { 43 | printf("Printer.SetFileOutputPath(): Exception %s\n", e.toString()); 44 | fout = null; 45 | return; 46 | } 47 | pout=new PrintStream(fout); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/CPABundle.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc; 2 | 3 | public class CPABundle extends PABundle { 4 | public long sum; 5 | public double denominator; 6 | public CPABundle(int nTraces) { 7 | super(nTraces); 8 | } 9 | public CPABundle(CryptoBitVector cbv) { 10 | super(cbv); 11 | } 12 | public void finalize() { 13 | sum = bv.hammingWeight(); 14 | denominator = Math.sqrt((bv.getLength() - sum)*sum); 15 | } 16 | public double rho2(CPABundle other) { 17 | long dotProduct = bv.dotProduct(other.bv); 18 | double numerator = (bv.getLength()*dotProduct)-(sum*other.sum); 19 | double denom = denominator*other.denominator; 20 | if(denom == 0.0) 21 | return denom; 22 | return numerator/denom; 23 | } 24 | @Override 25 | public double compute(PABundle o) { 26 | CPABundle other = (CPABundle)o; 27 | return rho2(other); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/CryptoBitVector.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc; 2 | 3 | import ghidra.pal.generic.DenseBitVector; 4 | 5 | public class CryptoBitVector extends DenseBitVector { 6 | public CryptoBitVector(int len) { 7 | super(len); 8 | } 9 | 10 | static int hammingWeight(long l) { 11 | /* // Naive version (per my memory of Hacker's Delight) 12 | long a = ((l>> 1)&0x5555555555555555l) + (l&0x5555555555555555l); 13 | long b = ((a>> 2)&0x3333333333333333l) + (a&0x3333333333333333l); 14 | long c = ((b>> 4)&0x0F0F0F0F0F0F0F0Fl) + (b&0x0F0F0F0F0F0F0F0Fl); 15 | long d = ((c>> 8)&0x00FF00FF00FF00FFl) + (c&0x00FF00FF00FF00FFl); 16 | long e = ((d>>16)&0x0000FFFF0000FFFFl) + (d&0x0000FFFF0000FFFFl); 17 | long f = ((e>>32)&0x00000000FFFFFFFFl) + (e&0x00000000FFFFFFFFl); 18 | return (int)f; 19 | */ 20 | /* // For machines with slow multiplications (per Wikipedia) 21 | l -= (l >> 1) & 0x5555555555555555l; //put count of each 2 bits into those 2 bits 22 | l = (l & 0x3333333333333333l) + ((l >> 2) & 0x3333333333333333l); //put count of each 4 bits into those 4 bits 23 | l = (l + (l >> 4)) & 0x0F0F0F0F0F0F0F0Fl; //put count of each 8 bits into those 8 bits 24 | l += l >> 8; //put count of each 16 bits into their lowest 8 bits 25 | l += l >> 16; //put count of each 32 bits into their lowest 8 bits 26 | l += l >> 32; //put count of each 64 bits into their lowest 8 bits 27 | return (int)l & 0x7f; 28 | */ 29 | // For machines with fast multiplications (per Wikipedia) 30 | l -= (l >> 1) & 0x5555555555555555l; //put count of each 2 bits into those 2 bits 31 | l = (l & 0x3333333333333333l) + ((l >> 2) & 0x3333333333333333l); //put count of each 4 bits into those 4 bits 32 | l = (l + (l >> 4)) & 0x0F0F0F0F0F0F0F0Fl; //put count of each 8 bits into those 8 bits 33 | return (int)((l * 0x0101010101010101l) >> 56); //returns left 8 bits of l + (l<<8) + (l<<16) + (l<<24) + ... 34 | } 35 | public int hammingWeight() { 36 | int hw = 0; 37 | for(int i = 0; i < Bits.length; i++) 38 | hw += hammingWeight(Bits[i]); 39 | return hw; 40 | } 41 | protected int dotProductInner(CryptoBitVector other, boolean doNot) { 42 | if(other.Length != this.Length) 43 | throw new IllegalArgumentException("CryptoBitVector::dotProduct(): size mismatch"); 44 | int dp = 0; 45 | for(int i = 0; i < this.Bits.length; i++) 46 | dp += hammingWeight(this.Bits[i] & (doNot ? ~other.Bits[i] : other.Bits[i])); 47 | return dp; 48 | } 49 | public int dotProduct(CryptoBitVector other) { 50 | return dotProductInner(other, false); 51 | } 52 | public int dotProductNotRhs(CryptoBitVector other) { 53 | return dotProductInner(other, true); 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/DPABundle.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc; 2 | 3 | public class DPABundle extends PABundle { 4 | public DPABundle(int nTraces) { 5 | super(nTraces); 6 | } 7 | public DPABundle(CryptoBitVector cbv) { 8 | super(cbv); 9 | } 10 | @Override 11 | public double compute(PABundle o) { 12 | DPABundle other = (DPABundle)o; 13 | int dpSet = this.bv.dotProduct(other.bv); 14 | int dpClear = this.bv.dotProductNotRhs(other.bv); 15 | return (double)(dpSet-dpClear)/((double)(this.bv.getLength())); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/PABundle.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc; 2 | 3 | abstract public class PABundle { 4 | public final CryptoBitVector bv; 5 | public PABundle(int nTraces) { 6 | bv = new CryptoBitVector(nTraces); 7 | } 8 | public PABundle(CryptoBitVector cbv) { 9 | bv = cbv; 10 | finalize(); 11 | } 12 | public void finalize() { 13 | 14 | } 15 | abstract public double compute(PABundle other); 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/PowerAnalysis.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc; 2 | 3 | import java.util.Arrays; 4 | import java.util.Comparator; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | import java.util.function.Function; 8 | import java.util.stream.IntStream; 9 | 10 | import ghidra.pal.util.Pair; 11 | import ghidra.pal.util.Printer; 12 | import ghidra.pal.wbc.PABundle; 13 | 14 | abstract public class PowerAnalysis { 15 | protected int highest_period[]; 16 | protected double highest_correlations[][]; 17 | public final int nKeys; 18 | public final int nBits; 19 | public final int nBitsPer; 20 | public final String quantityDesc; 21 | public PowerAnalysis(int numKeys, int numBits, int numBitsPerQuantity, String desc) { 22 | nKeys = numKeys; 23 | nBits = numBits; 24 | nBitsPer = numBitsPerQuantity; 25 | quantityDesc = desc; 26 | } 27 | 28 | protected Function fnBundle; 29 | public void setBundleFn(Function f) { 30 | fnBundle = f; 31 | } 32 | 33 | abstract protected Iterable> createGuess(P text, int sK); 34 | 35 | protected void preAnalysis() { 36 | // Allocate array for highest correlations per bit 37 | highest_period = new int[nBits]; 38 | 39 | // Allocate array for highest correlations per bit, per key 40 | highest_correlations = new double[nBits][nKeys]; 41 | } 42 | 43 | protected void recordMax(double cij, int nBit, int sK) { 44 | double aij = Math.abs(cij); 45 | double ahc = Math.abs(highest_correlations[nBit][sK]); 46 | if(ahc < aij) { 47 | highest_correlations[nBit][sK] = cij; 48 | //Printer.printf("New best correlation for bit %d, key %d: %f (previous was %f)\n", nBit, sK, cij, ahc); 49 | } 50 | double ahp = Math.abs(highest_correlations[nBit][highest_period[nBit]]); 51 | if(ahp < aij) { 52 | highest_period[nBit] = sK; 53 | //Printer.printf("New highest key for bit %d: %d (%f)\n", nBit, sK, aij); 54 | } 55 | } 56 | 57 | public void analyzeTrace(List points, List

texts) { 58 | preAnalysis(); 59 | 60 | int nTraces = texts.size(); 61 | @SuppressWarnings("unchecked") 62 | B[] pointsMapped = (B[])points.stream().map(fnBundle).toArray(PABundle[]::new); 63 | 64 | // Iterate through all subkeys 65 | for(int sK = 0; sK < nKeys; sK++) { 66 | // Map the plaintexts to the guesses for the current subkey 67 | final int k = sK; 68 | @SuppressWarnings("unchecked") 69 | Iterable>[] subkeyGuesses = texts.stream().map((x) -> createGuess(x,k)).toArray(Iterable[]::new); 70 | 71 | // Create nBits CPABundle objects, one per bit of output 72 | CryptoBitVector[] bitLevel = IntStream.range(0,nBits).mapToObj((x) -> new CryptoBitVector(nTraces)).toArray(CryptoBitVector[]::new); 73 | 74 | // For each plaintext => SBOX output guess 75 | for(int g = 0; g < subkeyGuesses.length; g++) { 76 | Iterator> bitIt = subkeyGuesses[g].iterator(); 77 | while(bitIt.hasNext()) { 78 | Pair n = bitIt.next(); 79 | bitLevel[n.x].assignBit(g, n.y==1); 80 | } 81 | } 82 | // So after this, bitLevel[nBits] contains CryptoBitVectors of the size of 83 | // the number of traces. The contents of the bitvectors are the raw bits 84 | // from the guesses. 85 | 86 | // Store that information into the guesses array 87 | for(int bitNum = 0; bitNum < bitLevel.length; bitNum++) { 88 | B blB = fnBundle.apply(bitLevel[bitNum]); 89 | for(B point : pointsMapped) 90 | recordMax(point.compute(blB), bitNum, sK); 91 | } 92 | } 93 | postAnalysisCommon(); 94 | } 95 | protected void postAnalysisCommon() { 96 | for(int i = 0; i < nBits; i++) { 97 | int hp = highest_period[i]; 98 | Printer.printf("Best correlation for bit %02d: subkey %02x %f\n", i, hp, highest_correlations[i][hp]); 99 | } 100 | for(int g = 0; g < nBits/nBitsPer; g++) { 101 | Score[] correlation_scores = IntStream.range(0,nKeys).mapToObj((x) -> new Score(x,0.0)).toArray(Score[]::new); 102 | for(int i = 0; i < nBitsPer; i++) { 103 | for(int sK = 0; sK < nKeys; sK++) { 104 | correlation_scores[sK].y += Math.abs(highest_correlations[g*nBitsPer+i][sK]); 105 | } 106 | } 107 | class Sortbyy implements Comparator { 108 | public int compare(Score l, Score r) { 109 | return l.y == r.y ? 0 : l.y > r.y ? -1 : 1; 110 | } 111 | } 112 | Arrays.sort(correlation_scores, new Sortbyy()); 113 | for(int i = 0; i < nBitsPer; i++) { 114 | Printer.printf("%s %d, best key %d: %02x %f\n", quantityDesc, g, i, correlation_scores[i].x, correlation_scores[i].y); 115 | } 116 | Printer.printf("-----\n"); 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/PowerAnalysisFactory.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc; 2 | 3 | import java.util.function.Function; 4 | 5 | import ghidra.pal.wbc.aes.AESDecAllPowerAnalysis; 6 | import ghidra.pal.wbc.aes.AESDecBytePowerAnalysis; 7 | import ghidra.pal.wbc.aes.AESEncAllPowerAnalysis; 8 | import ghidra.pal.wbc.aes.AESEncBytePowerAnalysis; 9 | import ghidra.pal.wbc.aes.AESPowerAnalysis; 10 | import ghidra.pal.wbc.des.DESAllPowerAnalysis; 11 | import ghidra.pal.wbc.des.DESGroupPowerAnalysis; 12 | import ghidra.pal.wbc.des.DESPowerAnalysis; 13 | 14 | public class PowerAnalysisFactory { 15 | static Function cpaFn = (x) -> new CPABundle(x); 16 | static Function dpaFn = (x) -> new DPABundle(x); 17 | protected static DESPowerAnalysis setCPABundleFn(DESPowerAnalysis d) { 18 | d.setBundleFn(cpaFn); 19 | return d; 20 | } 21 | protected static DESPowerAnalysis setDPABundleFn(DESPowerAnalysis d) { 22 | d.setBundleFn(dpaFn); 23 | return d; 24 | } 25 | protected static AESPowerAnalysis setCPABundleFn(AESPowerAnalysis d) { 26 | d.setBundleFn(cpaFn); 27 | return d; 28 | } 29 | protected static AESPowerAnalysis setDPABundleFn(AESPowerAnalysis d) { 30 | d.setBundleFn(dpaFn); 31 | return d; 32 | } 33 | 34 | protected static DESPowerAnalysis desAll(boolean useCPA) { 35 | if(useCPA) 36 | return setCPABundleFn(new DESAllPowerAnalysis()); 37 | return setDPABundleFn(new DESAllPowerAnalysis()); 38 | } 39 | @SuppressWarnings("unchecked") 40 | public static DESPowerAnalysis desCPA() { 41 | return (DESPowerAnalysis)desAll(true); 42 | } 43 | @SuppressWarnings("unchecked") 44 | public static DESPowerAnalysis desDPA() { 45 | return (DESPowerAnalysis)desAll(false); 46 | } 47 | 48 | protected static DESPowerAnalysis desGroup(int g, boolean useCPA) { 49 | if(useCPA) 50 | return setCPABundleFn(new DESGroupPowerAnalysis(g)); 51 | return setDPABundleFn(new DESGroupPowerAnalysis(g)); 52 | } 53 | @SuppressWarnings("unchecked") 54 | public static DESPowerAnalysis desCPA(int g) { 55 | return (DESPowerAnalysis)desGroup(g, true); 56 | } 57 | @SuppressWarnings("unchecked") 58 | public static DESPowerAnalysis desDPA(int g) { 59 | return (DESPowerAnalysis)desGroup(g, false); 60 | } 61 | 62 | protected static AESPowerAnalysis aesAll(int alg, boolean useCPA, boolean enc) { 63 | if(useCPA) { 64 | if(enc) 65 | return setCPABundleFn(new AESEncAllPowerAnalysis(alg)); 66 | return setCPABundleFn(new AESDecAllPowerAnalysis(alg)); 67 | } 68 | if(enc) 69 | return setDPABundleFn(new AESEncAllPowerAnalysis(alg)); 70 | return setDPABundleFn(new AESDecAllPowerAnalysis(alg)); 71 | } 72 | @SuppressWarnings("unchecked") 73 | public static AESPowerAnalysis aesCPA(int alg, boolean enc) { 74 | return (AESPowerAnalysis) aesAll(alg, true, enc); 75 | } 76 | @SuppressWarnings("unchecked") 77 | public static AESPowerAnalysis aesDPA(int alg, boolean enc) { 78 | return (AESPowerAnalysis) aesAll(alg, false, enc); 79 | } 80 | protected static AESPowerAnalysis aesByte(int b, int alg, boolean useCPA, boolean enc) { 81 | if(useCPA) { 82 | if(enc) 83 | return setCPABundleFn(new AESEncBytePowerAnalysis(b, alg)); 84 | return setCPABundleFn(new AESDecBytePowerAnalysis(b, alg)); 85 | } 86 | if(enc) 87 | return setDPABundleFn(new AESEncBytePowerAnalysis(b, alg)); 88 | return setDPABundleFn(new AESDecBytePowerAnalysis(b, alg)); 89 | } 90 | @SuppressWarnings("unchecked") 91 | public static AESPowerAnalysis aesCPA(int b, int alg, boolean enc) { 92 | return (AESPowerAnalysis) aesByte(b, alg, true, enc); 93 | } 94 | @SuppressWarnings("unchecked") 95 | public static AESPowerAnalysis aesDPA(int b, int alg, boolean enc) { 96 | return (AESPowerAnalysis) aesByte(b, alg, false, enc); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/Score.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc; 2 | 3 | public class Score { 4 | public int x; 5 | public double y; 6 | public Score(int k, double s) { 7 | x = k; 8 | y = s; 9 | } 10 | } -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/TraceAggregator.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | public class TraceAggregator { 9 | static int allSameLength(List> traces) { 10 | int length = -1; 11 | for(ArrayList trace : traces) { 12 | if(length == -1) 13 | length = trace.size(); 14 | else if(length != trace.size()) 15 | return -1; 16 | } 17 | return length; 18 | } 19 | 20 | static public List aggregate(List> traces) { 21 | int traceLen = allSameLength(traces); 22 | if(traceLen == -1) 23 | throw new IllegalArgumentException("TraceAggregator::Aggregate(): length mismatch"); 24 | List> itList = traces.stream().map((x) -> x.iterator()).collect(Collectors.toList()); 25 | Iterator firstIt = itList.get(0); 26 | List results = new ArrayList(); 27 | int nTraces = traces.size(); 28 | while(firstIt.hasNext()) { 29 | Byte[] curVals = itList.stream().map((i) -> i.next()).toArray(Byte[]::new); 30 | for(int j = 0; j < 8; j++) { 31 | CryptoBitVector dbv = new CryptoBitVector(nTraces); 32 | boolean asgTrue = false, asgFalse = false; 33 | for(int i = 0; i < curVals.length; i++) { 34 | int bit = (curVals[i] >> j) & 1; 35 | boolean wasOne = bit == 1; 36 | if(wasOne) 37 | asgTrue = true; 38 | else 39 | asgFalse = true; 40 | dbv.assignBit(i, wasOne); 41 | } 42 | if(asgTrue && asgFalse) 43 | results.add(dbv); 44 | } 45 | } 46 | return results; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/aes/AESDecAllPowerAnalysis.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc.aes; 2 | 3 | import ghidra.pal.util.ByteArrayBitEnumerator; 4 | import ghidra.pal.util.Pair; 5 | import ghidra.pal.wbc.PABundle; 6 | 7 | public class AESDecAllPowerAnalysis extends AESPowerAnalysis{ 8 | public final int Alg; // e.g. AES.AES128 9 | AESGuess ag = new AESGuess(); 10 | public AESDecAllPowerAnalysis(int alg) { 11 | super(128); 12 | Alg = alg; 13 | } 14 | protected Iterable> createGuess(Byte[] text, int sK) { 15 | return new ByteArrayBitEnumerator(ag.DecryptionGenerateGuessForAllBytes(unboxBytes(text), sK, Alg)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/aes/AESDecBytePowerAnalysis.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc.aes; 2 | 3 | import ghidra.pal.util.Pair; 4 | import ghidra.pal.util.IntegerBitEnumerator; 5 | import ghidra.pal.wbc.PABundle; 6 | 7 | public class AESDecBytePowerAnalysis extends AESPowerAnalysis { 8 | public final int nByte; 9 | public final int Alg; // e.g. AES.AES128 10 | AESGuess ag = new AESGuess(); 11 | public AESDecBytePowerAnalysis(int b, int alg) { 12 | super(8); 13 | nByte = b; 14 | Alg = alg; 15 | } 16 | protected Iterable> createGuess(Byte[] text, int sK) { 17 | return new IntegerBitEnumerator(ag.DecryptionGenerateGuessForByte(unboxBytes(text), nByte, sK, Alg),8); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/aes/AESEncAllPowerAnalysis.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc.aes; 2 | 3 | import ghidra.pal.util.ByteArrayBitEnumerator; 4 | import ghidra.pal.util.Pair; 5 | import ghidra.pal.wbc.PABundle; 6 | 7 | public class AESEncAllPowerAnalysis extends AESPowerAnalysis{ 8 | public final int Alg; // e.g. AES.AES128 9 | AESGuess ag = new AESGuess(); 10 | public AESEncAllPowerAnalysis(int alg) { 11 | super(128); 12 | Alg = alg; 13 | } 14 | protected Iterable> createGuess(Byte[] text, int sK) { 15 | return new ByteArrayBitEnumerator(ag.EncryptionGenerateGuessForAllBytes(unboxBytes(text), sK, Alg)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/aes/AESEncBytePowerAnalysis.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc.aes; 2 | 3 | import ghidra.pal.util.Pair; 4 | import ghidra.pal.util.IntegerBitEnumerator; 5 | import ghidra.pal.wbc.PABundle; 6 | 7 | public class AESEncBytePowerAnalysis extends AESPowerAnalysis { 8 | public final int nByte; 9 | public final int Alg; // e.g. AES.AES128 10 | AESGuess ag = new AESGuess(); 11 | public AESEncBytePowerAnalysis(int b, int alg) { 12 | super(8); 13 | nByte = b; 14 | Alg = alg; 15 | } 16 | protected Iterable> createGuess(Byte[] text, int sK) { 17 | return new IntegerBitEnumerator(ag.EncryptionGenerateGuessForByte(unboxBytes(text), nByte, sK, Alg),8); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/aes/AESGuess.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc.aes; 2 | 3 | class FirstSubBytesOutput extends RuntimeException { 4 | byte[] AfterSubBytes; 5 | public FirstSubBytesOutput(byte[] interim) { 6 | AfterSubBytes = interim; 7 | } 8 | } 9 | 10 | public class AESGuess extends AES { 11 | void AfterSubBytes() { 12 | AfterInvSubBytes(); 13 | } 14 | void AfterInvSubBytes() { 15 | byte[] interim = new byte[16]; 16 | System.arraycopy(m_IntermediateState, 0, interim, 0, 16); 17 | throw new FirstSubBytesOutput(interim); 18 | } 19 | byte EncryptionGenerateGuessForByte(byte[] Plaintext, int byteNum, int sK, int Alg) { 20 | byte[] key = new byte[16]; 21 | key[byteNum] = (byte)sK; 22 | try { 23 | FirstEncrypt(key, Plaintext, Alg); 24 | } 25 | catch (FirstSubBytesOutput e) { 26 | return e.AfterSubBytes[byteNum]; 27 | } 28 | return 0; 29 | } 30 | 31 | byte[] EncryptionGenerateGuessForAllBytes(byte[] Plaintext, int sK, int Alg) { 32 | byte[] key = new byte[16]; 33 | for(int i = 0; i < 16; i++) 34 | key[i] = (byte)sK; 35 | try { 36 | FirstEncrypt(key, Plaintext, Alg); 37 | } 38 | catch (FirstSubBytesOutput e) { 39 | return e.AfterSubBytes; 40 | } 41 | return null; 42 | } 43 | 44 | byte DecryptionGenerateGuessForByte(byte[] Plaintext, int byteNum, int sK, int Alg) { 45 | byte[] key = new byte[16]; 46 | key[byteNum] = (byte)sK; 47 | try { 48 | FirstDecrypt(key, Plaintext, Alg); 49 | } 50 | catch (FirstSubBytesOutput e) { 51 | return e.AfterSubBytes[byteNum]; 52 | } 53 | return 0; 54 | } 55 | 56 | byte[] DecryptionGenerateGuessForAllBytes(byte[] Plaintext, int sK, int Alg) { 57 | byte[] key = new byte[16]; 58 | for(int i = 0; i < 16; i++) 59 | key[i] = (byte)sK; 60 | try { 61 | FirstDecrypt(key, Plaintext, Alg); 62 | } 63 | catch (FirstSubBytesOutput e) { 64 | return e.AfterSubBytes; 65 | } 66 | return null; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/aes/AESPowerAnalysis.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc.aes; 2 | 3 | import ghidra.pal.wbc.PABundle; 4 | import ghidra.pal.wbc.PowerAnalysis; 5 | 6 | abstract public class AESPowerAnalysis extends PowerAnalysis { 7 | public AESPowerAnalysis(int b) { 8 | super(256,b,8,"Intermediate byte"); 9 | } 10 | // So far, the worst parts about Java have involved the interactions 11 | // between generics, primitive types, and arrays. Guess I have to unbox 12 | // these things... 13 | protected byte[] unboxBytes(Byte[] text) { 14 | byte[] t = new byte[text.length]; 15 | int j = 0; 16 | for(Byte b: text) 17 | t[j++] = b.byteValue(); 18 | return t; 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/dca/DCAFilterContext.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc.dca; 2 | 3 | public class DCAFilterContext { 4 | public long InsnEa; 5 | public long AccessEa; 6 | public boolean IsRead; 7 | public boolean IsWrite; 8 | public long AccessSize; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/des/DES.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc.des; 2 | 3 | import java.util.concurrent.ThreadLocalRandom; 4 | import ghidra.pal.util.Printer; 5 | 6 | public class DES { 7 | public static final int LOGFEISTEL = 0x01; 8 | public static final int LOGROUNDS = 0x02; 9 | public static final int LOGPERMUTATIONS = 0x04; 10 | protected long SubKeys[]; 11 | 12 | protected static final int IPTable[] = { 13 | 57, 49, 41, 33, 25, 17, 9, 1, 14 | 59, 51, 43, 35, 27, 19, 11, 3, 15 | 61, 53, 45, 37, 29, 21, 13, 5, 16 | 63, 55, 47, 39, 31, 23, 15, 7, 17 | 56, 48, 40, 32, 24, 16, 8, 0, 18 | 58, 50, 42, 34, 26, 18, 10, 2, 19 | 60, 52, 44, 36, 28, 20, 12, 4, 20 | 62, 54, 46, 38, 30, 22, 14, 6 21 | }; 22 | protected static final int FPTable[] = { 23 | 39, 7, 47, 15, 55, 23, 63, 31, 24 | 38, 6, 46, 14, 54, 22, 62, 30, 25 | 37, 5, 45, 13, 53, 21, 61, 29, 26 | 36, 4, 44, 12, 52, 20, 60, 28, 27 | 35, 3, 43, 11, 51, 19, 59, 27, 28 | 34, 2, 42, 10, 50, 18, 58, 26, 29 | 33, 1, 41, 9, 49, 17, 57, 25, 30 | 32, 0, 40, 8, 48, 16, 56, 24 31 | }; 32 | 33 | protected static final int PermutationTable[] = { 34 | 7, 28, 21, 10, 26, 2, 19, 13, 35 | 23, 29, 5, 0, 18, 8, 24, 30, 36 | 22, 1, 14, 27, 6, 9, 17, 31, 37 | 15, 4, 20, 3, 11, 12, 25, 16 38 | }; 39 | 40 | protected static final int InversePermutationTable[] = { 41 | 11, 17, 5, 27, 25, 10, 20, 0, 42 | 13, 21, 3, 28, 29, 7, 18, 24, 43 | 31, 22, 12, 6, 26, 2, 16, 8, 44 | 14, 30, 4, 19, 1, 9, 15, 23 45 | }; 46 | 47 | protected static final int SBOX[][] = { 48 | { 49 | 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1, 50 | 3, 10, 10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8, 51 | 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7, 52 | 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13 53 | }, 54 | { 55 | 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14, 56 | 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5, 57 | 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2, 58 | 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9 59 | }, 60 | { 61 | 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10, 62 | 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1, 63 | 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7, 64 | 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12 65 | }, 66 | { 67 | 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3, 68 | 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9, 69 | 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8, 70 | 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14 71 | }, 72 | { 73 | 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1, 74 | 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6, 75 | 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13, 76 | 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3 77 | }, 78 | { 79 | 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5, 80 | 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8, 81 | 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10, 82 | 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13 83 | }, 84 | { 85 | 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10, 86 | 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6, 87 | 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7, 88 | 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12 89 | }, 90 | { 91 | 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4, 92 | 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2, 93 | 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13, 94 | 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11 95 | } 96 | }; 97 | protected static final int ExpansionTable[] = { 98 | 31, 0, 1, 2, 3, 4, 99 | 3, 4, 5, 6, 7, 8, 100 | 7, 8, 9, 10, 11, 12, 101 | 11, 12, 13, 14, 15, 16, 102 | 15, 16, 17, 18, 19, 20, 103 | 19, 20, 21, 22, 23, 24, 104 | 23, 24, 25, 26, 27, 28, 105 | 27, 28, 29, 30, 31, 0 106 | }; 107 | protected static final int PermutationChoice1Table[] = { 108 | 56, 48, 40, 32, 24, 16, 8, 109 | 0, 57, 49, 41, 33, 25, 17, 110 | 9, 1, 58, 50, 42, 34, 26, 111 | 18, 10, 2, 59, 51, 43, 35, 112 | 62, 54, 46, 38, 30, 22, 14, 113 | 6, 61, 53, 45, 37, 29, 21, 114 | 13, 5, 60, 52, 44, 36, 28, 115 | 20, 12, 4, 27, 19, 11, 3 116 | }; 117 | protected static final int PermutationChoice2Table[] = { 118 | 13, 16, 10, 23, 0, 4, 119 | 2, 27, 14, 5, 20, 9, 120 | 22, 18, 11, 3, 25, 7, 121 | 15, 6, 26, 19, 12, 1, 122 | 40, 51, 30, 36, 46, 54, 123 | 29, 39, 50, 44, 32, 47, 124 | 43, 48, 38, 55, 33, 52, 125 | 45, 41, 49, 35, 28, 31 126 | }; 127 | protected static final int LeftRotations[] = { 128 | 1, 1, 2, 2, 129 | 2, 2, 2, 2, 130 | 1, 2, 2, 2, 131 | 2, 2, 2, 1 132 | }; 133 | protected static final long SetBit(long l, int nBit, long value) { 134 | return l | (value << nBit); 135 | } 136 | protected static final long GetBit(long l, int nBit) { 137 | return (l >> nBit) & 1l; 138 | } 139 | protected static final long InitialPermutation(long input) { 140 | long output = 0l; 141 | for(int i = 0; i < 64; i++) 142 | output = SetBit(output, i, GetBit(input, IPTable[i])); 143 | return output; 144 | } 145 | protected static final long FinalPermutation(long input) { 146 | long output = 0l; 147 | for(int i = 0; i < 64; i++) 148 | output = SetBit(output, i, GetBit(input, FPTable[i])); 149 | return output; 150 | } 151 | protected static final long Expand(long input) { 152 | long output = 0l; 153 | for(int i = 0; i < ExpansionTable.length; i++) 154 | output = SetBit(output, i, GetBit(input, ExpansionTable[i])); 155 | return output; 156 | } 157 | protected static final long Permute(long input) { 158 | long output = 0l; 159 | for(int i = 0; i < PermutationTable.length; i++) 160 | output = SetBit(output, i, GetBit(input, PermutationTable[i])); 161 | return output; 162 | } 163 | protected static final long PermuteInverse(long input) { 164 | long output = 0l; 165 | for(int i = 0; i < PermutationTable.length; i++) 166 | output = SetBit(output, i, GetBit(input, InversePermutationTable[i])); 167 | return output; 168 | } 169 | protected static final long PermutationChoice1(long input) { 170 | long output = 0l; 171 | for(int i = 0; i < PermutationChoice1Table.length; i++) 172 | output = SetBit(output, 55-i, GetBit(input, 63-PermutationChoice1Table[i])); 173 | return output; 174 | } 175 | protected static final long PermutationChoice2(long input) { 176 | long output = 0l; 177 | for(int i = 0; i < PermutationChoice2Table.length; i++) 178 | output = SetBit(output, 47-i, GetBit(input, 55-PermutationChoice2Table[i])); 179 | return output; 180 | } 181 | protected static final long RotateLeft(long x, int n, int numBits) { 182 | n %= numBits; 183 | long Mask = (1l << numBits) - 1; 184 | return ((x<> (numBits-n))) & Mask; 185 | } 186 | public DES() { 187 | SubKeys = new long[16]; 188 | } 189 | void GenerateSubkeys(long key, boolean decrypt) { 190 | long KeyState = PermutationChoice1(key); 191 | long KSL = (KeyState >> 28) & 0x0FFFFFFFl; 192 | long KSR = KeyState & 0x0FFFFFFFl; 193 | for(int i = 0; i < LeftRotations.length; ++i) { 194 | KSL = RotateLeft(KSL, LeftRotations[i], 28); 195 | KSR = RotateLeft(KSR, LeftRotations[i], 28); 196 | SubKeys[decrypt ? 15-i : i] = PermutationChoice2((KSL << 28) | KSR); 197 | } 198 | } 199 | void FeistelBegin(int round, long SubKey, long R, long LInverse) {} 200 | void FeistelAfterInit(int round, long SubKey, long R, long LInverse, long ExpandedR, long ERxorSubKey) {} 201 | void FeistelAfterGroup(int round, int group, int SBIdx, int PermutedSBIdx, int SBOut) {} 202 | void FeistelEnd(int round, long SBoxesOut, long OxorLInv, long FinalOoutput) {} 203 | long Feistel(int round, long SubKey, long R, long LInverse) { 204 | FeistelBegin(round, SubKey, R, LInverse); 205 | long ExpandedR = Expand(R); 206 | long ERxorSubKey = ExpandedR ^ SubKey; 207 | FeistelAfterInit(round, SubKey, R, LInverse, ExpandedR, ERxorSubKey); 208 | long Output = 0l; 209 | for(int i = 7; i >= 0; i--) { 210 | int SBIdx = (int)((ERxorSubKey >> (i*6)) & 0x3Fl); 211 | int SBOut = SBOX[7-i][SBIdx]; 212 | Output |= (long)SBOut << (4*i); 213 | FeistelAfterGroup(round, i, SBIdx, SBIdx, SBOut); 214 | } 215 | long OxorLInv = Output ^ LInverse; 216 | long FinalOutput = Permute(OxorLInv); 217 | FeistelEnd(round, Output, OxorLInv, FinalOutput); 218 | return FinalOutput; 219 | } 220 | // Can be used for either encryption or decryption 221 | void IsolatedRound(long SubKey, long Plaintext) { 222 | long State = InitialPermutation(Plaintext); 223 | long L = (State >> 32) & 0xFFFFFFFFl; 224 | long R = State & 0xFFFFFFFFl; 225 | long LInverse = PermuteInverse(L); 226 | @SuppressWarnings("unused") 227 | long NewR = Feistel(1, SubKey, R, LInverse); 228 | } 229 | long ProcessBlock(Long Key, long Block, boolean decrypt) { 230 | GenerateSubkeys(Key, decrypt); 231 | long AfterIP = InitialPermutation(Block); 232 | long State = AfterIP; 233 | for(int i = 1; i <= 16; i++) { 234 | long L = (State >> 32) & 0xFFFFFFFFl; 235 | long R = State & 0xFFFFFFFFl; 236 | long LInverse = PermuteInverse(L); 237 | long NewR = Feistel(i, SubKeys[i-1], R, LInverse); 238 | if(i != 16) 239 | State = (R << 32) | NewR; 240 | else 241 | State = (NewR << 32) | R; 242 | } 243 | long AfterFP = FinalPermutation(State); 244 | return AfterFP; 245 | } 246 | public long EncryptBlock(long Key, long Block) { 247 | return ProcessBlock(Key, Block, false); 248 | } 249 | public long DecryptBlock(long Key, long Block) { 250 | return ProcessBlock(Key, Block, true); 251 | } 252 | public void test() { 253 | for(int i = 0; i < FPTable.length; i++) { 254 | int d = FPTable[IPTable[i]]; 255 | if(d != i) { 256 | Printer.printf("FPTable[IPTable[%d]] != %d (was %d)\n", i, i, d); 257 | return; 258 | } 259 | d = IPTable[FPTable[i]]; 260 | if(d != i) { 261 | Printer.printf("IPTable[FPTable[%d]] != %d (was %d)\n", i, i, d); 262 | return; 263 | } 264 | } 265 | for(int i = 0; i < 10; i++) { 266 | long value = ThreadLocalRandom.current().nextLong(); 267 | long IP = InitialPermutation(value); 268 | long FP = FinalPermutation(IP); 269 | if(value != FP) { 270 | Printer.printf("FinalPermutation(InitialPermutation(%16x)) != %16x (was %16x)\n", value, value, FP); 271 | return; 272 | } 273 | FP = FinalPermutation(value); 274 | IP = InitialPermutation(FP); 275 | if(value != IP) { 276 | Printer.printf("InitialPermutation(FinalPermutation(%16x)) != %16x (was %16x)\n", value, value, IP); 277 | return; 278 | } 279 | value &= 0xFFFFFFFFL; 280 | long P = Permute(value); 281 | long I = PermuteInverse(P); 282 | if(value != I) { 283 | Printer.printf("PermuteInverse(Permute(%8x)) != %8x (was %8x)\n", value, value, I); 284 | return; 285 | } 286 | I = PermuteInverse(value); 287 | P = Permute(I); 288 | if(value != P) { 289 | Printer.printf("Permute(PermuteInverse(%8x)) != %8x (was %8x)\n", value, value, P); 290 | return; 291 | } 292 | } 293 | 294 | long key = 0x3032343234363236l; 295 | long pt = 0x1122334455667788l; 296 | long ct = 0xc403d32e2bc6cfeel; 297 | long end2end = EncryptBlock(key, pt); 298 | if(end2end != ct) { 299 | Printer.printf("DES: end-to-end test failed (encryption returned %16x, expected %16x)\n", end2end, ct); 300 | return; 301 | } 302 | long dec = DecryptBlock(key, end2end); 303 | if(dec != pt) { 304 | Printer.printf("DES: end-to-end test failed (decryption returned %16x, expected %16x)\n", dec, pt); 305 | return; 306 | } 307 | 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/des/DESAllPowerAnalysis.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc.des; 2 | 3 | import ghidra.pal.util.IntegerBitEnumerator; 4 | import ghidra.pal.util.Pair; 5 | import ghidra.pal.wbc.PABundle; 6 | 7 | public class DESAllPowerAnalysis extends DESPowerAnalysis { 8 | DESGuess dg = new DESGuess(); 9 | public DESAllPowerAnalysis() { 10 | super(32); 11 | } 12 | protected Iterable> createGuess(Long text, int sK) { 13 | return new IntegerBitEnumerator(dg.GenerateGuessForAllGroups(text,sK),32); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/des/DESGroupPowerAnalysis.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc.des; 2 | 3 | import ghidra.pal.util.Pair; 4 | import ghidra.pal.wbc.PABundle; 5 | import ghidra.pal.util.IntegerBitEnumerator; 6 | 7 | public class DESGroupPowerAnalysis extends DESPowerAnalysis { 8 | public final int nGroup; 9 | DESGuess dg = new DESGuess(); 10 | public DESGroupPowerAnalysis(int g) { 11 | super(4); 12 | nGroup = g; 13 | } 14 | protected Iterable> createGuess(Long text, int sK) { 15 | return new IntegerBitEnumerator(dg.GenerateGuessForGroup(text,nGroup,sK),4); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/des/DESGuess.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc.des; 2 | 3 | import ghidra.pal.wbc.des.DES; 4 | 5 | class FirstRoundOutput extends RuntimeException { 6 | long SBoxesOut; 7 | long OxorLInv; 8 | long FinalOutput; 9 | public FirstRoundOutput(long sBoxesOut, long oxorLInv, long finalOutput) { 10 | SBoxesOut = sBoxesOut; 11 | OxorLInv = oxorLInv; 12 | FinalOutput = finalOutput; 13 | } 14 | } 15 | 16 | public class DESGuess extends DES { 17 | void FeistelEnd(int round, long SBoxesOut, long OxorLInv, long FinalOutput) throws FirstRoundOutput { 18 | throw new FirstRoundOutput(SBoxesOut, OxorLInv, FinalOutput); 19 | } 20 | 21 | long GenerateGuessForGroup(long Plaintext, int group, int sK) { 22 | long CompleteSubkey = (long)sK << (group * 6); 23 | try { 24 | IsolatedRound(CompleteSubkey, Plaintext); 25 | } 26 | catch (FirstRoundOutput e) { 27 | return (e.OxorLInv >> (group * 4)) & 0xFl; 28 | } 29 | // Return statement above will always execute 30 | return 0; 31 | } 32 | 33 | long GenerateGuessForAllGroups(long Plaintext, int sK) { 34 | long CompleteSubkey = 0l; 35 | for(int i = 0; i < 8; i++) 36 | CompleteSubkey |= (long)sK << (i * 6); 37 | try { 38 | IsolatedRound(CompleteSubkey, Plaintext); 39 | } 40 | catch (FirstRoundOutput e) { 41 | return e.OxorLInv; 42 | } 43 | // Return statement above will always execute 44 | return 0; 45 | } 46 | } 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/main/java/ghidra/pal/wbc/des/DESPowerAnalysis.java: -------------------------------------------------------------------------------- 1 | package ghidra.pal.wbc.des; 2 | 3 | import java.util.Arrays; 4 | import java.util.Comparator; 5 | import java.util.stream.IntStream; 6 | 7 | import ghidra.pal.util.Printer; 8 | import ghidra.pal.wbc.PABundle; 9 | import ghidra.pal.wbc.PowerAnalysis; 10 | import ghidra.pal.wbc.Score; 11 | 12 | abstract public class DESPowerAnalysis extends PowerAnalysis { 13 | public DESPowerAnalysis(int b) { 14 | super(64,b,4,"Group"); 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/resources/images/README.txt: -------------------------------------------------------------------------------- 1 | The "src/resources/images" directory is intended to hold all image/icon files used by 2 | this module. 3 | -------------------------------------------------------------------------------- /src/test/java/README.test.txt: -------------------------------------------------------------------------------- 1 | The "test" directory is intended to hold unit test cases. The package structure within 2 | this folder should correspond to that found in the "src" folder. 3 | -------------------------------------------------------------------------------- /src/test/java/ghidra/pal/TVLAbstractInterpreterTest.java: -------------------------------------------------------------------------------- 1 | // I used this to make sure the abstract transformers were implemented 2 | // correctly. However, I haven't updated it for the final architecture of the 3 | // code, so I've just commented it out. Also, I have no idea how to make these 4 | // tests run in an automated fashion, given that it has to be running inside 5 | // of Ghidra with a program loaded. 6 | /* 7 | package ghidra.pal.absint.tvl; 8 | 9 | import ghidra.program.model.listing.Program; 10 | import ghidra.program.model.lang.Register; 11 | import ghidra.program.model.pcode.PcodeOp; 12 | import ghidra.program.model.pcode.Varnode; 13 | import ghidra.program.model.pcode.VarnodeTranslator; 14 | import ghidra.program.model.address.Address; 15 | import ghidra.program.model.address.AddressSpace; 16 | import ghidra.program.model.address.GenericAddressSpace; 17 | import ghidra.app.plugin.processors.sleigh.SleighLanguage; 18 | import ghidra.pcode.pcoderaw.PcodeOpRaw; 19 | import ghidra.pcode.opbehavior.OpBehavior; 20 | import ghidra.pcode.opbehavior.BinaryOpBehavior; 21 | import ghidra.pcode.opbehavior.UnaryOpBehavior; 22 | import java.util.concurrent.ThreadLocalRandom; 23 | import ghidra.app.services.ProgramManager; 24 | import ghidra.pal.absint.tvl.TVLBitVector; 25 | import ghidra.pal.generic.VisitorUnimplementedException; 26 | import ghidra.pal.util.JavaUtil; 27 | import ghidra.pal.util.Pair; 28 | import ghidra.pal.util.Printer; 29 | 30 | import static org.junit.Assert.*; 31 | import org.junit.Test; 32 | 33 | class TransformerTester { 34 | Register rAL, rAX, rEAX; 35 | Register rBL, rBX, rEBX; 36 | Register rCL, rCX, rECX; 37 | public Varnode vAL, vAX, vEAX; 38 | public Varnode vBL, vBX, vEBX; 39 | public Varnode vCL, vCX, vECX; 40 | TVLAbstractInterpreter tvlai; 41 | AddressSpace TestAddressSpace; 42 | Address TestAddress; 43 | int seqNo; 44 | 45 | public TransformerTester(Program currentProgram) 46 | { 47 | SleighLanguage l = (SleighLanguage)currentProgram.getLanguage(); 48 | VarnodeTranslator vt = new VarnodeTranslator(currentProgram); 49 | tvlai = new TVLAbstractInterpreter(l.isBigEndian()); 50 | TestAddressSpace = new GenericAddressSpace("TEST", 32, AddressSpace.TYPE_OTHER, 0); 51 | TestAddress = TestAddressSpace.getAddress(0); 52 | seqNo = 1; 53 | 54 | // Initialize Register and corresponding Varnode objects 55 | rAL = l.getRegister("AL"); vAL = vt.getVarnode(rAL); 56 | rBL = l.getRegister("BL"); vBL = vt.getVarnode(rBL); 57 | rCL = l.getRegister("CL"); vCL = vt.getVarnode(rCL); 58 | rAX = l.getRegister("AX"); vAX = vt.getVarnode(rAX); 59 | rBX = l.getRegister("BX"); vBX = vt.getVarnode(rBX); 60 | rCX = l.getRegister("CX"); vCX = vt.getVarnode(rCX); 61 | rEAX = l.getRegister("EAX"); vEAX = vt.getVarnode(rEAX); 62 | rEBX = l.getRegister("EBX"); vEBX = vt.getVarnode(rEBX); 63 | rECX = l.getRegister("ECX"); vECX = vt.getVarnode(rECX); 64 | } 65 | 66 | PcodeOp CreatePcodeOp(int op, Varnode[] inputs, Varnode output) 67 | { 68 | return new PcodeOp(TestAddress, seqNo++, op, inputs, output); 69 | } 70 | 71 | long GetBinaryPcodeResult(PcodeOp pcode, long valLhs, long valRhs) 72 | { 73 | PcodeOpRaw raw = new PcodeOpRaw(pcode); 74 | OpBehavior behave = raw.getBehavior(); 75 | assert(behave != null); 76 | assert(behave instanceof BinaryOpBehavior); 77 | BinaryOpBehavior binaryBehave = (BinaryOpBehavior) behave; 78 | Varnode lhs = pcode.getInput(0); 79 | // Varnode rhs = pcode.getInput(1); // unused 80 | Varnode out = pcode.getOutput(); 81 | return binaryBehave.evaluateBinary(out.getSize(), lhs.getSize(), valLhs, valRhs); 82 | } 83 | 84 | Pair TestBinaryPcode(int op, int nBytes, long valLhs, long valRhs, boolean randomize) 85 | { 86 | Varnode lhs, rhs, out; 87 | switch(nBytes) 88 | { 89 | case 1: lhs = vAL; rhs = vBL; out = vCL; break; 90 | case 2: lhs = vAX; rhs = vBX; out = vCX; break; 91 | case 4: lhs = vEAX; rhs = vEBX; out = vECX; break; 92 | default: assert(false); return null; 93 | } 94 | Varnode inputs[] = new Varnode[] { lhs, rhs }; 95 | PcodeOp p = CreatePcodeOp(op, inputs, out); 96 | long result = GetBinaryPcodeResult(p, valLhs, valRhs); 97 | tvlai.AbstractState.clear(); 98 | TVLBitVector bvlhs = new TVLBitVector(new GhidraSizeAdapter(nBytes), valLhs); 99 | TVLBitVector bvrhs = new TVLBitVector(new GhidraSizeAdapter(nBytes), valRhs); 100 | if(randomize) 101 | { 102 | TVLBitVector bv = ThreadLocalRandom.current().nextBoolean() ? bvlhs : bvrhs; 103 | bv.Value()[ThreadLocalRandom.current().nextInt(0, bv.Size())] = TVLBitVector.TVL_HALF; 104 | } 105 | tvlai.AbstractState.Associate(lhs, bvlhs); 106 | tvlai.AbstractState.Associate(rhs, bvrhs); 107 | try { 108 | tvlai.visit(null, p); 109 | } 110 | catch(VisitorUnimplementedException e) 111 | { 112 | Printer.println("Caught visitor unimplemented exception: "+e); 113 | return null; 114 | } 115 | TVLBitVector bvres = tvlai.AbstractState.Lookup(out); 116 | return new Pair(result,bvres); 117 | } 118 | 119 | long GetUnaryPcodeResult(PcodeOp pcode, long valLhs) 120 | { 121 | PcodeOpRaw raw = new PcodeOpRaw(pcode); 122 | OpBehavior behave = raw.getBehavior(); 123 | assert(behave != null); 124 | assert(behave instanceof UnaryOpBehavior); 125 | UnaryOpBehavior unaryBehave = (UnaryOpBehavior) behave; 126 | Varnode lhs = pcode.getInput(0); 127 | Varnode out = pcode.getOutput(); 128 | return unaryBehave.evaluateUnary(out.getSize(), lhs.getSize(), valLhs); 129 | } 130 | 131 | Pair TestUnaryPcode(int op, int nBytes, long valLhs, boolean randomize) 132 | { 133 | Varnode lhs, out; 134 | switch(nBytes) 135 | { 136 | case 1: lhs = vAL; out = vCL; break; 137 | case 2: lhs = vAX; out = vCX; break; 138 | case 4: lhs = vEAX; out = vECX; break; 139 | default: assert(false); return null; 140 | } 141 | Varnode inputs[] = new Varnode[] { lhs }; 142 | PcodeOp p = CreatePcodeOp(op, inputs, out); 143 | long result = GetUnaryPcodeResult(p, valLhs); 144 | tvlai.AbstractState.clear(); 145 | TVLBitVector bv = new TVLBitVector(new GhidraSizeAdapter(nBytes), valLhs); 146 | if(randomize) 147 | bv.Value()[ThreadLocalRandom.current().nextInt(0, bv.Size())] = TVLBitVector.TVL_HALF; 148 | tvlai.AbstractState.Associate(lhs, bv); 149 | 150 | try { 151 | tvlai.visit(null, p); 152 | } 153 | catch(VisitorUnimplementedException e) 154 | { 155 | Printer.println("Caught visitor unimplemented exception: "+e); 156 | return null; 157 | } 158 | TVLBitVector bvres = tvlai.AbstractState.Lookup(out); 159 | return new Pair(result,bvres); 160 | } 161 | } 162 | 163 | 164 | public class TVLAbstractInterpreterTest { 165 | Program currentProgram; 166 | void PrintIfConcreteTestFailure(int op, long alValue, long blValue, Pair res) { 167 | Pair cval = res.y.GetConstantValue(); 168 | if(cval == null) 169 | Printer.printf("Op %s: [al = %02x, bl = %02x] => non-constant %s\n", PcodeOp.getMnemonic(op), alValue, blValue, res.y.toString()); 170 | else if(!JavaUtil.CompareLongs(cval.y,res.x)) 171 | Printer.printf("Op %s: [al = %02x, bl = %02x] => %02x real, %02x abstract\n", PcodeOp.getMnemonic(op), alValue, blValue, res.x, cval.y); 172 | } 173 | 174 | void PrintIfRandomTestFailure(int op, long alValue, long blValue, Pair res) { 175 | byte[] AbsValue = res.y.Value(); 176 | long realRes = res.x; 177 | for(int i = 0; i < AbsValue.length; i++) 178 | { 179 | long bit = (realRes >> i) & 1L; 180 | if(AbsValue[i] != TVLBitVector.TVL_HALF) 181 | { 182 | if( (bit == 0L && AbsValue[i] != TVLBitVector.TVL_0) || 183 | (bit == 1L && AbsValue[i] != TVLBitVector.TVL_1) ) 184 | Printer.printf("Op %s: [al = %02x, bl = %02x] => %02x real, %s abstract, bit %d differs [%d/%s]\n", PcodeOp.getMnemonic(op), alValue, blValue, res.x, res.y.toString(), i, bit, TVLBitVector.Representation[AbsValue[i]]); 185 | } 186 | } 187 | } 188 | 189 | void TestUnimplemented() throws Exception { 190 | TransformerTester tt = new TransformerTester(currentProgram); 191 | int binaryOperators[] = new int[] { 192 | //PcodeOp.PTRADD, 193 | PcodeOp.PTRSUB, 194 | }; 195 | long blValue = 0x55; 196 | for(long alValue = 0; alValue < 0x100; alValue++) 197 | { 198 | for(int i = 0; i < binaryOperators.length; i++) 199 | { 200 | int op = binaryOperators[i]; 201 | PcodeOp p = tt.CreatePcodeOp(op, new Varnode[] {tt.vAL, tt.vBL}, tt.vCL); 202 | long res = tt.GetBinaryPcodeResult(p, alValue, blValue); 203 | Printer.printf("Op %s: [al = %02x, bl = %02x] => %02x\n", PcodeOp.getMnemonic(op), alValue, blValue, res); 204 | } 205 | } 206 | } 207 | 208 | public void TestAbstractTransformers() throws Exception { 209 | TransformerTester tt = new TransformerTester(currentProgram); 210 | int binaryOperators[] = new int[] { 211 | PcodeOp.INT_ADD, 212 | PcodeOp.INT_AND, 213 | PcodeOp.INT_CARRY, 214 | PcodeOp.INT_DIV, 215 | PcodeOp.INT_EQUAL, 216 | PcodeOp.INT_LEFT, 217 | PcodeOp.INT_LESS, 218 | PcodeOp.INT_LESSEQUAL, 219 | PcodeOp.INT_MULT, 220 | PcodeOp.INT_NOTEQUAL, 221 | PcodeOp.INT_OR, 222 | PcodeOp.INT_REM, 223 | PcodeOp.INT_RIGHT, 224 | PcodeOp.INT_SBORROW, 225 | PcodeOp.INT_SCARRY, 226 | PcodeOp.INT_SDIV, 227 | PcodeOp.INT_SLESS, 228 | PcodeOp.INT_SLESSEQUAL, 229 | PcodeOp.INT_SREM, 230 | PcodeOp.INT_SRIGHT, 231 | PcodeOp.INT_SUB, 232 | PcodeOp.INT_XOR 233 | }; 234 | int unaryOperators[] = new int[] { 235 | PcodeOp.COPY, 236 | PcodeOp.INT_2COMP, 237 | PcodeOp.INT_NEGATE, 238 | PcodeOp.INT_SEXT, 239 | PcodeOp.INT_ZEXT 240 | }; 241 | 242 | for(long alValue = 0; alValue < 0x100; alValue++) 243 | { 244 | for(long blValue = 0; blValue < 0x100; blValue++) 245 | { 246 | for(int i = 0; i < binaryOperators.length; i++) 247 | { 248 | int op = binaryOperators[i]; 249 | if(op == PcodeOp.INT_DIV || op == PcodeOp.INT_REM || op == PcodeOp.INT_SDIV || op == PcodeOp.INT_SREM) 250 | { 251 | if(JavaUtil.CompareLongs(blValue,0)) 252 | continue; 253 | } 254 | Pair res = tt.TestBinaryPcode(op, 1, alValue, blValue, false); 255 | PrintIfConcreteTestFailure(op, alValue, blValue, res); 256 | res = tt.TestBinaryPcode(op, 1, alValue, blValue, true); 257 | PrintIfRandomTestFailure(op, alValue, 0, res); 258 | } 259 | } 260 | for(int i = 0; i < unaryOperators.length; i++) 261 | { 262 | int op = unaryOperators[i]; 263 | Pair res = tt.TestUnaryPcode(op, 1, alValue, false); 264 | PrintIfConcreteTestFailure(op, alValue, 0, res); 265 | res = tt.TestUnaryPcode(op, 1, alValue, true); 266 | PrintIfRandomTestFailure(op, alValue, 0, res); 267 | } 268 | } 269 | Printer.println("Testing operators done"); 270 | } 271 | @Test 272 | public void testAll() 273 | { 274 | currentProgram = ProgramManager().getCurrentProgram(); 275 | } 276 | 277 | } 278 | 279 | */ --------------------------------------------------------------------------------