mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
Candidate release of source code.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
57
Ghidra/Features/Decompiler/ghidra_scripts/Decompile.java
Normal file
57
Ghidra/Features/Decompiler/ghidra_scripts/Decompile.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
327
Ghidra/Features/Decompiler/ghidra_scripts/GraphAST.java
Normal file
327
Ghidra/Features/Decompiler/ghidra_scripts/GraphAST.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
109
Ghidra/Features/Decompiler/ghidra_scripts/GraphASTAndFlow.java
Normal file
109
Ghidra/Features/Decompiler/ghidra_scripts/GraphASTAndFlow.java
Normal 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");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
251
Ghidra/Features/Decompiler/ghidra_scripts/ShowCCallsScript.java
Normal file
251
Ghidra/Features/Decompiler/ghidra_scripts/ShowCCallsScript.java
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
1083
Ghidra/Features/Decompiler/ghidra_scripts/ShowConstantUse.java
Normal file
1083
Ghidra/Features/Decompiler/ghidra_scripts/ShowConstantUse.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
143
Ghidra/Features/Decompiler/ghidra_scripts/SwitchOverride.java
Normal file
143
Ghidra/Features/Decompiler/ghidra_scripts/SwitchOverride.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user