From dbd2098a5903391c00c97d9109c16d54f6f93a63 Mon Sep 17 00:00:00 2001 From: James <49045138+ghidracadabra@users.noreply.github.com> Date: Fri, 14 Oct 2022 18:10:39 +0000 Subject: [PATCH 1/2] GP-2697 added script to find problematic stack writes --- .../DecompilerStackProblemsFinderScript.java | 218 ++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 Ghidra/Features/Decompiler/ghidra_scripts/DecompilerStackProblemsFinderScript.java diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/DecompilerStackProblemsFinderScript.java b/Ghidra/Features/Decompiler/ghidra_scripts/DecompilerStackProblemsFinderScript.java new file mode 100644 index 0000000000..3297f422c8 --- /dev/null +++ b/Ghidra/Features/Decompiler/ghidra_scripts/DecompilerStackProblemsFinderScript.java @@ -0,0 +1,218 @@ +/* ### + * 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 showing locations where the decompiled code writes a value within the containing +// function's body to the stack. This is a good indication that the decompiler's +// stack analysis is missing information. For example, the function or a callee might need +// to have its signature, calling convention, or "No Return" status adjusted. + +// @category Analysis + +import java.util.*; + +import ghidra.app.decompiler.*; +import ghidra.app.decompiler.parallel.*; +import ghidra.app.script.GhidraScript; +import ghidra.app.tablechooser.*; +import ghidra.program.model.address.*; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.Program; +import ghidra.program.model.pcode.*; +import ghidra.util.task.TaskMonitor; + +public class DecompilerStackProblemsFinderScript extends GhidraScript { + + @Override + protected void run() throws Exception { + + if (isRunningHeadless()) { + println("This script cannot be run headlessly"); + return; + } + + AddressSetView selection = currentSelection; + if (selection == null) { + selection = currentProgram.getMemory().getExecuteSet(); + } + + DecompilerCallback> callback = + new DecompilerCallback<>(currentProgram, new StackErrorConfigurer(currentProgram)) { + + @Override + public List process(DecompileResults results, TaskMonitor tMonitor) + throws Exception { + tMonitor.checkCanceled(); + return findStackErrors(results, tMonitor); + } + }; + + List> results = Collections.emptyList(); + try { + results = + ParallelDecompiler.decompileFunctions(callback, currentProgram, selection, monitor); + } + finally { + callback.dispose(); + } + + TableChooserDialog tableDialog = + createTableChooserDialog(currentProgram.getName() + " problematic stack writes", null); + configureTableColumns(tableDialog); + + boolean foundSomething = false; + for (List list : results) { + for (StackErrorRow row : list) { + tableDialog.add(row); + foundSomething = true; + } + } + if (!foundSomething) { + popup("No problematic writes found"); + return; + } + tableDialog.show(); + } + + private List findStackErrors(DecompileResults results, TaskMonitor tMonitor) + throws Exception { + + List rows = new ArrayList<>(); + HighFunction highFunction = results.getHighFunction(); + if (highFunction == null) { + return rows; + } + AddressSetView body = results.getFunction().getBody(); + AddressSpace addrSpace = body.getMinAddress().getAddressSpace(); + Iterator ops = highFunction.getPcodeOps(); + ops.forEachRemaining(op -> { + if (op.getOpcode() != PcodeOp.COPY) { + return; + } + if (!op.getOutput().getAddress().isStackAddress()) { + return; + } + Varnode input = op.getInput(0); + if (!input.isConstant()) { + return; + } + try { + Address addr = addrSpace.getAddress(input.getOffset()); + if (body.contains(addr)) { + rows.add(new StackErrorRow(results.getFunction(), op.getSeqnum().getTarget(), + input.getOffset())); + } + } + catch (AddressOutOfBoundsException e) { + //this is can happen when the constant is an encoding of a floating + //point value. + return; + } + }); + return rows; + } + + class StackErrorConfigurer implements DecompileConfigurer { + private Program p; + + public StackErrorConfigurer(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 StackErrorRow implements AddressableRowObject { + private Function func; + private Address errorAddress; + private long value; + + public StackErrorRow(Function func, Address errorAddress, long value) { + this.func = func; + this.errorAddress = errorAddress; + this.value = value; + } + + public Function getFunction() { + return func; + } + + public long getValue() { + return value; + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(func.getName()); + sb.append(" error address: "); + sb.append(errorAddress.toString()); + sb.append(", error value: "); + sb.append(Long.toUnsignedString(value, 16)); + return sb.toString(); + } + + @Override + public Address getAddress() { + return errorAddress; + } + + } + + private void configureTableColumns(TableChooserDialog dialog) { + + StringColumnDisplay functionNameColumn = new StringColumnDisplay() { + @Override + public String getColumnName() { + return "Function Name"; + } + + @Override + public String getColumnValue(AddressableRowObject rowObject) { + return ((StackErrorRow) rowObject).getFunction().getName(); + } + }; + + ColumnDisplay errorValueColumn = new AbstractComparableColumnDisplay<>() { + + @Override + public String getColumnValue(AddressableRowObject rowObject) { + long errorVal = ((StackErrorRow) rowObject).getValue(); + int size = rowObject.getAddress().getAddressSpace().getSize() / 4; + return String.format("0x%0" + size + "x", errorVal); + } + + @Override + public String getColumnName() { + return "Value"; + } + }; + + dialog.addCustomColumn(functionNameColumn); + dialog.addCustomColumn(errorValueColumn); + } + +} From 7ab05db386cebc80359ff435e9d5b40df1e22189 Mon Sep 17 00:00:00 2001 From: James <49045138+ghidracadabra@users.noreply.github.com> Date: Tue, 22 Nov 2022 20:34:06 +0000 Subject: [PATCH 2/2] GP-2697 addressing code review comments --- .../DecompilerStackProblemsFinderScript.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/DecompilerStackProblemsFinderScript.java b/Ghidra/Features/Decompiler/ghidra_scripts/DecompilerStackProblemsFinderScript.java index 3297f422c8..02bbef3455 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/DecompilerStackProblemsFinderScript.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/DecompilerStackProblemsFinderScript.java @@ -30,6 +30,7 @@ import ghidra.program.model.address.*; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; import ghidra.program.model.pcode.*; +import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; public class DecompilerStackProblemsFinderScript extends GhidraScript { @@ -65,6 +66,7 @@ public class DecompilerStackProblemsFinderScript extends GhidraScript { } finally { callback.dispose(); + monitor.checkCanceled(); } TableChooserDialog tableDialog = @@ -86,7 +88,7 @@ public class DecompilerStackProblemsFinderScript extends GhidraScript { } private List findStackErrors(DecompileResults results, TaskMonitor tMonitor) - throws Exception { + throws CancelledException { List rows = new ArrayList<>(); HighFunction highFunction = results.getHighFunction(); @@ -96,16 +98,18 @@ public class DecompilerStackProblemsFinderScript extends GhidraScript { AddressSetView body = results.getFunction().getBody(); AddressSpace addrSpace = body.getMinAddress().getAddressSpace(); Iterator ops = highFunction.getPcodeOps(); - ops.forEachRemaining(op -> { + while (ops.hasNext()) { + tMonitor.checkCanceled(); + PcodeOp op = ops.next(); if (op.getOpcode() != PcodeOp.COPY) { - return; + continue; } if (!op.getOutput().getAddress().isStackAddress()) { - return; + continue; } Varnode input = op.getInput(0); if (!input.isConstant()) { - return; + continue; } try { Address addr = addrSpace.getAddress(input.getOffset()); @@ -117,9 +121,9 @@ public class DecompilerStackProblemsFinderScript extends GhidraScript { catch (AddressOutOfBoundsException e) { //this is can happen when the constant is an encoding of a floating //point value. - return; + continue; } - }); + } return rows; } @@ -134,7 +138,7 @@ public class DecompilerStackProblemsFinderScript extends GhidraScript { public void configure(DecompInterface decompiler) { decompiler.toggleCCode(false); decompiler.toggleSyntaxTree(true); - decompiler.setSimplificationStyle("decompile"); + decompiler.setSimplificationStyle("normalize"); DecompileOptions opts = new DecompileOptions(); opts.grabFromProgram(p); decompiler.setOptions(opts);