mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GP-210 - Added new 'Copy Special' items to make copying from Ghidra to
Python and C++ easier
This commit is contained in:
parent
00ef824dbd
commit
ccedc6627a
15 changed files with 1024 additions and 416 deletions
|
@ -58,7 +58,6 @@ public class ByteViewerClipboardProvider extends ByteCopier
|
|||
PluginTool tool) {
|
||||
this.provider = provider;
|
||||
this.tool = tool;
|
||||
currentProgram = provider.getProgram();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -80,11 +79,6 @@ public class ByteViewerClipboardProvider extends ByteCopier
|
|||
|
||||
@Override
|
||||
public boolean paste(Transferable pasteData) {
|
||||
if (!supportsPasteTransferable(pasteData)) {
|
||||
tool.setStatusInfo("Paste failed: No valid data on clipboard", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// try the default paste
|
||||
return pasteBytes(pasteData);
|
||||
|
@ -106,42 +100,17 @@ public class ByteViewerClipboardProvider extends ByteCopier
|
|||
@Override
|
||||
public Transferable copy(TaskMonitor monitor) {
|
||||
String byteString = copyBytesAsString(currentSelection, true, monitor);
|
||||
String textSelection = provider.getCurrentTextSelection();
|
||||
return new ByteViewerTransferable(byteString, textSelection);
|
||||
String textSelection = getTextSelection();
|
||||
return new ByteStringTransferable(byteString, textSelection);
|
||||
}
|
||||
|
||||
protected String getTextSelection() {
|
||||
return provider.getCurrentTextSelection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transferable copySpecial(ClipboardType copyType, TaskMonitor monitor) {
|
||||
|
||||
String byteString = null;
|
||||
if (copyType == BYTE_STRING_TYPE) {
|
||||
byteString = copyBytesAsString(currentSelection, true, monitor);
|
||||
}
|
||||
else if (copyType == BYTE_STRING_NO_SPACE_TYPE) {
|
||||
byteString = copyBytesAsString(currentSelection, false, monitor);
|
||||
}
|
||||
else if (copyType == PYTHON_BYTE_STRING_TYPE) {
|
||||
byteString = "b'"
|
||||
+ copyBytesAsString(currentSelection, true, monitor).replaceAll(" ", "\\x") + "'";
|
||||
}
|
||||
else if (copyType == PYTHON_LIST_TYPE) {
|
||||
byteString = "[ "
|
||||
+ copyBytesAsString(currentSelection, true, monitor).replaceAll(" ", ", 0x") + " ]";
|
||||
}
|
||||
else if (copyType == CPP_BYTE_ARRAY_TYPE) {
|
||||
byteString = "{ "
|
||||
+ copyBytesAsString(currentSelection, true, monitor).replaceAll(" ", ", 0x") + " }";
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ByteViewerTransferable(byteString);
|
||||
}
|
||||
|
||||
void setSelection(ProgramSelection selection) {
|
||||
currentSelection = selection;
|
||||
updateEnablement();
|
||||
return copyBytes(copyType, monitor);
|
||||
}
|
||||
|
||||
private void updateEnablement() {
|
||||
|
@ -153,6 +122,11 @@ public class ByteViewerClipboardProvider extends ByteCopier
|
|||
currentLocation = location;
|
||||
}
|
||||
|
||||
void setSelection(ProgramSelection selection) {
|
||||
currentSelection = selection;
|
||||
updateEnablement();
|
||||
}
|
||||
|
||||
void setProgram(Program p) {
|
||||
currentProgram = p;
|
||||
currentLocation = null;
|
||||
|
|
|
@ -0,0 +1,239 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
package ghidra.app.plugin.core.byteviewer;
|
||||
|
||||
import static org.hamcrest.core.IsInstanceOf.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.util.ByteCopier;
|
||||
import ghidra.app.util.ByteCopier.ByteStringTransferable;
|
||||
import ghidra.app.util.ByteCopier.ProgrammingByteStringTransferable;
|
||||
import ghidra.app.util.ClipboardType;
|
||||
import ghidra.framework.cmd.Command;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.DummyTool;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class ByteViewerClipboardProviderTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
private Program program;
|
||||
private ByteViewerClipboardProvider clipboardProvider;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
program = createProgram();
|
||||
PluginTool tool = new DummyTool() {
|
||||
@Override
|
||||
public boolean execute(Command command, DomainObject obj) {
|
||||
boolean result = command.applyTo(obj);
|
||||
if (!result) {
|
||||
throw new AssertException("Failed to write bytes");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
clipboardProvider = new ByteViewerClipboardProvider(null, tool) {
|
||||
|
||||
// overridden to stub this method, since we don't have a real provider
|
||||
@Override
|
||||
protected String getTextSelection() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
clipboardProvider.setProgram(program);
|
||||
}
|
||||
|
||||
private Program createProgram() throws Exception {
|
||||
ProgramBuilder builder = new ProgramBuilder("default", ProgramBuilder._TOY, this);
|
||||
|
||||
builder.createMemory("test", "0x01001050", 20000);
|
||||
|
||||
builder.setBytes("0x01001050",
|
||||
"0e 5e f4 77 33 58 f4 77 91 45 f4 77 88 7c f4 77 8d 70 f5 77 05 62 f4 77 f0 a3 " +
|
||||
"f4 77 09 56 f4 77 10 17 f4 77 f7 29 f6 77 02 59 f4 77");
|
||||
|
||||
builder.setBytes("0x01002050", "00 00 00 00 00 00 00 00 00 00 00 00 00");
|
||||
|
||||
builder.createMemoryReference("0x01002cc0", "0x01002cf0", RefType.DATA,
|
||||
SourceType.USER_DEFINED);
|
||||
builder.createMemoryReference("0x01002d04", "0x01002d0f", RefType.DATA,
|
||||
SourceType.USER_DEFINED);
|
||||
|
||||
DataType dt = DataType.DEFAULT;
|
||||
Parameter p = new ParameterImpl(null, dt, builder.getProgram());
|
||||
builder.createEmptyFunction("ghidra", "0x01002cf5", 1, dt, p);
|
||||
builder.createEmptyFunction("sscanf", "0x0100415a", 1, dt, p);
|
||||
|
||||
builder.setBytes("0x0100418c", "ff 15 08 10 00 01");
|
||||
builder.disassemble("0x0100418c", 6);
|
||||
|
||||
return builder.getProgram();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyPasteSpecial_ByteString() throws Exception {
|
||||
int length = 4;
|
||||
clipboardProvider.setSelection(selection("0x01001050", length));
|
||||
ClipboardType type = ByteCopier.BYTE_STRING_TYPE;
|
||||
Transferable transferable = clipboardProvider.copySpecial(type, TaskMonitor.DUMMY);
|
||||
assertThat(transferable, instanceOf(ByteStringTransferable.class));
|
||||
|
||||
String byteString = (String) transferable.getTransferData(DataFlavor.stringFlavor);
|
||||
assertEquals("0e 5e f4 77", byteString);
|
||||
|
||||
String pasteAddress = "0x01002050";
|
||||
paste(pasteAddress, transferable);
|
||||
assertBytesAt(pasteAddress, "0e 5e f4 77", length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyPasteSpecial_ByteStringNoSpaces() throws Exception {
|
||||
int length = 4;
|
||||
clipboardProvider.setSelection(selection("0x01001050", length));
|
||||
ClipboardType type = ByteCopier.BYTE_STRING_NO_SPACE_TYPE;
|
||||
Transferable transferable = clipboardProvider.copySpecial(type, TaskMonitor.DUMMY);
|
||||
assertThat(transferable, instanceOf(ByteStringTransferable.class));
|
||||
|
||||
String byteString = (String) transferable.getTransferData(DataFlavor.stringFlavor);
|
||||
assertEquals("0e5ef477", byteString);
|
||||
|
||||
String pasteAddress = "0x01002050";
|
||||
paste(pasteAddress, transferable);
|
||||
assertBytesAt(pasteAddress, "0e 5e f4 77", length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyPasteSpecial_PythonByteString() throws Exception {
|
||||
|
||||
int length = 4;
|
||||
clipboardProvider.setSelection(selection("0x01001050", length));
|
||||
ClipboardType type = ByteCopier.PYTHON_BYTE_STRING_TYPE;
|
||||
Transferable transferable = clipboardProvider.copySpecial(type, TaskMonitor.DUMMY);
|
||||
assertThat(transferable, instanceOf(ProgrammingByteStringTransferable.class));
|
||||
|
||||
String byteString = (String) transferable.getTransferData(DataFlavor.stringFlavor);
|
||||
assertEquals("b'\\x0e\\x5e\\xf4\\x77'", byteString);
|
||||
|
||||
String pasteAddress = "0x01002050";
|
||||
paste(pasteAddress, transferable);
|
||||
assertBytesAt(pasteAddress, "0e 5e f4 77", length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyPasteSpecial_PythonListString() throws Exception {
|
||||
|
||||
int length = 4;
|
||||
clipboardProvider.setSelection(selection("0x01001050", 4));
|
||||
ClipboardType type = ByteCopier.PYTHON_LIST_TYPE;
|
||||
Transferable transferable = clipboardProvider.copySpecial(type, TaskMonitor.DUMMY);
|
||||
assertThat(transferable, instanceOf(ProgrammingByteStringTransferable.class));
|
||||
|
||||
String byteString = (String) transferable.getTransferData(DataFlavor.stringFlavor);
|
||||
assertEquals("[ 0x0e, 0x5e, 0xf4, 0x77 ]", byteString);
|
||||
|
||||
String pasteAddress = "0x01002050";
|
||||
paste(pasteAddress, transferable);
|
||||
assertBytesAt(pasteAddress, "0e 5e f4 77", length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyPasteSpecial_CppByteArray() throws Exception {
|
||||
|
||||
int length = 4;
|
||||
clipboardProvider.setSelection(selection("0x01001050", 4));
|
||||
ClipboardType type = ByteCopier.CPP_BYTE_ARRAY_TYPE;
|
||||
Transferable transferable = clipboardProvider.copySpecial(type, TaskMonitor.DUMMY);
|
||||
assertThat(transferable, instanceOf(ProgrammingByteStringTransferable.class));
|
||||
|
||||
String byteString = (String) transferable.getTransferData(DataFlavor.stringFlavor);
|
||||
assertEquals("{ 0x0e, 0x5e, 0xf4, 0x77 }", byteString);
|
||||
|
||||
String pasteAddress = "0x01002050";
|
||||
paste(pasteAddress, transferable);
|
||||
assertBytesAt(pasteAddress, "0e 5e f4 77", length);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private void paste(String address, Transferable transferable) {
|
||||
|
||||
tx(program, () -> {
|
||||
doPaste(address, transferable);
|
||||
});
|
||||
}
|
||||
|
||||
private void doPaste(String address, Transferable transferable) {
|
||||
clipboardProvider.setLocation(location(address));
|
||||
runSwing(() -> clipboardProvider.paste(transferable), false);
|
||||
|
||||
OptionDialog confirmDialog = waitForDialogComponent(OptionDialog.class);
|
||||
pressButtonByText(confirmDialog, "Yes");
|
||||
|
||||
waitForTasks();
|
||||
program.flushEvents();
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private void assertBytesAt(String address, String bytes, int length)
|
||||
throws MemoryAccessException {
|
||||
Memory memory = program.getMemory();
|
||||
byte[] memoryBytes = new byte[length];
|
||||
memory.getBytes(addr(address), memoryBytes, 0, length);
|
||||
|
||||
String memoryByteString = NumericUtilities.convertBytesToString(memoryBytes, " ");
|
||||
assertEquals(bytes, memoryByteString);
|
||||
}
|
||||
|
||||
private ProgramSelection selection(String addressString, int n) {
|
||||
Address address = addr(addressString);
|
||||
AddressSetView addresses = new AddressSet(address, address.add(n - 1));
|
||||
return new ProgramSelection(addresses);
|
||||
}
|
||||
|
||||
private Address addr(String addr) {
|
||||
return program.getAddressFactory().getAddress(addr);
|
||||
}
|
||||
|
||||
private ProgramLocation location(String addressString) {
|
||||
return new ProgramLocation(program, addr(addressString));
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue