mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-25 17:00:36 -09:00
GP-3728: Lazy loading of local symbols and other refactoring
This commit is contained in:
@@ -189,6 +189,30 @@ public class MachHeader implements StructConverter {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses only this {@link MachHeader}'s {@link SegmentCommand segments}
|
||||
*
|
||||
* @return A {@List} of this {@link MachHeader}'s {@link SegmentCommand segments}
|
||||
* @throws IOException If there was an IO-related error
|
||||
* @throws MachException if the load command is invalid
|
||||
*/
|
||||
public List<SegmentCommand> parseSegments() throws IOException, MachException {
|
||||
List<SegmentCommand> segments = new ArrayList<>();
|
||||
_reader.setPointerIndex(_commandIndex);
|
||||
for (int i = 0; i < nCmds; ++i) {
|
||||
int type = _reader.peekNextInt();
|
||||
if (type == LoadCommandTypes.LC_SEGMENT || type == LoadCommandTypes.LC_SEGMENT_64) {
|
||||
segments.add(new SegmentCommand(_reader, is32bit()));
|
||||
}
|
||||
else {
|
||||
type = _reader.readNextInt();
|
||||
int size = _reader.readNextInt();
|
||||
_reader.setPointerIndex(_reader.getPointerIndex() + size - 8);
|
||||
}
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
|
||||
public int getMagic() {
|
||||
return magic;
|
||||
}
|
||||
|
||||
@@ -112,6 +112,10 @@ public class SegmentCommand extends LoadCommand {
|
||||
return vmaddr;
|
||||
}
|
||||
|
||||
public void setVMaddress(long vmaddr) {
|
||||
this.vmaddr = vmaddr;
|
||||
}
|
||||
|
||||
public long getVMsize() {
|
||||
return vmsize;
|
||||
}
|
||||
|
||||
@@ -768,7 +768,7 @@ public class DyldCacheHeader implements StructConverter {
|
||||
}
|
||||
}
|
||||
|
||||
private void parseLocalSymbolsInfo(boolean shouldParse, MessageLog log, TaskMonitor monitor)
|
||||
public void parseLocalSymbolsInfo(boolean shouldParse, MessageLog log, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
if (!shouldParse || localSymbolsOffset == 0) {
|
||||
return;
|
||||
|
||||
@@ -115,6 +115,15 @@ public class DyldCacheLocalSymbolsInfo implements StructConverter {
|
||||
markupNList(program, localSymbolsInfoAddr, monitor, log);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link List} of {@link DyldCacheLocalSymbolsEntry}s.
|
||||
*
|
||||
* @return The {@link List} of {@link DyldCacheLocalSymbolsEntry}
|
||||
*/
|
||||
public List<DyldCacheLocalSymbolsEntry> getLocalSymbolsEntries() {
|
||||
return localSymbolsEntryList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link List} of {@link NList}.
|
||||
*
|
||||
@@ -125,12 +134,20 @@ public class DyldCacheLocalSymbolsInfo implements StructConverter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link List} of {@link DyldCacheLocalSymbolsEntry}s.
|
||||
* Gets the {@link List} of {@link NList} for the given dylib offset.
|
||||
*
|
||||
* @return The {@link List} of {@link DyldCacheLocalSymbolsEntry}
|
||||
* @param dylibOffset The offset of dylib in the DYLD Cache
|
||||
* @return The {@link List} of {@link NList} for the given dylib offset
|
||||
*/
|
||||
public List<DyldCacheLocalSymbolsEntry> getLocalSymbolsEntries() {
|
||||
return localSymbolsEntryList;
|
||||
public List<NList> getNList(long dylibOffset) {
|
||||
for (DyldCacheLocalSymbolsEntry entry : localSymbolsEntryList) {
|
||||
int index = entry.getNListStartIndex();
|
||||
int count = entry.getNListCount();
|
||||
if (dylibOffset == entry.getDylibOffset()) {
|
||||
return nlistList.subList(index, index + count);
|
||||
}
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -88,10 +88,10 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
|
||||
new SplitDyldCache(provider, options.processLocalSymbols(), log, monitor)) {
|
||||
|
||||
// Set image base
|
||||
setDyldCacheImageBase(splitDyldCache.getDyldCacheHeader(0));
|
||||
setDyldCacheImageBase(splitDyldCache);
|
||||
|
||||
// Set entry point
|
||||
setDyldCacheEntryPoint(splitDyldCache.getDyldCacheHeader(0));
|
||||
setDyldCacheEntryPoint(splitDyldCache);
|
||||
|
||||
// Setup memory
|
||||
// Check if local symbols are present
|
||||
@@ -125,25 +125,25 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
|
||||
/**
|
||||
* Sets the program's image base.
|
||||
*
|
||||
* @param dyldCacheHeader The "base" DYLD Cache header
|
||||
* @param splitDyldCache The split DYLD cache
|
||||
* @throws Exception if there was problem setting the program's image base
|
||||
*/
|
||||
private void setDyldCacheImageBase(DyldCacheHeader dyldCacheHeader) throws Exception {
|
||||
private void setDyldCacheImageBase(SplitDyldCache splitDyldCache) throws Exception {
|
||||
monitor.setMessage("Setting image base...");
|
||||
monitor.initialize(1);
|
||||
program.setImageBase(space.getAddress(dyldCacheHeader.getBaseAddress()), true);
|
||||
program.setImageBase(space.getAddress(splitDyldCache.getBaseAddress()), true);
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the program's entry point (if known).
|
||||
*
|
||||
* @param dyldCacheHeader The "base" DYLD Cache header
|
||||
* @param splitDyldCache The split DYLD cache
|
||||
* @throws Exception if there was problem setting the program's entry point
|
||||
*/
|
||||
private void setDyldCacheEntryPoint(DyldCacheHeader dyldCacheHeader) throws Exception {
|
||||
private void setDyldCacheEntryPoint(SplitDyldCache splitDyldCache) throws Exception {
|
||||
monitor.initialize(1, "Setting entry pointer base...");
|
||||
Long entryPoint = dyldCacheHeader.getEntryPoint();
|
||||
Long entryPoint = splitDyldCache.getDyldCacheHeader(0).getEntryPoint();
|
||||
if (entryPoint != null) {
|
||||
Address entryPointAddr = space.getAddress(entryPoint);
|
||||
program.getSymbolTable().addExternalEntryPoint(entryPointAddr);
|
||||
|
||||
@@ -218,6 +218,30 @@ public class DyldCacheUtils {
|
||||
public int size() {
|
||||
return providers.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base address of the split DYLD cache. This is where the cache should be loaded
|
||||
* in memory.
|
||||
*
|
||||
* @return The base address of the split DYLD cache
|
||||
*/
|
||||
public long getBaseAddress() {
|
||||
return headers.get(0).getBaseAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link DyldCacheLocalSymbolsInfo} from the split DYLD Cache files
|
||||
*
|
||||
* @return The {@link DyldCacheLocalSymbolsInfo} from the split DYLD Cache files, or null
|
||||
* if no local symbols are defined
|
||||
*/
|
||||
public DyldCacheLocalSymbolsInfo getLocalSymbolInfo() {
|
||||
return headers.stream()
|
||||
.map(h -> h.getLocalSymbolsInfo())
|
||||
.filter(info -> info != null)
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
|
||||
@@ -22,10 +22,12 @@ import org.apache.commons.collections4.BidiMap;
|
||||
import org.apache.commons.collections4.bidimap.DualHashBidiMap;
|
||||
import org.jdom.JDOMException;
|
||||
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.ByteProviderWrapper;
|
||||
import ghidra.app.util.bin.format.macho.MachException;
|
||||
import ghidra.app.util.bin.format.macho.MachHeader;
|
||||
import ghidra.app.util.bin.format.macho.commands.*;
|
||||
import ghidra.app.util.bin.format.macho.commands.SegmentCommand;
|
||||
import ghidra.app.util.bin.format.macho.commands.SegmentNames;
|
||||
import ghidra.app.util.bin.format.macho.prelink.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
@@ -44,26 +46,9 @@ public class MachoPrelinkUtils {
|
||||
*/
|
||||
public static boolean isMachoPrelink(ByteProvider provider, TaskMonitor monitor) {
|
||||
try {
|
||||
MachHeader header = new MachHeader(provider);
|
||||
BinaryReader reader = new BinaryReader(provider, header.isLittleEndian());
|
||||
reader.setPointerIndex(header.getSize());
|
||||
|
||||
// Doing a full header parse is too slow...we really just need to see if a segment
|
||||
// exists that starts with __PRELINK. Parse the minimal amount to do that check.
|
||||
for (int i = 0; i < header.getNumberOfCommands(); i++) {
|
||||
int type = reader.peekNextInt();
|
||||
if (type == LoadCommandTypes.LC_SEGMENT || type == LoadCommandTypes.LC_SEGMENT_64) {
|
||||
SegmentCommand segment = new SegmentCommand(reader, header.is32bit());
|
||||
if (segment.getSegmentName().startsWith("__PRELINK")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
type = reader.readNextInt();
|
||||
int size = reader.readNextInt();
|
||||
reader.setPointerIndex(reader.getPointerIndex() + size - 8);
|
||||
}
|
||||
}
|
||||
return new MachHeader(provider).parseSegments()
|
||||
.stream()
|
||||
.anyMatch(segment -> segment.getSegmentName().startsWith("__PRELINK"));
|
||||
}
|
||||
catch (MachException | IOException e) {
|
||||
// Assume it's not a Mach-O PRELINK...fall through
|
||||
|
||||
@@ -195,8 +195,14 @@ public class DyldCacheDylibExtractor {
|
||||
}
|
||||
byte[] bytes;
|
||||
if (segment == linkEditSegment) {
|
||||
bytes =
|
||||
createPackedLinkEditSegment(segmentProvider, packedLinkEditSize);
|
||||
bytes = createPackedLinkEditSegment(segmentProvider, packedLinkEditSize);
|
||||
|
||||
// We don't want our packed __LINKEDIT segment to overlap with other DYLIB's
|
||||
// that might get extracted and added to the same program. Rather than
|
||||
// computing the optimal address it should go at (which will required looking
|
||||
// at every other DYLIB in the cache which is slow), just make the address very
|
||||
// far away from the other DYLIB's
|
||||
segment.setVMaddress(textSegment.getVMaddress() << 4);
|
||||
}
|
||||
else {
|
||||
bytes = segmentProvider.readBytes(segment.getFileOffset(), segmentSize);
|
||||
@@ -221,28 +227,15 @@ public class DyldCacheDylibExtractor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link List} of local {@link NList symbol}s
|
||||
* Gets a {@link List} of local {@link NList symbol}s for the DYLIB being extracted
|
||||
*
|
||||
* @param splitDyldCache The {@link SplitDyldCache}
|
||||
* @return A {@link List} of local {@link NList symbol}s (could be empty)
|
||||
*/
|
||||
private List<NList> getLocalSymbols(SplitDyldCache splitDyldCache) {
|
||||
long base = splitDyldCache.getDyldCacheHeader(0).getBaseAddress();
|
||||
for (int i = 0; i < splitDyldCache.size(); i++) {
|
||||
DyldCacheHeader header = splitDyldCache.getDyldCacheHeader(i);
|
||||
DyldCacheLocalSymbolsInfo info = header.getLocalSymbolsInfo();
|
||||
if (info == null) {
|
||||
continue;
|
||||
}
|
||||
for (DyldCacheLocalSymbolsEntry entry : info.getLocalSymbolsEntries()) {
|
||||
int index = entry.getNListStartIndex();
|
||||
int count = entry.getNListCount();
|
||||
if (base + entry.getDylibOffset() == textSegment.getVMaddress() && count > 0) {
|
||||
return info.getNList().subList(index, index + count);
|
||||
}
|
||||
}
|
||||
}
|
||||
return List.of();
|
||||
long base = splitDyldCache.getBaseAddress();
|
||||
DyldCacheLocalSymbolsInfo info = splitDyldCache.getLocalSymbolInfo();
|
||||
return info != null ? info.getNList(textSegment.getVMaddress() - base) : List.of();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,14 +371,18 @@ public class DyldCacheDylibExtractor {
|
||||
*/
|
||||
private void fixupSegment(SegmentCommand segment, boolean is64bit) throws IOException {
|
||||
long adjustment = packedSegmentAdjustments.getOrDefault(segment, 0);
|
||||
if (segment.getFileOffset() > 0) {
|
||||
fixup(segment.getStartIndex() + (is64bit ? 0x28 : 0x20), adjustment,
|
||||
is64bit ? 8 : 4, segment);
|
||||
if (segment.getVMaddress() > 0) {
|
||||
set(segment.getStartIndex() + (is64bit ? 0x18 : 0x18), segment.getVMaddress(),
|
||||
is64bit ? 8 : 4);
|
||||
}
|
||||
if (segment.getVMsize() > 0) {
|
||||
set(segment.getStartIndex() + (is64bit ? 0x20 : 0x1c), segment.getVMsize(),
|
||||
is64bit ? 8 : 4);
|
||||
}
|
||||
if (segment.getFileOffset() > 0) {
|
||||
fixup(segment.getStartIndex() + (is64bit ? 0x28 : 0x20), adjustment,
|
||||
is64bit ? 8 : 4, segment);
|
||||
}
|
||||
if (segment.getFileSize() > 0) {
|
||||
set(segment.getStartIndex() + (is64bit ? 0x30 : 0x24), segment.getFileSize(),
|
||||
is64bit ? 8 : 4);
|
||||
|
||||
@@ -36,6 +36,7 @@ import ghidra.util.task.TaskMonitor;
|
||||
public class DyldCacheFileSystem extends GFileSystemBase {
|
||||
|
||||
private SplitDyldCache splitDyldCache;
|
||||
private boolean parsedLocalSymbols = false;
|
||||
private Map<DyldCacheSlideInfoCommon, List<DyldCacheSlideFixup>> slideFixupMap;
|
||||
private Map<GFile, Long> addrMap = new HashMap<>();
|
||||
private Map<GFile, Integer> indexMap = new HashMap<>();
|
||||
@@ -47,9 +48,13 @@ public class DyldCacheFileSystem extends GFileSystemBase {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
slideFixupMap = null;
|
||||
parsedLocalSymbols = false;
|
||||
addrMap.clear();
|
||||
indexMap.clear();
|
||||
splitDyldCache.close();
|
||||
if (splitDyldCache != null) {
|
||||
splitDyldCache.close();
|
||||
splitDyldCache = null;
|
||||
}
|
||||
super.close();
|
||||
}
|
||||
|
||||
@@ -68,6 +73,14 @@ public class DyldCacheFileSystem extends GFileSystemBase {
|
||||
slideFixupMap = DyldCacheDylibExtractor.getSlideFixups(splitDyldCache, monitor);
|
||||
}
|
||||
|
||||
if (!parsedLocalSymbols) {
|
||||
for (int i = 0; i < splitDyldCache.size(); i++) {
|
||||
splitDyldCache.getDyldCacheHeader(i)
|
||||
.parseLocalSymbolsInfo(true, new MessageLog(), monitor);
|
||||
}
|
||||
parsedLocalSymbols = true;
|
||||
}
|
||||
|
||||
try {
|
||||
return DyldCacheDylibExtractor.extractDylib(machHeaderStartIndexInProvider,
|
||||
splitDyldCache, index, slideFixupMap, file.getFSRL(), monitor);
|
||||
@@ -120,7 +133,7 @@ public class DyldCacheFileSystem extends GFileSystemBase {
|
||||
MessageLog log = new MessageLog();
|
||||
monitor.setMessage("Opening DYLD cache...");
|
||||
|
||||
splitDyldCache = new SplitDyldCache(provider, true, log, monitor);
|
||||
splitDyldCache = new SplitDyldCache(provider, false, log, monitor);
|
||||
for (int i = 0; i < splitDyldCache.size(); i++) {
|
||||
DyldCacheHeader header = splitDyldCache.getDyldCacheHeader(i);
|
||||
monitor.setMessage("Find files...");
|
||||
|
||||
Reference in New Issue
Block a user