mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
Merge remote-tracking branch 'origin/Ghidra_11.4'
This commit is contained in:
commit
b22ccaa260
12 changed files with 467 additions and 92 deletions
|
@ -16,6 +16,7 @@
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
|
import ghidra.features.base.values.GhidraValuesMap;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
|
@ -23,6 +24,8 @@ import ghidra.program.model.listing.Program;
|
||||||
//sets a property on the current program which can be used as
|
//sets a property on the current program which can be used as
|
||||||
//an executable category in BSim
|
//an executable category in BSim
|
||||||
public class SetExecutableCategoryScript extends GhidraScript {
|
public class SetExecutableCategoryScript extends GhidraScript {
|
||||||
|
private static final String PROPERTY_NAME = "Property Name";
|
||||||
|
private static final String PROPERTY_VALUE = "Property Value";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() throws Exception {
|
protected void run() throws Exception {
|
||||||
|
@ -30,16 +33,27 @@ public class SetExecutableCategoryScript extends GhidraScript {
|
||||||
popup("This script requires a program");
|
popup("This script requires a program");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Options opts = currentProgram.getOptions(Program.PROGRAM_INFO);
|
GhidraValuesMap valuesMap = new GhidraValuesMap();
|
||||||
String name = askString("Enter Property Name", "Name");
|
valuesMap.defineString(PROPERTY_NAME);
|
||||||
|
valuesMap.defineString(PROPERTY_VALUE);
|
||||||
|
|
||||||
|
valuesMap.setValidator((values, status) -> {
|
||||||
|
String name = valuesMap.getString(PROPERTY_NAME);
|
||||||
if (StringUtils.isAllBlank(name)) {
|
if (StringUtils.isAllBlank(name)) {
|
||||||
return;
|
status.setStatusText("Name cannot be blank");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
String value = askString("Enter Value of Property " + name, "Value");
|
String value = valuesMap.getString(PROPERTY_VALUE);
|
||||||
if (StringUtils.isAllBlank(value)) {
|
if (StringUtils.isAllBlank(value)) {
|
||||||
return;
|
status.setStatusText("Value cannot be blank");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
opts.setString(name, value);
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
askValues("Set Program Property", "Set Program Property", valuesMap);
|
||||||
|
Options opts = currentProgram.getOptions(Program.PROGRAM_INFO);
|
||||||
|
opts.setString(valuesMap.getString(PROPERTY_NAME), valuesMap.getString(PROPERTY_VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,7 @@ import ghidra.app.script.GhidraScript;
|
||||||
import ghidra.program.model.data.StringDataInstance;
|
import ghidra.program.model.data.StringDataInstance;
|
||||||
import ghidra.program.model.data.TranslationSettingsDefinition;
|
import ghidra.program.model.data.TranslationSettingsDefinition;
|
||||||
import ghidra.program.model.listing.Data;
|
import ghidra.program.model.listing.Data;
|
||||||
import ghidra.program.util.DefinedDataIterator;
|
import ghidra.program.util.DefinedStringIterator;
|
||||||
import util.CollectionUtils;
|
|
||||||
|
|
||||||
public class TranslateStringsScript extends GhidraScript {
|
public class TranslateStringsScript extends GhidraScript {
|
||||||
|
|
||||||
|
@ -40,8 +39,7 @@ public class TranslateStringsScript extends GhidraScript {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
monitor.initialize(currentProgram.getListing().getNumDefinedData());
|
monitor.initialize(currentProgram.getListing().getNumDefinedData());
|
||||||
monitor.setMessage("Translating strings");
|
monitor.setMessage("Translating strings");
|
||||||
for (Data data : CollectionUtils.asIterable(
|
for (Data data : DefinedStringIterator.forProgram(currentProgram, currentSelection)) {
|
||||||
DefinedDataIterator.definedStrings(currentProgram, currentSelection))) {
|
|
||||||
if (monitor.isCancelled()) {
|
if (monitor.isCancelled()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,11 @@ import ghidra.app.services.*;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.DataUtilities;
|
||||||
|
import ghidra.program.model.data.StringDataType;
|
||||||
import ghidra.program.model.listing.Data;
|
import ghidra.program.model.listing.Data;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.util.DefinedDataIterator;
|
import ghidra.program.util.DefinedStringIterator;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
@ -51,9 +52,7 @@ public class RustStringAnalyzer extends AbstractAnalyzer {
|
||||||
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
|
|
||||||
DefinedDataIterator dataIterator = DefinedDataIterator.definedStrings(program);
|
for (Data data : DefinedStringIterator.forProgram(program)) {
|
||||||
|
|
||||||
for (Data data : dataIterator) {
|
|
||||||
Address start = data.getAddress();
|
Address start = data.getAddress();
|
||||||
int length = data.getLength();
|
int length = data.getLength();
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class CombinedStringSearcher {
|
public class CombinedStringSearcher {
|
||||||
|
|
||||||
private DefinedStringIterator definedStringIterator;
|
private FoundDefinedStringIterator definedStringIterator;
|
||||||
private FoundString nextDefinedString;
|
private FoundString nextDefinedString;
|
||||||
private Program program;
|
private Program program;
|
||||||
private StringTableOptions options;
|
private StringTableOptions options;
|
||||||
|
@ -44,7 +44,7 @@ public class CombinedStringSearcher {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.accumulator = accumulator;
|
this.accumulator = accumulator;
|
||||||
definedStringIterator =
|
definedStringIterator =
|
||||||
new DefinedStringIterator(program, options.getWordModelInitialized());
|
new FoundDefinedStringIterator(program, options.getWordModelInitialized());
|
||||||
}
|
}
|
||||||
|
|
||||||
private FoundString findNextDefinedString() {
|
private FoundString findNextDefinedString() {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import static ghidra.program.util.string.FoundString.DefinedState.*;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.util.DefinedDataIterator;
|
import ghidra.program.util.DefinedStringIterator;
|
||||||
import ghidra.program.util.string.FoundString;
|
import ghidra.program.util.string.FoundString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,16 +42,16 @@ import ghidra.program.util.string.FoundString;
|
||||||
* <p>
|
* <p>
|
||||||
* The iterator is over when the resultQueue is empty and the defined data iterator's hasNext() method is false.
|
* The iterator is over when the resultQueue is empty and the defined data iterator's hasNext() method is false.
|
||||||
*/
|
*/
|
||||||
public class DefinedStringIterator implements Iterator<FoundString> {
|
public class FoundDefinedStringIterator implements Iterator<FoundString> {
|
||||||
|
|
||||||
private boolean isWordModelInitialized;
|
private boolean isWordModelInitialized;
|
||||||
private DataIterator stringDataIterator;
|
private DataIterator stringDataIterator;
|
||||||
private Program program;
|
private Program program;
|
||||||
|
|
||||||
DefinedStringIterator(Program program, boolean isWordModelInitialized) {
|
FoundDefinedStringIterator(Program program, boolean isWordModelInitialized) {
|
||||||
this.program = program;
|
this.program = program;
|
||||||
this.isWordModelInitialized = isWordModelInitialized;
|
this.isWordModelInitialized = isWordModelInitialized;
|
||||||
this.stringDataIterator = DefinedDataIterator.definedStrings(program);
|
this.stringDataIterator = DefinedStringIterator.forProgram(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -122,7 +122,7 @@ class ViewStringsTableModel extends AddressBasedTableModel<ProgramLocation> {
|
||||||
monitor.setCancelEnabled(true);
|
monitor.setCancelEnabled(true);
|
||||||
monitor.initialize(listing.getNumDefinedData());
|
monitor.initialize(listing.getNumDefinedData());
|
||||||
Swing.allowSwingToProcessEvents();
|
Swing.allowSwingToProcessEvents();
|
||||||
for (Data stringInstance : DefinedDataIterator.definedStrings(localProgram)) {
|
for (Data stringInstance : DefinedStringIterator.forProgram(localProgram)) {
|
||||||
accumulator.add(createIndexedStringInstanceLocation(localProgram, stringInstance));
|
accumulator.add(createIndexedStringInstanceLocation(localProgram, stringInstance));
|
||||||
monitor.increment();
|
monitor.increment();
|
||||||
}
|
}
|
||||||
|
@ -148,7 +148,7 @@ class ViewStringsTableModel extends AddressBasedTableModel<ProgramLocation> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDataInstance(Program localProgram, Data data) {
|
public void addDataInstance(Program localProgram, Data data) {
|
||||||
for (Data stringInstance : DefinedDataIterator.definedStrings(data)) {
|
for (Data stringInstance : DefinedStringIterator.forDataInstance(data)) {
|
||||||
addObject(createIndexedStringInstanceLocation(localProgram, stringInstance));
|
addObject(createIndexedStringInstanceLocation(localProgram, stringInstance));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,12 +30,12 @@ import ghidra.program.util.string.FoundString;
|
||||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||||
import ghidra.test.ToyProgramBuilder;
|
import ghidra.test.ToyProgramBuilder;
|
||||||
|
|
||||||
public class DefinedStringIteratorTest extends AbstractGhidraHeadlessIntegrationTest {
|
public class FoundDefinedStringIteratorTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||||
|
|
||||||
private ProgramDB program;
|
private ProgramDB program;
|
||||||
private ArrayDataType arrayDataType;
|
private ArrayDataType arrayDataType;
|
||||||
|
|
||||||
public DefinedStringIteratorTest() {
|
public FoundDefinedStringIteratorTest() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ public class DefinedStringIteratorTest extends AbstractGhidraHeadlessIntegration
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIterator() throws Exception {
|
public void testIterator() throws Exception {
|
||||||
DefinedStringIterator iterator = new DefinedStringIterator(program, false);
|
FoundDefinedStringIterator iterator = new FoundDefinedStringIterator(program, false);
|
||||||
|
|
||||||
assertTrue(iterator.hasNext());
|
assertTrue(iterator.hasNext());
|
||||||
FoundString foundString = iterator.next();
|
FoundString foundString = iterator.next();
|
||||||
|
@ -143,7 +143,7 @@ public class DefinedStringIteratorTest extends AbstractGhidraHeadlessIntegration
|
||||||
|
|
||||||
initializeStringModel();
|
initializeStringModel();
|
||||||
|
|
||||||
DefinedStringIterator iterator = new DefinedStringIterator(program, true);
|
FoundDefinedStringIterator iterator = new FoundDefinedStringIterator(program, true);
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
FoundString string = iterator.next();
|
FoundString string = iterator.next();
|
||||||
Address address = string.getAddress();
|
Address address = string.getAddress();
|
|
@ -20,6 +20,7 @@ import static org.junit.Assert.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.IteratorUtils;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -95,7 +96,7 @@ public class DefinedDataIteratorTest extends AbstractGhidraHeadlessIntegrationTe
|
||||||
builder.applyFixedLengthDataType("0x100", struct1DT, struct1DT.getLength());
|
builder.applyFixedLengthDataType("0x100", struct1DT, struct1DT.getLength());
|
||||||
|
|
||||||
List<Data> list =
|
List<Data> list =
|
||||||
CollectionUtils.asList((Iterable<Data>) DefinedDataIterator.definedStrings(program));
|
CollectionUtils.asList((Iterable<Data>) DefinedStringIterator.forProgram(program));
|
||||||
|
|
||||||
assertTrue(list.get(0).getAddress().getOffset() == 0x10);
|
assertTrue(list.get(0).getAddress().getOffset() == 0x10);
|
||||||
assertTrue(list.get(1).getAddress().getOffset() == 0x100 + 10);
|
assertTrue(list.get(1).getAddress().getOffset() == 0x100 + 10);
|
||||||
|
@ -114,8 +115,7 @@ public class DefinedDataIteratorTest extends AbstractGhidraHeadlessIntegrationTe
|
||||||
int lastEle = numElements - 1;
|
int lastEle = numElements - 1;
|
||||||
int elementSize = structArray.getElementLength();
|
int elementSize = structArray.getElementLength();
|
||||||
|
|
||||||
List<Data> list =
|
List<Data> list = IteratorUtils.toList(DefinedStringIterator.forProgram(program));
|
||||||
CollectionUtils.asList((Iterable<Data>) DefinedDataIterator.definedStrings(program));
|
|
||||||
|
|
||||||
assertEquals(list.get(0).getAddress().getOffset(), 0x10);
|
assertEquals(list.get(0).getAddress().getOffset(), 0x10);
|
||||||
assertEquals(list.get(1 + 0).getAddress().getOffset(), 0x100 + 10);
|
assertEquals(list.get(1 + 0).getAddress().getOffset(), 0x100 + 10);
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
/* ###
|
||||||
|
* 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.program.util;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.IteratorUtils;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ghidra.program.database.ProgramDB;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.program.model.listing.Data;
|
||||||
|
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||||
|
import ghidra.test.ToyProgramBuilder;
|
||||||
|
|
||||||
|
public class DefinedStringIteratorTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||||
|
|
||||||
|
private ToyProgramBuilder builder;
|
||||||
|
private ProgramDB program;
|
||||||
|
private DataTypeManager dtm;
|
||||||
|
private DataType intDT;
|
||||||
|
private StringDataType stringDT;
|
||||||
|
private CharDataType charDT;
|
||||||
|
private DataType charArray;
|
||||||
|
private StructureDataType struct1DT;
|
||||||
|
private int s1f2Offset = 10;
|
||||||
|
private int s1f3Offset = 50;
|
||||||
|
private ArrayDataType structArray;
|
||||||
|
private StructureDataType struct2DT;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
|
||||||
|
builder = new ToyProgramBuilder("DefinedStringIteratorTests", false);
|
||||||
|
program = builder.getProgram();
|
||||||
|
dtm = program.getDataTypeManager();
|
||||||
|
|
||||||
|
intDT = AbstractIntegerDataType.getSignedDataType(4, dtm);
|
||||||
|
stringDT = StringDataType.dataType;
|
||||||
|
charDT = new CharDataType(dtm);
|
||||||
|
charArray = new ArrayDataType(charDT, 20);
|
||||||
|
|
||||||
|
struct1DT = new StructureDataType("struct1", 100);
|
||||||
|
struct1DT.replaceAtOffset(0, intDT, -1, "f1", null);
|
||||||
|
struct1DT.replaceAtOffset(s1f2Offset, charArray, -1, "f2", null);
|
||||||
|
struct1DT.replaceAtOffset(s1f3Offset, stringDT, 10, "f3", null);
|
||||||
|
|
||||||
|
structArray = new ArrayDataType(struct1DT, 10, -1);
|
||||||
|
|
||||||
|
struct2DT = new StructureDataType("struct2", 200);
|
||||||
|
struct2DT.replaceAtOffset(0, intDT, -1, "f1", null);
|
||||||
|
struct2DT.replaceAtOffset(10, struct1DT, -1, "f2", null);
|
||||||
|
|
||||||
|
builder.createMemory("test", "0x0", 0x20000);
|
||||||
|
program = builder.getProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String addrStr(long offset) {
|
||||||
|
return builder.getAddress(offset).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Strings() throws Exception {
|
||||||
|
int str1Addr = 0x10;
|
||||||
|
int struct1Addr = 0x100;
|
||||||
|
|
||||||
|
builder.applyFixedLengthDataType("0x0", intDT, -1);
|
||||||
|
builder.createString(addrStr(str1Addr), "test1", StandardCharsets.UTF_8, true, stringDT);
|
||||||
|
builder.applyFixedLengthDataType(addrStr(struct1Addr), struct1DT, -1);
|
||||||
|
|
||||||
|
List<Data> list = IteratorUtils.toList(DefinedStringIterator.forProgram(program));
|
||||||
|
|
||||||
|
assertEquals(str1Addr, list.get(0).getAddress().getOffset());
|
||||||
|
assertEquals(struct1Addr + s1f2Offset, list.get(1).getAddress().getOffset());
|
||||||
|
assertEquals(struct1Addr + s1f3Offset, list.get(2).getAddress().getOffset());
|
||||||
|
|
||||||
|
assertEquals(3, list.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_AllowIteratorNextWithoutHasNextIck() throws Exception {
|
||||||
|
int str1Addr = 0x10;
|
||||||
|
int struct1Addr = 0x100;
|
||||||
|
|
||||||
|
builder.applyFixedLengthDataType("0x0", intDT, -1);
|
||||||
|
builder.createString(addrStr(str1Addr), "test1", StandardCharsets.UTF_8, true, stringDT);
|
||||||
|
builder.applyFixedLengthDataType(addrStr(struct1Addr), struct1DT, -1);
|
||||||
|
|
||||||
|
DefinedStringIterator it = DefinedStringIterator.forProgram(program);
|
||||||
|
|
||||||
|
assertEquals(str1Addr, it.next().getAddress().getOffset());
|
||||||
|
assertEquals(struct1Addr + s1f2Offset, it.next().getAddress().getOffset());
|
||||||
|
assertEquals(struct1Addr + s1f3Offset, it.next().getAddress().getOffset());
|
||||||
|
|
||||||
|
assertFalse(it.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_ArrayOfStructs() throws Exception {
|
||||||
|
int str1Addr = 0x10;
|
||||||
|
int s1ArrayAddr = 0x100;
|
||||||
|
|
||||||
|
builder.applyFixedLengthDataType("0x0", intDT, -1);
|
||||||
|
builder.createString(addrStr(str1Addr), "test1", StandardCharsets.UTF_8, true, stringDT);
|
||||||
|
builder.applyFixedLengthDataType(addrStr(s1ArrayAddr), structArray, -1);
|
||||||
|
|
||||||
|
int numElements = structArray.getNumElements();
|
||||||
|
int lastEle = numElements - 1;
|
||||||
|
int elementSize = structArray.getElementLength();
|
||||||
|
|
||||||
|
List<Data> list = IteratorUtils.toList(DefinedStringIterator.forProgram(program));
|
||||||
|
|
||||||
|
assertEquals(str1Addr, list.get(0).getAddress().getOffset());
|
||||||
|
|
||||||
|
int s1ArrayElemIndex = 1;
|
||||||
|
assertEquals(arrayElementAddr(s1ArrayAddr, elementSize, 0) + s1f2Offset,
|
||||||
|
list.get(s1ArrayElemIndex + 0).getAddress().getOffset());
|
||||||
|
assertEquals(arrayElementAddr(s1ArrayAddr, elementSize, 0) + s1f3Offset,
|
||||||
|
list.get(s1ArrayElemIndex + 1).getAddress().getOffset());
|
||||||
|
|
||||||
|
s1ArrayElemIndex = s1ArrayElemIndex + (lastEle * 2 /* 2 strs per struct */);
|
||||||
|
assertEquals(arrayElementAddr(s1ArrayAddr, elementSize, lastEle) + s1f2Offset,
|
||||||
|
list.get(s1ArrayElemIndex + 0).getAddress().getOffset());
|
||||||
|
assertEquals(arrayElementAddr(s1ArrayAddr, elementSize, lastEle) + s1f3Offset,
|
||||||
|
list.get(s1ArrayElemIndex + 1).getAddress().getOffset());
|
||||||
|
|
||||||
|
assertEquals(s1ArrayElemIndex + 2, list.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private long arrayElementAddr(long arrayAddr, int elemSize, int elemIndex) {
|
||||||
|
return arrayAddr + (elemSize * elemIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_DontLookAtArrayElements() throws Exception {
|
||||||
|
int str1Addr = 0x10;
|
||||||
|
int s1ArrayAddr = 0x100;
|
||||||
|
|
||||||
|
builder.applyFixedLengthDataType("0x0", intDT, -1); // +1 candidate count
|
||||||
|
builder.createString(addrStr(str1Addr), "test1", StandardCharsets.UTF_8, true, stringDT); // +1
|
||||||
|
builder.applyFixedLengthDataType(addrStr(s1ArrayAddr), structArray, -1); // +(1 + 10*3)
|
||||||
|
|
||||||
|
DataType byteArray = new ArrayDataType(ByteDataType.dataType, 2000, -1);
|
||||||
|
builder.applyFixedLengthDataType("0x1000", byteArray, -1); // +1
|
||||||
|
|
||||||
|
DefinedStringIterator it = DefinedStringIterator.forProgram(program);
|
||||||
|
List<Data> list = IteratorUtils.toList(it);
|
||||||
|
|
||||||
|
assertEquals(34, it.getDataCandidateCount()); // 1 + 1 + 1 + 10*3
|
||||||
|
assertEquals(21, list.size()); // 1 ds@0x10 + 2 per structArray element
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.pcode.HighFunctionDBUtil;
|
import ghidra.program.model.pcode.HighFunctionDBUtil;
|
||||||
import ghidra.program.model.pcode.PcodeOpAST;
|
import ghidra.program.model.pcode.PcodeOpAST;
|
||||||
import ghidra.program.util.DefinedDataIterator;
|
import ghidra.program.util.DefinedStringIterator;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.exception.InvalidInputException;
|
import ghidra.util.exception.InvalidInputException;
|
||||||
|
@ -101,9 +101,8 @@ public class FormatStringAnalyzer extends AbstractAnalyzer {
|
||||||
|
|
||||||
private void run(AddressSetView selection, TaskMonitor monitor) throws CancelledException {
|
private void run(AddressSetView selection, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
DefinedDataIterator dataIterator = DefinedDataIterator.definedStrings(currentProgram);
|
|
||||||
Map<Address, Data> stringsByAddress = new HashMap<>();
|
Map<Address, Data> stringsByAddress = new HashMap<>();
|
||||||
for (Data data : dataIterator) {
|
for (Data data : DefinedStringIterator.forProgram(currentProgram)) {
|
||||||
String s = data.getDefaultValueRepresentation();
|
String s = data.getDefaultValueRepresentation();
|
||||||
if (s.contains("%")) {
|
if (s.contains("%")) {
|
||||||
stringsByAddress.put(data.getAddress(), data);
|
stringsByAddress.put(data.getAddress(), data);
|
||||||
|
|
|
@ -28,6 +28,8 @@ import ghidra.program.model.listing.*;
|
||||||
* <p>
|
* <p>
|
||||||
* Data elements that are nested inside of composites or arrays are visited, not just the
|
* Data elements that are nested inside of composites or arrays are visited, not just the
|
||||||
* parent/containing data element.
|
* parent/containing data element.
|
||||||
|
* <p>
|
||||||
|
* Not thread safe.
|
||||||
*/
|
*/
|
||||||
public class DefinedDataIterator implements DataIterator {
|
public class DefinedDataIterator implements DataIterator {
|
||||||
|
|
||||||
|
@ -71,46 +73,6 @@ public class DefinedDataIterator implements DataIterator {
|
||||||
return new DefinedDataIterator(program, null, null, dataInstancePredicate);
|
return new DefinedDataIterator(program, null, null, dataInstancePredicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new iterator that traverses the entire Program's address space returning
|
|
||||||
* data instances that are strings.
|
|
||||||
*
|
|
||||||
* @param program Ghidra {@link Program} to search
|
|
||||||
* @return new iterator
|
|
||||||
*/
|
|
||||||
public static DefinedDataIterator definedStrings(Program program) {
|
|
||||||
return new DefinedDataIterator(program, null,
|
|
||||||
dataType -> StringDataInstance.isStringDataType(dataType),
|
|
||||||
data -> StringDataInstance.isString(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new iterator that traverses a portion of the Program's address space returning
|
|
||||||
* data instances that are strings.
|
|
||||||
*
|
|
||||||
* @param program Ghidra {@link Program} to search
|
|
||||||
* @param addrs addresses to limit the iteration to
|
|
||||||
* @return new iterator
|
|
||||||
*/
|
|
||||||
public static DefinedDataIterator definedStrings(Program program, AddressSetView addrs) {
|
|
||||||
return new DefinedDataIterator(program, addrs,
|
|
||||||
dataType -> StringDataInstance.isStringDataType(dataType),
|
|
||||||
data -> StringDataInstance.isString(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new iterator that traverses the address space of a single data item (ie. a
|
|
||||||
* composite or array data instance that needs to be recursed into).
|
|
||||||
*
|
|
||||||
* @param singleDataInstance Data instance
|
|
||||||
* @return new iterator
|
|
||||||
*/
|
|
||||||
public static DefinedDataIterator definedStrings(Data singleDataInstance) {
|
|
||||||
return new DefinedDataIterator(singleDataInstance,
|
|
||||||
dataType -> StringDataInstance.isStringDataType(dataType),
|
|
||||||
data -> StringDataInstance.isString(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Predicate<DataType> dataTypePredicate;
|
private Predicate<DataType> dataTypePredicate;
|
||||||
private Predicate<Data> dataInstancePredicate;
|
private Predicate<Data> dataInstancePredicate;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,234 @@
|
||||||
|
/* ###
|
||||||
|
* 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.program.util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import ghidra.program.model.address.AddressSetView;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterator that visits each defined string instance in the initialized memory of a Program
|
||||||
|
* or in the footprint of a specified data element.
|
||||||
|
* <p>
|
||||||
|
* Strings that are nested inside of composites or arrays are visited, not just the
|
||||||
|
* parent/containing data element.
|
||||||
|
* <p>
|
||||||
|
* Not thread safe.
|
||||||
|
*/
|
||||||
|
public class DefinedStringIterator implements DataIterator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new iterator that traverses the entire Program's address space returning
|
||||||
|
* data instances that are strings.
|
||||||
|
*
|
||||||
|
* @param program Ghidra {@link Program} to search
|
||||||
|
* @return new iterator
|
||||||
|
*/
|
||||||
|
public static DefinedStringIterator forProgram(Program program) {
|
||||||
|
return new DefinedStringIterator(program, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new iterator that traverses a portion of the Program's address space returning
|
||||||
|
* data instances that are strings.
|
||||||
|
*
|
||||||
|
* @param program Ghidra {@link Program} to search
|
||||||
|
* @param addrs addresses to limit the iteration to
|
||||||
|
* @return new iterator
|
||||||
|
*/
|
||||||
|
public static DefinedStringIterator forProgram(Program program, AddressSetView addrs) {
|
||||||
|
return new DefinedStringIterator(program, addrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new iterator that traverses the address space of a single data item (ie. a
|
||||||
|
* composite or array data instance that needs to be recursed into).
|
||||||
|
*
|
||||||
|
* @param singleDataInstance Data instance
|
||||||
|
* @return new iterator
|
||||||
|
*/
|
||||||
|
public static DefinedStringIterator forDataInstance(Data singleDataInstance) {
|
||||||
|
return new DefinedStringIterator(singleDataInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LIFO stack of iterators. Newly found iterators of sub-components are
|
||||||
|
* pushed onto the end and become the current iterator. When an iterator is exhausted,
|
||||||
|
* it is popped of the end and the uncovered iterator is now the current.
|
||||||
|
*/
|
||||||
|
private Deque<DataIterator> itStack = new ArrayDeque<>();
|
||||||
|
private Data currentDataResult;
|
||||||
|
private int dataCandidateCount; // mostly for tests to ensure we aren't doing unneeded work
|
||||||
|
|
||||||
|
private DefinedStringIterator(Program program, AddressSetView addrs) {
|
||||||
|
|
||||||
|
itStack.addLast(program.getListing()
|
||||||
|
.getDefinedData(
|
||||||
|
(addrs == null) ? program.getMemory().getAllInitializedAddressSet() : addrs,
|
||||||
|
true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private DefinedStringIterator(Data singleDataInstance) {
|
||||||
|
itStack.addLast(DataIterator.of(singleDataInstance));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
updateNextResultIfNeeded();
|
||||||
|
return currentDataResult != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Data next() {
|
||||||
|
updateNextResultIfNeeded();
|
||||||
|
if (currentDataResult == null) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
Data result = currentDataResult;
|
||||||
|
currentDataResult = null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDataCandidateCount() {
|
||||||
|
return dataCandidateCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataIterator currentIt() {
|
||||||
|
DataIterator it = null;
|
||||||
|
while ((it = itStack.peekLast()) != null && !it.hasNext()) {
|
||||||
|
itStack.removeLast();
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateNextResultIfNeeded() {
|
||||||
|
DataIterator it = null;
|
||||||
|
while (currentDataResult == null && (it = currentIt()) != null) {
|
||||||
|
dataCandidateCount++;
|
||||||
|
|
||||||
|
Data data = it.next();
|
||||||
|
DataType dt = data.getBaseDataType();
|
||||||
|
|
||||||
|
if (StringDataInstance.isString(data)) {
|
||||||
|
currentDataResult = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (dt instanceof Array arrayDT) {
|
||||||
|
// Will only get here for arrays of stuff that aren't char/wchar/int, which will
|
||||||
|
// be handled earlier by isString(data)
|
||||||
|
DataType elementDT = arrayDT.getDataType();
|
||||||
|
if (containsStringDataType(elementDT)) {
|
||||||
|
itStack.addLast(new ArrayElementIterator(data));
|
||||||
|
}
|
||||||
|
// side-effect: don't iterate arrays that have elements that aren't strings
|
||||||
|
}
|
||||||
|
else if (dt instanceof Composite comp && containsStringDataType(comp)) {
|
||||||
|
itStack.addLast(new StructDtcIterator(data, comp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsStringDataType(DataType dt) {
|
||||||
|
if (StringDataInstance.isStringDataType(dt)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (dt instanceof Array arrayDT) {
|
||||||
|
DataType elementDT = arrayDT.getDataType();
|
||||||
|
return arrayDT.getNumElements() != 0 && containsStringDataType(elementDT);
|
||||||
|
}
|
||||||
|
else if (dt instanceof Structure struct) {
|
||||||
|
// handle Structures and general Composite's separately so
|
||||||
|
// we can focus on just the defined elements of a structure
|
||||||
|
for (DataTypeComponent dtc : struct.getDefinedComponents()) {
|
||||||
|
if (dtc.getLength() != 0 && containsStringDataType(dtc.getDataType())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (dt instanceof Composite comp) {
|
||||||
|
for (DataTypeComponent dtc : comp.getComponents()) {
|
||||||
|
if (containsStringDataType(dtc.getDataType())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (dt instanceof TypeDef tdDT) {
|
||||||
|
return containsStringDataType(tdDT.getBaseDataType());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class StructDtcIterator implements DataIterator {
|
||||||
|
private Data data;
|
||||||
|
private int currentIndex;
|
||||||
|
private DataTypeComponent[] dtcs;
|
||||||
|
|
||||||
|
public StructDtcIterator(Data data, Composite compDT) {
|
||||||
|
this.data = data;
|
||||||
|
this.dtcs = compDT.getDefinedComponents();
|
||||||
|
advanceToNextGoodDtcIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return currentIndex < dtcs.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void advanceToNextGoodDtcIndex() {
|
||||||
|
currentIndex++;
|
||||||
|
while (currentIndex < dtcs.length &&
|
||||||
|
(dtcs[currentIndex].getLength() == 0 || dtcs[currentIndex].isBitFieldComponent())) {
|
||||||
|
currentIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Data next() {
|
||||||
|
Data result = data.getComponentContaining(dtcs[currentIndex].getOffset());
|
||||||
|
advanceToNextGoodDtcIndex();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ArrayElementIterator implements DataIterator {
|
||||||
|
private Data data;
|
||||||
|
private int currentIndex;
|
||||||
|
private int elementCount;
|
||||||
|
|
||||||
|
public ArrayElementIterator(Data data) {
|
||||||
|
this.data = data;
|
||||||
|
this.elementCount = data.getNumComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return currentIndex < elementCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Data next() {
|
||||||
|
Data result = data.getComponent(currentIndex);
|
||||||
|
currentIndex++;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue