mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
Merge remote-tracking branch 'origin/Ghidra_11.4'
This commit is contained in:
commit
db8e21463b
14 changed files with 321 additions and 217 deletions
|
@ -43,6 +43,9 @@ python3 -m pip install psutil protobuf==3.20.3
|
|||
</LI>
|
||||
</UL>
|
||||
|
||||
<P>If you're using <TT>lldb</TT> from the Android NDK and do not have Pip, see <A href=
|
||||
"#setup_ndk">Setup for Android NDK</A></P>
|
||||
|
||||
<P>If you are offline, or would like to use our provided packages, we still use Pip, but with a
|
||||
more complicated invocation:</P>
|
||||
|
||||
|
@ -205,5 +208,63 @@ perl -i -pe 's/(?<=pendingNMI\x00{4})\x00/\x01/' macOS_15-1234567.vmss
|
|||
<P>This has the same options as the <A href="#ssh">LLDB via SSH</A> launcher, which are
|
||||
necessary for connecting to the Android debugger, but executes via the normal lldb
|
||||
mechanism.</P>
|
||||
|
||||
<H3><A name="setup_ndk"></A>Setup for Android NDK</H3>
|
||||
|
||||
<P>If you're using the copy of <TT>lldb</TT> included with the Android NDK (Native Development
|
||||
Kit), it may not include <TT>pip</TT>. Notably, this is the case on Windows at the time of
|
||||
writing. Fortunately, you can retrieve the components to install Pip into the NDK from an
|
||||
official Python distribution.</P>
|
||||
|
||||
<OL>
|
||||
<LI>
|
||||
First, figure out the version of Python that is embedded in the NDK's build of LLDB, and
|
||||
get its path. (If you know the path to lldb, you probably already know the path to its
|
||||
Python.) From a Windows Command Prompt or Powershell:
|
||||
<PRE>
|
||||
PS> C:\path\to\android-ndk\...\lldb
|
||||
(lldb) script
|
||||
>>> import sys
|
||||
>>> sys.version
|
||||
[copy down the version indicated]
|
||||
>>> sys.path
|
||||
[look for the paths ending with Lib and DLLs, and copy them down]
|
||||
</PRE>
|
||||
</LI>
|
||||
|
||||
<LI>Now, obtain the same version of Python from the official Python website, and install or
|
||||
unpack it.</LI>
|
||||
|
||||
<LI>Locate your new installation of Python. If you don't already know where it landed, this
|
||||
can be found by examining the Properties of the Python shortcut in your Start Menu.</LI>
|
||||
|
||||
<LI>There should be a <TT>Lib\ensurepip</TT> directory in the official Python installation.
|
||||
Copy this into the same place in the Android NDK's build of Python.</LI>
|
||||
|
||||
<LI>
|
||||
There are also three native modules that need to be copied from the official Python's
|
||||
<TT>DLLs\</TT> directory to the same in the NDK's build. This is to support SSL for
|
||||
downloading packages from PyPI: (Substitue the ??'s appropriately.)
|
||||
|
||||
<UL>
|
||||
<LI><TT>_ssl.pyd</TT></LI>
|
||||
|
||||
<LI><TT>libssl-??.dll</TT></LI>
|
||||
|
||||
<LI><TT>libcrypto-??.dll</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
|
||||
<LI>
|
||||
We should now have enough to bootstrap the NDK's Python with Pip. Again at the Windows
|
||||
Command Prompt or Powershell:
|
||||
<PRE>
|
||||
PS> C:\path\to\android-ndk\...\python -m ensurepip
|
||||
PS> C:\path\to\android-ndk\...\python -m pip install ...
|
||||
</PRE>
|
||||
See the <A href="#setup">Setup</A> section for the arguments to pass to <TT>pip install
|
||||
...</TT>.
|
||||
</LI>
|
||||
</OL>
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
|
|
@ -178,7 +178,7 @@ public class DtFilterDialog extends DialogComponentProvider {
|
|||
this.type = type;
|
||||
this.typeCb = new GCheckBox(type);
|
||||
this.typeDefCb = new GCheckBox();
|
||||
this.typeDefCb.setName(type + "Typedefs");
|
||||
this.typeDefCb.setName(type + "TypeDefs");
|
||||
}
|
||||
|
||||
JComponent getLeft() {
|
||||
|
|
|
@ -137,40 +137,38 @@ public class DtFilterState {
|
|||
DataType baseDt = DataTypeUtils.getBaseDataType(dt);
|
||||
|
||||
if (dt instanceof Array) {
|
||||
return passes(arraysFilter, dt, baseDt);
|
||||
return passes(arraysFilter, dt);
|
||||
}
|
||||
|
||||
if (dt instanceof Pointer) {
|
||||
return passes(pointersFilter, dt, baseDt);
|
||||
return passes(pointersFilter, dt);
|
||||
}
|
||||
|
||||
if (baseDt instanceof Enum) {
|
||||
return passes(enumsFilter, dt, baseDt);
|
||||
return passes(enumsFilter, dt);
|
||||
}
|
||||
|
||||
if (baseDt instanceof Function) {
|
||||
return passes(functionsFilter, dt, baseDt);
|
||||
return passes(functionsFilter, dt);
|
||||
}
|
||||
|
||||
if (baseDt instanceof Structure) {
|
||||
return passes(structuresFilter, dt, baseDt);
|
||||
return passes(structuresFilter, dt);
|
||||
}
|
||||
|
||||
if (baseDt instanceof Union) {
|
||||
return passes(unionsFilter, dt, baseDt);
|
||||
return passes(unionsFilter, dt);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean passes(DtTypeFilter filter, DataType dt, DataType baseDt) {
|
||||
if (filter.isTypeActive()) {
|
||||
return true;
|
||||
private boolean passes(DtTypeFilter filter, DataType dt) {
|
||||
if (dt instanceof TypeDef) {
|
||||
return filter.isTypeDefActive();
|
||||
}
|
||||
if (filter.isTypeDefActive() && dt instanceof TypeDef) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return filter.isTypeActive();
|
||||
}
|
||||
|
||||
public void save(SaveState parentSaveState) {
|
||||
|
|
|
@ -65,50 +65,57 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
return loadSpecs;
|
||||
}
|
||||
|
||||
// Efficient check to fail fast
|
||||
byte[] magicBytes = provider.readBytes(0, 4);
|
||||
if (!MachConstants.isMagic(LittleEndianDataConverter.INSTANCE.getInt(magicBytes))) {
|
||||
return loadSpecs;
|
||||
// This loader can handle both Mach-O files as well as Universal Binary files. If it's a
|
||||
// Universal Binary, each Mach-O it contains will be presented as a single "preferred"
|
||||
// load spec, forcing the user to have to select the desired processor from the import
|
||||
// dialog.
|
||||
List<ByteProvider> allProviders = new ArrayList<>();
|
||||
boolean onlyPreferred;
|
||||
if (isUniveralBinary(provider)) {
|
||||
allProviders.addAll(getUniveralBinaryProviders(provider));
|
||||
onlyPreferred = true;
|
||||
}
|
||||
else {
|
||||
allProviders.add(provider);
|
||||
onlyPreferred = false;
|
||||
}
|
||||
|
||||
try {
|
||||
MachHeader machHeader = new MachHeader(provider);
|
||||
String magic =
|
||||
CpuTypes.getMagicString(machHeader.getCpuType(), machHeader.getCpuSubType());
|
||||
String compiler = detectCompilerName(machHeader);
|
||||
List<QueryResult> results = QueryOpinionService.query(MACH_O_NAME, magic, compiler);
|
||||
for (QueryResult result : results) {
|
||||
loadSpecs.add(new LoadSpec(this, machHeader.getImageBase(), result));
|
||||
for (ByteProvider machoProvider : allProviders) {
|
||||
byte[] magicBytes = machoProvider.readBytes(0, 4);
|
||||
if (!MachConstants.isMagic(LittleEndianDataConverter.INSTANCE.getInt(magicBytes))) {
|
||||
continue;
|
||||
}
|
||||
if (loadSpecs.isEmpty()) {
|
||||
loadSpecs.add(new LoadSpec(this, machHeader.getImageBase(), true));
|
||||
try {
|
||||
MachHeader machHeader = new MachHeader(machoProvider);
|
||||
String magic =
|
||||
CpuTypes.getMagicString(machHeader.getCpuType(), machHeader.getCpuSubType());
|
||||
String compiler = detectCompilerName(machHeader);
|
||||
List<QueryResult> results = QueryOpinionService.query(MACH_O_NAME, magic, compiler);
|
||||
for (QueryResult result : results) {
|
||||
if (!onlyPreferred || result.preferred) {
|
||||
loadSpecs.add(new LoadSpec(this, machHeader.getImageBase(), result));
|
||||
}
|
||||
}
|
||||
if (loadSpecs.isEmpty() && !onlyPreferred) {
|
||||
loadSpecs.add(new LoadSpec(this, machHeader.getImageBase(), true));
|
||||
}
|
||||
}
|
||||
catch (MachException e) {
|
||||
// not a problem, just don't add it
|
||||
}
|
||||
}
|
||||
catch (MachException e) {
|
||||
// not a problem, just don't add it
|
||||
}
|
||||
|
||||
return loadSpecs;
|
||||
}
|
||||
|
||||
private String detectCompilerName(MachHeader machHeader) throws IOException {
|
||||
List<String> sectionNames = machHeader.parseSegments()
|
||||
.stream()
|
||||
.flatMap(seg -> seg.getSections().stream())
|
||||
.map(section -> section.getSectionName())
|
||||
.toList();
|
||||
if (SwiftUtils.isSwift(sectionNames)) {
|
||||
return SwiftUtils.SWIFT_COMPILER;
|
||||
}
|
||||
if (GoRttiMapper.hasGolangSections(sectionNames)) {
|
||||
return GoConstants.GOLANG_CSPEC_NAME;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
|
||||
|
||||
if (isUniveralBinary(provider)) {
|
||||
provider = matchUniversalBinaryProvider(provider, loadSpec, monitor);
|
||||
}
|
||||
|
||||
try {
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||
|
||||
|
@ -292,16 +299,6 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if reexports should be performed
|
||||
*
|
||||
* @param options a {@link List} of {@link Option}s
|
||||
* @return True if reexports should be performed; otherwise, false
|
||||
*/
|
||||
private boolean shouldPerformReexports(List<Option> options) {
|
||||
return OptionUtils.getOption(REEXPORT_OPTION_NAME, options, REEXPORT_OPTION_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
|
@ -361,33 +358,6 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link List} of reexport library paths from the given {@link Program}
|
||||
*
|
||||
* @param program The {@link Program}
|
||||
* @param log The log
|
||||
* @return A {@link List} of reexport library paths from the given {@link Program}
|
||||
* @throws MachException if there was a problem parsing the Mach-O {@link Program}
|
||||
* @throws IOException if there was an IO-related error
|
||||
*/
|
||||
private List<String> getReexportPaths(Program program, MessageLog log)
|
||||
throws MachException, IOException {
|
||||
Symbol header =
|
||||
program.getSymbolTable().getSymbols(MachoProgramBuilder.HEADER_SYMBOL).next();
|
||||
if (header == null) {
|
||||
log.appendMsg("Failed to lookup reexport paths...couldn't find '%s' symbol"
|
||||
.formatted(MachoProgramBuilder.HEADER_SYMBOL));
|
||||
return List.of();
|
||||
}
|
||||
ByteProvider p = new MemoryByteProvider(program.getMemory(), header.getAddress());
|
||||
return new MachHeader(p).parseReexports()
|
||||
.stream()
|
||||
.map(DynamicLibraryCommand::getDynamicLibrary)
|
||||
.map(DynamicLibrary::getName)
|
||||
.map(LoadCommandString::getString)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
|
@ -429,6 +399,141 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the given {@link ByteProvider} is a Universal Binary
|
||||
*
|
||||
* @param provider The {@link ByteProvider} to check
|
||||
* @return True if the given {@link ByteProvider} is a Universal Binary; otherwise, false
|
||||
* @throws IOException if there was an IO-related error
|
||||
*/
|
||||
private boolean isUniveralBinary(ByteProvider provider) throws IOException {
|
||||
BinaryReader reader = new BinaryReader(provider, false);
|
||||
int magic = reader.readInt(0);
|
||||
return magic == FatHeader.FAT_MAGIC || magic == FatHeader.FAT_CIGAM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link List} of {@link ByteProviderWrapper}s, one for each entry in the Universal
|
||||
* Binary
|
||||
*
|
||||
* @param provider The Universal Binary's provider
|
||||
* @return A {@link List} of {@link ByteProviderWrapper}s, one for each entry in the Universal
|
||||
* Binary
|
||||
* @throws IOException if an IO-related error occurred
|
||||
*/
|
||||
private List<ByteProviderWrapper> getUniveralBinaryProviders(ByteProvider provider)
|
||||
throws IOException {
|
||||
List<ByteProviderWrapper> wrappers = new ArrayList<>();
|
||||
try {
|
||||
FatHeader fatHeader = new FatHeader(provider);
|
||||
List<Long> machStarts = fatHeader.getMachStarts();
|
||||
List<Long> machSizes = fatHeader.getMachSizes();
|
||||
for (int i = 0; i < machStarts.size(); i++) {
|
||||
wrappers.add(new ByteProviderWrapper(provider, machStarts.get(i), machSizes.get(i),
|
||||
provider.getFSRL()));
|
||||
}
|
||||
}
|
||||
catch (MachException | UbiException e) {
|
||||
// not a problem, just don't add it
|
||||
}
|
||||
return wrappers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to match a Mach-O entry in the given Universal Binary {@link ByteProvider} to the
|
||||
* given {@link LoadSpec}
|
||||
*
|
||||
* @param provider A Universal Binary {@link ByteProvider}
|
||||
* @param loadSpec The {@link LoadSpec} to match
|
||||
* @param monitor A {@link TaskMonitor monitor}
|
||||
* @return The matched Mach-O {@link ByteProvider}, or {@code null} if a match was not found
|
||||
* @throws IOException if an IO-related error occurred
|
||||
*/
|
||||
private ByteProvider matchUniversalBinaryProvider(ByteProvider provider, LoadSpec loadSpec,
|
||||
TaskMonitor monitor) throws IOException {
|
||||
ByteProvider ret = null;
|
||||
boolean stop = false;
|
||||
for (ByteProvider machoProvider : getUniveralBinaryProviders(provider)) {
|
||||
for (LoadSpec ls : findSupportedLoadSpecs(machoProvider)) {
|
||||
if (monitor.isCancelled()) {
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
if (loadSpec.getLanguageCompilerSpec().equals(ls.getLanguageCompilerSpec())) {
|
||||
ret = machoProvider;
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret == null) {
|
||||
throw new IOException("Failed to match the load spec to a Universal Binary Mach-O");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to detect a more specific compiler from the Mach-O
|
||||
*
|
||||
* @param machHeader The {@link MachHeader}
|
||||
* @return The detected compiler name, or {@code null} if one could be detected
|
||||
* @throws IOException if an IO-related error occurred
|
||||
*/
|
||||
private String detectCompilerName(MachHeader machHeader) throws IOException {
|
||||
List<String> sectionNames = machHeader.parseSegments()
|
||||
.stream()
|
||||
.flatMap(seg -> seg.getSections().stream())
|
||||
.map(section -> section.getSectionName())
|
||||
.toList();
|
||||
if (SwiftUtils.isSwift(sectionNames)) {
|
||||
return SwiftUtils.SWIFT_COMPILER;
|
||||
}
|
||||
if (GoRttiMapper.hasGolangSections(sectionNames)) {
|
||||
return GoConstants.GOLANG_CSPEC_NAME;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if reexports should be performed
|
||||
*
|
||||
* @param options a {@link List} of {@link Option}s
|
||||
* @return True if reexports should be performed; otherwise, false
|
||||
*/
|
||||
private boolean shouldPerformReexports(List<Option> options) {
|
||||
return OptionUtils.getOption(REEXPORT_OPTION_NAME, options, REEXPORT_OPTION_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link List} of reexport library paths from the given {@link Program}
|
||||
*
|
||||
* @param program The {@link Program}
|
||||
* @param log The log
|
||||
* @return A {@link List} of reexport library paths from the given {@link Program}
|
||||
* @throws MachException if there was a problem parsing the Mach-O {@link Program}
|
||||
* @throws IOException if there was an IO-related error
|
||||
*/
|
||||
private List<String> getReexportPaths(Program program, MessageLog log)
|
||||
throws MachException, IOException {
|
||||
Symbol header =
|
||||
program.getSymbolTable().getSymbols(MachoProgramBuilder.HEADER_SYMBOL).next();
|
||||
if (header == null) {
|
||||
log.appendMsg("Failed to lookup reexport paths...couldn't find '%s' symbol"
|
||||
.formatted(MachoProgramBuilder.HEADER_SYMBOL));
|
||||
return List.of();
|
||||
}
|
||||
ByteProvider p = new MemoryByteProvider(program.getMemory(), header.getAddress());
|
||||
return new MachHeader(p).parseReexports()
|
||||
.stream()
|
||||
.map(DynamicLibraryCommand::getDynamicLibrary)
|
||||
.map(DynamicLibrary::getName)
|
||||
.map(LoadCommandString::getString)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* "Reexports" symbols from to a {@link Program}
|
||||
*
|
||||
|
|
|
@ -1,113 +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.
|
||||
*/
|
||||
package ghidra.app.util.opinion;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.format.macho.MachException;
|
||||
import ghidra.app.util.bin.format.ubi.FatHeader;
|
||||
import ghidra.app.util.bin.format.ubi.UbiException;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* A {@link Loader} for Mach-O files contained in a Universal Binary
|
||||
*/
|
||||
public class UniversalMachoLoader extends MachoLoader {
|
||||
|
||||
public final static String UNIVERSAL_MACH_O_NAME = "Universal Mach-O";
|
||||
private static final long MIN_BYTE_LENGTH = 4;
|
||||
|
||||
@Override
|
||||
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
|
||||
List<LoadSpec> loadSpecs = new ArrayList<>();
|
||||
|
||||
// Efficient check to fail fast
|
||||
if (provider.length() < MIN_BYTE_LENGTH) {
|
||||
return loadSpecs;
|
||||
}
|
||||
|
||||
// Efficient check to fail fast
|
||||
BinaryReader reader = new BinaryReader(provider, false);
|
||||
int magic = reader.readInt(0);
|
||||
if (magic != FatHeader.FAT_MAGIC && magic != FatHeader.FAT_CIGAM) {
|
||||
return loadSpecs;
|
||||
}
|
||||
|
||||
// Only add the preferred load specs for each Mach-O so only 1 entry for each architecture
|
||||
// shows up. Keep them all preferred though so the user is forced to pick one (headless
|
||||
// will just use the "first preferred" by default)
|
||||
for (ByteProvider wrapper : getWrappers(provider)) {
|
||||
super.findSupportedLoadSpecs(wrapper).stream()
|
||||
.filter(LoadSpec::isPreferred)
|
||||
.forEach(loadSpecs::add);
|
||||
}
|
||||
|
||||
return loadSpecs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
|
||||
for (ByteProvider wrapper : getWrappers(provider)) {
|
||||
for (LoadSpec ls : super.findSupportedLoadSpecs(wrapper)) {
|
||||
if (monitor.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
if (loadSpec.getLanguageCompilerSpec().equals(ls.getLanguageCompilerSpec())) {
|
||||
super.load(wrapper, loadSpec, options, program, monitor, log);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return UNIVERSAL_MACH_O_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link List} of {@link ByteProviderWrapper}s, one for each entry in the Universal
|
||||
* Binary
|
||||
*
|
||||
* @param provider The Universal Binary's provider
|
||||
* @return A {@link List} of {@link ByteProviderWrapper}s, one for each entry in the Universal
|
||||
* Binary
|
||||
* @throws IOException if an IO-related error occurred
|
||||
*/
|
||||
private List<ByteProviderWrapper> getWrappers(ByteProvider provider) throws IOException {
|
||||
List<ByteProviderWrapper> wrappers = new ArrayList<>();
|
||||
try {
|
||||
FatHeader fatHeader = new FatHeader(provider);
|
||||
List<Long> machStarts = fatHeader.getMachStarts();
|
||||
List<Long> machSizes = fatHeader.getMachSizes();
|
||||
for (int i = 0; i < machStarts.size(); i++) {
|
||||
wrappers.add(new ByteProviderWrapper(provider, machStarts.get(i), machSizes.get(i),
|
||||
provider.getFSRL()));
|
||||
}
|
||||
}
|
||||
catch (MachException | UbiException e) {
|
||||
// not a problem, just don't add it
|
||||
}
|
||||
return wrappers;
|
||||
}
|
||||
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
package ghidra.app.plugin.core.datamgr;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Container;
|
||||
|
@ -959,7 +960,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
// Now also turn off typedefs
|
||||
performAction(action, provider, false);
|
||||
dialog = waitForDialogComponent(DtFilterDialog.class);
|
||||
setToggleButtonSelected(dialog.getComponent(), "StructuresTypedefs", false);
|
||||
setToggleButtonSelected(dialog.getComponent(), "StructuresTypeDefs", false);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForTree();
|
||||
|
||||
|
@ -967,6 +968,26 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
assertType("TypeDefToMyStruct", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilter_Structures_HideTypeDefs() {
|
||||
|
||||
assertStructures(true);
|
||||
assertType("TypeDefToMyStruct", true);
|
||||
|
||||
// press the filter button
|
||||
DockingActionIf action = getAction(plugin, "Show Filter");
|
||||
performAction(action, provider, false);
|
||||
DtFilterDialog dialog = waitForDialogComponent(DtFilterDialog.class);
|
||||
|
||||
// turn off Structure TypeDefs
|
||||
setToggleButtonSelected(dialog.getComponent(), "StructuresTypeDefs", false);
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForTree();
|
||||
|
||||
assertStructures(true); // still have structures
|
||||
assertType("TypeDefToMyStruct", false); // no longer have structure typedefs
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilter_ClonedProvider() {
|
||||
|
||||
|
@ -1058,14 +1079,14 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
}
|
||||
|
||||
private Map<String, DataTypeNode> getNodes(DataTypeManager dtm) {
|
||||
|
||||
|
||||
DataTypeArchiveGTree gTree = provider.getGTree();
|
||||
GTreeNode rootNode = gTree.getViewRoot();
|
||||
GTreeNode dtmNode = rootNode.getChild(dtm.getName());
|
||||
assertNotNull(dtmNode);
|
||||
|
||||
|
||||
expandNode(dtmNode);
|
||||
|
||||
|
||||
Map<String, DataTypeNode> nodesByName = new HashMap<>();
|
||||
Iterator<GTreeNode> it = dtmNode.iterator(true);
|
||||
for (GTreeNode node : CollectionUtils.asIterable(it)) {
|
||||
|
@ -1076,7 +1097,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
DataType dt = dtNode.getDataType();
|
||||
nodesByName.put(dt.getName(), dtNode);
|
||||
}
|
||||
|
||||
|
||||
return nodesByName;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ public class DefinedStringIteratorTest extends AbstractGhidraHeadlessIntegration
|
|||
private int s1f3Offset = 50;
|
||||
private ArrayDataType structArray;
|
||||
private StructureDataType struct2DT;
|
||||
private StructureDataType struct3DT;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
@ -68,6 +69,9 @@ public class DefinedStringIteratorTest extends AbstractGhidraHeadlessIntegration
|
|||
struct2DT.replaceAtOffset(0, intDT, -1, "f1", null);
|
||||
struct2DT.replaceAtOffset(10, struct1DT, -1, "f2", null);
|
||||
|
||||
struct3DT = new StructureDataType("struct3", 200);
|
||||
struct3DT.replaceAtOffset(0, charArray, -1, "f1", null);
|
||||
|
||||
builder.createMemory("test", "0x0", 0x20000);
|
||||
program = builder.getProgram();
|
||||
}
|
||||
|
@ -144,6 +148,19 @@ public class DefinedStringIteratorTest extends AbstractGhidraHeadlessIntegration
|
|||
assertEquals(s1ArrayElemIndex + 2, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_StructFirstField() throws Exception {
|
||||
// ensure we get the first field of a struct
|
||||
|
||||
int structAddr = 0x100;
|
||||
|
||||
builder.applyFixedLengthDataType(addrStr(structAddr), struct3DT, -1);
|
||||
|
||||
DefinedStringIterator it = DefinedStringIterator.forProgram(program);
|
||||
List<Data> list = IteratorUtils.toList(it);
|
||||
assertEquals(1, list.size());
|
||||
}
|
||||
|
||||
private long arrayElementAddr(long arrayAddr, int elemSize, int elemIndex) {
|
||||
return arrayAddr + (elemSize * elemIndex);
|
||||
}
|
||||
|
@ -155,7 +172,7 @@ public class DefinedStringIteratorTest extends AbstractGhidraHeadlessIntegration
|
|||
|
||||
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)
|
||||
builder.applyFixedLengthDataType(addrStr(s1ArrayAddr), structArray, -1); // +(1 + 10 + 10*3)
|
||||
|
||||
DataType byteArray = new ArrayDataType(ByteDataType.dataType, 2000, -1);
|
||||
builder.applyFixedLengthDataType("0x1000", byteArray, -1); // +1
|
||||
|
@ -163,7 +180,7 @@ public class DefinedStringIteratorTest extends AbstractGhidraHeadlessIntegration
|
|||
DefinedStringIterator it = DefinedStringIterator.forProgram(program);
|
||||
List<Data> list = IteratorUtils.toList(it);
|
||||
|
||||
assertEquals(34, it.getDataCandidateCount()); // 1 + 1 + 1 + 10*3
|
||||
assertEquals(44, it.getDataCandidateCount()); // 1 + 1 + 1 + 10 + 10*3
|
||||
assertEquals(21, list.size()); // 1 ds@0x10 + 2 per structArray element
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,9 @@ public class PdbAnalyzer extends AbstractAnalyzer {
|
|||
|
||||
File pdbFile = PdbAnalyzerCommon.findPdb(this, program, searchUntrustedLocations, monitor);
|
||||
if (pdbFile == null) {
|
||||
// warnings have already been logged
|
||||
// Warnings have already been logged, but nice to have a post-analysis pop-up that
|
||||
// PDB analysis was not done
|
||||
log.appendMsg(NAME, "Aborted: Could not find an appropriate PDB file; see log");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -156,7 +158,7 @@ public class PdbAnalyzer extends AbstractAnalyzer {
|
|||
* Normally the analyzer would locate the PDB file on its own, but if a
|
||||
* headless script wishes to override the analyzer's behaivor, it can
|
||||
* use this method to specify a file.
|
||||
*
|
||||
*
|
||||
* @param program {@link Program}
|
||||
* @param pdbFile the pdb file
|
||||
*/
|
||||
|
@ -171,7 +173,7 @@ public class PdbAnalyzer extends AbstractAnalyzer {
|
|||
* Normally when the analyzer attempts to locate a matching PDB file it
|
||||
* will default to NOT searching untrusted symbol servers. A headless script could
|
||||
* use this method to allow the analyzer to search untrusted symbol servers.
|
||||
*
|
||||
*
|
||||
* @param program {@link Program}
|
||||
* @param allowUntrusted boolean flag, true means analyzer can search untrusted symbol
|
||||
* servers
|
||||
|
|
|
@ -167,7 +167,9 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
|
|||
pdbFile = PdbAnalyzerCommon.findPdb(this, program, searchUntrustedLocations, monitor);
|
||||
}
|
||||
if (pdbFile == null) {
|
||||
// warnings have already been logged
|
||||
// Warnings have already been logged, but nice to have a post-analysis pop-up that
|
||||
// PDB analysis was not done
|
||||
log.appendMsg(NAME, "Aborted: Could not find an appropriate PDB file; see log");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -177,7 +177,7 @@ public class DefinedStringIterator implements DataIterator {
|
|||
|
||||
private static class StructDtcIterator implements DataIterator {
|
||||
private Data data;
|
||||
private int currentIndex;
|
||||
private int currentIndex = -1;
|
||||
private DataTypeComponent[] dtcs;
|
||||
|
||||
public StructDtcIterator(Data data, Composite compDT) {
|
||||
|
|
|
@ -388,6 +388,12 @@ public abstract class AbstractGdbTraceRmiTest extends AbstractGhidraHeadedDebugg
|
|||
return new MemDump(address, buf.toByteArray());
|
||||
}
|
||||
|
||||
protected void waitDomainObjectClosed(String path) {
|
||||
DomainFile df = env.getProject().getProjectData().getFile(path);
|
||||
assertNotNull(df);
|
||||
waitForPass(() -> assertFalse(df.isOpen()));
|
||||
}
|
||||
|
||||
protected ManagedDomainObject openDomainObject(String path) throws Exception {
|
||||
DomainFile df = env.getProject().getProjectData().getFile(path);
|
||||
assertNotNull(df);
|
||||
|
|
|
@ -160,7 +160,6 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest {
|
|||
|
||||
@Test
|
||||
public void testStopTrace() throws Exception {
|
||||
// TODO: This test assumes gdb and the target file bash are x86-64
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra trace connect %s
|
||||
|
@ -169,10 +168,8 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest {
|
|||
ghidra trace stop
|
||||
quit
|
||||
""".formatted(PREAMBLE, addr));
|
||||
DomainFile dfBash = env.getProject().getProjectData().getFile("/New Traces/gdb/bash");
|
||||
assertNotNull(dfBash);
|
||||
// TODO: Given the 'quit' command, I'm not sure this assertion is checking anything.
|
||||
assertFalse(dfBash.isOpen());
|
||||
// NOTE: Given the 'quit' command, I'm not sure this assertion is checking anything.
|
||||
waitDomainObjectClosed("/New Traces/gdb/bash");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -278,6 +275,7 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest {
|
|||
ghidra trace tx-commit
|
||||
quit
|
||||
""".formatted(PREAMBLE, addr));
|
||||
waitDomainObjectClosed("/New Traces/no-save");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/no-save")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
assertEquals(0, tb.trace.getTimeManager().getAllSnapshots().size());
|
||||
|
@ -294,6 +292,7 @@ public class GdbCommandsTest extends AbstractGdbTraceRmiTest {
|
|||
ghidra trace save
|
||||
quit
|
||||
""".formatted(PREAMBLE, addr));
|
||||
waitDomainObjectClosed("/New Traces/save");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/save")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
assertEquals(1, tb.trace.getTimeManager().getAllSnapshots().size());
|
||||
|
|
|
@ -418,6 +418,12 @@ public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebug
|
|||
return new MemDump(address, buf.toByteArray());
|
||||
}
|
||||
|
||||
protected void waitDomainObjectClosed(String path) {
|
||||
DomainFile df = env.getProject().getProjectData().getFile(path);
|
||||
assertNotNull(df);
|
||||
waitForPass(() -> assertFalse(df.isOpen()));
|
||||
}
|
||||
|
||||
protected ManagedDomainObject openDomainObject(String path) throws Exception {
|
||||
DomainFile df = env.getProject().getProjectData().getFile(path);
|
||||
assertNotNull(df);
|
||||
|
|
|
@ -167,10 +167,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
|
|||
ghidra trace stop
|
||||
quit
|
||||
""".formatted(PREAMBLE, addr, getSpecimenPrint()));
|
||||
DomainFile df = env.getProject().getProjectData().getFile("/New Traces/lldb/expPrint");
|
||||
assertNotNull(df);
|
||||
// TODO: Given the 'quit' command, I'm not sure this assertion is checking anything.
|
||||
assertFalse(df.isOpen());
|
||||
// NOTE: Given the 'quit' command, I'm not sure this assertion is checking anything.
|
||||
waitDomainObjectClosed("/New Traces/lldb/expPrint");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -270,6 +268,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
|
|||
ghidra trace tx-commit
|
||||
quit
|
||||
""".formatted(PREAMBLE, addr, getSpecimenPrint()));
|
||||
waitDomainObjectClosed("/New Traces/no-save");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/no-save")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
assertEquals(0, tb.trace.getTimeManager().getAllSnapshots().size());
|
||||
|
@ -286,6 +285,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
|
|||
ghidra trace save
|
||||
quit
|
||||
""".formatted(PREAMBLE, addr, getSpecimenPrint()));
|
||||
waitDomainObjectClosed("/New Traces/save");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/save")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
assertEquals(1, tb.trace.getTimeManager().getAllSnapshots().size());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue