From 6fb5d60384833d6a145d3e564b9ecf8a706d279b Mon Sep 17 00:00:00 2001 From: GhidraKraken Date: Tue, 9 Apr 2024 16:45:11 -0400 Subject: [PATCH] GP-4480: Added script to paste address/bytes copied as text from a Ghidra listing --- .../CreateEmptyProgramScript.java | 56 +---- .../PasteCopiedListingBytesScript.java | 194 ++++++++++++++++++ 2 files changed, 198 insertions(+), 52 deletions(-) create mode 100644 Ghidra/Features/Base/ghidra_scripts/PasteCopiedListingBytesScript.java diff --git a/Ghidra/Features/Base/ghidra_scripts/CreateEmptyProgramScript.java b/Ghidra/Features/Base/ghidra_scripts/CreateEmptyProgramScript.java index e8554fc835..2648ac4393 100644 --- a/Ghidra/Features/Base/ghidra_scripts/CreateEmptyProgramScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/CreateEmptyProgramScript.java @@ -17,25 +17,18 @@ //the language selected by the user. //@category Program -import javax.swing.SwingUtilities; - -import docking.DialogComponentProvider; import ghidra.app.script.GhidraScript; import ghidra.app.services.ProgramManager; -import ghidra.plugin.importer.NewLanguagePanel; import ghidra.program.database.ProgramDB; import ghidra.program.model.lang.*; import ghidra.program.model.listing.Program; import ghidra.util.Msg; public class CreateEmptyProgramScript extends GhidraScript { - private NewLanguageDialog dialog = new NewLanguageDialog(); - @Override public void run() throws Exception { - SwingUtilities.invokeAndWait(() -> state.getTool().showDialog(dialog)); - LanguageCompilerSpecPair pair = dialog.getSelectedLanguageCompilerSpecPair(); + LanguageCompilerSpecPair pair = askLanguage("New Program: Select Language", "Select"); if (pair == null) { println("User cancelled operation."); } @@ -44,7 +37,9 @@ public class CreateEmptyProgramScript extends GhidraScript { Language language = pair.getLanguage(); CompilerSpec compilerSpec = pair.getCompilerSpec(); - Program program = new ProgramDB("Untitled", language, compilerSpec, this); + String name = "Untitled-" + language.getLanguageID().toString().replace(':', '_') + + "_" + compilerSpec.getCompilerSpecID(); + Program program = new ProgramDB(name, language, compilerSpec, this); ProgramManager programManager = state.getTool().getService(ProgramManager.class); programManager.openProgram(program); @@ -56,47 +51,4 @@ public class CreateEmptyProgramScript extends GhidraScript { } } } - - private class NewLanguageDialog extends DialogComponentProvider { - private NewLanguagePanel panel; - private boolean isOK; - - NewLanguageDialog() { - super("New Program: Select Language", true, true, true, false); - - panel = new NewLanguagePanel(); - panel.setShowRecommendedCheckbox(false); - - addWorkPanel(panel); - addOKButton(); - addCancelButton(); - setPreferredSize(500, 250); - } - - @Override - protected void okCallback() { - if (panel.getSelectedLcsPair() == null) { - setStatusText("Please select a language."); - return; - } - isOK = true; - close(); - } - - @Override - public void close() { - super.close(); - panel.dispose(); - } - - @Override - protected void cancelCallback() { - isOK = false; - close(); - } - - LanguageCompilerSpecPair getSelectedLanguageCompilerSpecPair() { - return isOK ? panel.getSelectedLcsPair() : null; - } - } } diff --git a/Ghidra/Features/Base/ghidra_scripts/PasteCopiedListingBytesScript.java b/Ghidra/Features/Base/ghidra_scripts/PasteCopiedListingBytesScript.java new file mode 100644 index 0000000000..ae15cdf5a3 --- /dev/null +++ b/Ghidra/Features/Base/ghidra_scripts/PasteCopiedListingBytesScript.java @@ -0,0 +1,194 @@ +/* ### + * 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. + */ +//Useful for getting bytes into a program that have been copied and pasted +//as text onto a website or other text documents. If there is no program open +//when the script is run, you will be prompted to select a processor and a +//new empty program will be created. Text in the copy buffer will be parsed +//to extract address and bytes, and everything else will be ignored. +//Example listing text: +// LAB_0007aaca XREF[1]: +// 0007aac0(j) +// 0007aaca 01 24 +// movs r4,#0x1 +// 0007aacc 00 28 +// cmp r0,#0x0 +// 0007aad2 00 24 +// movs r4,#0x0 +// +// 0007aad6 1f d0 +// beq LAB_0007ab18 +//@category Program +//@menupath Edit.Paste Listing Text +import java.awt.datatransfer.*; +import java.io.IOException; +import java.util.*; + +import docking.dnd.GClipboard; +import ghidra.app.script.GhidraScript; +import ghidra.app.services.ProgramManager; +import ghidra.program.model.address.*; +import ghidra.program.model.mem.*; +import ghidra.util.exception.CancelledException; + +public class PasteCopiedListingBytesScript extends GhidraScript { + public void run() throws Exception { + int id = 0; + if (currentProgram == null) { + runScript("CreateEmptyProgramScript"); + currentProgram = state.getTool().getService(ProgramManager.class).getCurrentProgram(); + } + if (currentProgram == null) { + print("No Program"); + return; + } + Memory memory = currentProgram.getMemory(); + + // get data from the clip board and turn it into a string + String ClipboardText = retrieveClipBoardText(); + if (ClipboardText == null) { + println("Nothing is copied to your clip board"); + return; + } + + // evaluate the copy buffer and get the byte array + Map bytesToAdd = parseListingStringToByte(ClipboardText); + if (bytesToAdd.isEmpty()) { + println("There are no bytes copied to your clip board"); + return; + } + + // Check if memory block with the byte+address exists + boolean exists = checkForExistingMemory(memory, bytesToAdd); + + // quit if any bytes/addresses exist + if (exists) { + boolean overwrite = + askYesNo("Bytes Exist", "Do you wish to overwrite existing memory?"); + if (!overwrite) { + println("stopped"); + return; + } + } + + id = currentProgram.startTransaction("Create Missing Memory"); + try { + // create memoryBlocks for address ranges that don't already exist + createMissingMemory(bytesToAdd, memory); + + // set bytes in memory blocks + setBytesInMemory(bytesToAdd, memory); + } + finally { + currentProgram.endTransaction(id, true); + } + println("Created " + getNeededAddressSet(bytesToAdd)); + } + + private void createMissingMemory(Map bytesToAdd, Memory memory) + throws CancelledException, Exception { + AddressSet neededMem = getNeededAddressSet(bytesToAdd); + AddressSet Overlap = memory.intersect(neededMem); + neededMem = neededMem.subtract(Overlap); + for (AddressRange addr : neededMem) { + memory.createInitializedBlock("PastedBytes", addr.getMinAddress(), addr.getLength(), + (byte) 0, monitor, false); + } + } + + private String retrieveClipBoardText() throws UnsupportedFlavorException, IOException { + Clipboard systemClipboard = GClipboard.getSystemClipboard(); + Transferable contents = systemClipboard.getContents(this); + if (contents.toString().isEmpty()) { + return null; + } + if (contents.isDataFlavorSupported(DataFlavor.stringFlavor)) { + return (String) contents.getTransferData(DataFlavor.stringFlavor); + } + return null; + } + + private void setBytesInMemory(Map byteMap, Memory memory) + throws CancelledException, Exception, MemoryAccessException, MemoryBlockException { + for (Address addr : byteMap.keySet()) { + monitor.checkCancelled(); + setBytes(addr, byteMap.get(addr)); + } + } + + private boolean checkForExistingMemory(Memory memory, Map Addresses) + throws Exception { + AddressSet neededMem = getNeededAddressSet(Addresses); + AddressSet Overlap = memory.intersect(neededMem); + if (Overlap.isEmpty()) { + return false; + } + return true; + } + + private AddressSet getNeededAddressSet(Map Addresses) + throws CancelledException { + AddressSet addrSet = new AddressSet(); + for (Address addr : Addresses.keySet()) { + monitor.checkCancelled(); + int addrCount = Addresses.get(addr).length; + addrSet.add(addr, addr.add(addrCount - 1)); + } + return addrSet; + } + + private Map parseListingStringToByte(String ClipboardText) + throws CancelledException { + String[] bufferLines = ClipboardText.split("\n"); + Map newMap = new HashMap(); + for (String line : bufferLines) { + monitor.checkCancelled(); + line = line.trim(); + String[] words = line.split(" "); + String startOfLine = words[0]; + Address firstAddress = toAddr(startOfLine); + if (firstAddress == null) { + continue; + } + List bytesFound = new ArrayList(); + for (String word : words) { + monitor.checkCancelled(); + if (word == words[0]) { + continue; + } + if (word.isBlank() || word.length() > 2) { + break; + } + try { + Integer.parseInt(word, 16); + } + catch (Exception e) { + break; + } + bytesFound.add(word); + + } + byte[] newBytes = new byte[bytesFound.size()]; + int i = 0; + for (String byteString : bytesFound) { + monitor.checkCancelled(); + byte bVal = (byte) Integer.parseInt(byteString, 16); + newBytes[i++] = bVal; + } + newMap.put(firstAddress, newBytes); + } + return newMap; + } +}