Candidate release of source code.

This commit is contained in:
Dan
2019-03-26 13:45:32 -04:00
parent db81e6b3b0
commit 79d8f164f8
12449 changed files with 2800756 additions and 16 deletions

View File

@@ -0,0 +1,257 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Displays a table comparing the sizes of functions to the sizes of their decompilations.
// Use this script to help identify functions that are being decompiled incorrectly. If you find
// a function with many instructions whose decompilation is quite short, there might be something fishy going on
// with the return value.
//
// Note: a value of -1.0 in the "Ratio" column indicates a failure during decompilation.
// @category Analysis
import java.util.*;
import com.google.common.collect.Iterators;
import ghidra.app.decompiler.*;
import ghidra.app.decompiler.parallel.*;
import ghidra.app.script.GhidraScript;
import ghidra.app.tablechooser.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.util.task.TaskMonitor;
public class CompareFunctionSizesScript extends GhidraScript {
@Override
protected void run() throws Exception {
if (isRunningHeadless()) {
printf("This script cannot be run headlessly.\n");
return;
}
DecompilerCallback<FuncBodyData> callback = new DecompilerCallback<FuncBodyData>(
currentProgram, new CompareFunctionSizesScriptConfigurer(currentProgram)) {
@Override
public FuncBodyData process(DecompileResults results, TaskMonitor tMonitor)
throws Exception {
InstructionIterator instIter = currentProgram.getListing().getInstructions(
results.getFunction().getBody(), true);
int numInstructions = Iterators.size(instIter);
//indicate failure of decompilation by having 0 high pcode ops
int numHighOps = 0;
if (results.getHighFunction() != null &&
results.getHighFunction().getPcodeOps() != null) {
numHighOps = Iterators.size(results.getHighFunction().getPcodeOps());
}
return new FuncBodyData(results.getFunction(), numInstructions, numHighOps);
}
};
Set<Function> funcsToDecompile = new HashSet<>();
FunctionIterator fIter = currentProgram.getFunctionManager().getFunctionsNoStubs(true);
Iterators.addAll(funcsToDecompile, fIter);
if (funcsToDecompile.isEmpty()) {
popup("No functions to decompile!");
return;
}
List<FuncBodyData> funcBodyData = ParallelDecompiler.decompileFunctions(callback,
currentProgram, funcsToDecompile, monitor);
monitor.checkCanceled();
TableChooserDialog tableDialog =
createTableChooserDialog(currentProgram.getName() + " function sizes", null);
configureTableColumns(tableDialog);
tableDialog.show();
for (FuncBodyData bodyData : funcBodyData) {
tableDialog.add(bodyData);
}
}
class CompareFunctionSizesScriptConfigurer implements DecompileConfigurer {
private Program p;
public CompareFunctionSizesScriptConfigurer(Program prog) {
p = prog;
}
@Override
public void configure(DecompInterface decompiler) {
decompiler.toggleCCode(false);
decompiler.toggleSyntaxTree(true);
decompiler.setSimplificationStyle("decompile");
DecompileOptions opts = new DecompileOptions();
opts.grabFromProgram(p);
decompiler.setOptions(opts);
}
}
/**
* Table stuff
*/
static class FuncBodyData implements AddressableRowObject {
private int numInstructions;
private int numHighOps;
private double ratio;
private Function func;
public FuncBodyData(Function f, int numInst, int numHigh) {
func = f;
numInstructions = numInst;
numHighOps = numHigh;
if (numHighOps == 0) {
ratio = -1.0;
}
else {
ratio = (numHighOps * 1.0) / numInstructions;
}
}
public int getNumInstructions() {
return numInstructions;
}
public int getNumHighOps() {
return numHighOps;
}
public Function getFunction() {
return func;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(func.getName());
sb.append(" instructions: ");
sb.append(Integer.toString(numInstructions));
sb.append(", high ops: ");
sb.append(Integer.toString(numHighOps));
return sb.toString();
}
@Override
public Address getAddress() {
return func.getEntryPoint();
}
public double getRatio() {
return ratio;
}
}
interface RowEntries {
void add(FuncBodyData row);
void setMessage(String message);
void clear();
}
class TableEntryList implements RowEntries {
private TableChooserDialog tDialog;
public TableEntryList(TableChooserDialog dialog) {
tDialog = dialog;
}
@Override
public void add(FuncBodyData row) {
tDialog.add(row);
}
@Override
public void setMessage(String message) {
tDialog.setMessage(message);
}
@Override
public void clear() {
return;
}
}
private void configureTableColumns(TableChooserDialog dialog) {
StringColumnDisplay functionNameColumn = new StringColumnDisplay() {
@Override
public String getColumnName() {
return "Function Name";
}
@Override
public String getColumnValue(AddressableRowObject rowObject) {
return ((FuncBodyData) rowObject).getFunction().getName();
}
};
ColumnDisplay<Integer> highOpsColumn = new AbstractComparableColumnDisplay<Integer>() {
@Override
public Integer getColumnValue(AddressableRowObject rowObject) {
return ((FuncBodyData) rowObject).getNumHighOps();
}
@Override
public String getColumnName() {
return "Num High Ops";
}
};
ColumnDisplay<Integer> instructionColumn = new AbstractComparableColumnDisplay<Integer>() {
@Override
public Integer getColumnValue(AddressableRowObject rowObject) {
return ((FuncBodyData) rowObject).getNumInstructions();
}
@Override
public String getColumnName() {
return "Num Instructions";
}
};
ColumnDisplay<Double> ratioColumn = new AbstractComparableColumnDisplay<Double>() {
@Override
public Double getColumnValue(AddressableRowObject rowObject) {
return ((FuncBodyData) rowObject).getRatio();
}
@Override
public String getColumnName() {
return "Ratio";
}
};
dialog.addCustomColumn(functionNameColumn);
dialog.addCustomColumn(highOpsColumn);
dialog.addCustomColumn(instructionColumn);
dialog.addCustomColumn(ratioColumn);
}
}

View File

@@ -0,0 +1,44 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Causes a .exports file to be created for a .dll imported as a program.
// There may be a corresponding .def file in the Ghidra\Features\Base\data\symbols\win directory
// which helps give names to ordinal numbers.
// The program does not need to be analyzed for the .exports file to be created.
// Just import the program, and don't analyze, and run this script.
//
// The name of the .exports file will be printed when the script finishes.
//
//@category Windows
//@keybinding
//@menupath
//@toolbar
import generic.jar.ResourceFile;
import ghidra.app.script.GhidraScript;
import ghidra.app.util.opinion.LibraryLookupTable;
public class CreateExportFileForDLL extends GhidraScript {
@Override
public void run() throws Exception {
// push this .dll into the location of the system .exports files.
// must have write permissions.
ResourceFile file = LibraryLookupTable.createFile(currentProgram, false, true, monitor);
println("Created .exports file : " + file.getAbsolutePath());
}
}

View File

@@ -0,0 +1,47 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Automatically creates a structure definition based on the references seen to the structure
// To use this, place the cursor on a function parameter for example func(int *this),
// (for a C++ this call function)
// This script will automatically create a structure definition for the pointed at structure
// and fill it out based on the references found by the decompiler.
//
// If the parameter is already a structure pointer, any new references found will be added
// to the structure, even if the structure must grow.
//
// Eventually this WILL be put into a global type analyzer, but for now it is most useful.
//
// This script assumes good flow, that switch stmts are good.
//
// This script CAN be used in the decompiler by assigning a Binding a Keyboard key to it, then
// placing the cursor on the variable in the decompiler that is a structure pointer (even if it
// isn't one now, and then pressing the Quick key.
//
//@category Data Types
//@keybinding F6
import ghidra.app.plugin.core.decompile.actions.FillOutStructureCmd;
import ghidra.app.script.GhidraScript;
public class CreateStructure extends GhidraScript {
@Override
public void run() {
FillOutStructureCmd fillCmd =
new FillOutStructureCmd(currentProgram, currentLocation, state.getTool());
fillCmd.applyTo(currentProgram, this.monitor);
}
}

View File

@@ -0,0 +1,57 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//Decompile an entire program
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.plugin.core.script.Ingredient;
import ghidra.app.plugin.core.script.IngredientDescription;
import ghidra.app.script.GatherParamPanel;
import ghidra.app.script.GhidraScript;
import ghidra.app.util.Option;
import ghidra.app.util.exporter.CppExporter;
public class Decompile extends GhidraScript implements Ingredient {
@Override
public void run() throws Exception {
IngredientDescription[] ingredients = getIngredientDescriptions();
for (IngredientDescription ingredient : ingredients) {
state.addParameter(ingredient.getID(), ingredient.getLabel(), ingredient.getType(),
ingredient.getDefaultValue());
}
if (!state.displayParameterGatherer("Script Options")) {
return;
}
File outputFile = (File) state.getEnvironmentVar("COutputFile");
CppExporter cppExporter = new CppExporter();
List<Option> options = new ArrayList<Option>();
options.add(new Option(CppExporter.CREATE_HEADER_FILE, new Boolean(false)));
cppExporter.setOptions(options);
cppExporter.setExporterServiceProvider(state.getTool());
cppExporter.export(outputFile, currentProgram, null, monitor);
}
@Override
public IngredientDescription[] getIngredientDescriptions() {
IngredientDescription[] retVal = new IngredientDescription[] {
new IngredientDescription("COutputFile", "Output C File", GatherParamPanel.FILE, "") };
return retVal;
}
}

View File

@@ -0,0 +1,482 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Finds potential problems in code that will cause trouble for the decompiler.
//
// This script essentially runs the decompiler on each currently defined function.
// Any function that has potential issues in the decompiler output is flagged in a table with a suggestion.
// For example any references to "in_" variables, or "unaff_" are flagged with a potential solution.
// This is very prototype at this point, but it can help diagnose initial analysis of a binary for problems that
// affect decompiled output and thus the cleaness of code for follow on uses. Things like unidentified
// epilog functions that have side-effects on the stack will be uncovered.
//
// @category Analysis
import java.util.ArrayList;
import java.util.Iterator;
import ghidra.app.decompiler.*;
import ghidra.app.script.GhidraScript;
import ghidra.app.tablechooser.*;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.util.OptionsService;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceIterator;
public class FindPotentialDecompilerProblems extends GhidraScript {
private IssueEntries entryList = null;
private DecompInterface decomplib;
DecompileResults lastResults = null;
@Override
public void run() throws Exception {
TableChooserDialog tableDialog = null;
TableChooserExecutor executor = null;
try {
if (this.isRunningHeadless()) {
entryList = new IssueEntryList();
}
else {
tableDialog =
createTableChooserDialog("Decompiler Inconsistency Problems", executor);
configureTableColumns(tableDialog);
tableDialog.show();
tableDialog.setMessage("Searching...");
entryList = new TableEntryList(tableDialog);
}
// get the decompiler context
// setup the decompiler
decomplib = setUpDecompiler(currentProgram);
FunctionIterator funcIter = currentProgram.getFunctionManager().getFunctions(true);
while (funcIter.hasNext() && !monitor.isCancelled()) {
Function func = funcIter.next();
// no real function here.
if (currentProgram.getListing().getInstructionAt(func.getEntryPoint()) == null) {
continue;
}
if (tableDialog != null) {
tableDialog.setMessage("Decompiling - " + func.getName());
}
DecompileResults decompResult = decompileFunction(func, decomplib);
HighFunction hf = decompResult.getHighFunction();
if (hf == null) {
entryList.add(new ProblemLocations(currentProgram, func.getEntryPoint(), null,
"", "Decompilation Error"));
continue;
}
Iterator<HighSymbol> symIter = hf.getLocalSymbolMap().getSymbols();
while (symIter.hasNext() && !monitor.isCancelled()) {
HighSymbol sym = symIter.next();
HighVariable highVar = sym.getHighVariable();
if (!(highVar instanceof HighLocal)) {
continue;
}
if (sym.getName().startsWith("in_") && !sym.getName().equals("in_FS_OFFSET")) {
// Has an input variable that is not a parameter
Address funcAddr =
getFirstFuncWithVar(func, sym.getHighVariable().getRepresentative());
String badness =
"Missing input register param, or bad register param defined in called function";
if (sym.getName().startsWith("in_stack_ff")) {
badness =
"Too many stack parameters defined in a called function. May need to redefine in the called function.";
}
if (sym.getName().startsWith("in_stack_00")) {
badness =
"Too few stack parameters defined for this function. May need to redfine parameters.";
}
entryList.add(new ProblemLocations(currentProgram, func.getEntryPoint(),
funcAddr, sym.getName(), badness));
}
if (sym.getName().startsWith("unaff_")) {
Address firstAddr = getFirstCalledFunction(func);
if (sym.getName().equals("unaff_EBP")) {
entryList.add(new ProblemLocations(currentProgram, firstAddr,
func.getEntryPoint(), sym.getName(),
"Suspect function is EH_PROLOG setup"));
continue;
}
// Has a sideffect variable
String possible = (firstAddr != null ? "Sideffect from a call"
: "Undefined paramter or global register save");
entryList.add(new ProblemLocations(currentProgram, func.getEntryPoint(),
sym.getPCAddress(), sym.getName(), possible));
}
if (sym.getName().startsWith("extraout")) {
// Has a sideffect variable
Address funcAddr =
getFirstFuncWithVar(func, sym.getHighVariable().getRepresentative());
if (funcAddr == null) {
funcAddr = sym.getHighVariable().getRepresentative().getAddress();
}
String possible = (funcAddr != null ? "Bad paramter in called function"
: "Extra return value, Global register, or Function register Sideffect");
if (sym.getName().startsWith("extraout_var")) {
possible =
"Called function does not return a Solid type. Undefined4 might need to be int.";
}
entryList.add(new ProblemLocations(currentProgram, funcAddr,
func.getEntryPoint(), sym.getName(), possible));
}
}
}
if (this.isRunningHeadless()) {
// Do the cases, or just create a selection
IssueEntryList issueList = (IssueEntryList) entryList;
int numEntries = issueList.getNumEntries();
for (int i = 0; i < numEntries; i++) {
ProblemLocations entry = issueList.getEntry(i);
if (entry.isFixed()) {
continue;
}
println(entry.toString());
// this will actually do the fixup for all places currently
// calling this location
if (executor != null) {
executor.execute(entry);
}
}
}
else {
entryList.setMessage("Found Potential Problems");
}
}
finally {
tableDialog.setMessage("Finished");
decomplib.dispose();
}
}
private Address getFirstFuncWithVar(Function func, Varnode vn) {
Address variableAddr = vn.getAddress();
if (variableAddr == null) {
return Address.NO_ADDRESS;
}
ReferenceIterator riter =
func.getProgram().getReferenceManager().getReferenceIterator(func.getEntryPoint());
while (riter.hasNext()) {
Reference ref = riter.next();
if (!func.getBody().contains(ref.getFromAddress())) {
return Address.NO_ADDRESS;
}
// return the first call for the function
if (ref.getReferenceType().isCall()) {
if (ref.getToAddress() == null) {
continue;
}
Function calledFunc =
func.getProgram().getFunctionManager().getFunctionAt(ref.getToAddress());
if (calledFunc == null) {
continue;
}
Parameter[] params = calledFunc.getParameters();
for (Parameter param : params) {
Address addr = param.getMinAddress();
if (addr != null && addr.equals(variableAddr)) {
return ref.getToAddress();
}
}
}
}
return Address.NO_ADDRESS;
}
private Address getFirstCalledFunction(Function func) {
ReferenceIterator riter =
func.getProgram().getReferenceManager().getReferenceIterator(func.getEntryPoint());
while (riter.hasNext()) {
Reference ref = riter.next();
if (!func.getBody().contains(ref.getFromAddress())) {
return Address.NO_ADDRESS;
}
// return the first call for the function
if (ref.getReferenceType().isCall()) {
return ref.getToAddress();
}
}
return Address.NO_ADDRESS;
}
private Address lastDecompiledFuncAddr = null;
private DecompInterface setUpDecompiler(Program program) {
DecompInterface decompInterface = new DecompInterface();
DecompileOptions options;
options = new DecompileOptions();
OptionsService service = state.getTool().getService(OptionsService.class);
if (service != null) {
ToolOptions opt = service.getOptions("Decompiler");
options.grabFromToolAndProgram(null, opt, program);
}
decompInterface.setOptions(options);
decompInterface.toggleCCode(true);
decompInterface.toggleSyntaxTree(true);
decompInterface.setSimplificationStyle("decompile");
decompInterface.openProgram(program);
return decompInterface;
}
public DecompileResults decompileFunction(Function f, DecompInterface decompInterface) {
// don't decompile the function again if it was the same as the last one
//
if (f.getEntryPoint().equals(lastDecompiledFuncAddr)) {
return lastResults;
}
lastResults = null;
lastResults = decompInterface.decompileFunction(f,
decompInterface.getOptions().getDefaultTimeout(), monitor);
lastDecompiledFuncAddr = f.getEntryPoint();
return lastResults;
}
private void configureTableColumns(TableChooserDialog dialog) {
StringColumnDisplay explanationColumn = new StringColumnDisplay() {
@Override
public String getColumnName() {
return "Potential Problem";
}
@Override
public String getColumnValue(AddressableRowObject rowObject) {
ProblemLocations entry = (ProblemLocations) rowObject;
return entry.getExplanation();
}
};
StringColumnDisplay funcColumn = new StringColumnDisplay() {
@Override
public String getColumnName() {
return "Func Name";
}
@Override
public String getColumnValue(AddressableRowObject rowObject) {
ProblemLocations entry = (ProblemLocations) rowObject;
Function func = entry.getProgram().getFunctionManager().getFunctionContaining(
entry.getAddress());
if (func == null) {
return "";
}
return func.getName();
}
};
ColumnDisplay<Address> probLocColumn = new AbstractComparableColumnDisplay<Address>() {
@Override
public String getColumnName() {
return "Problem Loc";
}
@Override
public Address getColumnValue(AddressableRowObject rowObject) {
ProblemLocations probLocation = (ProblemLocations) rowObject;
return probLocation.getWhyAddr();
}
};
StringColumnDisplay varNameColumn = new StringColumnDisplay() {
@Override
public String getColumnName() {
return "Var Name";
}
@Override
public String getColumnValue(AddressableRowObject rowObject) {
ProblemLocations probLocation = (ProblemLocations) rowObject;
return probLocation.getVarName();
}
@Override
public int compare(AddressableRowObject o1, AddressableRowObject o2) {
return getColumnValue(o1).compareTo(getColumnValue(o2));
}
};
StringColumnDisplay statusColumn = new StringColumnDisplay() {
@Override
public String getColumnName() {
return "Status";
}
@Override
public String getColumnValue(AddressableRowObject rowObject) {
ProblemLocations probLocation = (ProblemLocations) rowObject;
return probLocation.getStatus().toString();
}
};
dialog.addCustomColumn(funcColumn);
dialog.addCustomColumn(statusColumn);
dialog.addCustomColumn(probLocColumn);
dialog.addCustomColumn(varNameColumn);
dialog.addCustomColumn(explanationColumn);
}
class ProblemLocations implements AddressableRowObject {
private Program program;
private Address addr;
private Address whyAddr;
private String varName;
private String explanation;
private String status;
ProblemLocations(Program prog, Address suspectNoRetAddr, Address whyAddr, String varName,
String explanation) {
this.addr = suspectNoRetAddr;
this.whyAddr = whyAddr;
this.varName = varName;
this.explanation = explanation;
this.program = prog;
}
public boolean isFixed() {
return getStatus().equals("fixed");
}
public void setStatus(String status) {
this.status = status;
}
public Program getProgram() {
return program;
}
@Override
public Address getAddress() {
return getFuncAddr();
}
public Address getFuncAddr() {
if (addr == null) {
return Address.NO_ADDRESS;
}
return addr;
}
public Address getWhyAddr() {
if (whyAddr == null) {
return Address.NO_ADDRESS;
}
return whyAddr;
}
public String getVarName() {
return varName;
}
public String getExplanation() {
return explanation;
}
public String getStatus() {
if (status != null) {
return status;
}
return "";
}
@Override
public String toString() {
return "Issue at:" + getAddress() + " found: " + getVarName() + " " +
getExplanation() + " at " + getWhyAddr();
}
}
interface IssueEntries {
void add(ProblemLocations location);
int getNumEntries();
void setMessage(String string);
}
class TableEntryList implements IssueEntries {
private TableChooserDialog tableDialog;
public TableEntryList(TableChooserDialog tableDialog) {
this.tableDialog = tableDialog;
}
@Override
public void add(ProblemLocations location) {
tableDialog.add(location);
}
@Override
public void setMessage(String string) {
tableDialog.setMessage(string);
}
@Override
public int getNumEntries() {
return tableDialog.getRowCount();
}
}
class IssueEntryList implements IssueEntries {
ArrayList<ProblemLocations> list = new ArrayList<ProblemLocations>();
@Override
public void add(ProblemLocations location) {
list.add(location);
}
@Override
public void setMessage(String string) {
// do nothing
}
@Override
public int getNumEntries() {
return list.size();
}
public ProblemLocations getEntry(int i) {
return list.get(i);
}
}
}

View File

