mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GP-4480: Added script to paste address/bytes copied as text from a
Ghidra listing
This commit is contained in:
parent
c7fcf1fff0
commit
6fb5d60384
2 changed files with 198 additions and 52 deletions
|
@ -17,25 +17,18 @@
|
||||||
//the language selected by the user.
|
//the language selected by the user.
|
||||||
//@category Program
|
//@category Program
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
|
|
||||||
import docking.DialogComponentProvider;
|
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
import ghidra.app.services.ProgramManager;
|
import ghidra.app.services.ProgramManager;
|
||||||
import ghidra.plugin.importer.NewLanguagePanel;
|
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
public class CreateEmptyProgramScript extends GhidraScript {
|
public class CreateEmptyProgramScript extends GhidraScript {
|
||||||
private NewLanguageDialog dialog = new NewLanguageDialog();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() throws Exception {
|
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) {
|
if (pair == null) {
|
||||||
println("User cancelled operation.");
|
println("User cancelled operation.");
|
||||||
}
|
}
|
||||||
|
@ -44,7 +37,9 @@ public class CreateEmptyProgramScript extends GhidraScript {
|
||||||
Language language = pair.getLanguage();
|
Language language = pair.getLanguage();
|
||||||
CompilerSpec compilerSpec = pair.getCompilerSpec();
|
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 programManager = state.getTool().getService(ProgramManager.class);
|
||||||
programManager.openProgram(program);
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<Address, byte[]> 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<Address, byte[]> 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<Address, byte[]> 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<Address, byte[]> Addresses)
|
||||||
|
throws Exception {
|
||||||
|
AddressSet neededMem = getNeededAddressSet(Addresses);
|
||||||
|
AddressSet Overlap = memory.intersect(neededMem);
|
||||||
|
if (Overlap.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AddressSet getNeededAddressSet(Map<Address, byte[]> 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<Address, byte[]> parseListingStringToByte(String ClipboardText)
|
||||||
|
throws CancelledException {
|
||||||
|
String[] bufferLines = ClipboardText.split("\n");
|
||||||
|
Map<Address, byte[]> newMap = new HashMap<Address, byte[]>();
|
||||||
|
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<String> bytesFound = new ArrayList<String>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue