mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-25 17:00:36 -09:00
GP-3770: Adding files __BRANCH_STUBS and __BRANCH_GOTS to
MachoFileSetFileSystem
This commit is contained in:
@@ -54,4 +54,6 @@ public final class SegmentNames {
|
||||
|
||||
public final static String SEG_TEXT_EXEC = "__TEXT_EXEC";
|
||||
public final static String SEG_PRELINK_TEXT = "__PRELINK_TEXT";
|
||||
public final static String SEG_BRANCH_STUBS = "__BRANCH_STUBS";
|
||||
public final static String SEG_BRANCH_GOTS = "__BRANCH_GOTS";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* ###
|
||||
* 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.file.formats.ios.fileset;
|
||||
|
||||
/**
|
||||
* An entry in the {@link MachoFileSetFileSystem}
|
||||
*
|
||||
* @param id The id of the entry
|
||||
* @param offset The offset of the entry in the provider
|
||||
* @param isBranchSegment True if this entry represents a branch segment; false if it represents
|
||||
* an LC_FILESET_ENTRY Mach-O
|
||||
*/
|
||||
public record MachoFileSetEntry(String id, long offset, boolean isBranchSegment) {}
|
||||
@@ -18,9 +18,10 @@ package ghidra.file.formats.ios.fileset;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import ghidra.app.util.bin.ByteArrayProvider;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.format.macho.MachException;
|
||||
import ghidra.app.util.bin.format.macho.MachHeader;
|
||||
import ghidra.app.util.bin.format.macho.*;
|
||||
import ghidra.app.util.bin.format.macho.commands.SegmentCommand;
|
||||
import ghidra.file.formats.ios.ExtractedMacho;
|
||||
import ghidra.formats.gfilesystem.FSRL;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
@@ -60,4 +61,49 @@ public class MachoFileSetExtractor {
|
||||
extractedMacho.pack();
|
||||
return extractedMacho.getByteProvider(fsrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link ByteProvider} that contains a single segment from a Mach-O file set
|
||||
*
|
||||
* @param provider The Mach-O file set {@link ByteProvider}
|
||||
* @param segment The segment to extract
|
||||
* @param fsrl {@link FSRL} to assign to the resulting {@link ByteProvider}
|
||||
* @param monitor {@link TaskMonitor}
|
||||
* @return {@link ByteProvider} containing the bytes of the single-segment Mach-O
|
||||
* @throws MachException If there was an error creating Mach-O headers
|
||||
* @throws IOException If there was an IO-related issue with extracting the segment
|
||||
* @throws CancelledException If the user cancelled the operation
|
||||
*/
|
||||
public static ByteProvider extractSegment(ByteProvider provider, SegmentCommand segment, FSRL fsrl, TaskMonitor monitor) throws IOException, MachException, CancelledException {
|
||||
|
||||
int magic = MachConstants.MH_MAGIC_64;
|
||||
int allSegmentsSize = SegmentCommand.size(magic);
|
||||
|
||||
// Mach-O Header
|
||||
byte[] header =
|
||||
MachHeader.create(magic, 0x100000c, 0x80000002, 6, 1, allSegmentsSize, 0x42100085, 0);
|
||||
|
||||
// Segment command
|
||||
byte[] segmentCommandBytes =
|
||||
SegmentCommand.create(magic, segment.getSegmentName(), segment.getVMaddress(),
|
||||
segment.getVMsize(), header.length + allSegmentsSize, segment.getFileSize(),
|
||||
segment.getMaxProtection(), segment.getInitProtection(), 0, segment.getFlags());
|
||||
|
||||
// Segment data
|
||||
byte[] segmentDataBytes =
|
||||
provider.readBytes(segment.getFileOffset(), segment.getFileSize());
|
||||
|
||||
// Combine pieces
|
||||
int totalSize = header.length + allSegmentsSize + segmentDataBytes.length;
|
||||
byte[] result = new byte[totalSize + FOOTER_V1.length];
|
||||
System.arraycopy(header, 0, result, 0, header.length);
|
||||
System.arraycopy(segmentCommandBytes, 0, result, header.length, segmentCommandBytes.length);
|
||||
System.arraycopy(segmentDataBytes, 0, result, header.length + segmentCommandBytes.length,
|
||||
segmentDataBytes.length);
|
||||
|
||||
// Add footer
|
||||
System.arraycopy(FOOTER_V1, 0, result, result.length - FOOTER_V1.length, FOOTER_V1.length);
|
||||
|
||||
return new ByteArrayProvider(result, fsrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,13 +37,14 @@ import ghidra.util.task.TaskMonitor;
|
||||
* A {@link GFileSystem} implementation for Mach-O file set entries
|
||||
*/
|
||||
@FileSystemInfo(type = MachoFileSetFileSystem.MACHO_FILESET_FSTYPE, description = "Mach-O file set", factory = MachoFileSetFileSystemFactory.class)
|
||||
public class MachoFileSetFileSystem extends AbstractFileSystem<FileSetEntryCommand> {
|
||||
public class MachoFileSetFileSystem extends AbstractFileSystem<MachoFileSetEntry> {
|
||||
|
||||
public static final String MACHO_FILESET_FSTYPE = "machofileset";
|
||||
|
||||
private ByteProvider provider;
|
||||
private ByteProvider fixedUpProvider;
|
||||
private Map<FileSetEntryCommand, List<SegmentCommand>> entrySegmentMap;
|
||||
private MachHeader header;
|
||||
private Map<MachoFileSetEntry, List<SegmentCommand>> entrySegmentMap;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MachoFileSetFileSystem}
|
||||
@@ -68,16 +69,35 @@ public class MachoFileSetFileSystem extends AbstractFileSystem<FileSetEntryComma
|
||||
MessageLog log = new MessageLog();
|
||||
try {
|
||||
monitor.setMessage("Opening Mach-O file set...");
|
||||
MachHeader header = new MachHeader(provider).parse();
|
||||
header = new MachHeader(provider).parse();
|
||||
SegmentCommand textSegment = header.getSegment(SegmentNames.SEG_TEXT);
|
||||
if (textSegment == null) {
|
||||
throw new MachException(SegmentNames.SEG_TEXT + " not found!");
|
||||
}
|
||||
|
||||
// File set entries
|
||||
for (FileSetEntryCommand cmd : header.getLoadCommands(FileSetEntryCommand.class)) {
|
||||
fsIndex.storeFile(cmd.getFileSetEntryId().getString(), fsIndex.getFileCount(),
|
||||
false, -1, cmd);
|
||||
MachHeader entryHeader = new MachHeader(provider, cmd.getFileOffset());
|
||||
entrySegmentMap.put(cmd, entryHeader.parseSegments());
|
||||
MachoFileSetEntry entry = new MachoFileSetEntry(cmd.getFileSetEntryId().getString(),
|
||||
cmd.getFileOffset(), false);
|
||||
fsIndex.storeFile(entry.id(), fsIndex.getFileCount(), false, -1, entry);
|
||||
entrySegmentMap.put(entry,
|
||||
new MachHeader(provider, entry.offset()).parseSegments());
|
||||
}
|
||||
|
||||
// BRANCH segments, if present
|
||||
SegmentCommand branchStubs = header.getSegment(SegmentNames.SEG_BRANCH_STUBS);
|
||||
if (branchStubs != null) {
|
||||
MachoFileSetEntry entry =
|
||||
new MachoFileSetEntry(SegmentNames.SEG_BRANCH_STUBS.substring(2), 0, true);
|
||||
fsIndex.storeFile(entry.id(), fsIndex.getFileCount(), false, -1, entry);
|
||||
entrySegmentMap.put(entry, List.of(branchStubs));
|
||||
}
|
||||
SegmentCommand branchGots = header.getSegment(SegmentNames.SEG_BRANCH_GOTS);
|
||||
if (branchGots != null) {
|
||||
MachoFileSetEntry entry =
|
||||
new MachoFileSetEntry(SegmentNames.SEG_BRANCH_GOTS.substring(2), 0, true);
|
||||
fsIndex.storeFile(entry.id(), fsIndex.getFileCount(), false, -1, entry);
|
||||
entrySegmentMap.put(entry, List.of(branchGots));
|
||||
}
|
||||
|
||||
monitor.setMessage("Getting chained pointers...");
|
||||
@@ -106,27 +126,31 @@ public class MachoFileSetFileSystem extends AbstractFileSystem<FileSetEntryComma
|
||||
@Override
|
||||
public ByteProvider getByteProvider(GFile file, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
FileSetEntryCommand cmd = fsIndex.getMetadata(file);
|
||||
if (cmd == null) {
|
||||
MachoFileSetEntry entry = fsIndex.getMetadata(file);
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return MachoFileSetExtractor.extractFileSetEntry(fixedUpProvider, cmd.getFileOffset(),
|
||||
if (entry.isBranchSegment()) {
|
||||
return MachoFileSetExtractor.extractSegment(fixedUpProvider,
|
||||
header.getSegment("__" + entry.id()), file.getFSRL(), monitor);
|
||||
}
|
||||
return MachoFileSetExtractor.extractFileSetEntry(fixedUpProvider, entry.offset(),
|
||||
file.getFSRL(), monitor);
|
||||
}
|
||||
catch (MachException e) {
|
||||
throw new IOException(
|
||||
"Invalid Mach-O header detected at 0x%x".formatted(cmd.getFileOffset()));
|
||||
"Invalid Mach-O header detected at 0x%x".formatted(entry.offset()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
|
||||
FileAttributes result = new FileAttributes();
|
||||
FileSetEntryCommand cmd = fsIndex.getMetadata(file);
|
||||
if (cmd != null) {
|
||||
result.add(NAME_ATTR, cmd.getFileSetEntryId().getString());
|
||||
result.add(PATH_ATTR, cmd.getFileSetEntryId().getString());
|
||||
MachoFileSetEntry entry = fsIndex.getMetadata(file);
|
||||
if (entry != null) {
|
||||
result.add(NAME_ATTR, entry.id());
|
||||
result.add(PATH_ATTR, entry.id());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -144,7 +168,7 @@ public class MachoFileSetFileSystem extends AbstractFileSystem<FileSetEntryComma
|
||||
/**
|
||||
* {@return the map of file set entry segments}
|
||||
*/
|
||||
public Map<FileSetEntryCommand, List<SegmentCommand>> getEntrySegmentMap() {
|
||||
public Map<MachoFileSetEntry, List<SegmentCommand>> getEntrySegmentMap() {
|
||||
return entrySegmentMap;
|
||||
}
|
||||
|
||||
@@ -164,6 +188,9 @@ public class MachoFileSetFileSystem extends AbstractFileSystem<FileSetEntryComma
|
||||
fixedUpProvider.close();
|
||||
fixedUpProvider = null;
|
||||
}
|
||||
if (header != null) {
|
||||
header = null;
|
||||
}
|
||||
fsIndex.clear();
|
||||
entrySegmentMap.clear();
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ import docking.action.builder.ActionBuilder;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.context.ProgramLocationActionContext;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.util.bin.format.macho.commands.FileSetEntryCommand;
|
||||
import ghidra.app.util.bin.format.macho.commands.SegmentCommand;
|
||||
import ghidra.app.util.opinion.MachoFileSetExtractLoader;
|
||||
import ghidra.file.formats.ios.fileset.MachoFileSetEntry;
|
||||
import ghidra.file.formats.ios.fileset.MachoFileSetFileSystem;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
import ghidra.framework.plugintool.*;
|
||||
@@ -106,13 +106,12 @@ public class MachoFileSetBuilderPlugin extends Plugin {
|
||||
long refAddr = refAddress.getOffset();
|
||||
try (FileSystemRef fsRef = openMachoFileSet(program, monitor)) {
|
||||
MachoFileSetFileSystem fs = (MachoFileSetFileSystem) fsRef.getFilesystem();
|
||||
Map<FileSetEntryCommand, List<SegmentCommand>> entrySegmentMap =
|
||||
fs.getEntrySegmentMap();
|
||||
Map<MachoFileSetEntry, List<SegmentCommand>> entrySegmentMap = fs.getEntrySegmentMap();
|
||||
String fsPath = null;
|
||||
for (FileSetEntryCommand cmd : entrySegmentMap.keySet()) {
|
||||
for (SegmentCommand segment : entrySegmentMap.get(cmd)) {
|
||||
for (MachoFileSetEntry entry : entrySegmentMap.keySet()) {
|
||||
for (SegmentCommand segment : entrySegmentMap.get(entry)) {
|
||||
if (segment.contains(refAddr)) {
|
||||
fsPath = cmd.getFileSetEntryId().getString();
|
||||
fsPath = entry.id();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user