@@ -0,0 +1,104 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Fix any unknown switch instructions with the decompiler.
//
// Your mileage may vary! This should only be run after existing code has been found.
// The results should be checked for validity.
//
// @category Analysis
import java.util.*;
import ghidra.app.cmd.function.DecompilerSwitchAnalysisCmd;
import ghidra.app.decompiler.DecompileResults;
import ghidra.app.decompiler.parallel.DecompilerCallback;
import ghidra.app.decompiler.parallel.ParallelDecompiler;
import ghidra.app.plugin.core.analysis.SwitchAnalysisDecompileConfigurer;
import ghidra.app.plugin.core.bookmark.BookmarkEditCmd;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.FlowType;
import ghidra.program.model.symbol.Reference;
import ghidra.util.task.TaskMonitor;
public class FixSwitchStatementsWithDecompiler extends GhidraScript {
@Override
public void run() throws Exception {
Map<Function, Instruction> instructionsByFunction = filterFunctions();
DecompilerCallback<Void> callback = new DecompilerCallback<Void>(currentProgram,
new SwitchAnalysisDecompileConfigurer(currentProgram)) {
@Override
public Void process(DecompileResults results, TaskMonitor m) throws Exception {
Function func = results.getFunction();
DecompilerSwitchAnalysisCmd cmd = new DecompilerSwitchAnalysisCmd(results);
cmd.applyTo(currentProgram);
Instruction instr = instructionsByFunction.get(func);
BookmarkEditCmd bmcmd = new BookmarkEditCmd(instr.getMinAddress(),
BookmarkType.INFO, "FixSwitchStatementsWithDecompiler", "Fixed switch stmt");
bmcmd.applyTo(currentProgram);
return null;
}
};
Set<Function> functions = instructionsByFunction.keySet();
try {
ParallelDecompiler.decompileFunctions(callback, currentProgram, functions, monitor);
}
finally {
callback.dispose();
}
}
private Map<Function, Instruction> filterFunctions() {
// search for all dynamic jump locations that have no target
Map<Function, Instruction> results = new HashMap<>();
Listing list = currentProgram.getListing();
FunctionManager functionManager = currentProgram.getFunctionManager();
InstructionIterator it = list.getInstructions(true);
while (it.hasNext()) {
if (monitor.isCancelled()) {
return Collections.emptyMap();
}
Instruction instr = it.next();
FlowType flowType = instr.getFlowType();
if (!flowType.isJump() || !flowType.isComputed()) {
continue;
}
Reference[] refsFrom = instr.getReferencesFrom();
if (refsFrom.length == 0 || refsFrom.length > 2) {
continue;
}
Function func = functionManager.getFunctionContaining(instr.getMinAddress());
if (func == null) {
println("No function at " + instr.getMinAddress());
continue;
}
results.put(func, instr);
}
return results;
}
}

View File

