Merge remote-tracking branch 'origin/Ghidra_11.4'

This commit is contained in:
Ryan Kurtz 2025-05-15 11:58:09 -04:00
commit 33b9688d7c
15 changed files with 138 additions and 121 deletions

View file

@ -4,9 +4,9 @@
* 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.
@ -43,9 +43,10 @@ public final class DyldInfoCommandConstants {
public final static int BIND_TYPE_TEXT_ABSOLUTE32 = 2;
public final static int BIND_TYPE_TEXT_PCREL32 = 3;
public final static int BIND_SPECIAL_DYLIB_SELF = 0;
public final static int BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1;
public final static int BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2;
public final static int BIND_SPECIAL_DYLIB_SELF = 0;
public final static int BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1;
public final static int BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2;
public final static int BIND_SPECIAL_DYLIB_WEAK_LOOKUP = -3;
public final static int BIND_SYMBOL_FLAGS_WEAK_IMPORT = 0x1;
public final static int BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION = 0x8;

View file

@ -28,15 +28,15 @@ import ghidra.app.util.opinion.MachoProgramBuilder;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.program.model.symbol.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class DyldChainedFixups {
public static final int RELOCATION_TYPE = 0x8888;
/**
* Walks the chained fixup information and collects a {@link List} of {@link DyldFixup}s that
* will need to be applied to the image
@ -67,10 +67,10 @@ public class DyldChainedFixups {
long chainLoc = page + nextOff;
final long chainValue = DyldChainedPtr.getChainValue(reader, chainLoc, pointerFormat);
long newChainValue = chainValue;
Long newChainValue = chainValue;
boolean isAuthenticated = DyldChainedPtr.isAuthenticated(pointerFormat, chainValue);
boolean isBound = DyldChainedPtr.isBound(pointerFormat, chainValue);
Symbol symbol = null;
String symbol = null;
Integer libOrdinal = null;
if (isBound) {
@ -88,13 +88,16 @@ public class DyldChainedFixups {
int chainOrdinal = (int) DyldChainedPtr.getOrdinal(pointerFormat, chainValue);
long addend = DyldChainedPtr.getAddend(pointerFormat, chainValue);
DyldChainedImport chainedImport = chainedImports.getChainedImport(chainOrdinal);
List<Symbol> globalSymbols = symbolTable.getGlobalSymbols(chainedImport.getName());
symbol = SymbolUtilities.replaceInvalidChars(chainedImport.getName(), true);
libOrdinal = chainedImport.getLibOrdinal();
List<Symbol> globalSymbols = symbolTable.getGlobalSymbols(symbol);
if (globalSymbols.size() > 0) {
symbol = globalSymbols.get(0);
newChainValue = symbol.getAddress().getOffset();
libOrdinal = chainedImport.getLibOrdinal();
newChainValue = globalSymbols.getFirst().getAddress().getOffset();
newChainValue += isAuthenticated ? auth_value_add : addend;
}
else {
newChainValue = null;
}
newChainValue += isAuthenticated ? auth_value_add : addend;
}
else {
if (isAuthenticated) {
@ -110,7 +113,7 @@ public class DyldChainedFixups {
}
fixups.add(new DyldFixup(chainLoc, newChainValue, DyldChainedPtr.getSize(pointerFormat),
symbol != null ? symbol.getName() : null, libOrdinal));
symbol, libOrdinal));
next = DyldChainedPtr.getNext(pointerFormat, chainValue);
nextOff += next * DyldChainedPtr.getStride(pointerFormat);
@ -128,12 +131,11 @@ public class DyldChainedFixups {
* @param log The log
* @param monitor A cancellable monitor
* @return A {@link List} of fixed up {@link Address}'s
* @throws MemoryAccessException If there was a problem accessing memory
* @throws CancelledException If the user cancelled the operation
*/
public static List<Address> fixupChainedPointers(List<DyldFixup> fixups, Program program,
Address imagebase, List<String> libraryPaths, MessageLog log, TaskMonitor monitor)
throws MemoryAccessException, CancelledException {
throws CancelledException {
List<Address> fixedAddrs = new ArrayList<>();
if (fixups.isEmpty()) {
return fixedAddrs;
@ -142,37 +144,42 @@ public class DyldChainedFixups {
monitor.initialize(fixups.size(), "Fixing up chained pointers...");
for (DyldFixup fixup : fixups) {
monitor.increment();
Status status = Status.FAILURE;
Status status = Status.UNSUPPORTED;
Address addr = imagebase.add(fixup.offset());
String symbol = fixup.symbol();
long[] value = new long[] {};
try {
if (fixup.size() == 8 || fixup.size() == 4) {
if (fixup.size() == 8) {
memory.setLong(addr, fixup.value());
if (fixup.value() != null) {
if (fixup.size() == 8 || fixup.size() == 4) {
if (fixup.size() == 8) {
memory.setLong(addr, fixup.value());
}
else {
memory.setInt(addr, fixup.value().intValue());
}
fixedAddrs.add(addr);
status = Status.APPLIED_OTHER;
}
else {
memory.setInt(addr, (int) fixup.value());
value = new long[] { fixup.value() };
}
if (symbol != null && fixup.libOrdinal() != null) {
value = new long[] { fixup.libOrdinal() };
try {
MachoProgramBuilder.fixupExternalLibrary(program, libraryPaths,
fixup.libOrdinal(), symbol);
}
catch (Exception e) {
log.appendMsg("WARNING: Problem fixing up symbol '%s' - %s"
.formatted(symbol, e.getMessage()));
}
fixedAddrs.add(addr);
status = Status.APPLIED_OTHER;
}
else {
status = Status.UNSUPPORTED;
}
}
catch (Exception e) {
status = Status.FAILURE;
}
finally {
program.getRelocationTable()
.add(addr, status, 0, new long[] { fixup.value() }, fixup.size(),
fixup.symbol() != null ? fixup.symbol() : null);
}
if (fixup.symbol() != null && fixup.libOrdinal() != null) {
try {
MachoProgramBuilder.fixupExternalLibrary(program, libraryPaths,
fixup.libOrdinal(), fixup.symbol());
}
catch (Exception e) {
log.appendMsg("WARNING: Problem fixing up symbol '%s' - %s"
.formatted(fixup.symbol(), e.getMessage()));
}
.add(addr, status, RELOCATION_TYPE, value, fixup.size(), symbol);
}
}
log.appendMsg("Fixed up " + fixedAddrs.size() + " chained pointers.");

View file

@ -47,14 +47,16 @@ public class DyldChainedImport implements StructConverter {
switch (imports_format) {
case DYLD_CHAINED_IMPORT: {
int ival = reader.readNextInt();
lib_ordinal = ival & 0xff;
int ordinal = ival & 0xff;
lib_ordinal = ordinal > 0xf0 ? (byte) ordinal : ordinal;
weak_import = ((ival >> 8) & 1) == 1;
name_offset = (ival >> 9 & 0x7fffff);
break;
}
case DYLD_CHAINED_IMPORT_ADDEND: {
int ival = reader.readNextInt();
lib_ordinal = ival & 0xff;
int ordinal = ival & 0xff;
lib_ordinal = ordinal > 0xf0 ? (byte) ordinal : ordinal;
weak_import = ((ival >> 8) & 1) == 1;
name_offset = (ival >> 9 & 0x7fffff);
addend = reader.readNextInt();
@ -62,7 +64,8 @@ public class DyldChainedImport implements StructConverter {
}
case DYLD_CHAINED_IMPORT_ADDEND64: {
long ival = reader.readNextLong();
lib_ordinal = (int) (ival & 0xffff);
int ordinal = (int) (ival & 0xffff);
lib_ordinal = ordinal > 0xfff0 ? (short) ordinal : ordinal;
weak_import = ((ival >> 16) & 1) == 1;
name_offset = ((ival >> 32) & 0xffffffffL);
addend = reader.readNextLong();

View file

@ -207,7 +207,7 @@ public class DyldCacheSlideInfo4 extends DyldCacheSlideInfoCommon {
chainValue += valueAdd /* + slide */;
}
fixups.add(new DyldFixup(dataOffset, chainValue, 4, null, null));
fixups.add(new DyldFixup(dataOffset, Long.valueOf(chainValue), 4, null, null));
}
return fixups;

View file

@ -173,7 +173,7 @@ public abstract class DyldCacheSlideInfoCommon implements StructConverter {
memory.setLong(addr, fixup.value());
}
else {
memory.setInt(addr, (int) fixup.value());
memory.setInt(addr, fixup.value().intValue());
}
}

View file

@ -19,9 +19,9 @@ package ghidra.app.util.bin.format.macho.dyld;
* Stores information needed to perform a dyld pointer fixup
*
* @param offset The offset of where to perform the fixup (from some base address/index)
* @param value The fixed up value
* @param value The fixed up value, or {@code null} if this fixup is unsupported
* @param size The size of the fixup in bytes
* @param symbol The symbol associated with the fixup (could be null)
* @param libOrdinal The library ordinal associated with the fixup (could be null)
*/
public record DyldFixup(long offset, long value, int size, String symbol, Integer libOrdinal) {}
public record DyldFixup(long offset, Long value, int size, String symbol, Integer libOrdinal) {}

View file

@ -684,17 +684,14 @@ public class MachoProgramBuilder {
monitor.increment();
int symbolIndex = indirectSymbols.get(i);
NList symbol = symbolTableCommand.getSymbolAt(symbolIndex);
if (symbol != null) {
String name = SymbolUtilities.replaceInvalidChars(symbol.getString(), true);
if (name != null && name.length() > 0) {
Function stubFunc = createOneByteFunction(name, startAddr);
if (stubFunc != null) {
ExternalLocation loc = program.getExternalManager()
.addExtLocation(Library.UNKNOWN, name, null,
SourceType.IMPORTED);
stubFunc.setThunkedFunction(loc.createFunction());
}
}
String name =
symbol != null ? SymbolUtilities.replaceInvalidChars(symbol.getString(), true)
: "STUB_" + startAddr;
Function stubFunc = createOneByteFunction(name, startAddr);
if (stubFunc != null && symbol != null) {
ExternalLocation loc = program.getExternalManager()
.addExtLocation(Library.UNKNOWN, name, null, SourceType.IMPORTED);
stubFunc.setThunkedFunction(loc.createFunction());
}
startAddr = startAddr.add(symbolSize);
@ -1283,7 +1280,8 @@ public class MachoProgramBuilder {
String libraryPath = null;
if (command instanceof DynamicLibraryCommand dylibCommand) {
if (command instanceof DynamicLibraryCommand dylibCommand &&
dylibCommand.getCommandType() != LoadCommandTypes.LC_ID_DYLIB) {
DynamicLibrary dylib = dylibCommand.getDynamicLibrary();
libraryPath = dylib.getName().getString();
}
@ -1861,6 +1859,15 @@ public class MachoProgramBuilder {
*/
public static void fixupExternalLibrary(Program program, List<String> libraryPaths,
int libraryOrdinal, String symbol) throws Exception {
switch (libraryOrdinal) {
case DyldInfoCommandConstants.BIND_SPECIAL_DYLIB_SELF:
case DyldInfoCommandConstants.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE:
case DyldInfoCommandConstants.BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
case DyldInfoCommandConstants.BIND_SPECIAL_DYLIB_WEAK_LOOKUP:
return;
}
ExternalManager extManager = program.getExternalManager();
int libraryIndex = libraryOrdinal - 1;
if (libraryIndex < 0 || libraryIndex >= libraryPaths.size()) {

View file

@ -4,9 +4,9 @@
* 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.
@ -52,6 +52,46 @@ public class ExternalSymbolResolver implements Closeable {
StringUtilities.pad("" + libraryIndex, ' ', 4));
}
/**
* Returns an ordered list of library names, as specified by the logic/rules of the original
* operating system's loader (eg. Elf / MachO dynamic library loading / symbol resolving
* rules)
*
* @param program The {@link Program}
* @return list of library names, in original order
*/
public static List<String> getOrderedRequiredLibraryNames(Program program) {
TreeMap<Integer, String> orderLibraryMap = new TreeMap<>();
Options options = program.getOptions(Program.PROGRAM_INFO);
for (String optionName : options.getOptionNames()) {
// Legacy programs may have the old "ELF Required Library [" program property, so
// we should not assume that the option name starts exactly with
// REQUIRED_LIBRARY_PROPERTY_PREFIX. We must deal with a potential substring at the
// start of the option name.
int prefixIndex = optionName.indexOf(REQUIRED_LIBRARY_PROPERTY_PREFIX);
if (prefixIndex == -1 || !optionName.endsWith("]")) {
continue;
}
String libName = options.getString(optionName, null);
if (libName == null) {
continue;
}
String indexStr = optionName
.substring(prefixIndex + REQUIRED_LIBRARY_PROPERTY_PREFIX.length(),
optionName.length() - 1)
.trim();
try {
orderLibraryMap.put(Integer.parseInt(indexStr), libName.trim());
}
catch (NumberFormatException e) {
Msg.error(ExternalSymbolResolver.class,
"Program contains invalid property: " + optionName);
}
}
return new ArrayList<>(orderLibraryMap.values());
}
private final ProjectData projectData;
private final TaskMonitor monitor;
private final List<ProgramSymbolResolver> programsToFix = new ArrayList<>();
@ -333,7 +373,7 @@ public class ExternalSymbolResolver implements Closeable {
private List<ExtLibInfo> getLibsToSearch() throws CancelledException {
List<ExtLibInfo> result = new ArrayList<>();
ExternalManager externalManager = program.getExternalManager();
for (String libName : getOrderedRequiredLibraryNames()) {
for (String libName : getOrderedRequiredLibraryNames(program)) {
Library lib = externalManager.getExternalLibrary(libName);
String libPath = lib != null ? lib.getAssociatedProgramPath() : null;
Program libProg = libPath != null ? getLibraryProgram(libPath) : null;
@ -409,46 +449,6 @@ public class ExternalSymbolResolver implements Closeable {
}
return symbolIds;
}
/**
* Returns an ordered list of library names, as specified by the logic/rules of the original
* operating system's loader (eg. Elf / MachO dynamic library loading / symbol resolving
* rules)
*
* @return list of library names, in original order
*/
private Collection<String> getOrderedRequiredLibraryNames() {
TreeMap<Integer, String> orderLibraryMap = new TreeMap<>();
Options options = program.getOptions(Program.PROGRAM_INFO);
for (String optionName : options.getOptionNames()) {
// Legacy programs may have the old "ELF Required Library [" program property, so
// we should not assume that the option name starts exactly with
// REQUIRED_LIBRARY_PROPERTY_PREFIX. We must deal with a potential substring at the
// start of the option name.
int prefixIndex = optionName.indexOf(REQUIRED_LIBRARY_PROPERTY_PREFIX);
if (prefixIndex == -1 || !optionName.endsWith("]")) {
continue;
}
String libName = options.getString(optionName, null);
if (libName == null) {
continue;
}
String indexStr = optionName
.substring(prefixIndex + REQUIRED_LIBRARY_PROPERTY_PREFIX.length(),
optionName.length() - 1)
.trim();
try {
orderLibraryMap.put(Integer.parseInt(indexStr), libName.trim());
}
catch (NumberFormatException e) {
Msg.error(ExternalSymbolResolver.class,
"Program contains invalid property: " + optionName);
}
}
return orderLibraryMap.values();
}
}
/**

View file

@ -352,9 +352,9 @@ id="the-listings-are-not-in-sync-i.e.-they-do-not-move-together."
class="level3">
<h3>The listings are not in sync, i.e., they do not move together.</h3>
<p>First, check that synchronization is enabled. This is the default
behavior, but, still, check it first. In the top-right of the Dynamic
Listing is its local drop-down menu. Click it and check that
<strong>Auto-Sync Cursor with Static Listing</strong> is selected.</p>
behavior, but, still, check it first. In the <strong>Debugger →
Synchronization</strong> menu, check that <strong>Synchronize Static and
Dynamic Locations</strong> is selected.</p>
<p>If that does not work, check the top-left label of the Dynamic
Listing to see what module you are in. Also check the Debug Console
window. If you are in a system library, e.g., <code>ld-linux</code>,

View file

@ -167,8 +167,7 @@ The re-use of connections and/or the use of multiple concurrent connections is *
First, check that synchronization is enabled.
This is the default behavior, but, still, check it first.
In the top-right of the Dynamic Listing is its local drop-down menu.
Click it and check that **Auto-Sync Cursor with Static Listing** is selected.
In the **Debugger &rarr; Synchronization** menu, check that **Synchronize Static and Dynamic Locations** is selected.
If that does not work, check the top-left label of the Dynamic Listing to see what module you are in.
Also check the Debug Console window.

View file

@ -162,8 +162,7 @@ src="images/breakpoint-enable.png" alt="set breakpoint" /> Set
Breakpoint, press <strong><code>K</code></strong> on the keyboard, or
double-click the margin.</li>
<li>From the Breakpoints window, use the <strong>Set Breakpoint</strong>
dropdown to access the various breakpoint actions defined by
GDB.</li>
dropdown to access the various breakpoint actions defined by GDB.</li>
<li>From the Terminal window, use the GDB command, e.g.,
<code>break main</code>.</li>
</ol>

View file

@ -239,11 +239,11 @@ caused the target to break. This only applies to snapshots that were
created because of an event, which is most.</li>
<li>The <strong>PC</strong> column gives the address of the next
instruction.</li>
<li>The <strong>Module</strong> column gives the name of the module
containing the PC.</li>
<li>The <strong>Function</strong> column gives the name of the function
containing the PC mapped to its static program database, if
available.</li>
<li>The <strong>Module</strong> column gives the name of the module
containing the PC.</li>
<li>The <strong>Description</strong> column describes the event that
generated the snapshot. This can be edited in the table, or by pressing
<strong><code>CTRL</code>-<code>SHIFT</code>-<code>N</code></strong> to

View file

@ -128,8 +128,8 @@ The columns are:
* The **Event Thread** column indicates which thread caused the target to break.
This only applies to snapshots that were created because of an event, which is most.
* The **PC** column gives the address of the next instruction.
* The **Function** column gives the name of the function containing the PC mapped to its static program database, if available.
* The **Module** column gives the name of the module containing the PC.
* The **Function** column gives the name of the function containing the PC mapped to its static program database, if available.
* The **Description** column describes the event that generated the snapshot.
This can be edited in the table, or by pressing **`CTRL`-`SHIFT`-`N`** to mark interesting snapshots.

View file

@ -55,9 +55,9 @@ Mappings</a></li>
<li><a href="#moving-knowledge-from-dynamic-to-static"
id="toc-moving-knowledge-from-dynamic-to-static">Moving Knowledge from
Dynamic to Static</a></li>
<li><a href="#exercise-export-and-map-ncurses"
id="toc-exercise-export-and-map-ncurses">Exercise: Export and Map
<code>ncurses</code></a></li>
<li><a href="#exercise-export-and-map-system-supplied-dso"
id="toc-exercise-export-and-map-system-supplied-dso">Exercise: Export
and Map <code>system-supplied DSO</code></a></li>
<li><a href="#exercise-cheat-like-the-devs"
id="toc-exercise-cheat-like-the-devs">Exercise: Cheat Like the
Devs</a></li>
@ -276,7 +276,7 @@ anyway.</p></li>
<li><p>The table displays the <em>copy plan</em>. For a new program,
this will copy with an identical mapping of addresses, which is probably
the best plan, since the target system has already applied fixups. Do
not change any addresses, lest your corrupt references in the
not change any addresses, lest you corrupt references in the
copy.</p></li>
<li><p>Click <strong>Copy</strong>.</p></li>
<li><p>When prompted, name the program <code>libncurses</code>.</p></li>
@ -296,8 +296,9 @@ libncurses</strong>.</li>
<li>Check the proposed mapping and click <strong>OK</strong>.</li>
</ol>
</section>
<section id="exercise-export-and-map-ncurses" class="level2">
<h2>Exercise: Export and Map <code>ncurses</code></h2>
<section id="exercise-export-and-map-system-supplied-dso"
class="level2">
<h2>Exercise: Export and Map <code>system-supplied DSO</code></h2>
<p>Repeat this technique for the “system-supplied DSO” module. In
practice, there is no real reason to do this, but this particular module
prevents you from using <strong>Import From File System</strong>.</p>

View file

@ -144,7 +144,7 @@ For demonstration, we will walk through this second case, pretending we cannot l
1. It is probably best to include everything, though **Bytes** is the bare minimum.
1. The table displays the *copy plan*.
For a new program, this will copy with an identical mapping of addresses, which is probably the best plan, since the target system has already applied fixups.
Do not change any addresses, lest your corrupt references in the copy.
Do not change any addresses, lest you corrupt references in the copy.
1. Click **Copy**.
1. When prompted, name the program `libncurses`.
1. You may need to click the `termmines` tab in the Static Listing to get the UI to completely update.
@ -157,7 +157,7 @@ Undoubtedly, we would like to map that new program into our dynamic session.
1. In the top pane of the Modules window, right-click `libncurses` and choose **Map to libncurses**.
1. Check the proposed mapping and click **OK**.
## Exercise: Export and Map `ncurses`
## Exercise: Export and Map `system-supplied DSO`
Repeat this technique for the "system-supplied DSO" module.
In practice, there is no real reason to do this, but this particular module prevents you from using **Import From File System**.