diff --git a/Ghidra/Features/Base/ghidra_scripts/FindRunsOfPointersWithTableScript.java b/Ghidra/Features/Base/ghidra_scripts/FindRunsOfPointersWithTableScript.java deleted file mode 100644 index fdab50176c..0000000000 --- a/Ghidra/Features/Base/ghidra_scripts/FindRunsOfPointersWithTableScript.java +++ /dev/null @@ -1,409 +0,0 @@ -/* ### - * 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. - */ -// New Table Stuff -//@category Search - -import java.util.ArrayList; -import java.util.List; - -import ghidra.app.script.GhidraScript; -import ghidra.app.tablechooser.*; -import ghidra.program.model.address.*; -import ghidra.program.model.mem.Memory; -import ghidra.program.model.mem.MemoryAccessException; - -public class FindRunsOfPointersWithTableScript extends GhidraScript { - private List resultsArray = new ArrayList(); - public static final int LITTLE_ENDIAN = 0; - public static final int BIG_ENDIAN = 1; - - @Override - public void run() throws Exception { - - int size = currentProgram.getAddressFactory().getDefaultAddressSpace().getSize(); - if (size != 32) { - println("This script only works on 32-bit programs."); - return; - } - - TableChooserExecutor executor = createTableExecutor(); - TableChooserDialog tableDialog = createTableChooserDialog("Runs of Pointers", executor); - configureTableColumns(tableDialog); - tableDialog.show(); - tableDialog.setMessage("Searching..."); - Memory memory = currentProgram.getMemory(); - long distance; - // TODO add option to work only on selection - AddressIterator addrIter = memory.getAddresses(true); - //println("Memory range: " + memory.getMinAddress() + " - " + memory.getMaxAddress()); - Address prevAddress = null; - while (addrIter.hasNext() && !monitor.isCancelled()) { - Address addr = addrIter.next(); - try { - // get the value in address form of the bytes at address a - int addrInt = memory.getInt(addr); - long addrLong = addrInt & 0xffffffffL; - Address testAddr = addr.getNewAddress(addrLong); - - if ((addrLong != 0) && (memory.contains(testAddr))) { - if (prevAddress != null) { - distance = addr.subtract(prevAddress); - } - else { - distance = 0; - } - - PossiblePtrs pp = new PossiblePtrs(addr, testAddr, distance); - resultsArray.add(pp); - //println(addr.toString() + " " + testAddr.toString() + " " + distance); - prevAddress = addr; - } - } - catch (MemoryAccessException e) { - break; - } - catch (AddressOutOfBoundsException e) { - break; - } - } - - // go through the list of pointers and only print out the ones with a run of the same distance between them - // keep the one before the run and include the last one with the same distance - //println("tableAddress distance tableSize"); - if (resultsArray.size() == 0) { - tableDialog.setMessage("Done! Found no hits"); - return; - } - - long dist = resultsArray.get(0).getDistanceFromLast(); - int tableSize = 0; - Address topAddress = null; - int i = 1; - while (i < resultsArray.size() && !monitor.isCancelled()) { - // for(int i=1;i= 3) { - tableSize++; - - Address ref = findRef(topAddress, dist); - //println(topAddress.toString() + " " + dist + " " + tableSize); - Table pointerTable = new Table(topAddress, dist, tableSize, ref); - tableDialog.add(pointerTable); - } - tableSize = 0; - dist = thisDist; - } - i++; - } - tableDialog.setMessage("Done! Found " + tableDialog.getRowCount() + " hits"); - - // print out results -// println("Table address Dist bet ptrs Num ptrs Ref found"); -// for(int j=0;j secondAddressColumn = - new AbstractComparableColumnDisplay
() { - @Override - public String getColumnName() { - return "Offset Address"; - } - - @Override - public Address getColumnValue(AddressableRowObject rowObject) { - Table table = (Table) rowObject; - return table.getAddress().add(100); - } - }; - - tableDialog.addCustomColumn(distanceColumn); - tableDialog.addCustomColumn(secondAddressColumn); - tableDialog.addCustomColumn(numberOfPointersColumn); - tableDialog.addCustomColumn(referenceFoundColumn); - } - - private TableChooserExecutor createTableExecutor() { - TableChooserExecutor executor = new TableChooserExecutor() { - @Override - public String getButtonName() { - return "Hit Me"; - } - - @Override - public boolean execute(AddressableRowObject rowObject) { - return true; - } - }; - return executor; - } - - // find the first ref starting at topAddr and working back dist - pointersize - // once a ref is found, stop - it doesn't make much sense that there would be more than one. - Address findRef(Address topAddress, long dist) { - - Memory memory = currentProgram.getMemory(); - Address ref = null; - - //change later to handle 64 bits too - byte[] maskBytes = new byte[4]; - for (int i = 0; i < 4; i++) { - maskBytes[i] = (byte) 0xff; - } - - // search memory for the byte patterns within the range of topAddr and topAddr - dist - // make a structure of found bytes/topAddr offset???? - boolean noRefFound = true; - boolean tryPrevAddr = true; - long longIndex = 0; - while (noRefFound && tryPrevAddr) { - Address testAddr = topAddress.subtract(longIndex); - byte[] addressBytes = turnAddressIntoBytes(testAddr); - - //println("TestAddr = " + testAddr.toString()); - Address found = memory.findBytes(currentProgram.getMinAddress(), addressBytes, - maskBytes, true, monitor); - if (found != null) { - ref = found; - // println("Found ref at " + found.toString()); - noRefFound = false; - } - else { - longIndex++; - // check to see if we are at the top of the range of possible refs - if (longIndex > (dist - 4)) {// change the four to pointer size when I add 64bit - tryPrevAddr = false; - } - - } - } - return ref; - } - - byte[] turnAddressIntoBytes(Address addr) { -// turn addresses into bytes - - byte[] addressBytes = new byte[4]; // only 32-bit for now - change later to add 64 bit - // This is the correct way to do turn a long into an address - long addrLong = addr.getOffset(); - - int endian = getEndian(); - - if (endian == BIG_ENDIAN) { - // put bytes in forward order - addressBytes = bytesForward(addrLong); - } - else if (endian == LITTLE_ENDIAN) { - // put bytes in reverse order - addressBytes = bytesReversed(addrLong); - } - else { - println("Unknown endian - cannot find references."); - return null; - } - - return addressBytes; - } - - byte[] bytesForward(long addr) { - byte[] bytes = new byte[4]; // only works for 32-bit for now-later add 64 - for (int i = 0; i < 4; i++) { - bytes[i] = (byte) ((addr >> (24 - (i * 8))) & 0xff); - } - return bytes; - } - - byte[] bytesReversed(long addr) { - byte[] bytes = new byte[4]; // only works for 32-bit for now-later add 64 - for (int i = 3; i >= 0; i--) { - bytes[3 - i] = (byte) ((addr >> (24 - (i * 8))) & 0xff); - } - return bytes; - } - - // find references to the possible table - // start looking at the top of the array and work back the distance between the pointers in - // the table -// Address [] findReferenceToTable(Address topAddress, long dist){ -// -// ArrayList
foundAddrs = new ArrayList
(); -// long counter = 0; -// while((foundAddrs.size() == 0) || (counter == (dist-1))){ -// List
newList = findReferences(topAddress.subtract(dist-counter)); -// for(int i=0;i findReferences(Address addr){ -// FindPossibleReferences fpr = new FindPossibleReferences(currentProgram,getEndian()); -// return fpr.findReferences(addr); -//} - - public int getEndian() { - - if (currentProgram.getLanguage().isBigEndian()) { - return 1; // BIG_ENDIAN - } - return 0; // LITTLE_ENDIAN - } - -// info about the pushed parameter that gets applied to the calling functions params and locals and referenced data - class PossiblePtrs { - - private Address addrOfPtr; - private Address possiblePtr; - private long distanceFromLast; - - PossiblePtrs(Address addrOfPtr, Address possiblePtr, long distanceFromLast) { - - this.addrOfPtr = addrOfPtr; - this.possiblePtr = possiblePtr; - this.distanceFromLast = distanceFromLast; - } - - public Address getAddrOfPtr() { - return addrOfPtr; - } - - public Address getPossiblePointer() { - return possiblePtr; - } - - public long getDistanceFromLast() { - return distanceFromLast; - } - } - - class Table implements AddressableRowObject { - private Address topAddr; - private long distance; - private int numPointers; - Address ref; - - Table(Address topAddr, long distance, int numPointers, Address ref) { - this.topAddr = topAddr; - this.distance = distance; - this.numPointers = numPointers; - this.ref = ref; - } - - @Override - public Address getAddress() { - return getTopAddr(); - } - - public Address getTopAddr() { - return topAddr; - } - - public long getDistance() { - return distance; - } - - public int getNumPointers() { - return numPointers; - } - - public Address getRef() { - return ref; - } - - } -} diff --git a/Ghidra/Features/Base/ghidra_scripts/FindSharedReturnFunctionsScript.java b/Ghidra/Features/Base/ghidra_scripts/FindSharedReturnFunctionsScript.java index 9025d37040..323205253c 100644 --- a/Ghidra/Features/Base/ghidra_scripts/FindSharedReturnFunctionsScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/FindSharedReturnFunctionsScript.java @@ -75,8 +75,10 @@ public class FindSharedReturnFunctionsScript extends GhidraScript { @Override public String getColumnValue(AddressableRowObject rowObject) { SharedReturnLocations entry = (SharedReturnLocations) rowObject; - Function func = entry.getProgram().getFunctionManager().getFunctionContaining( - entry.getWhyAddr()); + Function func = entry.getProgram() + .getFunctionManager() + .getFunctionContaining( + entry.getWhyAddr()); if (func == null) { return ""; } @@ -93,8 +95,10 @@ public class FindSharedReturnFunctionsScript extends GhidraScript { @Override public String getColumnValue(AddressableRowObject rowObject) { SharedReturnLocations entry = (SharedReturnLocations) rowObject; - Function func = entry.getProgram().getFunctionManager().getFunctionContaining( - entry.getAddress()); + Function func = entry.getProgram() + .getFunctionManager() + .getFunctionContaining( + entry.getAddress()); if (func == null) { return ""; } @@ -148,29 +152,26 @@ public class FindSharedReturnFunctionsScript extends GhidraScript { return "Fixup SharedReturn"; } + @Override + public boolean useBulkTransaction() { + return true; + } + @Override public boolean execute(AddressableRowObject rowObject) { SharedReturnLocations sharedRetLoc = (SharedReturnLocations) rowObject; - System.out.println("Fixup Shared Return Jump at : " + rowObject.getAddress()); + println("Fixup Shared Return Jump at : " + rowObject.getAddress()); Program cp = sharedRetLoc.getProgram(); Address entry = sharedRetLoc.getAddress(); - // gonna change something, have to open a transaction - int trans = cp.startTransaction("Fixup Shared Return Jump at " + entry); - try { - addBookMark(cp, entry, "Shared Return Jump"); + addBookMark(cp, entry, "Shared Return Jump"); - if (!sharedRetLoc.getStatus().equals("fixed")) { - fixSharedReturnLocation(cp, entry); - } - - addBookMark(cp, sharedRetLoc.getWhyAddr(), sharedRetLoc.getExplanation()); - } - finally { - cp.endTransaction(trans, true); + if (!sharedRetLoc.getStatus().equals("fixed")) { + fixSharedReturnLocation(cp, entry); } + addBookMark(cp, sharedRetLoc.getWhyAddr(), sharedRetLoc.getExplanation()); return false; // don't remove row } diff --git a/Ghidra/Features/Base/ghidra_scripts/FixupNoReturnFunctionsScript.java b/Ghidra/Features/Base/ghidra_scripts/FixupNoReturnFunctionsScript.java index 769d0cb7f1..3eae29e63b 100644 --- a/Ghidra/Features/Base/ghidra_scripts/FixupNoReturnFunctionsScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/FixupNoReturnFunctionsScript.java @@ -112,8 +112,10 @@ public class FixupNoReturnFunctionsScript extends GhidraScript { @Override public String getColumnValue(AddressableRowObject rowObject) { NoReturnLocations entry = (NoReturnLocations) rowObject; - Function func = entry.getProgram().getFunctionManager().getFunctionContaining( - entry.getAddress()); + Function func = entry.getProgram() + .getFunctionManager() + .getFunctionContaining( + entry.getAddress()); if (func == null) { return ""; } @@ -269,6 +271,11 @@ public class FixupNoReturnFunctionsScript extends GhidraScript { return "Fixup NoReturn"; } + @Override + public boolean useBulkTransaction() { + return true; + } + @Override public boolean execute(AddressableRowObject rowObject) { NoReturnLocations noRetLoc = (NoReturnLocations) rowObject; @@ -283,20 +290,13 @@ public class FixupNoReturnFunctionsScript extends GhidraScript { return false; } - // gonna change something, have to open a transaction - int trans = cp.startTransaction("Fixup No Return at " + entry); - try { - addBookMark(cp, entry, "Non Returning Function"); + addBookMark(cp, entry, "Non Returning Function"); - if (!noRetLoc.isFixed()) { - repairDamage(cp, func, entry); - } + if (!noRetLoc.isFixed()) { + repairDamage(cp, func, entry); + } - addBookMark(cp, noRetLoc.getWhyAddr(), noRetLoc.getExplanation()); - } - finally { - cp.endTransaction(trans, true); - } + addBookMark(cp, noRetLoc.getWhyAddr(), noRetLoc.getExplanation()); return false; // don't remove row } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/tablechooser/TableChooserDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/tablechooser/TableChooserDialog.java index 8eb5aebc98..720a5ce4a4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/tablechooser/TableChooserDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/tablechooser/TableChooserDialog.java @@ -112,8 +112,9 @@ public class TableChooserDialog extends DialogComponentProvider navigatable.addNavigatableListener(this); table.installNavigation(goToService, navigatable); } - table.getSelectionModel().addListSelectionListener( - e -> setOkEnabled(table.getSelectedRowCount() > 0)); + table.getSelectionModel() + .addListSelectionListener( + e -> setOkEnabled(table.getSelectedRowCount() > 0)); GhidraTableFilterPanel filterPanel = new GhidraTableFilterPanel<>(table, model); @@ -241,7 +242,14 @@ public class TableChooserDialog extends DialogComponentProvider monitor.initialize(rowObjects.size()); try { - List deleted = doProcessRowObjects(rowObjects, monitor); + List deleted; + if (executor.useBulkTransaction()) { + deleted = doProcessRowsInTransaction(rowObjects, monitor); + } + else { + deleted = doProcessRows(rowObjects, monitor); + } + for (AddressableRowObject rowObject : deleted) { model.removeObject(rowObject); } @@ -254,8 +262,9 @@ public class TableChooserDialog extends DialogComponentProvider } } - private List doProcessRowObjects(List rowObjects, + private List doProcessRows(List rowObjects, TaskMonitor monitor) { + List deleted = new ArrayList<>(); for (AddressableRowObject rowObject : rowObjects) { if (monitor.isCancelled()) { @@ -280,6 +289,18 @@ public class TableChooserDialog extends DialogComponentProvider return deleted; } + private List doProcessRowsInTransaction( + List rowObjects, TaskMonitor monitor) { + + int tx = program.startTransaction("Table Chooser: " + getTitle()); + try { + return doProcessRows(rowObjects, monitor); + } + finally { + program.endTransaction(tx, true); + } + } + public void addCustomColumn(ColumnDisplay columnDisplay) { model.addCustomColumn(columnDisplay); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/tablechooser/TableChooserExecutor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/tablechooser/TableChooserExecutor.java index e1e7f537df..9eca7be7d4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/tablechooser/TableChooserExecutor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/tablechooser/TableChooserExecutor.java @@ -15,6 +15,14 @@ */ package ghidra.app.tablechooser; +/** + * The interface clients must implement to use the {@link TableChooserDialog}. This class is the + * callback that is used to process items from the dialog's table as users select one or more + * rows in the table and then press the table's "apply" button. + * + *

See the notes on {@link #useBulkTransaction()}. We recommend you override that method to + * return true. + */ public interface TableChooserExecutor { /** @@ -33,4 +41,30 @@ public interface TableChooserExecutor { * @return true if the rowObject should be removed from the table, false otherwise */ public boolean execute(AddressableRowObject rowObject); + + /** + * When true, the calls to {@link #execute(AddressableRowObject)} will be wrapped in a + * transaction. This allows clients to not use transactions themselves. This can increase + * performance when the user selects a large number of rows in the table to be executed, as + * this API will only use on transaction for all rows, instead of one per row. + * + *

When true, this will use one transaction per button press of the + * {@link TableChooserDialog}. Thus, if the user only processes a single row per button + * press, then there is no performance gain over the traditional use of transactions. + * + *

We recommend clients override this method to return true. + * + *

Note: when false is returned from this method, no transaction is created before the + * call to {@link #execute(AddressableRowObject)}--the client is responsible for + * transaction management in this case. + * + *

For backward compatibility, this method defaults to returning false. You must override + * this method to return true in your code to make use of the single bulk transaction. + * + * @return true to use bulk transactions; false to use no transaction at all when calling + * {@link #execute(AddressableRowObject)}; default is false + */ + public default boolean useBulkTransaction() { + return false; // false by default for backward compatibility + } } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/ShowConstantUse.java b/Ghidra/Features/Decompiler/ghidra_scripts/ShowConstantUse.java index 24e7cea701..d2d8d09886 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/ShowConstantUse.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/ShowConstantUse.java @@ -40,8 +40,7 @@ import ghidra.program.model.pcode.*; import ghidra.program.model.symbol.Reference; import ghidra.program.model.symbol.ReferenceIterator; import ghidra.program.util.*; -import ghidra.util.SystemUtilities; -import ghidra.util.UndefinedFunction; +import ghidra.util.*; import ghidra.util.exception.CancelledException; import ghidra.util.exception.InvalidInputException; @@ -157,10 +156,9 @@ public class ShowConstantUse extends GhidraScript { } /** - * Builds the configurable columns for the TableDialog. More columns could - * be added. + * Builds the configurable columns for the TableDialog. More columns could be added. * - * @param tableChooserDialog + * @param tableChooserDialog the dialog */ private void configureTableColumns(TableChooserDialog tableChooserDialog) { // First column added is the Constant value that is found. @@ -254,8 +252,10 @@ public class ShowConstantUse extends GhidraScript { @Override public String getColumnValue(AddressableRowObject rowObject) { ConstUseLocation entry = (ConstUseLocation) rowObject; - Function func = entry.getProgram().getFunctionManager().getFunctionContaining( - entry.getAddress()); + Function func = entry.getProgram() + .getFunctionManager() + .getFunctionContaining( + entry.getAddress()); if (func == null) { return ""; } @@ -299,7 +299,7 @@ public class ShowConstantUse extends GhidraScript { * script by creating an artificial ScriptState. This is a useful technique * for other scripts as well. * - * @return + * @return the executor */ @SuppressWarnings("unused") private TableChooserExecutor createTableExecutor() { @@ -311,26 +311,22 @@ public class ShowConstantUse extends GhidraScript { return "Create Structure"; } + @Override + public boolean useBulkTransaction() { + return true; + } + @Override public boolean execute(AddressableRowObject rowObject) { ConstUseLocation constLoc = (ConstUseLocation) rowObject; - System.out.println("Follow Structure : " + rowObject.getAddress()); + println("Follow Structure : " + rowObject.getAddress()); Program cp = constLoc.getProgram(); Address entry = constLoc.getAddress(); - // If we will change something in program, have to open a - // transaction - int trans = cp.startTransaction("Run Script" + entry); - try { - System.out.println("Create Structure at " + entry); - - runScript("CreateStructure.java", cp, entry); - } - finally { - cp.endTransaction(trans, true); - } + println("Create Structure at " + entry); + runScript("CreateStructure.java", cp, entry); return false; // don't remove row from display table } @@ -347,7 +343,7 @@ public class ShowConstantUse extends GhidraScript { } } catch (Exception exc) { - exc.printStackTrace(); + Msg.error(this, "Exception running script", exc); } throw new IllegalArgumentException("Script does not exist: " + name); } @@ -393,7 +389,7 @@ public class ShowConstantUse extends GhidraScript { * decompiler. In the decompiler this could be a local/parameter at any * point in the decompiler. In the listing, it must be a parameter variable. * - * @return + * @return the varnode */ private Varnode getVarnodeLocation() { Varnode var = null; @@ -559,7 +555,7 @@ public class ShowConstantUse extends GhidraScript { * - accumulate entries. Don't like passing it, but this way the * user gets immediate feedback as locations are found * @return a map of Addresses->constants (constants could be NULL) - * @throws CancelledException + * @throws CancelledException if cancelled */ private HashMap backtrackToConstant(Varnode var, TableChooserDialog tableChooserDialog) throws CancelledException { @@ -587,15 +583,12 @@ public class ShowConstantUse extends GhidraScript { * Backtrack to a constant given a start position of a parameter of a given * function Useful if you want to start from a function paramter. * - * @param f - * - function to start in - * @param paramIndex - * - parameter index to backtrack from - * @param tableChooserDialog - * - accumulate entries. Don't like passing it, but this way the - * user gets immediate feedback as locations are found - * @return a map of Addresses->constants (constants could be NULL) - * @throws CancelledException + * @param f function to start in + * @param paramIndex parameter index to backtrack from + * @param tableChooserDialog accumulate entries. Don't like passing it, but this way the + * user gets immediate feedback as locations are found + * @return a map of Addresses to constants (constants could be NULL) + * @throws CancelledException if cancelled */ private HashMap backtrackParamToConstant(Function f, int paramIndex, TableChooserDialog tableChooserDialog) throws CancelledException { @@ -699,14 +692,7 @@ public class ShowConstantUse extends GhidraScript { this.addConstantProblem(tableDialog, address, problem); } - /** - * Analyze a functions references - * - * @param constUse - * @param funcVarUse - * @param funcList - */ - public void analyzeFunction(HashMap constUse, DecompInterface decompInterface, + private void analyzeFunction(HashMap constUse, DecompInterface decompInterface, Program prog, Function f, Address refAddr, FunctionParamUse funcVarUse, int paramIndex, ArrayList defUseList, ArrayList funcList) { if (f == null) { @@ -722,12 +708,9 @@ public class ShowConstantUse extends GhidraScript { Iterator ops = hfunction.getPcodeOps(refAddr.getPhysicalAddress()); while (ops.hasNext() && !monitor.isCancelled()) { PcodeOpAST pcodeOpAST = ops.next(); - // System.out.println(pcodeOpAST); if (pcodeOpAST.getOpcode() == PcodeOp.CALL) { // get the second parameter - Varnode parm = pcodeOpAST.getInput(paramIndex + 1); // 1st param - // is the - // call dest + Varnode parm = pcodeOpAST.getInput(paramIndex + 1); // 1st param is the call dest if (parm == null) { constUse.put(instr.getAddress(), null); String problem = " *** Warning, it appears that function '" +