@@ -0,0 +1,327 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//Decompile the function at the cursor, then build data-flow graph (AST)
//@category PCode
import java.util.*;
import ghidra.app.decompiler.*;
import ghidra.app.script.GhidraScript;
import ghidra.app.services.GraphService;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.graph.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.*;
import ghidra.util.Msg;
public class GraphAST extends GhidraScript {
protected static final String COLOR_ATTRIBUTE = "Color";
protected static final String ICON_ATTRIBUTE = "Icon";
Function func;
HighFunction high;
GraphData graph;
int edgecount;
@Override
public void run() throws Exception {
PluginTool tool = state.getTool();
if (tool == null) {
println("Script is not running in GUI");
}
GraphService graphSvc = tool.getService(GraphService.class);
if (graphSvc == null) {
Msg.showError(this,
tool.getToolFrame(),
"GraphAST Error",
"GraphService not found: Please add a graph service provider to your tool");
return;
}
func = this.getFunctionContaining(this.currentAddress);
if (func == null) {
Msg.showWarn(this,
state.getTool().getToolFrame(),
"GraphAST Error",
"No Function at current location");
return;
}
buildAST();
graph = graphSvc.createGraphContent();
buildGraph();
GraphDisplay graphDisplay = graphSvc.getGraphDisplay(true);
// graphDisplay.defineVertexAttribute(CODE_ATTRIBUTE); //
// graphDisplay.defineVertexAttribute(SYMBOLS_ATTRIBUTE);
// graphDisplay.defineEdgeAttribute(EDGE_TYPE_ATTRIBUTE);
graphDisplay.setGraphData(graph);
// Install a handler so the selection/location will map
graphDisplay.setSelectionHandler(new GraphASTSelectionHandler(graphSvc, high,func.getProgram().getAddressFactory()));
}
private void buildAST() throws DecompileException {
DecompileOptions options = new DecompileOptions();
DecompInterface ifc = new DecompInterface();
ifc.setOptions(options);
if ( !ifc.openProgram(this.currentProgram) ) {
throw new DecompileException("Decompiler", "Unable to initialize: "+ifc.getLastMessage());
}
ifc.setSimplificationStyle("normalize");
DecompileResults res = ifc.decompileFunction(func, 30, null);
high = res.getHighFunction();
}
private String getVarnodeKey(VarnodeAST vn) {
PcodeOp op = vn.getDef();
String id;
if (op != null)
id = op.getSeqnum().getTarget().toString(true) + " v " + Integer.toString(vn.getUniqueId());
else
id = "i v " + Integer.toString(vn.getUniqueId());
return id;
}
private String getOpKey(PcodeOpAST op) {
SequenceNumber sq = op.getSeqnum();
String id = sq.getTarget().toString(true) + " o " +Integer.toString(op.getSeqnum().getTime());
return id;
}
protected GraphVertex createVarnodeVertex(VarnodeAST vn) {
String name = vn.getAddress().toString(true);
String id = getVarnodeKey(vn);
String colorattrib = "Red";
if (vn.isConstant())
colorattrib = "DarkGreen";
else if (vn.isRegister()) {
colorattrib = "Blue";
Register reg = func.getProgram().getRegister(vn.getAddress(),vn.getSize());
if (reg != null)
name = reg.getName();
}
else if (vn.isUnique())
colorattrib = "Black";
else if (vn.isPersistant())
colorattrib = "DarkOrange";
else if (vn.isAddrTied())
colorattrib = "Orange";
GraphVertex vert = graph.createVertex(name, id);
if (vn.isInput())
vert.setAttribute(ICON_ATTRIBUTE, "TriangleDown");
else
vert.setAttribute(ICON_ATTRIBUTE, "Circle");
vert.setAttribute(COLOR_ATTRIBUTE,colorattrib);
return vert;
}
protected GraphVertex createOpVertex(PcodeOpAST op) {
String name = op.getMnemonic();
String id = getOpKey(op);
int opcode = op.getOpcode();
if ((opcode==PcodeOp.LOAD)||(opcode==PcodeOp.STORE)) {
Varnode vn = op.getInput(0);
AddressSpace addrspace = func.getProgram().getAddressFactory().getAddressSpace((int)vn.getOffset());
name += ' ' + addrspace.getName();
}
else if (opcode == PcodeOp.INDIRECT) {
Varnode vn = op.getInput(1);
if (vn != null) {
PcodeOp indOp = high.getOpRef((int)vn.getOffset());
if (indOp != null) {
name += " (" + indOp.getMnemonic() +')';
}
}
}
GraphVertex vert = graph.createVertex(name, id);
vert.setAttribute(ICON_ATTRIBUTE, "Square");
return vert;
}
protected GraphVertex getVarnodeVertex(HashMap<Integer,GraphVertex> vertices,VarnodeAST vn) {
GraphVertex res;
res = vertices.get(vn.getUniqueId());
if (res == null) {
res = createVarnodeVertex(vn);
vertices.put(vn.getUniqueId(), res);
}
return res;
}
protected GraphEdge createEdge(GraphVertex in,GraphVertex out) {
String id = Integer.toString(edgecount);
edgecount += 1;
return graph.createEdge(id, in, out);
}
protected void buildGraph() {
HashMap<Integer, GraphVertex> vertices = new HashMap<Integer, GraphVertex>();
edgecount = 0;
Iterator<PcodeOpAST> opiter = getPcodeOpIterator();
while(opiter.hasNext()) {
PcodeOpAST op = opiter.next();
GraphVertex o = createOpVertex(op);
for(int i=0;i<op.getNumInputs();++i) {
int opcode = op.getOpcode();
if ((i==0)&&((opcode==PcodeOp.LOAD)||(opcode==PcodeOp.STORE)))
continue;
if ((i==1)&&(opcode==PcodeOp.INDIRECT))
continue;
VarnodeAST vn = (VarnodeAST)op.getInput(i);
if (vn != null) {
GraphVertex v = getVarnodeVertex(vertices,vn);
createEdge(v,o);
}
}
VarnodeAST outvn = (VarnodeAST)op.getOutput();
if (outvn != null) {
GraphVertex outv = getVarnodeVertex(vertices,outvn);
if (outv != null)
createEdge(o,outv);
}
}
}
protected Iterator<PcodeOpAST> getPcodeOpIterator() {
Iterator<PcodeOpAST> opiter = high.getPcodeOps();
return opiter;
}
class GraphASTSelectionHandler implements GraphSelectionHandler {
private boolean active; // true if the window is active
private boolean enabled;
HighFunction highfunc;
private GraphService graphService;
private AddressFactory addrFactory;
public GraphASTSelectionHandler(GraphService graphService,HighFunction highfunc,AddressFactory addrFactory) {
active = false;
enabled = true;
this.graphService = graphService;
this.highfunc = highfunc;
this.addrFactory = addrFactory;
}
private Address keyToAddress(String key) {
int firstcolon = key.indexOf(':');
if (firstcolon == -1) return null;
int firstspace = key.indexOf(' ');
String addrspacestring = key.substring(0,firstcolon);
String addrstring = key.substring(firstcolon+1,firstspace);
AddressSpace spc = addrFactory.getAddressSpace(addrspacestring);
if (spc == null) return null;
try {
return spc.getAddress(addrstring);
} catch (AddressFormatException e) {
return null;
}
}
public String getGraphType() {
return "Data-flow AST";
}
public boolean isActive() {
return active;
}
public boolean isEnabled() {
return enabled;
}
public void locate(String renoirLocation) {
Address addr = keyToAddress(renoirLocation);
if (addr==null) return;
graphService.fireLocationEvent(addr);
}
public String locate(Object ghidraLocation) {
if (!(ghidraLocation instanceof Address))
return null;
Address addr = (Address)ghidraLocation;
Iterator<PcodeOpAST> iter = highfunc.getPcodeOps(addr);
if (iter.hasNext()) {
PcodeOpAST op = iter.next();
return getOpKey(op);
}
return null;
}
public boolean notify(String notificationType) {
return false;
}
public void select(String[] renoirSelections) {
if (!enabled)
return;
AddressSet set = new AddressSet();
for (int i = 0; i < renoirSelections.length; i++) {
Address addr = keyToAddress(renoirSelections[i]);
if (addr == null) {
continue;
}
set.addRange(addr,addr);
}
graphService.fireSelectionEvent(set);
}
public String[] select(Object ghidraSelection) {
String [] keys;
if (ghidraSelection == null) {
keys = new String[0];
return keys;
}
if (!(ghidraSelection instanceof AddressSetView)) {
return null; // selection not understood
}
AddressSetView set = (AddressSetView) ghidraSelection;
ArrayList<String> ops = new ArrayList<String>();
Iterator<PcodeOpAST> iter = highfunc.getPcodeOps();
while(iter.hasNext()) {
PcodeOpAST op = iter.next();
Address addr = op.getSeqnum().getTarget();
if (set.contains(addr)) {
ops.add(getOpKey(op));
VarnodeAST vn = (VarnodeAST)op.getOutput();
if (vn != null)
ops.add(getVarnodeKey(vn));
}
}
keys = new String[ ops.size() ];
return ops.toArray(keys);
}
public void setActive(boolean active) {
this.active = active;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}

View File

@@ -0,0 +1,109 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//Decompile the function at the cursor, then build data-flow graph (AST) with flow edges
//@category PCode
import java.util.*;
import ghidra.program.model.graph.GraphEdge;
import ghidra.program.model.graph.GraphVertex;
import ghidra.program.model.pcode.*;
public class GraphASTAndFlow extends GraphAST {
@Override
protected void buildGraph() {
HashMap<Integer, GraphVertex> vertices = new HashMap<Integer, GraphVertex>();
edgecount = 0;
Iterator<PcodeOpAST> opiter = getPcodeOpIterator();
HashMap<PcodeOp, GraphVertex> map = new HashMap<PcodeOp, GraphVertex>();
while (opiter.hasNext()) {
PcodeOpAST op = opiter.next();
GraphVertex o = createOpVertex(op);
map.put(op, o);
for (int i = 0; i < op.getNumInputs(); ++i) {
if ((i == 0) &&
((op.getOpcode() == PcodeOp.LOAD) || (op.getOpcode() == PcodeOp.STORE))) {
continue;
}
if ((i == 1)&&(op.getOpcode() == PcodeOp.INDIRECT))
continue;
VarnodeAST vn = (VarnodeAST) op.getInput(i);
if (vn != null) {
GraphVertex v = getVarnodeVertex(vertices, vn);
createEdge(v, o);
}
}
VarnodeAST outvn = (VarnodeAST) op.getOutput();
if (outvn != null) {
GraphVertex outv = getVarnodeVertex(vertices, outvn);
if (outv != null) {
createEdge(o, outv);
}
}
}
opiter = getPcodeOpIterator();
HashSet<PcodeBlockBasic> seenParents = new HashSet<PcodeBlockBasic>();
HashMap<PcodeBlock, GraphVertex> first = new HashMap<PcodeBlock, GraphVertex>();
HashMap<PcodeBlock, GraphVertex> last = new HashMap<PcodeBlock, GraphVertex>();
while (opiter.hasNext()) {
PcodeOpAST op = opiter.next();
PcodeBlockBasic parent = op.getParent();
if (seenParents.contains(parent)) {
continue;
}
Iterator<PcodeOp> iterator = parent.getIterator();
PcodeOp prev = null;
PcodeOp next = null;
while (iterator.hasNext()) {
next = iterator.next();
if (prev == null && map.containsKey(next)) {
first.put(parent, map.get(next));
}
if (prev != null && map.containsKey(prev) && map.containsKey(next)) {
GraphEdge edge = createEdge(map.get(prev), map.get(next));
edge.setAttribute(COLOR_ATTRIBUTE, "Black");
}
prev = next;
}
if (next != null && map.containsKey(next)) {
last.put(parent, map.get(next));
}
seenParents.add(parent);
}
Set<PcodeBlock> keySet = first.keySet();
for (PcodeBlock block : keySet) {
for (int i = 0; i < block.getInSize(); i++) {
PcodeBlock in = block.getIn(i);
if (last.containsKey(in)) {
GraphEdge edge = createEdge(last.get(in), first.get(block));
edge.setAttribute(COLOR_ATTRIBUTE, "Red");
}
}
// All outs were already handled by the ins! Don't make two links!
// for (int i = 0; i < block.getOutSize(); i++) {
// PcodeBlock out = block.getOut(i);
// if (first.containsKey(out)) {
// GraphEdge edge = createEdge(last.get(block), first.get(out));
// edge.setAttribute(COLOR_ATTRIBUTE, "Red");
// }
// }
}
}
}

View File

@@ -0,0 +1,30 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//Decompile the function at the cursor, then build data-flow graph (AST) for the current address
//@category PCode
import java.util.Iterator;
import ghidra.program.model.pcode.PcodeOpAST;
public class GraphSelectedAST extends GraphAST {
protected Iterator<PcodeOpAST> getPcodeOpIterator() {
Iterator<PcodeOpAST> opiter = high.getPcodeOps(this.currentAddress);
return opiter;
}
}

View File

@@ -0,0 +1,251 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Given a routine, show all the calls to that routine and their parameters.
// Place the cursor on a function (can be an external .dll function).
// Execute the script.
// The decompiler will be run on everything that calls the function at the cursor
// All calls to the function will display with their parameters to the function.
//
// This script assumes good flow, that switch stmts are good.
//
//@category Functions
import java.util.Iterator;
import ghidra.app.decompiler.*;
import ghidra.app.script.GhidraScript;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.util.OptionsService;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.PcodeOpAST;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.Symbol;
public class ShowCCallsScript extends GhidraScript {
private Address lastAddr = null;
@Override
public void run() throws Exception {
if (currentLocation == null) {
println("No Location.");
return;
}
Listing listing = currentProgram.getListing();
Function func = listing.getFunctionContaining(currentAddress);
if (func == null) {
println("No Function at address " + currentAddress);
return;
}
DecompInterface decomplib = setUpDecompiler(currentProgram);
try {
if (!decomplib.openProgram(currentProgram)) {
println("Decompile Error: " + decomplib.getLastMessage());
return;
}
// call decompiler for all refs to current function
Symbol sym = this.getSymbolAt(func.getEntryPoint());
Reference refs[] = sym.getReferences(null);
for (int i = 0; i < refs.length; i++) {
if (monitor.isCancelled()) {
break;
}
// get function containing.
Address refAddr = refs[i].getFromAddress();
Function refFunc = currentProgram.getFunctionManager()
.getFunctionContaining(refAddr);
if (refFunc == null) {
continue;
}
// decompile function
// look for call to this function
// display call
analyzeFunction(decomplib, currentProgram, refFunc, refAddr);
}
}
finally {
decomplib.dispose();
}
lastAddr = null;
}
private DecompInterface setUpDecompiler(Program program) {
DecompInterface decomplib = new DecompInterface();
DecompileOptions options;
options = new DecompileOptions();
OptionsService service = state.getTool().getService(OptionsService.class);
if (service != null) {
ToolOptions opt = service.getOptions("Decompiler");
options.grabFromToolAndProgram(null,opt,program);
}
decomplib.setOptions(options);
decomplib.toggleCCode(true);
decomplib.toggleSyntaxTree(true);
decomplib.setSimplificationStyle("decompile");
return decomplib;
}
/**
* Analyze a functions references
*/
public void analyzeFunction(DecompInterface decomplib, Program prog, Function f, Address refAddr) {
if (f == null) {
return;
}
// don't decompile the function again if it was the same as the last one
//
if (!f.getEntryPoint().equals(lastAddr))
decompileFunction(f, decomplib);
lastAddr = f.getEntryPoint();
Instruction instr = prog.getListing().getInstructionAt(refAddr);
if (instr == null) {
return;
}
println(printCall(f, refAddr));
}
HighFunction hfunction = null;
ClangTokenGroup docroot = null;
public boolean decompileFunction(Function f, DecompInterface decomplib) {
// decomplib.setSimplificationStyle("normalize", null);
// HighFunction hfunction = decomplib.decompileFunction(f);
DecompileResults decompRes = decomplib.decompileFunction(f, decomplib.getOptions().getDefaultTimeout(), monitor);
//String statusMsg = decomplib.getDecompileMessage();
hfunction = decompRes.getHighFunction();
docroot = decompRes.getCCodeMarkup();
if (hfunction == null)
return false;
return true;
}
/**
* get the pcode ops that refer to an address
*/
public Iterator<PcodeOpAST> getPcodeOps(Address refAddr) {
if (hfunction == null) {
return null;
}
Iterator<PcodeOpAST> piter = hfunction.getPcodeOps(refAddr.getPhysicalAddress());
return piter;
}
public String printCall(Function f, Address refAddr) {
StringBuffer buff = new StringBuffer();
printCall(refAddr, docroot, buff, false, false);
return buff.toString();
}
private boolean printCall(Address refAddr, ClangNode node, StringBuffer buff, boolean didStart, boolean isCall) {
if (node == null) {
return false;
}
Address min = node.getMinAddress();
Address max = node.getMaxAddress();
if (min == null)
return false;
if (refAddr.getPhysicalAddress().equals(max) && node instanceof ClangStatement) {
ClangStatement stmt = (ClangStatement) node;
// Don't check for an actual call. The call could be buried more deeply. As long as the original call reference site
// is the max address, then display the results.
// So this block assumes that the last address contained in the call will be the
// address you are looking for.
// - This could lead to strange behavior if the call reference is placed on some address
// that is not the final call point used by the decompiler.
// - Also if there is a delay slot, then the last address for the call reference point
// might not be the last address for the block of PCode.
//if (stmt.getPcodeOp().getOpcode() == PcodeOp.CALL) {
if (!didStart) {
Address nodeAddr = node.getMaxAddress();
// Decompiler only knows base space.
// If reference came from an overlay space, convert address back
if (refAddr.getAddressSpace().isOverlaySpace()) {
nodeAddr = refAddr.getAddressSpace().getOverlayAddress(nodeAddr);
}
buff.append(" " + nodeAddr + " : ");
}
buff.append(" " + toString(stmt));
return true;
//}
}
for (int j = 0; j < node.numChildren(); j++) {
isCall = node instanceof ClangStatement;
didStart |= printCall(refAddr, node.Child(j), buff, didStart, isCall);
}
return didStart;
}
public String toString(ClangStatement node) {
StringBuffer buffer = new StringBuffer();
int open=-1;
for (int j = 0; j < node.numChildren(); j++) {
ClangNode subNode = node.Child(j);
if (subNode instanceof ClangSyntaxToken) {
ClangSyntaxToken syntaxNode = (ClangSyntaxToken) subNode;
if (syntaxNode.getOpen() != -1) {
if (node.Child(j+2) instanceof ClangTypeToken) {
open = syntaxNode.getOpen();
continue;
}
}
if (syntaxNode.getClose() == open && open != -1) {
open = -1;
continue;
}
}
if (open != -1) {
continue;
}
buffer.append(subNode.toString());
}
return buffer.toString();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,657 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Identify and mark string parameters for functions by examining all references to defined strings
// to see which functions they are passed into. If a defined string is passed into
// a function, then that parameter must be a pointer to a string.
//
// WARNING: This script does not attempt to discover var-arg situations. It should be changed to do so.
//
// The engine for the script is the decompiler.
//
// The guts of this script past the main could be used to analyze
// constants passed to any function on any processor.
// It is not restricted to windows.
//
//@category Analysis
import ghidra.app.decompiler.*;
import ghidra.app.script.GhidraScript;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.OptionsService;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.PrototypeModel;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.*;
import ghidra.util.exception.*;
import java.util.*;
public class StringParameterPropagator extends GhidraScript {
// TODO!! Error handling needs a lot of work !!
private DecompInterface decomplib;
class FuncInfo {
int minParamSeen = 256;
int maxParamSeen = 0;
BitSet paramsNoted = new BitSet();
DataType dt = null;
boolean conflictingDT = false;
// set that a given parameter had the attribute being tracked
void setParamSeen(int paramIndex) {
paramsNoted.set(paramIndex);
}
void setDataTypeSeen(DataType dt) {
if (conflictingDT) {
return;
}
if (dt == null) {
return;
}
if (this.dt != null && !this.dt.isEquivalent(dt)) {
conflictingDT = true;
return;
}
this.dt = dt;
}
// record the number of parameters a particular function has
void setNumParamsSeen(int numParams) {
if (numParams < minParamSeen) {
minParamSeen = numParams;
}
if (numParams > maxParamSeen) {
maxParamSeen = numParams;
}
}
// true if all functions had the same number of parameters
boolean numParamsAgree() {
return (maxParamSeen == minParamSeen);
}
int getMinParamsSeen() {
return minParamSeen;
}
DataType getDataType() {
if (conflictingDT) {
return DataType.DEFAULT;
}
return dt;
}
// get a list of parameter indexes that had the attribute being tracked
ArrayList<Integer> getParamsSeen() {
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 0; i <= maxParamSeen; i++) {
if (paramsNoted.get(i)) {
list.add(i);
}
}
return list;
}
public int getMaxParamsSeen() {
return maxParamSeen;
}
}
@Override
public void run() throws Exception {
try {
decomplib = setUpDecompiler(currentProgram);
if (!decomplib.openProgram(currentProgram)) {
println("Decompile Error: " + decomplib.getLastMessage());
return;
}
HashSet<Address> stringLocationSet = new HashSet<Address>();
HashSet<Address> callingFuncLocationSet = new HashSet<Address>();
monitor.setMessage("Finding References to Data");
long start = System.currentTimeMillis();
// for each string data type defined
collectStringDataReferenceLocations(stringLocationSet, callingFuncLocationSet);
// collectDataRefenceLocations(stringLocationSet, callingFuncLocationSet);
// collectSymbolDataRefenceLocations(stringLocationSet, callingFuncLocationSet);
long end = System.currentTimeMillis();
println("Initial search took : " + (end - start) / 1000 + " seconds");
// iterate over functions
HashMap<Address, FuncInfo> funcParamMap = new HashMap<Address, FuncInfo>();
//Iterator<Address> callingFuncIter = callingFuncLocationSet.iterator();
while ((callingFuncLocationSet.size() > 0) && !monitor.isCancelled()) {
Iterator<Address> callingFuncIter = callingFuncLocationSet.iterator();
if (!callingFuncIter.hasNext()) {
break;
}
Address entry = callingFuncIter.next();
callingFuncIter.remove();
Function func = currentProgram.getFunctionManager().getFunctionAt(entry);
if (func == null) {
continue;
}
monitor.setMessage("Analyzing calls in " + func.getName());
// look at each call
// if param points to a string data type.
// put on HashMap of function -> param#
analyzeFunction(funcParamMap, decomplib, currentProgram, func, stringLocationSet);
}
// iterate over HashMap of functions
HashSet<Address> doneItSet = new HashSet<Address>();
Iterator<Address> entryIter = funcParamMap.keySet().iterator();
while (entryIter.hasNext() && !monitor.isCancelled()) {
Address entry = entryIter.next();
FuncInfo funcInfo = funcParamMap.get(entry);
// TODO: Need to detect Var-args situation.
// maybe record the number of params the function had last time, versus now?
if (doneItSet.contains(entry)) {
continue;
}
doneItSet.add(entry);
Function calledFunc = getFunctionAt(entry);
if (calledFunc == null) {
calledFunc = createFunction(entry, null);
}
if (calledFunc == null || !decompileFunction(calledFunc, decomplib)) {
continue;
}
// if Param not already char *
// store params to database
// set param# to char *
int minParams = funcInfo.getMinParamsSeen();
int maxParams = funcInfo.getMaxParamsSeen();
boolean couldBeVararg = !funcInfo.numParamsAgree();
if (!funcInfo.numParamsAgree()) {
currentProgram.getBookmarkManager().setBookmark(calledFunc.getEntryPoint(),
BookmarkType.NOTE, this.getClass().getName(),
"Number of parameters disagree min: " + minParams + " max: " + maxParams);
println("WARNING : Number of params disagree for " + calledFunc.getName() +
" @ " + entry);
if (minParams > 6) {
continue;
}
}
ArrayList<Integer> paramsSeen = funcInfo.getParamsSeen();
while (paramsSeen.size() > 0) {
int paramIndex = paramsSeen.remove(0);
if (paramIndex > minParams) {
println("WARNING: at " + calledFunc.getName() + ", Couldn't apply param " +
paramIndex);
continue;
}
DataType dt = new PointerDataType(funcInfo.getDataType());
@SuppressWarnings("unused")
boolean mustRedo =
checkParams(calledFunc, dt, paramIndex, minParams, couldBeVararg);
}
// if the signature changes, must redo the function
// so get it off the done-list, and put it back on list of functions to look at
// if (mustRedo) {
// doneItSet.remove(entry);
// // redo the function that called this function, because this function changed its parameters
// redoAddress = func.getEntryPoint();
// break;
// }
}
end = System.currentTimeMillis();
println("Total took : " + (end - start) / 1000 + " seconds");
}
finally {
if (decomplib != null) {
decomplib.dispose();
}
}
}
@SuppressWarnings("unused")
private void collectSymbolDataRefenceLocations(HashSet<Address> dataItemLocationSet,
HashSet<Address> referringFuncLocationSet) {
SymbolTable symtab = currentProgram.getSymbolTable();
SymbolIterator symiter = symtab.getAllSymbols(true);
int count = 0;
while (symiter.hasNext() && !monitor.isCancelled()) {
Symbol sym = symiter.next();
if (!sym.hasReferences()) {
continue;
}
Address addr = sym.getAddress();
if (count == 0) {
monitor.setMessage("looking at : " + addr);
}
count = (count + 1) % 1024;
Data data = currentProgram.getListing().getDataAt(addr);
if (data == null) {
Function func = currentProgram.getFunctionManager().getFunctionAt(addr);
if (func == null) {
// no function, no data, get out
continue;
}
}
Reference[] refs = sym.getReferences(null);
for (int i = 0; i < refs.length && !monitor.isCancelled(); i++) {
Reference ref = refs[i];
// don't want flow references
if (ref.getReferenceType().isFlow()) {
continue;
}
// don't want reference to stack, although maybe we do...
if (!ref.isMemoryReference()) {
continue;
}
dataItemLocationSet.add(ref.getToAddress());
Function func =
currentProgram.getFunctionManager().getFunctionContaining(ref.getFromAddress());
if (func == null) {
continue;
}
referringFuncLocationSet.add(func.getEntryPoint());
}
}
}
private void collectStringDataReferenceLocations(HashSet<Address> stringLocationSet,
HashSet<Address> referringFuncLocationSet) {
DataIterator dataIter = currentProgram.getListing().getDefinedData(true);
while (dataIter.hasNext() && !monitor.isCancelled()) {
Data data = dataIter.next();
// put string dt in addr set
DataType dt = data.getDataType();
if (!(dt instanceof StringDataType || dt instanceof TerminatedStringDataType ||
dt instanceof UnicodeDataType || dt instanceof TerminatedUnicodeDataType)) {
continue;
}
stringLocationSet.add(data.getAddress());
ReferenceIterator refIter =
currentProgram.getReferenceManager().getReferencesTo(data.getAddress());
// find functions referencing the string
while (refIter.hasNext()) {
Reference ref = refIter.next();
Function func =
currentProgram.getFunctionManager().getFunctionContaining(ref.getFromAddress());
if (func == null) {
Data rData = currentProgram.getListing().getDefinedDataAt(ref.getFromAddress());
if (rData == null) {
continue;
}
if (rData.isPointer()) {
ReferenceIterator dataRefIter = rData.getReferenceIteratorTo();
while (dataRefIter.hasNext()) {
Reference dataRef = dataRefIter.next();
func =
currentProgram.getFunctionManager().getFunctionContaining(
dataRef.getFromAddress());
if (func == null) {
continue;
}
referringFuncLocationSet.add(func.getEntryPoint());
}
}
continue;
}
referringFuncLocationSet.add(func.getEntryPoint());
}
}
}
@SuppressWarnings("unused")
private void collectDataRefenceLocations(HashSet<Address> dataItemLocationSet,
HashSet<Address> referringFuncLocationSet) {
int count = 0;
ReferenceIterator iter =
currentProgram.getReferenceManager().getReferenceIterator(
currentProgram.getMinAddress());
while (iter.hasNext() && !monitor.isCancelled()) {
Reference ref = iter.next();
if (count == 0) {
monitor.setMessage("looking at : " + ref.getToAddress());
}
count = (count + 1) % 1024;
// don't want flow references
if (ref.getReferenceType().isFlow()) {
continue;
}
// don't want reference to stack, although maybe we do...
if (!ref.isMemoryReference()) {
continue;
}
dataItemLocationSet.add(ref.getToAddress());
Function func =
currentProgram.getFunctionManager().getFunctionContaining(ref.getFromAddress());
if (func == null) {
continue;
}
referringFuncLocationSet.add(func.getEntryPoint());
}
}
private void markStringParam(HashMap<Address, FuncInfo> constUse, Address refAddr,
Address entry, int paramIndex, int numParams) {
FuncInfo curVal = constUse.get(entry);
if (curVal == null) {
curVal = new FuncInfo();
constUse.put(entry, curVal);
}
curVal.setNumParamsSeen(numParams); // saw this many params
curVal.setParamSeen(paramIndex); // saw string at param x
Data data = currentProgram.getListing().getDefinedDataAt(refAddr);
DataType dt = null;
String name = "Ptr";
if (data != null) {
dt = data.getDataType();
name = dt.getName();
}
curVal.setDataTypeSeen(dt);
println("found " + name + " param for " + entry + " param " + paramIndex);
}
@SuppressWarnings("deprecation")
private boolean checkParams(Function func, DataType dt, int paramIndex, int minParams,
boolean couldBeVararg) {
if (func == null) {
return false;
}
PrototypeModel initialConvention = func.getCallingConvention();
if (!func.hasVarArgs()) {
fixupParams(func, minParams, couldBeVararg);
}
// make sure prototype of called function didn't change!
PrototypeModel convention = func.getCallingConvention();
if (initialConvention == null && convention != null) {
return true;
}
if (convention == null) {
convention = currentProgram.getCompilerSpec().getDefaultCallingConvention();
}
if (initialConvention != null && !convention.getName().equals(initialConvention.getName())) {
return true;
}
// don't create strings past varargs
if (func.hasVarArgs() && paramIndex >= func.getParameterCount()) {
return false;
}
Parameter param = func.getParameter(paramIndex);
if (param != null && param.getDataType() instanceof PointerDataType) {
return false;
}
if (param == null) {
if (convention == null) {
return false;
}
VariableStorage storage =
convention.getArgLocation(paramIndex, func.getParameters(), dt, currentProgram);
if (storage.isUnassignedStorage()) {
return false;
}
try {
param = new ParameterImpl(null, dt, storage, func.getProgram());
// TODO: Fix deprecated method call
param = func.addParameter(param, SourceType.USER_DEFINED);
}
catch (DuplicateNameException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (InvalidInputException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (param == null) {
return false;
}
currentProgram.getBookmarkManager().setBookmark(func.getEntryPoint(), BookmarkType.NOTE,
this.getClass().getName(), "Created " + dt.getName() + " parameter");
return false;
}
@SuppressWarnings("deprecation")
private void fixupParams(Function f, int minParams, boolean couldBeVararg) {
if (f == null) {
return;
}
if (couldBeVararg) {
for (int i = f.getParameterCount(); i > minParams && i > 0; i--) {
f.removeParameter(i - 1);
}
f.setVarArgs(true);
}
// must make number of parameters agree with function, because will be storing off a structure ptr
LocalSymbolMap vmap = hfunction.getLocalSymbolMap();
int numParams = vmap.getNumParams();
if (f.getParameterCount() == numParams && f.getParameterCount() == minParams) {
return;
}
if (minParams == numParams) {
try {
HighFunctionDBUtil.commitParamsToDatabase(hfunction, true, SourceType.USER_DEFINED);
}
catch (DuplicateNameException e) {
throw new AssertException("Unexpected exception", e);
}
catch (InvalidInputException e) {
println(" ** problem at \n" + e.getMessage());
}
return;
}
// make sure prototype of called function didn't change!
PrototypeModel convention = f.getCallingConvention();
if (convention == null) {
convention = currentProgram.getCompilerSpec().getDefaultCallingConvention();
}
for (int i = 1; i <= minParams; i++) {
if (i < f.getParameterCount()) {
continue;
}
VariableStorage storage =
convention.getArgLocation(i - 1, f.getParameters(), DataType.DEFAULT,
currentProgram);
if (storage.isUnassignedStorage()) {
break;
}
try {
Parameter param =
new ParameterImpl(null, DataType.DEFAULT, storage, f.getProgram());
// TODO: Fix deprecated method call
f.addParameter(param, SourceType.ANALYSIS);
}
catch (DuplicateNameException e) {
println(" ** problem at \n" + e.getMessage());
}
catch (InvalidInputException e) {
println(" ** problem at \n" + e.getMessage());
}
}
}
/**
* Analyze a functions references
* @param constUse
*/
public void analyzeFunction(HashMap<Address, FuncInfo> constUse,
DecompInterface decompInterface, Program prog, Function f,
HashSet<Address> stringLocationSet) {
if (f == null) {
return;
}
if (!decompileFunction(f, decompInterface)) {
return;
}
Address entry = f.getEntryPoint();
Iterator<PcodeOpAST> ops = hfunction.getPcodeOps();
while (ops.hasNext() && !monitor.isCancelled()) {
PcodeOpAST pcodeOpAST = ops.next();
// System.out.println(pcodeOpAST);
if (pcodeOpAST.getOpcode() != PcodeOp.CALL) {
continue;
}
Varnode calledFunc = pcodeOpAST.getInput(0);
if (calledFunc == null || !calledFunc.isAddress()) {
continue;
}
Address calledFuncAddr = calledFunc.getAddress();
// rifle through parameters
int numParams = pcodeOpAST.getNumInputs();
for (int i = 1; i < numParams; i++) {
Varnode parm = pcodeOpAST.getInput(i); // 1st param is the call dest
if (parm == null) {
continue;
}
// follow back to a const if possible
ArrayList<PcodeOp> localDefUseList = new ArrayList<PcodeOp>();
// check out the constUse list to see if we fished out a constant. Don't follow out of function
// see if it is a constant
if (parm.isConstant()) {
// then this is a resource id
// lookup the resource and create a reference
long value = parm.getOffset();
// TODO: not so fast, if there is a defUseList, must apply it to get the real constant USED!
try {
value = applyDefUseList(value, localDefUseList);
// constUse.put(calledFuncAddr, i);
}
catch (InvalidInputException exc) {
// Do nothing
}
long mask =
0xffffffffffffffffL >>> ((8 - entry.getAddressSpace().getPointerSize()) * 8);
Address possibleAddr = entry.getNewAddress(mask & value);
if (stringLocationSet.contains(possibleAddr)) {
markStringParam(constUse, possibleAddr, calledFuncAddr, i - 1,
numParams - 1);
}
}
if (parm.isAddress()) {
if (stringLocationSet.contains(parm.getAddress())) {
markStringParam(constUse, parm.getAddress(), calledFuncAddr, i - 1,
numParams - 1);
}
}
}
}
}
private long applyDefUseList(long value, ArrayList<PcodeOp> defUseList)
throws InvalidInputException {
if (defUseList.size() > 0)
throw new InvalidInputException();
return value;
}
// Decompiler stuff
private HighFunction hfunction = null;
//private ClangTokenGroup docroot = null;
private Address lastDecompiledFuncAddr = null;
private DecompInterface setUpDecompiler(Program program) {
DecompInterface decompInterface = new DecompInterface();
DecompileOptions options;
options = new DecompileOptions();
PluginTool tool = state.getTool();
if (tool != null) {
OptionsService service = tool.getService(OptionsService.class);
if (service != null) {
ToolOptions opt = service.getOptions("Decompiler");
options.grabFromToolAndProgram(null, opt, program);
}
}
decompInterface.setOptions(options);
decompInterface.toggleCCode(true);
decompInterface.toggleSyntaxTree(true);
decompInterface.setSimplificationStyle("decompile");
return decompInterface;
}
public boolean decompileFunction(Function f, DecompInterface decompInterface) {
// don't decompile the function again if it was the same as the last one
//
if (f.getEntryPoint().equals(lastDecompiledFuncAddr))
return true;
try {
DecompileResults decompRes =
decompInterface.decompileFunction(f,
decompInterface.getOptions().getDefaultTimeout(), monitor);
hfunction = decompRes.getHighFunction();
}
catch (Exception exc) {
exc.printStackTrace();
return false;
}
if (hfunction == null)
return false;
lastDecompiledFuncAddr = f.getEntryPoint();
return true;
}
}

View File

@@ -0,0 +1,143 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//Override indirect jump destinations
//
// This script allows the user to manually specify the destinations of an indirect jump (switch)
// to the decompiler, if it can't figure out the destinations itself or does so incorrectly.
// To use, create a selection that contains:
// the (one) instruction performing the indirect jump to override
// other instructions whose addresses are interpreted as destinations of the switch
// then run this script
//
// You can also pre-add the COMPUTED_JUMP references to the branch instruction before running the
// script, and simply put the cursor on the computed branching instruction.
//@category Repair
import java.util.ArrayList;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.JumpTable;
import ghidra.program.model.symbol.*;
public class SwitchOverride extends GhidraScript {
private Address collectSelectedJumpData(Listing listing,AddressSetView select,ArrayList<Address> destlist) {
Address branchind = null;
AddressIterator iter = select.getAddresses(true);
while(iter.hasNext()) {
Address addr = iter.next();
Instruction inst = listing.getInstructionAt(addr);
if (isComputedBranchInstruction(inst)) {
branchind = addr;
}
else if (inst != null) {
destlist.add(addr);
}
}
return branchind;
}
private Address collectPointJumpData(Listing listing,
Address addr, ArrayList<Address> destlist) {
Address branchind = null;
// current location must be a callfixup, or an indirect Jump
Instruction instr = currentProgram.getListing().getInstructionAt(addr);
if (isComputedBranchInstruction(instr)) {
branchind = addr;
}
// add any jump references already added
Reference[] referencesFrom = instr.getReferencesFrom();
for (Reference reference : referencesFrom) {
RefType referenceType = reference.getReferenceType();
if (referenceType.isJump()) {
destlist.add(reference.getToAddress());
}
}
return branchind;
}
private boolean isComputedBranchInstruction(Instruction instr) {
if (instr == null) {
return false;
}
FlowType flowType = instr.getFlowType();
if (flowType == RefType.COMPUTED_JUMP) {
return true;
}
if (flowType.isCall()) {
// is it a callfixup?
Reference[] referencesFrom = instr.getReferencesFrom();
for (Reference reference : referencesFrom) {
if (reference.getReferenceType().isCall()) {
Function func = currentProgram.getFunctionManager().getFunctionAt(reference.getToAddress());
if (func != null && func.getCallFixup() != null) {
return true;
}
}
}
}
return false;
}
@Override
public void run() throws Exception {
ArrayList<Address> destlist = new ArrayList<Address>();
Address branchind = null;
if (currentSelection != null && !currentSelection.isEmpty()) {
branchind = collectSelectedJumpData(currentProgram.getListing(),currentSelection,destlist);
} else {
branchind = collectPointJumpData(currentProgram.getListing(),currentLocation.getAddress(),destlist);
}
if (branchind==null) {
println("Please highlight or place the cursor on the instruction performing the computed jump");
return;
}
if (destlist.size()==0) {
println("Please highlight destination instructions in addition to instruction performing switch");
println(" Or put CONDITIONAL_JUMP destination references at the branching instruction");
return;
}
Function function = this.getFunctionContaining(branchind);
if (function==null) {
println("Computed jump instruction must be in a Function body.");
return;
}
Instruction instr = currentProgram.getListing().getInstructionAt(branchind);
for (Address address : destlist) {
instr.addOperandReference(0, address, RefType.COMPUTED_JUMP, SourceType.USER_DEFINED);
}
// Allocate an override jumptable
JumpTable jumpTab = new JumpTable(branchind,destlist,true);
jumpTab.writeOverride(function);
// fixup the body now that there are jump references
CreateFunctionCmd.fixupFunctionBody(currentProgram, function, monitor);
}
}

View File

@@ -0,0 +1,32 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import ghidra.app.script.GhidraScript;
import ghidra.framework.options.Options;
import ghidra.program.model.lang.BasicCompilerSpec;
public class TurnOnLanguage extends GhidraScript {
@Override
protected void run() throws Exception {
Options decompilerPropertyList = currentProgram.getOptions(BasicCompilerSpec.DECOMPILER_PROPERTY_LIST_NAME);
decompilerPropertyList.registerOption(
BasicCompilerSpec.DECOMPILER_OUTPUT_LANGUAGE,
BasicCompilerSpec.DECOMPILER_OUTPUT_DEF,
null,
BasicCompilerSpec.DECOMPILER_OUTPUT_DESC);
}
}

View File

@@ -0,0 +1,780 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Given certain Key windows API calls, tries to create references at the use of windows Resources.
* This script uses the decompiler and the simplified Pcode AST to locate constant values passed to key
* functions like LoadStringW, LoadIconW, etc...
*
* The guts of this script past the main could be used to analyze
* constants passed to any function on any processor.
* It is not restricted to windows.
*
* The assumption is made that default program analysis has already been run in order to retrieve
* the best results from this script.
* @category Windows
*/
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ghidra.app.decompiler.*;
import ghidra.app.script.GhidraScript;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.OptionsService;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressSetPropertyMap;
import ghidra.util.UndefinedFunction;
import ghidra.util.exception.*;
public class WindowsResourceReference extends GhidraScript {
private static final String WINDOWS_RESOURCE_CHECKED_PROPERTYMAP = "WindowsResourceChecked";
private DecompInterface decomplib;
ArrayList<Address> routines = new ArrayList<>(); //Holds the address of found resource routines
ArrayList<Integer> paramIndexes = new ArrayList<>(); //Holds the index of resource arguments on the stack
ArrayList<ArrayList<PcodeOp>> defUseLists = new ArrayList<>();
protected AddressSetPropertyMap alreadyDoneAddressSetPropertyMap;
public AddressSetPropertyMap getOrCreatePropertyMap(Program program, String mapName) {
if (alreadyDoneAddressSetPropertyMap != null) {
return alreadyDoneAddressSetPropertyMap;
}
alreadyDoneAddressSetPropertyMap = program.getAddressSetPropertyMap(mapName);
if (alreadyDoneAddressSetPropertyMap != null) {
return alreadyDoneAddressSetPropertyMap;
}
try {
alreadyDoneAddressSetPropertyMap = program.createAddressSetPropertyMap(mapName);
}
catch (DuplicateNameException e) {
throw new AssertException(
"Can't get DuplicateNameException since we tried to get it first");
}
return alreadyDoneAddressSetPropertyMap;
}
@Override
public void run() throws Exception {
// This code was added so that the analyzer (which calls a script) would not print script messages but if
// run as a script it would still show output in the console.
// It was also added to get the createBookmark option from the analyzer options.
// The printScriptMsgs flag is checked every time the script tries to print.
// The createBookmarks flag is checked every time the script tries to make a bookmark.
// This also allows headless scripts to print if no args are passed but if they want no messages they
// should pass the argument "false".
// This also allows headless scripts to create bookmarks if no arguments are passed but if they want no
// bookmarks they should pass the argument "false".
// These are the default values if no arguments set them.
boolean printScriptMsgs = true;
boolean createBookmarks = true;
// This gets the first argument if there are one or more arguments.
String[] scriptArgs = getScriptArgs();
if (scriptArgs.length >= 1) {
if ("false".equals(scriptArgs[0])) {
printScriptMsgs = false;
}
}
// This gets the second argument if there is one.
if (scriptArgs.length == 2) {
if ("false".equals(scriptArgs[1])) {
createBookmarks = false;
}
}
AddressSetView restrictedSet = currentSelection;
getOrCreatePropertyMap(currentProgram, WINDOWS_RESOURCE_CHECKED_PROPERTYMAP);
// If this is the whole address space, look at everything
// and ignore already done property
if (restrictedSet == null || restrictedSet.isEmpty() ||
restrictedSet.hasSameAddresses(currentProgram.getMemory())) {
restrictedSet = null;
alreadyDoneAddressSetPropertyMap.clear();
}
// If this is a partial address set, then ignore anywhere with a done it property
try {
decomplib = setUpDecompiler(currentProgram);
if (decomplib == null) {
if (printScriptMsgs) {
println("Decompile Error: " + decomplib.getLastMessage());
}
return;
}
// Hold address and lookup constant value pairs for each resource lookup
HashMap<Address, Long> constLocs;
// Set of Resource name lookups. If unknown or variable Rsrc_ name
// Rsrc_* wildcard allowed except when calling addResourceTableReferences()
constLocs = associateResource("AfxMessageBox", 1, restrictedSet, printScriptMsgs);
addResourceTableReferences(constLocs, "Rsrc_StringTable", printScriptMsgs,
createBookmarks);
constLocs = associateResource("CreateDialogParamA", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_Dialog", printScriptMsgs, createBookmarks);
constLocs = associateResource("CreateDialogParamW", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_Dialog", printScriptMsgs, createBookmarks);
constLocs = associateResource("DialogBoxParamA", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_Dialog", printScriptMsgs, createBookmarks);
constLocs = associateResource("DialogBoxParamW", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_Dialog", printScriptMsgs, createBookmarks);
constLocs = associateResource("FindResourceA", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_*", printScriptMsgs, createBookmarks);
constLocs = associateResource("FindResourceW", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_*", printScriptMsgs, createBookmarks);
constLocs = associateResource("FindResourceHandle", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_*", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadAcceleratorsA", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_Accelerator", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadAcceleratorsW", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_Accelerator", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadBitmapA", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_Bitmap", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadBitmapW", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_Bitmap", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadCursorA", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_*", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadCursorW", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_*", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadIconA", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_GroupIcon", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadIconW", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_GroupIcon", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadImageA", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_*", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadImageW", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_*", printScriptMsgs, createBookmarks);
constLocs = associateResource("RegLoadMUIStringW", 6, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_MUI", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadMenuA", 2, restrictedSet, printScriptMsgs);
addResourceTableReferences(constLocs, "Rsrc_Menu", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadMenuW", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_Menu", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadRegTypeLib", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_*", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadStringA", 2, restrictedSet, printScriptMsgs);
addResourceTableReferences(constLocs, "Rsrc_StringTable", printScriptMsgs,
createBookmarks);
constLocs = associateResource("LoadStringW", 2, restrictedSet, printScriptMsgs);
addResourceTableReferences(constLocs, "Rsrc_StringTable", printScriptMsgs,
createBookmarks);
constLocs = associateResource("LoadTypeLib", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_*", printScriptMsgs, createBookmarks);
constLocs = associateResource("LoadTypeLibEx", 2, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_*", printScriptMsgs, createBookmarks);
constLocs = associateResource("PlaySoundW", 1, restrictedSet, printScriptMsgs);
addResourceReferences(constLocs, "Rsrc_WAVE", printScriptMsgs, createBookmarks);
}
finally {
decomplib.dispose();
}
}
/**
* Associates a resource name with the name and ID of that resource
* @param resourceRoutine - Name of the resource routine
* @param paramIndex - Argument index of windows function call for resource lookup
* @param restrictedSet - Address space to use
* @param printScriptMsgs - if true, print output; if false, do not print any output;
* @return HashMap<Address, Long> map of addresses
*/
private HashMap<Address, Long> associateResource(String resourceRoutine, int paramIndex,
AddressSetView restrictedSet, boolean printScriptMsgs) {
HashMap<Address, Long> constUse = new HashMap<>();
Symbol symbol = lookupRoutine(resourceRoutine, printScriptMsgs);
if (symbol == null) {
return constUse;
}
//Continue along if a symbol was found
routines.add(symbol.getAddress());
paramIndexes.add(paramIndex);
ArrayList<PcodeOp> defUseList = new ArrayList<>();
defUseLists.add(defUseList);
HashSet<Address> doneRoutines = new HashSet<>();
//Have a list of routines found based on symbol lookups
while (routines.size() > 0) {
// get the next routine to lookup
Address addr = routines.remove(0);
paramIndex = paramIndexes.remove(0);
defUseList = defUseLists.remove(0);
if (doneRoutines.contains(addr)) {
continue;
}
doneRoutines.add(addr);
// Get the list of references to this address
ReferenceIterator referencesTo =
currentProgram.getReferenceManager().getReferencesTo(addr);
for (Reference reference : referencesTo) {
if (monitor.isCancelled()) {
break;
}
// Get the address of the function which is referenced
Address refAddr = reference.getFromAddress();
// if set is null, do no checks
if (restrictedSet != null && !restrictedSet.contains(refAddr)) {
continue;
}
// was this location already checked?
if (alreadyDoneAddressSetPropertyMap != null) {
if (alreadyDoneAddressSetPropertyMap.contains(refAddr)) {
continue;
}
alreadyDoneAddressSetPropertyMap.add(refAddr, refAddr);
}
Function refFunc =
currentProgram.getFunctionManager().getFunctionContaining(refAddr);
if (refFunc == null) {
refFunc = UndefinedFunction.findFunction(currentProgram, refAddr, monitor);
}
// this is an indirect reference, need to add the references to here.
if (refFunc == null && reference.isExternalReference()) {
routines.add(reference.getFromAddress());
paramIndexes.add(paramIndex);
defUseLists.add(new ArrayList<PcodeOp>());
continue;
}
if (refFunc == null) {
continue;
}
// decompile function
// look for call to this function
// display call
@SuppressWarnings("unchecked")
ArrayList<PcodeOp> localDefUseList = (ArrayList<PcodeOp>) defUseList.clone();
monitor.setMessage(
"Analyzing : " + refFunc.getName() + " for refs to " + resourceRoutine);
analyzeFunction(constUse, decomplib, currentProgram, refFunc, refAddr, paramIndex,
localDefUseList);
}
}
return constUse;
}
/**
* Checks to see if the current resource routine is found in the
* programs symbol table.
* @param resourceRoutine - Name of the resource routine
* @param printScriptMsgs - if true, print output; if false, do not print any output;
* @return Symbol - found symbol based on resourceRoutine
*/
private Symbol lookupRoutine(String resourceRoutine, boolean printScriptMsgs) {
Symbol foundSym = null;
// Get the symbols that match the current resource routine
SymbolIterator symbols = currentProgram.getSymbolTable().getSymbols(resourceRoutine);
while (symbols.hasNext()) {
//If a match is found get the function at the address of the found symbol
foundSym = symbols.next();
Function functionAt =
currentProgram.getFunctionManager().getFunctionAt(foundSym.getAddress());
if (functionAt != null) {
return foundSym;
}
}
if (foundSym != null && printScriptMsgs) {
println("References to the " + resourceRoutine + " routine:");
}
return foundSym;
}
/**
* Analyze a functions references.
* Populates the address/value pairs of resource address and ID
* @param constUse - resource address/value pairs
*/
public void analyzeFunction(HashMap<Address, Long> constUse, DecompInterface decompiler,
Program prog, Function f, Address refAddr, int paramIndex,
ArrayList<PcodeOp> defUseList) {
if (f == null) {
return;
}
Instruction instr = prog.getListing().getInstructionAt(refAddr);
if (instr == null) {
return;
}
decompileFunction(f, decompiler);
if (hfunction == null) {
return; // failed to decompile
}
Iterator<PcodeOpAST> ops = hfunction.getPcodeOps(refAddr);
while (ops.hasNext()) {
if (monitor.isCancelled()) {
break;
}
PcodeOpAST pcodeOpAST = ops.next();
if (pcodeOpAST.getOpcode() == PcodeOp.CALL) {
// get the second parameter
Varnode parm = pcodeOpAST.getInput(paramIndex); // 1st param is the call dest
if (parm == null) {
return;
}
// see if it is a constant
if (parm.isConstant()) {
// then this is a resource id
// lookup the resource and create a reference
long value = parm.getOffset();
// TODO: not so fast, if there is a defUseList, must apply it to get the real constant USED!
try {
value = applyDefUseList(value, defUseList);
constUse.put(instr.getAddress(), value);
}
catch (InvalidInputException exc) {
// don't worry about error
}
}
else {
followToParam(constUse, defUseList, hfunction, parm, null);
}
// if this is anything else, get the high variable to see if it can be traced back to a param
// then repeat with any calls to this function at whatever the param is
}
}
}
/**
* Decompile the function
* @param f
* @param decompiler
* @return boolean - true if successful
*/
public boolean decompileFunction(Function f, DecompInterface decompiler) {
// don't decompile the function again if it was the same as the last one
if (f.getEntryPoint().equals(lastDecompiledFuncAddr)) {
return true;
}
DecompileResults decompRes =
decompiler.decompileFunction(f, decompiler.getOptions().getDefaultTimeout(), monitor);
hfunction = decompRes.getHighFunction();
if (hfunction == null) {
return false;
}
lastDecompiledFuncAddr = f.getEntryPoint();
return true;
}
/**
* Prints out the address of a found resource along with the name of
* the resource.
* @param constLocs - Address value pairs of the resource function
* @param resourceName - name of the resource
* @param printScriptMsgs - if true, print output; if false, do not print any output;
* @param createBookmarks - if true, create bookmarks where references are found; if false, do not create any bookmarks;
* @throws CancelledException
*/
private void addResourceReferences(HashMap<Address, Long> constLocs, String resourceName,
boolean printScriptMsgs, boolean createBookmarks) throws CancelledException {
Set<Address> keys;
Iterator<Address> locIter;
keys = constLocs.keySet();
locIter = keys.iterator();
while (locIter.hasNext()) {
monitor.checkCanceled();
Address loc = locIter.next();
Instruction instr = currentProgram.getListing().getInstructionAt(loc);
long rsrcID = constLocs.get(loc);
Address rsrcAddr = findResource(resourceName + "_" + Long.toHexString(rsrcID), 0);
if (rsrcAddr != null) {
//Get the full symbol name including constant digits
String symName = getSymbolAt(rsrcAddr).getName();
//Match on the name without the constant digit values
String pattern = "([a-z]|[A-Z])*_?([a-z]|[A-Z])*(_[A-Z]+[a-z]+)?";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(symName);
//Default to resourceName argument passed in unless found better match below
String rsrcName = resourceName;
if (m.find()) {
rsrcName = m.group();
}
instr.addMnemonicReference(rsrcAddr, RefType.DATA, SourceType.ANALYSIS);
if (createBookmarks) {
currentProgram.getBookmarkManager().setBookmark(instr.getMinAddress(),
BookmarkType.ANALYSIS, "WindowsResourceReference",
"Added Resource Reference");
}
if (printScriptMsgs) {
println(" " + instr.getMinAddress().toString() + " : Found " + rsrcName +
" reference");
}
}
}
}
/**
* Prints out the address of a found resource along with the name of
* the resource.
* @param constLocs - Address value pairs of the resource function
* @param tableName - Name of the resource table
* @param printScriptMsgs - if true, print output; if false, do not print any output;
* @param createBookmarks - if true, create bookmarks where references are found; if false, do not create any bookmarks;
* @throws CancelledException
*/
private void addResourceTableReferences(HashMap<Address, Long> constLocs, String tableName,
boolean printScriptMsgs, boolean createBookmarks) throws CancelledException {
Set<Address> keys;
Iterator<Address> locIter;
//Get the set of address locations which call the resource function
keys = constLocs.keySet();
locIter = keys.iterator();
//Iterate though the set of address locations
while (locIter.hasNext()) {
monitor.checkCanceled();
Address loc = locIter.next();
Instruction instr = currentProgram.getListing().getInstructionAt(loc);
Long rsrcID = constLocs.get(loc);
Address rsrcAddr = null;
if (rsrcID != null) {
rsrcAddr = findResource(tableName, rsrcID);
}
if (rsrcAddr != null) {
instr.addMnemonicReference(rsrcAddr, RefType.DATA, SourceType.ANALYSIS);
if (createBookmarks) {
currentProgram.getBookmarkManager().setBookmark(instr.getMinAddress(),
BookmarkType.ANALYSIS, "WindowsResourceReference",
"Added Resource Table Reference");
}
if (printScriptMsgs) {
println(" " + instr.getMinAddress().toString() + " : Found " +
tableName + " table reference " + rsrcID);
}
}
}
}
/**
* Returns the address of the resource located in the program
* @param tableName - Name of the resource table
* @param rsrcID - ID of the resource to find
* @return Address of the found resource
* @throws CancelledException
*/
private Address findResource(String tableName, long rsrcID) throws CancelledException {
SymbolIterator siter =
currentProgram.getSymbolTable().getSymbolIterator(tableName + "*", true);
if (!siter.hasNext()) {
return null;
}
// Search each table
while (siter.hasNext()) {
monitor.checkCanceled();
Symbol sym = siter.next();
String symName = sym.getName();
long curRsrcID = rsrcID;
if (rsrcID != 0) {
symName = symName.replaceAll(tableName + "_", "");
//Find the specific table number of the resource
String pattern = "_+[0-9]+";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(symName);
String hexString = "";
if (m.find()) {
//Strip off the constant value to leave just the resource number
hexString = symName.replaceAll(m.group(), "");
}
curRsrcID = Integer.parseInt(hexString, 16);
curRsrcID = (curRsrcID - 1) * 0x10;
curRsrcID = rsrcID - curRsrcID;
if (curRsrcID > 0x10) {
continue; // tables have 16 entries
}
if (curRsrcID < 0) {
continue;
}
}
Address currItemAddr = sym.getAddress();
Data data = currentProgram.getListing().getDataAt(currItemAddr);
while (curRsrcID > 0) {
currItemAddr = data.getAddress();
data = currentProgram.getListing().getDataAfter(currItemAddr);
if (data == null || !data.isDefined()) {
break;
}
curRsrcID--;
}
if (data == null) {
continue;
}
if (curRsrcID == 0) {
return data.getAddress();
}
}
return null;
}
private long applyDefUseList(long value, ArrayList<PcodeOp> defUseList)
throws InvalidInputException {
if (defUseList == null || defUseList.size() <= 0) {
return value;
}
Iterator<PcodeOp> iterator = defUseList.iterator();
while (iterator.hasNext()) {
PcodeOp pcodeOp = iterator.next();
int opcode = pcodeOp.getOpcode();
switch (opcode) {
case PcodeOp.INT_AND:
if (pcodeOp.getInput(0).isConstant()) {
value = value & pcodeOp.getInput(0).getOffset();
}
else if (pcodeOp.getInput(1).isConstant()) {
value = value & pcodeOp.getInput(1).getOffset();
}
else {
throw new InvalidInputException(
" Unhandled Pcode OP " + pcodeOp.toString());
}
break;
default:
throw new InvalidInputException(" Unhandled Pcode OP " + pcodeOp.toString());
}
}
return value;
}
/**
* Finds the parameter passed into the resource function call
* @param constUse - Key value pairs of the resource function calls
* @param defUseList
* @param highFunction
* @param vnode
* @param doneSet
*/
private void followToParam(HashMap<Address, Long> constUse, ArrayList<PcodeOp> defUseList,
HighFunction highFunction, Varnode vnode, HashSet<SequenceNumber> doneSet) {
HighVariable hvar = vnode.getHigh();
if (hvar instanceof HighParam) {
Function function = highFunction.getFunction();
routines.add(function.getEntryPoint());
paramIndexes.add(((HighParam) hvar).getSlot() + 1);
defUseLists.add(defUseList);
return;
}
// follow back up through
PcodeOp def = vnode.getDef();
if (def == null) {
return;
}
// have we done this vnode source already?
Address vPCAddr = vnode.getPCAddress();
SequenceNumber seqnum = def.getSeqnum();
if (doneSet == null) {
doneSet = new HashSet<>();
}
if (seqnum != null && doneSet.contains(seqnum)) {
return;
}
doneSet.add(seqnum);
int opcode = def.getOpcode();
switch (opcode) {
case PcodeOp.COPY:
if (def.getInput(0).isConstant()) {
long value = def.getInput(0).getOffset();
try {
value = applyDefUseList(value, defUseList);
constUse.put(def.getOutput().getPCAddress(), value);
}
catch (InvalidInputException exc) {
// ignore
}
return;
}
followToParam(constUse, defUseList, highFunction, def.getInput(0), doneSet);
return;
case PcodeOp.INT_ZEXT:
followToParam(constUse, defUseList, highFunction, def.getInput(0), doneSet);
return;
case PcodeOp.MULTIEQUAL:
followToParam(constUse, defUseList, highFunction, def.getInput(0), doneSet);
@SuppressWarnings("unchecked")
ArrayList<PcodeOp> splitUseList = (ArrayList<PcodeOp>) defUseList.clone();
followToParam(constUse, splitUseList, highFunction, def.getInput(1), doneSet);
return;
case PcodeOp.CAST:
// Cast will expose more Pcode, and could be attached to the same address!
if (vPCAddr.equals(def.getInput(0).getPCAddress())) {
doneSet.remove(vPCAddr);
}
followToParam(constUse, defUseList, highFunction, def.getInput(0), doneSet);
return;
case PcodeOp.INDIRECT:
if (def.getOutput().getAddress().equals(def.getInput(0).getAddress())) {
followToParam(constUse, defUseList, highFunction, def.getInput(0), doneSet);
return;
}
break;
case PcodeOp.INT_AND:
if (def.getInput(1).isConstant()) {
defUseList.add(0, def);
followToParam(constUse, defUseList, highFunction, def.getInput(0), doneSet);
return;
}
break;
case PcodeOp.INT_ADD:
if (def.getInput(1).isConstant()) {
defUseList.add(0, def);
followToParam(constUse, defUseList, highFunction, def.getInput(0), doneSet);
return;
}
if (vnode.getHigh() instanceof HighParam) {
// don't handle non-constants for now
}
break;
}
// println(" Lost IT! " + vnode.getPCAddress());
}
@SuppressWarnings("unused")
private boolean isHexDigit(char charAt) {
if (Character.isDigit(charAt)) {
return true;
}
if ("abcdef".indexOf(charAt) >= 0) {
return true;
}
if ("ABCDEF".indexOf(charAt) >= 0) {
return true;
}
return false;
}
// Decompiler stuff
private HighFunction hfunction = null;
private Address lastDecompiledFuncAddr = null;
private DecompInterface setUpDecompiler(Program program) {
DecompInterface decompiler = new DecompInterface();
DecompileOptions options;
options = new DecompileOptions();
PluginTool tool = state.getTool();
if (tool != null) {
OptionsService service = tool.getService(OptionsService.class);
if (service != null) {
ToolOptions opt = service.getOptions("Decompiler");
options.grabFromToolAndProgram(null, opt, program);
}
}
decompiler.setOptions(options);
decompiler.toggleCCode(true);
decompiler.toggleSyntaxTree(true);
decompiler.setSimplificationStyle("decompile");
decompiler.openProgram(program);
return decompiler;
}
}