GP-2071: Refactoring Mach-O things

This commit is contained in:
Ryan Kurtz 2022-05-24 13:53:33 -04:00
parent 2474de38d0
commit ac0d7f6d43
46 changed files with 977 additions and 985 deletions

View File

@ -27,7 +27,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* Represents a mach_header structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* @see <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
*/
public class MachHeader implements StructConverter {
private int magic;
@ -142,7 +142,7 @@ public class MachHeader implements StructConverter {
long currentIndex = _commandIndex;
for (int i = 0; i < nCmds; ++i) {
_reader.setPointerIndex(currentIndex);
LoadCommand lc = LoadCommandTypes.getLoadCommand(_reader, this);
LoadCommand lc = LoadCommandFactory.getLoadCommand(_reader, this);
_commands.add(lc);
currentIndex += lc.getCommandSize();
}

View File

@ -30,9 +30,7 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a build_version_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a build_version_command structure
*/
public class BuildVersionCommand extends LoadCommand {
@ -43,7 +41,7 @@ public class BuildVersionCommand extends LoadCommand {
private BuildToolVersion[] buildToolVersions;
BuildVersionCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
platform = reader.readNextInt();
minos = reader.readNextInt();

View File

@ -30,7 +30,7 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a LC_DYLD_CHAINED_FIXUPS command.
* Represents a dyld_chained_fixups_command structure
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
*/

View File

@ -29,9 +29,7 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a dyld_info_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a dyld_info_command structure
*/
public class DyldInfoCommand extends LoadCommand {
private int rebase_off;
@ -46,7 +44,7 @@ public class DyldInfoCommand extends LoadCommand {
private int export_size;
DyldInfoCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
rebase_off = reader.readNextInt();
rebase_size = reader.readNextInt();

View File

@ -29,15 +29,13 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a dylib_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a dylib_command structure
*/
public class DynamicLibraryCommand extends LoadCommand {
private DynamicLibrary dylib;
DynamicLibraryCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
dylib = new DynamicLibrary(reader, this);
}

View File

@ -29,15 +29,13 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a dylinker_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a dylinker_command structure
*/
public class DynamicLinkerCommand extends LoadCommand {
private LoadCommandString name;
public DynamicLinkerCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
DynamicLinkerCommand(BinaryReader reader) throws IOException {
super(reader);
name = new LoadCommandString(reader, this);
}

View File

@ -34,8 +34,6 @@ import ghidra.util.task.TaskMonitor;
/**
* Represents a dysymtab_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
*/
public class DynamicSymbolTableCommand extends LoadCommand {
@ -66,7 +64,7 @@ public class DynamicSymbolTableCommand extends LoadCommand {
private List<RelocationInfo> localRelocations = new ArrayList<RelocationInfo>();
DynamicSymbolTableCommand(BinaryReader reader, MachHeader header) throws IOException {
initLoadCommand(reader);
super(reader);
ilocalsym = reader.readNextInt();
nlocalsym = reader.readNextInt();

View File

@ -29,9 +29,7 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents an encryption_info_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents an encryption_info_command structure
*/
public class EncryptedInformationCommand extends LoadCommand {
private int cryptoff;
@ -41,7 +39,7 @@ public class EncryptedInformationCommand extends LoadCommand {
private boolean is32bit;
EncryptedInformationCommand(BinaryReader reader, boolean is32bit) throws IOException {
initLoadCommand(reader);
super(reader);
this.is32bit = is32bit;
cryptoff = reader.readNextInt();

View File

@ -29,16 +29,14 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents an entry_point_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents an entry_point_command structure
*/
public class EntryPointCommand extends LoadCommand {
private long entryOffset;
private long stackSize;
EntryPointCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
entryOffset = reader.readNextLong();
stackSize = reader.readNextLong();
}

View File

@ -29,52 +29,63 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a kext_command
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-7195.60.75/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a fileset_entry_command
*/
public class FileSetEntryCommand extends LoadCommand {
private long vmaddr;
private long fileoff;
private String entryName;
private long unknown;
private LoadCommandString entryId;
private int reserved;
boolean is32bit;
public FileSetEntryCommand(BinaryReader reader, boolean is32bit) throws IOException {
initLoadCommand(reader);
this.is32bit = is32bit;
if (is32bit) {
vmaddr = reader.readNextUnsignedInt();
fileoff = reader.readNextUnsignedInt();
unknown = reader.readNextUnsignedInt();
}
else {
vmaddr = reader.readNextLong();
fileoff = reader.readNextLong();
unknown = reader.readNextLong();
}
int stringSize = this.getCommandSize() - (8 + 3 * (is32bit ? 4 : 8));
entryName = reader.readNextAsciiString(stringSize);
}
public String getFileSetEntryName() {
return entryName;
/**
* Creates and parses a new {@link FileSetEntryCommand}
*
* @param reader A {@link BinaryReader reader} that points to the start of the load command
* @throws IOException if an IO-related error occurs while parsing
*/
FileSetEntryCommand(BinaryReader reader) throws IOException {
super(reader);
vmaddr = reader.readNextLong();
fileoff = reader.readNextLong();
entryId = new LoadCommandString(reader, this);
reserved = reader.readNextInt();
}
/**
* Gets the virtual address of the DYLIB
*
* @return The virtual address of the DYLIB
*/
public long getVMaddress() {
return vmaddr;
}
/**
* Gets the file offset of the DYLIB
*
* @return the file offset of the DYLIB
*/
public long getFileOffset() {
return fileoff;
}
public long getUnknown() {
return unknown;
/**
* Gets the identifier of the DYLIB
*
* @return the identifier of the DYLIB
*/
public LoadCommandString getFileSetEntryId() {
return entryId;
}
/**
* Gets the reserved field (should just be padding)
*
* @return The reserved field
*/
public int getReserved() {
return reserved;
}
@Override
@ -82,20 +93,10 @@ public class FileSetEntryCommand extends LoadCommand {
StructureDataType struct = new StructureDataType(getCommandName(), 0);
struct.add(DWORD, "cmd", null);
struct.add(DWORD, "cmdsize", null);
if (is32bit) {
struct.add(DWORD, "vmaddr", null);
struct.add(DWORD, "fileoff", null);
struct.add(DWORD, "unknown", null);
}
else {
struct.add(QWORD, "vmaddr", null);
struct.add(QWORD, "fileoff", null);
struct.add(QWORD, "unknown", null);
}
int stringSize = getCommandSize() - (8 + 3 * (is32bit ? 4 : 8));
struct.add(new StringDataType(), stringSize, "fileSetEntryname", null);
struct.add(QWORD, "vmaddr", null);
struct.add(QWORD, "fileoff", null);
struct.add(entryId.toDataType(), "entry_id", null);
struct.add(DWORD, "reserved", null);
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
@ -115,7 +116,7 @@ public class FileSetEntryCommand extends LoadCommand {
Address addr = baseAddress.getNewAddress(getStartIndex());
DataType fileSetEntryDT = toDataType();
api.createData(addr, fileSetEntryDT);
api.setPlateComment(addr, getFileSetEntryName());
api.setPlateComment(addr, entryId.getString());
}
}
catch (Exception e) {
@ -125,6 +126,6 @@ public class FileSetEntryCommand extends LoadCommand {
@Override
public String toString() {
return getFileSetEntryName();
return entryId.getString();
}
}

View File

@ -29,16 +29,14 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a fvmfile_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a fvmfile_command structure
*/
public class FixedVirtualMemoryFileCommand extends LoadCommand {
private LoadCommandString name;
private int header_addr;
public FixedVirtualMemoryFileCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
}
/**

View File

@ -29,16 +29,14 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a linkedit_data_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a linkedit_data_command structure
*/
public class LinkEditDataCommand extends LoadCommand {
private int dataoff;
private int datasize;
LinkEditDataCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
dataoff = reader.readNextInt();
datasize = reader.readNextInt();
}
@ -66,7 +64,7 @@ public class LinkEditDataCommand extends LoadCommand {
Address address = baseAddress.getNewAddress(getStartIndex());
api.createData(address, toDataType());
api.setPlateComment(address,
LoadCommandTypes.getLoadCommentTypeName(getCommandType()));
LoadCommandTypes.getLoadCommandName(getCommandType()));
//TODO markup actual data

View File

@ -31,9 +31,7 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a linker_option_command structure
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a linker_option_command structure
*/
public class LinkerOptionCommand extends LoadCommand {
@ -41,7 +39,7 @@ public class LinkerOptionCommand extends LoadCommand {
private List<String> linkerOptions;
LinkerOptionCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
count = reader.readNextInt();
linkerOptions = new ArrayList<>(count);
long readerIndex = reader.getPointerIndex();

View File

@ -28,23 +28,30 @@ import ghidra.program.model.listing.ProgramModule;
import ghidra.util.task.TaskMonitor;
/**
* Represents a load_command structure.
* Represents a load_command structure
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* @see <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
*/
public abstract class LoadCommand implements StructConverter {
private long startIndex;
private int cmd;
private int cmdsize;
protected void initLoadCommand(BinaryReader reader) throws IOException {
/**
* Creates a new {@link LoadCommand}
*
* @param reader A {@link BinaryReader} that points to the start of the load command
* @throws IOException if there was an IO-related error
*/
public LoadCommand(BinaryReader reader) throws IOException {
startIndex = reader.getPointerIndex();
cmd = reader.readNextInt();
cmdsize = reader.readNextInt();
}
/**
* Returns the binary start index of this load command.
* Returns the binary start index of this load command
*
* @return the binary start index of this load command
*/
public long getStartIndex() {
@ -52,29 +59,33 @@ public abstract class LoadCommand implements StructConverter {
}
/**
* Type of load command
* @return type of load command
* Gets the type of this load command
*
* @return The type of this load command
*/
public int getCommandType() {
return cmd;
}
/**
* Total size of command in bytes
* @return total size of command in bytes
* Gets the size of this load command in bytes
*
* @return The size of this load command in bytes
*/
public int getCommandSize() {
return cmdsize;
}
/**
* Returns the name of this command.
* @return the name of this command
* Gets the name of this load command
*
* @return The name of this load command
*/
public abstract String getCommandName();
/**
* Mark-up the program with the data structures for this load command.
* Marks-up the program with the data structures for this load command
*
* @param header the mach header
* @param api the flat program api
* @param baseAddress the base address to apply the mark-up

View File

@ -0,0 +1,142 @@
/* ###
* 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.bin.format.macho.commands;
import static ghidra.app.util.bin.format.macho.commands.LoadCommandTypes.*;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.macho.MachException;
import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.bin.format.macho.threadcommand.ThreadCommand;
import ghidra.util.Msg;
/**
* A factory used to create {@link LoadCommand}s
*/
public class LoadCommandFactory {
/**
* Creates a {@link LoadCommand}
*
* @param reader A {@link BinaryReader} positioned at the start of the load command to create
* @param header The {@link MachHeader} that contains the load command to create
* @return A {@link LoadCommand}
* @throws IOException if there was an IO-related error
* @throws MachException if there was a problem parsing the load command
*/
public static LoadCommand getLoadCommand(BinaryReader reader, MachHeader header)
throws IOException, MachException {
int type = reader.peekNextInt();
switch (type) {
case LC_SEGMENT:
return new SegmentCommand(reader, header.is32bit());
case LC_SYMTAB:
return new SymbolTableCommand(reader, header);
case LC_SYMSEG:
return new SymbolCommand(reader);
case LC_THREAD:
case LC_UNIXTHREAD:
return new ThreadCommand(reader, header);
case LC_LOADFVMLIB:
case LC_IDFVMLIB:
return new FixedVirtualMemorySharedLibraryCommand(reader);
case LC_IDENT:
return new IdentCommand(reader);
case LC_FVMFILE:
return new FixedVirtualMemoryFileCommand(reader);
case LC_PREPAGE:
return new UnsupportedLoadCommand(reader, type);
case LC_DYSYMTAB:
return new DynamicSymbolTableCommand(reader, header);
case LC_LOAD_DYLIB:
case LC_ID_DYLIB:
case LC_LOAD_UPWARD_DYLIB:
case LC_DYLD_ENVIRONMENT:
return new DynamicLibraryCommand(reader);
case LC_LOAD_DYLINKER:
case LC_ID_DYLINKER:
return new DynamicLinkerCommand(reader);
case LC_PREBOUND_DYLIB:
return new PreboundDynamicLibraryCommand(reader);
case LC_ROUTINES:
return new RoutinesCommand(reader, header.is32bit());
case LC_SUB_FRAMEWORK:
return new SubFrameworkCommand(reader);
case LC_SUB_UMBRELLA:
return new SubUmbrellaCommand(reader);
case LC_SUB_CLIENT:
return new SubClientCommand(reader);
case LC_SUB_LIBRARY:
return new SubLibraryCommand(reader);
case LC_TWOLEVEL_HINTS:
return new TwoLevelHintsCommand(reader);
case LC_PREBIND_CKSUM:
return new PrebindChecksumCommand(reader);
case LC_LOAD_WEAK_DYLIB:
return new DynamicLibraryCommand(reader);
case LC_SEGMENT_64:
return new SegmentCommand(reader, header.is32bit());
case LC_ROUTINES_64:
return new RoutinesCommand(reader, header.is32bit());
case LC_UUID:
return new UuidCommand(reader);
case LC_RPATH:
return new RunPathCommand(reader);
case LC_CODE_SIGNATURE:
case LC_SEGMENT_SPLIT_INFO:
case LC_DATA_IN_CODE:
case LC_OPTIMIZATION_HINT:
case LC_DYLIB_CODE_SIGN_DRS:
return new LinkEditDataCommand(reader);
case LC_REEXPORT_DYLIB:
return new DynamicLibraryCommand(reader);
case LC_ENCRYPTION_INFO:
case LC_ENCRYPTION_INFO_64:
return new EncryptedInformationCommand(reader, header.is32bit());
case LC_DYLD_INFO:
case LC_DYLD_INFO_ONLY:
return new DyldInfoCommand(reader);
case LC_VERSION_MIN_MACOSX:
case LC_VERSION_MIN_IPHONEOS:
case LC_VERSION_MIN_TVOS:
case LC_VERSION_MIN_WATCHOS:
return new VersionMinCommand(reader);
case LC_FUNCTION_STARTS:
return new FunctionStartsCommand(reader);
case LC_MAIN:
return new EntryPointCommand(reader);
case LC_SOURCE_VERSION:
return new SourceVersionCommand(reader);
case LC_LAZY_LOAD_DYLIB:
return new DynamicLibraryCommand(reader);
case LC_LINKER_OPTIONS:
return new LinkerOptionCommand(reader);
case LC_BUILD_VERSION:
return new BuildVersionCommand(reader);
case LC_DYLD_EXPORTS_TRIE:
return new LinkEditDataCommand(reader);
case LC_DYLD_CHAINED_FIXUPS:
return new DyldChainedFixupsCommand(reader);
case LC_FILESET_ENTRY:
return new FileSetEntryCommand(reader);
default:
Msg.warn(header, "Unsupported load command " + Integer.toHexString(type));
return new UnsupportedLoadCommand(reader, type);
}
}
}

View File

@ -24,9 +24,9 @@ import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* Represents an lc_str union.
* Represents an lc_str union
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* @see LoadCommand
*/
public class LoadCommandString implements StructConverter {
private int offset;

View File

@ -16,296 +16,209 @@
*/
package ghidra.app.util.bin.format.macho.commands;
import java.io.IOException;
import java.lang.reflect.Field;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.macho.MachException;
import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.bin.format.macho.threadcommand.ThreadCommand;
import ghidra.util.Msg;
/**
* Constants for the cmd field of all load commands, the type
* {@link LoadCommand} types
*/
public final class LoadCommandTypes {
public static LoadCommand getLoadCommand(BinaryReader reader, MachHeader header)
throws IOException, MachException {
int type = reader.peekNextInt();
switch (type) {
case LC_SEGMENT: {
return new SegmentCommand(reader, header.is32bit());
}
case LC_SYMTAB: {
return new SymbolTableCommand(reader, header);
}
case LC_SYMSEG: {
return new SymbolCommand(reader);
}
case LC_THREAD:
case LC_UNIXTHREAD: {
return new ThreadCommand(reader, header);
}
case LC_LOADFVMLIB:
case LC_IDFVMLIB: {
return new FixedVirtualMemorySharedLibraryCommand(reader);
}
case LC_IDENT: {
return new IdentCommand(reader);
}
case LC_FVMFILE: {
return new FixedVirtualMemoryFileCommand(reader);
}
case LC_PREPAGE: {
return new UnsupportedLoadCommand(reader, type);
}
case LC_DYSYMTAB: {
return new DynamicSymbolTableCommand(reader, header);
}
case LC_LOAD_DYLIB:
case LC_ID_DYLIB:
case LC_LOAD_UPWARD_DYLIB:
case LC_DYLD_ENVIRONMENT: {
return new DynamicLibraryCommand(reader);
}
case LC_LOAD_DYLINKER:
case LC_ID_DYLINKER: {
return new DynamicLinkerCommand(reader);
}
case LC_PREBOUND_DYLIB: {
return new PreboundDynamicLibraryCommand(reader);
}
case LC_ROUTINES: {
return new RoutinesCommand(reader, header.is32bit());
}
case LC_SUB_FRAMEWORK: {
return new SubFrameworkCommand(reader);
}
case LC_SUB_UMBRELLA: {
return new SubUmbrellaCommand(reader);
}
case LC_SUB_CLIENT: {
return new SubClientCommand(reader);
}
case LC_SUB_LIBRARY: {
return new SubLibraryCommand(reader);
}
case LC_TWOLEVEL_HINTS: {
return new TwoLevelHintsCommand(reader);
}
case LC_PREBIND_CKSUM: {
return new PrebindChecksumCommand(reader);
}
case LC_LOAD_WEAK_DYLIB: {
return new DynamicLibraryCommand(reader);
}
case LC_SEGMENT_64: {
return new SegmentCommand(reader, header.is32bit());
}
case LC_ROUTINES_64: {
return new RoutinesCommand(reader, header.is32bit());
}
case LC_UUID: {
return new UuidCommand(reader);
}
case LC_RPATH: {
return new RunPathCommand(reader);
}
case LC_CODE_SIGNATURE:
case LC_SEGMENT_SPLIT_INFO:
case LC_DATA_IN_CODE:
case LC_OPTIMIZATION_HINT:
case LC_DYLIB_CODE_SIGN_DRS: {
return new LinkEditDataCommand(reader);
}
case LC_REEXPORT_DYLIB: {
return new DynamicLibraryCommand(reader);
}
case LC_ENCRYPTION_INFO:
case LC_ENCRYPTION_INFO_64: {
return new EncryptedInformationCommand(reader, header.is32bit());
}
case LC_DYLD_INFO:
case LC_DYLD_INFO_ONLY: {
return new DyldInfoCommand(reader);
}
case LC_VERSION_MIN_MACOSX:
case LC_VERSION_MIN_IPHONEOS:
case LC_VERSION_MIN_TVOS:
case LC_VERSION_MIN_WATCHOS: {
return new VersionMinCommand(reader);
}
case LC_FUNCTION_STARTS: {
return new FunctionStartsCommand(reader);
}
case LC_MAIN: {
return new EntryPointCommand(reader);
}
case LC_SOURCE_VERSION: {
return new SourceVersionCommand(reader);
}
case LC_LAZY_LOAD_DYLIB: {
return new DynamicLibraryCommand(reader);
}
case LC_BUILD_VERSION: {
return new BuildVersionCommand(reader);
}
case LC_LINKER_OPTIONS: {
return new LinkerOptionCommand(reader);
}
case LC_DYLD_EXPORTS_TRIE:
return new LinkEditDataCommand(reader);
case LC_DYLD_CHAINED_FIXUPS:
return new DyldChainedFixupsCommand(reader);
case LC_FILESET_ENTRY:
return new FileSetEntryCommand(reader, header.is32bit());
default: {
Msg.warn(header, "Unsupported load command " + Integer.toHexString(type));
return new UnsupportedLoadCommand(reader, type);
}
}
}
//@formatter:off
/**
* After MacOS X 10.1 when a new load command is added that is required to be
* understood by the dynamic linker for the image to execute properly the
* LC_REQ_DYLD bit will be or'ed into the load command constant. If the dynamic
* linker sees such a load command it it does not understand will issue a
* "unknown load command required for execution" error and refuse to use the
* image. Other load commands without this bit that are not understood will
* simply be ignored.
*/
public final static int LC_REQ_DYLD = 0x80000000;
/** segment of this file to be mapped */
public final static int LC_SEGMENT = 0x1;
/** link-edit stab symbol table info */
public final static int LC_SYMTAB = 0x2;
/** link-edit gdb symbol table info (obsolete) */
public final static int LC_SYMSEG = 0x3;
/** thread */
public final static int LC_THREAD = 0x4;
/** unix thread (includes a stack) */
public final static int LC_UNIXTHREAD = 0x5;
/** load a specified fixed VM shared library */
public final static int LC_LOADFVMLIB = 0x6;
/** fixed VM shared library identification */
public final static int LC_IDFVMLIB = 0x7;
/** object identification info (obsolete) */
public final static int LC_IDENT = 0x8;
/** fixed VM file inclusion (internal use) */
public final static int LC_FVMFILE = 0x9;
/** prepage command (internal use) */
public final static int LC_PREPAGE = 0xa;
/** dynamic link-edit symbol table info */
public final static int LC_DYSYMTAB = 0xb;
/** load a dynamically linked shared library */
public final static int LC_LOAD_DYLIB = 0xc;
/** dynamically linked shared lib ident */
public final static int LC_ID_DYLIB = 0xd;
/** load a dynamic linker */
public final static int LC_LOAD_DYLINKER = 0xe;
/** dynamic linker identification */
public final static int LC_ID_DYLINKER = 0xf;
/** modules prebound for a dynamically linked shared library */
public final static int LC_PREBOUND_DYLIB = 0x10;
/** image routines */
public final static int LC_ROUTINES = 0x11;
/** sub framework */
public final static int LC_SUB_FRAMEWORK = 0x12;
/** sub umbrella */
public final static int LC_SUB_UMBRELLA = 0x13;
/** sub client */
public final static int LC_SUB_CLIENT = 0x14;
/** sub library */
public final static int LC_SUB_LIBRARY = 0x15;
/** two-level namespace lookup hints */
public final static int LC_TWOLEVEL_HINTS = 0x16;
/** prebind checksum */
public final static int LC_PREBIND_CKSUM = 0x17;
/** load a dynamically linked shared library that is allowed to be missing (all symbols are weak imported) */
public final static int LC_LOAD_WEAK_DYLIB = 0x18 | LC_REQ_DYLD;
/** 64-bit segment of this file to be mapped */
public final static int LC_SEGMENT_64 = 0x19;
/** 64-bit image routines */
public final static int LC_ROUTINES_64 = 0x1a;
/** specifies the 128 bit UUID for an image */
public final static int LC_UUID = 0x1b;
/** Run path additions */
public final static int LC_RPATH = 0x1c | LC_REQ_DYLD;
/** local of code signature */
public final static int LC_CODE_SIGNATURE = 0x1d;
/** local of info to split segments */
public final static int LC_SEGMENT_SPLIT_INFO = 0x1e;
/** load and re-export dylib */
public final static int LC_REEXPORT_DYLIB = 0x1f | LC_REQ_DYLD;
/** Delay load of dylib until first use */
public final static int LC_LAZY_LOAD_DYLIB = 0x20;
/** encrypted segment information */
public final static int LC_ENCRYPTION_INFO = 0x21;
/** compressed dyld information */
public final static int LC_DYLD_INFO = 0x22;
/** compressed dyld information only */
public final static int LC_DYLD_INFO_ONLY = 0x22 | LC_REQ_DYLD;
/** Load upward dylib */
public final static int LC_LOAD_UPWARD_DYLIB = 0x23 | LC_REQ_DYLD;
/** Build for MacOSX min OS version */
public final static int LC_VERSION_MIN_MACOSX = 0x24;
/** Build for iPhoneOS min OS version */
public final static int LC_VERSION_MIN_IPHONEOS = 0x25;
/** Compressed table of function start addresses */
public final static int LC_FUNCTION_STARTS = 0x26;
/** String for DYLD to treat environment variable */
public final static int LC_DYLD_ENVIRONMENT = 0x27;
/** Replacement for LC_UNIXTHREAD */
public final static int LC_MAIN = 0x28 | LC_REQ_DYLD;
/** Table of non-instructions in __text */
public final static int LC_DATA_IN_CODE = 0x29;
/** Source version used to build binary */
public final static int LC_SOURCE_VERSION = 0x2a;
/** Code signing DRs copied from linked dylibs */
public final static int LC_DYLIB_CODE_SIGN_DRS = 0x2b;
/** 64-bit encrypted segment information */
public final static int LC_ENCRYPTION_INFO_64 = 0x2c;
/** Linker options in MH_OBJECT files */
public final static int LC_LINKER_OPTIONS = 0x2d;
/** Optimization hints in MH_OBJECT files */
public final static int LC_OPTIMIZATION_HINT = 0x2e;
/** Build for AppleTV min OS version */
public final static int LC_VERSION_MIN_TVOS = 0x2f;
/** Build for Watch min OS version */
public final static int LC_VERSION_MIN_WATCHOS = 0x30;
/** Arbitrary data included within a Mach-O file */
public final static int LC_NOTE = 0x31;
/** Build for platform min OS version */
public final static int LC_BUILD_VERSION = 0x32;
/** used with LinkeditDataCommand, payload is trie **/
/** Used with linkedit_data_command, payload is trie **/
public final static int LC_DYLD_EXPORTS_TRIE = 0x33 | LC_REQ_DYLD;
/** used with LinkeditDataCommand **/
/** Used with linkedit_data_command **/
public final static int LC_DYLD_CHAINED_FIXUPS = 0x34 | LC_REQ_DYLD;
/** used with fileset_entry_command **/
/** Used with fileset_entry_command **/
public final static int LC_FILESET_ENTRY = 0x35 | LC_REQ_DYLD;
//@formatter:on
/**
* Returns a string for the given load command type.
* @param type the load command type
* @return a string for the given load command type
* Gets the name of the given load command type
*
* @param type The load command type
* @return The name of the given load command type
*/
public final static String getLoadCommentTypeName(int type) {
Field[] fields = LoadCommandTypes.class.getDeclaredFields();
for (Field field : fields) {
if (field.getName().startsWith("LC_")) {
try {
Integer value = (Integer) field.get(null);
if (type == value) {
return field.getName();
}
}
catch (Exception e) {
break;
public final static String getLoadCommandName(int type) {
for (Field field : LoadCommandTypes.class.getDeclaredFields()) {
if (!field.getName().startsWith("LC_")) {
continue;
}
try {
Integer value = (Integer) field.get(null);
if (type == value) {
return field.getName();
}
}
catch (Exception e) {
break;
}
}
return "Unknown load command type: " + Integer.toHexString(type);
}
}

View File

@ -30,7 +30,7 @@ import ghidra.util.task.TaskMonitor;
public abstract class ObsoleteCommand extends LoadCommand {
public ObsoleteCommand(BinaryReader reader) throws IOException, MachException {
initLoadCommand(reader);
super(reader);
throw new ObsoleteException();
}

View File

@ -29,15 +29,13 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a prebind_cksum_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a prebind_cksum_command structure
*/
public class PrebindChecksumCommand extends LoadCommand {
private int cksum;
PrebindChecksumCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
cksum = reader.readNextInt();
}

View File

@ -29,9 +29,7 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a prebound_dylib_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a prebound_dylib_command structure
*/
public class PreboundDynamicLibraryCommand extends LoadCommand {
private LoadCommandString name;
@ -39,7 +37,7 @@ public class PreboundDynamicLibraryCommand extends LoadCommand {
private LoadCommandString linkedModules;
PreboundDynamicLibraryCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
name = new LoadCommandString(reader, this);
nmodules = reader.readNextInt();
linkedModules = new LoadCommandString(reader, this);

View File

@ -29,9 +29,7 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a routines_command and routines_command_64 structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a routines_command and routines_command_64 structure
*/
public class RoutinesCommand extends LoadCommand {
private long init_address;
@ -46,7 +44,7 @@ public class RoutinesCommand extends LoadCommand {
private boolean is32bit;
RoutinesCommand(BinaryReader reader, boolean is32bit) throws IOException {
initLoadCommand(reader);
super(reader);
this.is32bit = is32bit;
if (is32bit) {
init_address = reader.readNextUnsignedInt();

View File

@ -29,15 +29,13 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a rpath_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a rpath_command structure
*/
public class RunPathCommand extends LoadCommand {
private LoadCommandString path;
RunPathCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
path = new LoadCommandString(reader, this);
}

View File

@ -31,9 +31,7 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a segment_command and segment_command_64 structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a segment_command and segment_command_64 structure
*/
public class SegmentCommand extends LoadCommand {
@ -51,7 +49,7 @@ public class SegmentCommand extends LoadCommand {
private List<Section> sections = new ArrayList<Section>();
public SegmentCommand(BinaryReader reader, boolean is32bit) throws IOException {
initLoadCommand(reader);
super(reader);
this.is32bit = is32bit;
segname = reader.readNextAsciiString(MachConstants.NAME_LENGTH);

View File

@ -29,15 +29,13 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a source_version_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a source_version_command structure
*/
public class SourceVersionCommand extends LoadCommand {
private long version;
SourceVersionCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
version = reader.readNextLong();
}

View File

@ -29,15 +29,13 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a sub_client_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a sub_client_command structure
*/
public class SubClientCommand extends LoadCommand {
private LoadCommandString client;
SubClientCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
client = new LoadCommandString(reader, this);
}

View File

@ -29,15 +29,13 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a sub_framework_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a sub_framework_command structure
*/
public class SubFrameworkCommand extends LoadCommand {
private LoadCommandString umbrella;
SubFrameworkCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
umbrella = new LoadCommandString(reader, this);
}

View File

@ -29,15 +29,13 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a sub_library_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a sub_library_command structure
*/
public class SubLibraryCommand extends LoadCommand {
private LoadCommandString sub_library;
SubLibraryCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
sub_library = new LoadCommandString(reader, this);
}

View File

@ -29,15 +29,13 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a sub_umbrella_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a sub_umbrella_command structure
*/
public class SubUmbrellaCommand extends LoadCommand {
private LoadCommandString sub_umbrella;
SubUmbrellaCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
sub_umbrella = new LoadCommandString(reader, this);
}

View File

@ -36,9 +36,7 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a symtab_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a symtab_command structure
*/
public class SymbolTableCommand extends LoadCommand {
private int symoff;
@ -49,7 +47,7 @@ public class SymbolTableCommand extends LoadCommand {
private List<NList> symbols = new ArrayList<NList>();
public SymbolTableCommand(BinaryReader reader, MachHeader header) throws IOException {
initLoadCommand(reader);
super(reader);
symoff = reader.readNextInt();
nsyms = reader.readNextInt();

View File

@ -32,9 +32,7 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a twolevel_hints_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a twolevel_hints_command structure
*/
public class TwoLevelHintsCommand extends LoadCommand {
private int offset;
@ -42,7 +40,7 @@ public class TwoLevelHintsCommand extends LoadCommand {
private List<TwoLevelHint> hints = new ArrayList<TwoLevelHint>();
TwoLevelHintsCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
offset = reader.readNextInt();
nhints = reader.readNextInt();

View File

@ -32,7 +32,7 @@ public class UnsupportedLoadCommand extends LoadCommand {
private int type;
UnsupportedLoadCommand(BinaryReader reader, int type) throws IOException {
initLoadCommand(reader);
super(reader);
this.type = type;
}

View File

@ -29,15 +29,13 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a uuid_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a uuid_command structure
*/
public class UuidCommand extends LoadCommand {
private byte[] uuid;
UuidCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
uuid = reader.readNextByteArray(16);
}

View File

@ -29,9 +29,7 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a version_min_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a version_min_command structure
*/
public class VersionMinCommand extends LoadCommand {
@ -39,7 +37,7 @@ public class VersionMinCommand extends LoadCommand {
private int sdk;
VersionMinCommand(BinaryReader reader) throws IOException {
initLoadCommand(reader);
super(reader);
version = reader.readNextInt();
sdk = reader.readNextInt();
@ -61,7 +59,7 @@ public class VersionMinCommand extends LoadCommand {
Address addr = baseAddress.getNewAddress(getStartIndex());
api.createData(addr, toDataType());
api.setPlateComment(addr,
LoadCommandTypes.getLoadCommentTypeName(getCommandType()));
LoadCommandTypes.getLoadCommandName(getCommandType()));
}
}
catch (Exception e) {

View File

@ -147,7 +147,7 @@ abstract public class AbstractDyldInfoState {
header.getLoadCommands(FileSetEntryCommand.class);
for (FileSetEntryCommand fileSetEntryCommand : fileSetEntries) {
if (fileSetEntryCommand.getFileOffset() == segment.getFileOffset()) {
return fileSetEntryCommand.getFileSetEntryName();
return fileSetEntryCommand.getFileSetEntryId().getString();
}
}
return segment.getSegmentName();

View File

@ -20,7 +20,7 @@ package ghidra.app.util.bin.format.macho.prelink;
* http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/libkern/libkern/prelink.h
*
*/
public final class PrelinkConstants {
public final class MachoPrelinkConstants {
public static final String TITLE = "iOS Prelink";

View File

@ -17,7 +17,7 @@ package ghidra.app.util.bin.format.macho.prelink;
import java.util.*;
public class PrelinkMap {
public class MachoPrelinkMap {
private Map<String, Object> map = new HashMap<String, Object>();
@ -30,12 +30,12 @@ public class PrelinkMap {
public void put( String key, boolean value ) {
map.put( key, value );
}
public void put( String key, PrelinkMap value ) {
public void put( String key, MachoPrelinkMap value ) {
map.put( key, value );
}
public String getPrelinkBundlePath() {
Object value = map.get( PrelinkConstants.kPrelinkBundlePathKey );
Object value = map.get( MachoPrelinkConstants.kPrelinkBundlePathKey );
if ( value instanceof String ) {
return (String)value;
}
@ -43,7 +43,7 @@ public class PrelinkMap {
}
public String getPrelinkUUID() {
Object value = map.get( PrelinkConstants.kPrelinkInterfaceUUIDKey );
Object value = map.get( MachoPrelinkConstants.kPrelinkInterfaceUUIDKey );
if ( value instanceof String ) {
return (String)value;
}
@ -51,7 +51,7 @@ public class PrelinkMap {
}
public long getPrelinkKmodInfo() {
Object value = map.get( PrelinkConstants.kPrelinkKmodInfoKey );
Object value = map.get( MachoPrelinkConstants.kPrelinkKmodInfoKey );
if ( value instanceof Long ) {
return (Long)value;
}
@ -62,7 +62,7 @@ public class PrelinkMap {
}
public long getPrelinkExecutable() {
Object value = map.get( PrelinkConstants.kPrelinkExecutableKey );
Object value = map.get( MachoPrelinkConstants.kPrelinkExecutableKey );
if ( value instanceof Long ) {
return (Long)value;
}
@ -73,7 +73,7 @@ public class PrelinkMap {
}
public long getPrelinkExecutableSize() {
Object value = map.get( PrelinkConstants.kPrelinkExecutableSizeKey );
Object value = map.get( MachoPrelinkConstants.kPrelinkExecutableSizeKey );
if ( value instanceof Long ) {
return (Long)value;
}
@ -84,7 +84,7 @@ public class PrelinkMap {
}
public long getPrelinkExecutableLoadAddr() {
Object value = map.get( PrelinkConstants.kPrelinkExecutableLoadKey );
Object value = map.get( MachoPrelinkConstants.kPrelinkExecutableLoadKey );
if ( value instanceof Long ) {
return (Long)value;
}
@ -95,7 +95,7 @@ public class PrelinkMap {
}
public long getPrelinkModuleIndex() {
Object value = map.get(PrelinkConstants.kPrelinkModuleIndexKey);
Object value = map.get(MachoPrelinkConstants.kPrelinkModuleIndexKey);
if (value instanceof Long) {
return (Long) value;
}

View File

@ -26,11 +26,10 @@ import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.bin.format.macho.Section;
import ghidra.app.util.bin.format.macho.commands.SegmentCommand;
import ghidra.util.NumericUtilities;
import ghidra.util.SystemUtilities;
import ghidra.util.task.TaskMonitor;
import ghidra.util.xml.XmlUtilities;
public class PrelinkParser {
public class MachoPrelinkParser {
private static final String TAG_DATA = "data";
private static final String TAG_FALSE = "false";
@ -47,12 +46,12 @@ public class PrelinkParser {
private MachHeader mainHeader;
private ByteProvider provider;
public PrelinkParser(MachHeader mainHeader, ByteProvider provider) {
public MachoPrelinkParser(MachHeader mainHeader, ByteProvider provider) {
this.mainHeader = mainHeader;
this.provider = provider;
}
public List<PrelinkMap> parse(TaskMonitor monitor)
public List<MachoPrelinkMap> parse(TaskMonitor monitor)
throws IOException, JDOMException, NoPreLinkSectionException {
InputStream inputStream = findPrelinkInputStream();
@ -62,7 +61,7 @@ public class PrelinkParser {
Document doc = sax.build(inputStream);
Element root = doc.getRootElement();
List<PrelinkMap> list = new ArrayList<PrelinkMap>();
List<MachoPrelinkMap> list = new ArrayList<MachoPrelinkMap>();
if (root.getName().equals(TAG_ARRAY)) { // iOS version before 4.x
process(root.getChildren(), list, monitor);
@ -87,7 +86,7 @@ public class PrelinkParser {
return list;
}
private void processTopDict(TaskMonitor monitor, List<PrelinkMap> list,
private void processTopDict(TaskMonitor monitor, List<MachoPrelinkMap> list,
Element dictRootElement) {
Iterator<?> iterator = dictRootElement.getChildren().iterator();
while (iterator.hasNext()) {
@ -101,22 +100,22 @@ public class PrelinkParser {
}
}
private void processKey(TaskMonitor monitor, List<PrelinkMap> list, Iterator<?> iterator,
private void processKey(TaskMonitor monitor, List<MachoPrelinkMap> list, Iterator<?> iterator,
Element element) {
String value = element.getValue();
if (value.equals(PrelinkConstants.kPrelinkPersonalitiesKey)) {
if (value.equals(MachoPrelinkConstants.kPrelinkPersonalitiesKey)) {
Element arrayElement = (Element) iterator.next();
if (arrayElement.getChildren().size() == 0) {
//should be empty...
}
}
else if (value.equals(PrelinkConstants.kPrelinkInfoDictionaryKey)) {
else if (value.equals(MachoPrelinkConstants.kPrelinkInfoDictionaryKey)) {
Element arrayElement = (Element) iterator.next();
process(arrayElement.getChildren(), list, monitor);
}
}
private void process(List<?> children, List<PrelinkMap> list, TaskMonitor monitor) {
private void process(List<?> children, List<MachoPrelinkMap> list, TaskMonitor monitor) {
monitor.setMessage("Processing prelink information...");
for (int i = 0; i < children.size(); ++i) {
@ -125,14 +124,14 @@ public class PrelinkParser {
}
Element element = (Element) children.get(i);
if (element.getName().equals(TAG_DICT)) {
PrelinkMap map = processElement(element, monitor);
MachoPrelinkMap map = processElement(element, monitor);
list.add(map);
}
}
}
private PrelinkMap processElement(Element parentElement, TaskMonitor monitor) {
PrelinkMap map = new PrelinkMap();
private MachoPrelinkMap processElement(Element parentElement, TaskMonitor monitor) {
MachoPrelinkMap map = new MachoPrelinkMap();
Iterator<?> iterator = parentElement.getChildren().iterator();
while (iterator.hasNext()) {
if (monitor.isCancelled()) {
@ -150,7 +149,7 @@ public class PrelinkParser {
return map;
}
private String processValue(Element keyElement, Element valueElement, PrelinkMap map,
private String processValue(Element keyElement, Element valueElement, MachoPrelinkMap map,
TaskMonitor monitor) {
String key = keyElement.getValue();
if (valueElement.getName().equals(TAG_STRING)) {
@ -172,7 +171,7 @@ public class PrelinkParser {
return valueElement.getValue();
}
else if (valueElement.getName().equals(TAG_DICT)) {
PrelinkMap dictMap = processElement(valueElement, monitor);
MachoPrelinkMap dictMap = processElement(valueElement, monitor);
map.put(key, dictMap);
return dictMap.toString();
}
@ -187,7 +186,7 @@ public class PrelinkParser {
}
}
private String processString(PrelinkMap map, String key, Element valueElement) {
private String processString(MachoPrelinkMap map, String key, Element valueElement) {
String value = valueElement.getValue();
String id = valueElement.getAttributeValue("ID");
String idref = valueElement.getAttributeValue("IDREF");
@ -204,7 +203,7 @@ public class PrelinkParser {
return value;
}
private String processInteger(PrelinkMap map, String key, Element valueElement) {
private String processInteger(MachoPrelinkMap map, String key, Element valueElement) {
String value = valueElement.getValue();
String id = valueElement.getAttributeValue("ID");
String idref = valueElement.getAttributeValue("IDREF");
@ -214,6 +213,7 @@ public class PrelinkParser {
numericValue = NumericUtilities.parseHexLong(value);
}
catch (Exception e) {
// do nothing
}
if (id != null) {
@ -229,7 +229,7 @@ public class PrelinkParser {
return value;
}
private String processArray(Element arrayElement, PrelinkMap map, TaskMonitor monitor) {
private String processArray(Element arrayElement, MachoPrelinkMap map, TaskMonitor monitor) {
if (!arrayElement.getName().equals(TAG_ARRAY)) {
throw new RuntimeException("not an array element");
}
@ -257,9 +257,9 @@ public class PrelinkParser {
InputStream prelinkInputStream = null;
List<SegmentCommand> segments = mainHeader.getLoadCommands(SegmentCommand.class);
for (SegmentCommand segment : segments) {
if (segment.getSegmentName().equals(PrelinkConstants.kPrelinkSegment_iOS_1x) ||
segment.getSegmentName().equals(PrelinkConstants.kPrelinkInfoSegment)) {
Section section = segment.getSectionByName(PrelinkConstants.kPrelinkInfoSection);
if (segment.getSegmentName().equals(MachoPrelinkConstants.kPrelinkSegment_iOS_1x) ||
segment.getSegmentName().equals(MachoPrelinkConstants.kPrelinkInfoSegment)) {
Section section = segment.getSectionByName(MachoPrelinkConstants.kPrelinkInfoSection);
if (section != null && section.getSize() > 0) {
byte[] bytes = provider.readBytes(section.getOffset(), section.getSize() - 1);
@ -283,8 +283,6 @@ public class PrelinkParser {
}
// <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
debug(bytes);
//fix bytes so XML will parse...
prelinkInputStream = new ByteArrayInputStream(trimmed.getBytes());
@ -297,21 +295,4 @@ public class PrelinkParser {
}
return prelinkInputStream;
}
private void debug(byte[] bytes) {
try {
if (SystemUtilities.isInDevelopmentMode()) {
File file = File.createTempFile("__PRELINK_INFO", ".xml");
OutputStream out = new FileOutputStream(file);
try {
out.write(bytes);
}
finally {
out.close();
}
}
}
catch (Exception e) {
}
}
}

View File

@ -30,16 +30,14 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a thread_command structure.
*
* @see <a href="https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
* Represents a thread_command structure
*/
public class ThreadCommand extends LoadCommand {
private ThreadStateHeader threadStateHeader;
private ThreadState threadState;
public ThreadCommand(BinaryReader reader, MachHeader header) throws IOException {
initLoadCommand(reader);
super(reader);
threadStateHeader = new ThreadStateHeader(reader);

View File

@ -48,12 +48,12 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader {
/** Default value for loader option to create memory blocks for DYLIB sections */
static final boolean CREATE_DYLIB_SECTIONS_OPTION_DEFAULT = false;
/** Loader option to add relocation entries for each fixed chain pointer */
static final String ADD_RELOCATION_ENTRIES_OPTION_NAME =
"Add relocation entries for fixed chain pointers";
/** Loader option to add relocation entries for chained fixups */
static final String ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_NAME =
"Add relocation entries for chained fixups";
/** Default value for loader option add relocation entries */
static final boolean ADD_RELOCATION_ENTRIES_OPTION_DEFAULT = false;
/** Default value for loader option add chained fixups relocation entries */
static final boolean ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_DEFAULT = false;
/** Loader option to combine split DYLD Cache files (.1, .2, .symbol, etc) into one program */
static final String COMBINE_SPLIT_FILES_OPTION_NAME =
@ -98,7 +98,7 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader {
DyldCacheProgramBuilder.buildProgram(program, provider,
MemoryBlockUtils.createFileBytes(program, provider, monitor),
shouldProcessSymbols(options), shouldCreateDylibSections(options),
shouldAddRelocationEntries(options), shouldCombineSplitFiles(options), log,
shouldAddChainedFixupsRelocations(options), shouldCombineSplitFiles(options), log,
monitor);
}
catch (CancelledException e) {
@ -120,9 +120,9 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader {
list.add(
new Option(CREATE_DYLIB_SECTIONS_OPTION_NAME, CREATE_DYLIB_SECTIONS_OPTION_DEFAULT,
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-createDylibSections"));
list.add(new Option(ADD_RELOCATION_ENTRIES_OPTION_NAME,
ADD_RELOCATION_ENTRIES_OPTION_DEFAULT, Boolean.class,
Loader.COMMAND_LINE_ARG_PREFIX + "-addRelocationEntries"));
list.add(new Option(ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_NAME,
ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_DEFAULT, Boolean.class,
Loader.COMMAND_LINE_ARG_PREFIX + "-addChainedFixupsRelocations"));
list.add(new Option(COMBINE_SPLIT_FILES_OPTION_NAME, COMBINE_SPLIT_FILES_OPTION_DEFAULT,
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-combineSplitFiles"));
}
@ -139,9 +139,9 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader {
CREATE_DYLIB_SECTIONS_OPTION_DEFAULT);
}
private boolean shouldAddRelocationEntries(List<Option> options) {
return OptionUtils.getOption(ADD_RELOCATION_ENTRIES_OPTION_NAME, options,
ADD_RELOCATION_ENTRIES_OPTION_DEFAULT);
private boolean shouldAddChainedFixupsRelocations(List<Option> options) {
return OptionUtils.getOption(ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_NAME, options,
ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_DEFAULT);
}
private boolean shouldCombineSplitFiles(List<Option> options) {

View File

@ -46,7 +46,6 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
private boolean shouldProcessSymbols;
private boolean shouldCreateDylibSections;
private boolean shouldAddRelocationEntries;
private boolean shouldCombineSplitFiles;
/**
@ -58,8 +57,8 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
* @param shouldProcessSymbols True if symbols should be processed; otherwise, false
* @param shouldCreateDylibSections True if memory blocks should be created for DYLIB sections;
* otherwise, false
* @param shouldAddRelocationEntries True to create a relocation entry for each fixed up pointer
* in pointer chain
* @param shouldAddChainedFixupsRelocations True if relocations should be added for chained
* fixups; otherwise, false
* @param shouldCombineSplitFiles True if split DYLD Cache files should be automatically
* imported and combined into 1 program; otherwise, false
* @param log The log
@ -67,12 +66,11 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
*/
protected DyldCacheProgramBuilder(Program program, ByteProvider provider, FileBytes fileBytes,
boolean shouldProcessSymbols, boolean shouldCreateDylibSections,
boolean shouldAddRelocationEntries, boolean shouldCombineSplitFiles, MessageLog log,
TaskMonitor monitor) {
super(program, provider, fileBytes, log, monitor);
boolean shouldAddChainedFixupsRelocations, boolean shouldCombineSplitFiles,
MessageLog log, TaskMonitor monitor) {
super(program, provider, fileBytes, shouldAddChainedFixupsRelocations, log, monitor);
this.shouldProcessSymbols = shouldProcessSymbols;
this.shouldCreateDylibSections = shouldCreateDylibSections;
this.shouldAddRelocationEntries = shouldAddRelocationEntries;
this.shouldCombineSplitFiles = shouldCombineSplitFiles;
}
@ -85,8 +83,8 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
* @param shouldProcessSymbols True if symbols should be processed; otherwise, false
* @param shouldCreateDylibSections True if memory blocks should be created for DYLIB sections;
* otherwise, false
* @param addRelocationEntries True to create a relocation entry for each fixed up pointer in
* pointer chain; otherwise, false
* @param shouldAddChainedFixupsRelocations True if relocations should be added for chained
* fixups; otherwise, false
* @param shouldCombineSplitFiles True if split DYLD Cache files should be automatically
* imported and combined into 1 program; otherwise, false
* @param log The log
@ -95,11 +93,11 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
*/
public static void buildProgram(Program program, ByteProvider provider, FileBytes fileBytes,
boolean shouldProcessSymbols, boolean shouldCreateDylibSections,
boolean addRelocationEntries, boolean shouldCombineSplitFiles, MessageLog log,
TaskMonitor monitor) throws Exception {
boolean shouldAddChainedFixupsRelocations, boolean shouldCombineSplitFiles,
MessageLog log, TaskMonitor monitor) throws Exception {
DyldCacheProgramBuilder dyldCacheProgramBuilder = new DyldCacheProgramBuilder(program,
provider, fileBytes, shouldProcessSymbols, shouldCreateDylibSections,
addRelocationEntries, shouldCombineSplitFiles, log, monitor);
shouldAddChainedFixupsRelocations, shouldCombineSplitFiles, log, monitor);
dyldCacheProgramBuilder.build();
}
@ -268,7 +266,8 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
int version = info.getVersion();
log.appendMsg("Fixing page chains version: " + version);
info.fixPageChains(program, dyldCacheHeader, shouldAddRelocationEntries, log, monitor);
info.fixPageChains(program, dyldCacheHeader, shouldAddChainedFixupsRelocations, log,
monitor);
}
}

View File

@ -23,10 +23,10 @@ import java.util.*;
import ghidra.app.util.*;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.bin.format.macho.prelink.PrelinkMap;
import ghidra.app.util.bin.format.ubi.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.model.DomainObject;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.listing.Program;
import ghidra.util.LittleEndianDataConverter;
@ -41,12 +41,12 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
public final static String MACH_O_NAME = "Mac OS X Mach-O";
private static final long MIN_BYTE_LENGTH = 4;
/** Loader option to add relocation entries for each fixed chain pointer */
static final String ADD_RELOCATION_ENTRIES_OPTION_NAME =
"Add relocation entries for fixed chain pointers";
/** Loader option to add relocation entries for chained fixups */
static final String ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_NAME =
"Add relocation entries for chained fixups";
/** Default value for loader option add relocation entries */
static final boolean ADD_RELOCATION_ENTRIES_OPTION_DEFAULT = true;
/** Default value for loader option add chained fixups relocation entries */
static final boolean ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_DEFAULT = true;
@Override
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
@ -88,22 +88,16 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
try {
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
if (MachoPrelinkUtils.hasChainedLoadCommand(provider, monitor)) {
MachoPrelinkProgramBuilder.buildProgram(program, provider, fileBytes,
Collections.emptyList(), shouldAddRelocationEntries(options), log, monitor);
return;
}
// A Mach-O file may contain PRELINK information. If so, we use a special
// program builder that knows how to deal with it.
List<PrelinkMap> prelinkList = MachoPrelinkUtils.parsePrelinkXml(provider, monitor);
if (!prelinkList.isEmpty()) {
MachoPrelinkProgramBuilder.buildProgram(program, provider, fileBytes, prelinkList,
shouldAddRelocationEntries(options), log, monitor);
return;
if (MachoPrelinkUtils.isMachoPrelink(provider, monitor)) {
MachoPrelinkProgramBuilder.buildProgram(program, provider, fileBytes,
shouldAddChainedFixupsRelocations(options), log, monitor);
}
else {
MachoProgramBuilder.buildProgram(program, provider, fileBytes,
shouldAddChainedFixupsRelocations(options), log, monitor);
}
MachoProgramBuilder.buildProgram(program, provider, fileBytes, log, monitor);
}
catch (CancelledException e) {
return;
@ -121,9 +115,22 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
return MACH_O_NAME;
}
private boolean shouldAddRelocationEntries(List<Option> options) {
return OptionUtils.getOption(ADD_RELOCATION_ENTRIES_OPTION_NAME, options,
ADD_RELOCATION_ENTRIES_OPTION_DEFAULT);
@Override
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
DomainObject domainObject, boolean loadIntoProgram) {
List<Option> list =
super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram);
if (!loadIntoProgram) {
list.add(new Option(ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_NAME,
ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_DEFAULT, Boolean.class,
Loader.COMMAND_LINE_ARG_PREFIX + "-addChainedFixupsRelocations"));
}
return list;
}
private boolean shouldAddChainedFixupsRelocations(List<Option> options) {
return OptionUtils.getOption(ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_NAME, options,
ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_DEFAULT);
}
/**

View File

@ -15,28 +15,20 @@
*/
package ghidra.app.util.opinion;
import static ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
import org.apache.commons.collections4.BidiMap;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.bin.format.macho.commands.*;
import ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr;
import ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr.DyldChainType;
import ghidra.app.util.bin.format.macho.prelink.PrelinkMap;
import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.bin.format.macho.Section;
import ghidra.app.util.bin.format.macho.commands.FileSetEntryCommand;
import ghidra.app.util.bin.format.macho.prelink.MachoPrelinkMap;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.Pointer64DataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
@ -45,9 +37,7 @@ import ghidra.util.task.TaskMonitor;
*/
public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
private List<PrelinkMap> prelinkList;
private boolean shouldAddRelocationEntries;
private List<Address> chainedFixups = new ArrayList<>();
/**
* Creates a new {@link MachoPrelinkProgramBuilder} based on the given information.
@ -55,17 +45,16 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
* @param program The {@link Program} to build up.
* @param provider The {@link ByteProvider} that contains the Mach-O's bytes.
* @param fileBytes Where the Mach-O's bytes came from.
* @param prelinkList Parsed {@link PrelinkMap PRELINK} information.
* @param shouldAddRelocationEntries true if relocation records should be created
* @param shouldAddChainedFixupsRelocations True if relocations should be added for chained
* fixups; otherwise, false.
* @param log The log.
* @param monitor A cancelable task monitor.
* @throws Exception if a problem occurs.
*/
protected MachoPrelinkProgramBuilder(Program program, ByteProvider provider,
FileBytes fileBytes, List<PrelinkMap> prelinkList, boolean shouldAddRelocationEntries,
MessageLog log, TaskMonitor monitor) {
super(program, provider, fileBytes, log, monitor);
this.prelinkList = prelinkList;
this.shouldAddRelocationEntries = shouldAddRelocationEntries;
FileBytes fileBytes, boolean shouldAddChainedFixupsRelocations, MessageLog log,
TaskMonitor monitor) throws Exception {
super(program, provider, fileBytes, shouldAddChainedFixupsRelocations, log, monitor);
}
/**
@ -74,17 +63,17 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
* @param program The {@link Program} to build up.
* @param provider The {@link ByteProvider} that contains the Mach-O's bytes.
* @param fileBytes Where the Mach-O's bytes came from.
* @param prelinkList Parsed {@link PrelinkMap PRELINK} information.
* @param addRelocationEntries true if relocation records should be added
* @param addChainedFixupsRelocations True if relocations should be added for chained fixups;
* otherwise, false.
* @param log The log.
* @param monitor A cancelable task monitor.
* @throws Exception if a problem occurs.
*/
public static void buildProgram(Program program, ByteProvider provider, FileBytes fileBytes,
List<PrelinkMap> prelinkList, boolean addRelocationEntries, MessageLog log,
TaskMonitor monitor) throws Exception {
boolean addChainedFixupsRelocations, MessageLog log, TaskMonitor monitor)
throws Exception {
MachoPrelinkProgramBuilder machoPrelinkProgramBuilder = new MachoPrelinkProgramBuilder(
program, provider, fileBytes, prelinkList, addRelocationEntries, log, monitor);
program, provider, fileBytes, addChainedFixupsRelocations, log, monitor);
machoPrelinkProgramBuilder.build();
}
@ -94,100 +83,19 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
// We want to handle the start of the Mach-O normally. It represents the System.kext.
super.build();
// fixup any slide or headers before markup or regular relocation
fixPreLinkAddresses();
doRelocations();
}
@Override
protected void doRelocations() throws Exception {
processDyldInfo(false);
markupHeaders(machoHeader, setupHeaderAddr(machoHeader.getAllSegments()));
markupSections();
processProgramVars();
processSectionRelocations();
processExternalRelocations();
processLocalRelocations();
}
protected void fixPreLinkAddresses() throws MemoryAccessException, CancelledException,
Exception, IOException, MachException {
// Fixup any chained pointers
List<Address> fixedAddresses = fixupChainedPointers();
processPreLinkMachoInfo();
// Create pointers at any fixed-up addresses, that don't have header data created at them
for (Address addr : fixedAddresses) {
monitor.checkCanceled();
try {
program.getListing().createData(addr, Pointer64DataType.dataType);
}
catch (CodeUnitInsertionException e) {
// No worries, something presumably more important was there already
}
// Now process the inner Mach-O's. Newer formats use LC_FILESET_ENTRY. Older formats
// require scanning and XML parsing.
List<MachoInfo> machoInfoList = processPrelinkFileSet();
if (machoInfoList.isEmpty()) {
machoInfoList = processPrelinkXml();
}
}
protected void processPreLinkMachoInfo() throws Exception, IOException, MachException {
List<PrelinkMachoInfo> prelinkMachoInfoList = new ArrayList<>();
// if has fileSetEntryCommands, that tells where the prelinked headers are
List<FileSetEntryCommand> fileSetEntries =
machoHeader.getLoadCommands(FileSetEntryCommand.class);
if (fileSetEntries != null && fileSetEntries.size() > 0) {
for (FileSetEntryCommand fileSetEntryCommand : fileSetEntries) {
prelinkMachoInfoList
.add(new PrelinkMachoInfo(provider, fileSetEntryCommand.getFileOffset(),
space.getAddress(fileSetEntryCommand.getVMaddress()),
fileSetEntryCommand.getFileSetEntryName()));
}
}
else {
// The rest of the Mach-O's live in the memory segments that the System.kext already
// defined. Therefore, we really just want to go through and do additional markup on them
// since they are already loaded in.
List<Long> machoHeaderOffsets =
MachoPrelinkUtils.findPrelinkMachoHeaderOffsets(provider, monitor);
if (machoHeaderOffsets.isEmpty()) {
return;
}
// Match PRELINK information to the Mach-O's we've found
BidiMap<PrelinkMap, Long> map = MachoPrelinkUtils.matchPrelinkToMachoHeaderOffsets(
provider, prelinkList, machoHeaderOffsets, monitor);
// Determine the starting address of the PRELINK Mach-O's
long prelinkStart = MachoPrelinkUtils.getPrelinkStartAddr(machoHeader);
Address prelinkStartAddr = null;
if (prelinkStart == 0) {
// Probably iOS 12, which doesn't define a proper __PRELINK_TEXT segment.
// Assume the file offset is the same as the offset from image base.
prelinkStartAddr = program.getImageBase().add(machoHeaderOffsets.get(0));
}
else {
prelinkStartAddr = space.getAddress(prelinkStart);
}
// Create an "info" object for each PRELINK Mach-O, which will make processing them easier
for (Long machoHeaderOffset : machoHeaderOffsets) {
prelinkMachoInfoList.add(new PrelinkMachoInfo(provider, machoHeaderOffset,
prelinkStartAddr.add(machoHeaderOffset - machoHeaderOffsets.get(0)),
map.getKey(machoHeaderOffset)));
}
}
// Process each PRELINK Mach-O
Collections.sort(prelinkMachoInfoList);
monitor.initialize(prelinkMachoInfoList.size());
for (int i = 0; i < prelinkMachoInfoList.size(); i++) {
PrelinkMachoInfo info = prelinkMachoInfoList.get(i);
PrelinkMachoInfo next = null;
if (i < prelinkMachoInfoList.size() - 1) {
next = prelinkMachoInfoList.get(i + 1);
Collections.sort(machoInfoList);
monitor.initialize(machoInfoList.size());
for (int i = 0; i < machoInfoList.size(); i++) {
MachoInfo info = machoInfoList.get(i);
MachoInfo next = null;
if (i < machoInfoList.size() - 1) {
next = machoInfoList.get(i + 1);
}
info.processMemoryBlocks();
@ -196,6 +104,75 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
monitor.incrementProgress(1);
}
// Do things that needed to wait until after the inner Mach-O's are processed
super.markupChainedFixups(chainedFixups);
}
/**
* Processes the LC_FILESET_ENTRY commands to generate a {@link List} of discovered Mach-O's
*
* @return A {@link List} of discovered Mach-O's
* @throws Exception if a problem occurs
*/
private List<MachoInfo> processPrelinkFileSet() throws Exception {
List<MachoInfo> machoInfoList = new ArrayList<>();
for (FileSetEntryCommand cmd : machoHeader.getLoadCommands(FileSetEntryCommand.class)) {
MachoInfo info = new MachoInfo(provider, cmd.getFileOffset(),
space.getAddress(cmd.getVMaddress()), cmd.getFileSetEntryId().getString());
machoInfoList.add(info);
}
return machoInfoList;
}
/**
* Processes the PRELINK XML to generate a {@link List} of discovered Mach-O's
*
* @return A {@link List} of discovered Mach-O's
* @throws Exception if a problem occurs
*/
private List<MachoInfo> processPrelinkXml() throws Exception {
List<MachoInfo> machoInfoList = new ArrayList<>();
List<Long> machoHeaderOffsets =
MachoPrelinkUtils.findPrelinkMachoHeaderOffsets(provider, monitor);
if (machoHeaderOffsets.isEmpty()) {
return machoInfoList;
}
List<MachoPrelinkMap> prelinkList = MachoPrelinkUtils.parsePrelinkXml(provider, monitor);
// Match PRELINK information to the Mach-O's we've found
BidiMap<MachoPrelinkMap, Long> map = MachoPrelinkUtils.matchPrelinkToMachoHeaderOffsets(
provider, prelinkList, machoHeaderOffsets, monitor);
// Determine the starting address of the PRELINK Mach-O's
long prelinkStart = MachoPrelinkUtils.getPrelinkStartAddr(machoHeader);
Address prelinkStartAddr = null;
if (prelinkStart == 0) {
// Probably iOS 12, which doesn't define a proper __PRELINK_TEXT segment.
// Assume the file offset is the same as the offset from image base.
prelinkStartAddr = program.getImageBase().add(machoHeaderOffsets.get(0));
}
else {
prelinkStartAddr = space.getAddress(prelinkStart);
}
// Create an "info" object for each PRELINK Mach-O, which will make processing them easier
for (Long machoHeaderOffset : machoHeaderOffsets) {
String name = "";
MachoPrelinkMap prelink = map.getKey(machoHeaderOffset);
if (prelink != null) {
String path = prelink.getPrelinkBundlePath();
if (path != null) {
name = new File(path).getName();
}
}
machoInfoList.add(new MachoInfo(provider, machoHeaderOffset,
prelinkStartAddr.add(machoHeaderOffset - machoHeaderOffsets.get(0)), name));
}
return machoInfoList;
}
@Override
@ -204,376 +181,44 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
// Do nothing. This is not applicable for a PRELINK Mach-O.
}
/**
* Fixes up any chained pointers. Relies on the __thread_starts section being present.
*
* @return A list of addresses where pointer fixes were performed.
* @throws MemoryAccessException if there was a problem reading/writing memory.
*/
private List<Address> fixupChainedPointers() throws MemoryAccessException, CancelledException {
List<Address> fixedAddresses = new ArrayList<>();
// if has Chained Fixups load command, use it
List<DyldChainedFixupsCommand> loadCommands =
machoHeader.getLoadCommands(DyldChainedFixupsCommand.class);
for (LoadCommand loadCommand : loadCommands) {
DyldChainedFixupsCommand linkCmd = (DyldChainedFixupsCommand) loadCommand;
DyldChainedFixupHeader chainHeader = linkCmd.getChainHeader();
DyldChainedStartsInImage chainedStartsInImage = chainHeader.getChainedStartsInImage();
DyldChainedStartsInSegment[] chainedStarts = chainedStartsInImage.getChainedStarts();
for (DyldChainedStartsInSegment chainStart : chainedStarts) {
fixedAddresses.addAll(processSegmentPointerChain(chainHeader, chainStart));
}
log.appendMsg("Fixed up " + fixedAddresses.size() + " chained pointers.");
}
// if pointer chains fixed by DyldChainedFixupsCommands, then all finished
if (loadCommands.size() > 0) {
return fixedAddresses;
}
// if has thread_starts use to fixup chained pointers
Section threadStarts = machoHeader.getSection(SegmentNames.SEG_TEXT, "__thread_starts");
if (threadStarts == null) {
return Collections.emptyList();
}
Address threadSectionStart = null;
Address threadSectionEnd = null;
threadSectionStart = space.getAddress(threadStarts.getAddress());
threadSectionEnd = threadSectionStart.add(threadStarts.getSize() - 1);
monitor.setMessage("Fixing up chained pointers...");
long nextOffSize = (memory.getInt(threadSectionStart) & 1) * 4 + 4;
Address chainHead = threadSectionStart.add(4);
while (chainHead.compareTo(threadSectionEnd) < 0 && !monitor.isCancelled()) {
int headStartOffset = memory.getInt(chainHead);
if (headStartOffset == 0xFFFFFFFF || headStartOffset == 0) {
break;
}
Address chainStart = program.getImageBase().add(headStartOffset & 0xffffffffL);
fixedAddresses.addAll(processPointerChain(chainStart, nextOffSize));
chainHead = chainHead.add(4);
}
log.appendMsg("Fixed up " + fixedAddresses.size() + " chained pointers.");
return fixedAddresses;
}
private List<Address> processSegmentPointerChain(DyldChainedFixupHeader chainHeader,
DyldChainedStartsInSegment chainStart)
throws MemoryAccessException, CancelledException {
List<Address> fixedAddresses = new ArrayList<Address>();
long fixedAddressCount = 0;
if (chainStart.getPointerFormat() == 0) {
return fixedAddresses;
}
long dataPageStart = chainStart.getSegmentOffset();
dataPageStart = dataPageStart + program.getImageBase().getOffset();
long pageSize = chainStart.getPageSize();
long pageStartsCount = chainStart.getPageCount();
long authValueAdd = 0;
short[] pageStarts = chainStart.getPage_starts();
short ptrFormatValue = chainStart.getPointerFormat();
DyldChainType ptrFormat = DyldChainType.lookupChainPtr(ptrFormatValue);
monitor.setMessage("Fixing " + ptrFormat.getName() + " chained pointers...");
monitor.setMaximum(pageStartsCount);
for (int index = 0; index < pageStartsCount; index++) {
monitor.checkCanceled();
long page = dataPageStart + (pageSize * index);
monitor.setProgress(index);
int pageEntry = pageStarts[index] & 0xffff;
if (pageEntry == DYLD_CHAINED_PTR_START_NONE) {
continue;
}
List<Address> unchainedLocList = new ArrayList<>(1024);
long pageOffset = pageEntry; // first entry is byte based
switch (ptrFormat) {
case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
processPointerChain(chainHeader, unchainedLocList, ptrFormat, page, pageOffset,
authValueAdd);
break;
// These might work, but have not been fully tested!
case DYLD_CHAINED_PTR_64:
case DYLD_CHAINED_PTR_64_OFFSET:
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
case DYLD_CHAINED_PTR_32:
case DYLD_CHAINED_PTR_32_CACHE:
case DYLD_CHAINED_PTR_32_FIRMWARE:
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
processPointerChain(chainHeader, unchainedLocList, ptrFormat, page, pageOffset,
authValueAdd);
break;
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
default:
log.appendMsg(
"WARNING: Pointer Chain format " + ptrFormat + " not processed yet!");
break;
}
fixedAddressCount += unchainedLocList.size();
fixedAddresses.addAll(unchainedLocList);
}
log.appendMsg(
"Fixed " + fixedAddressCount + " " + ptrFormat.getName() + " chained pointers.");
return fixedAddresses;
@Override
protected void markupChainedFixups(List<Address> fixups) throws CancelledException {
// Just save the list.
// We need to delay doing the markup until after we process all the inner Mach-O's.
this.chainedFixups = fixups;
}
/**
* Fixes up any chained pointers, starting at the given address.
*
* @param chainHeader fixup header chains
* @param unchainedLocList list of locations that were unchained
* @param pointerFormat format of pointers within this chain
* @param page within data pages that has pointers to be unchained
* @param nextOff offset within the page that is the chain start
* @param auth_value_add value to be added to each chain pointer
*
* @throws MemoryAccessException IO problem reading file
* @throws CancelledException user cancels
* Convenience class to store information we need about an individual inner Mach-O
*/
private void processPointerChain(DyldChainedFixupHeader chainHeader,
List<Address> unchainedLocList, DyldChainType pointerFormat, long page, long nextOff,
long auth_value_add) throws MemoryAccessException, CancelledException {
long imageBaseOffset = program.getImageBase().getOffset();
Address chainStart = memory.getProgram().getLanguage().getDefaultSpace().getAddress(page);
byte origBytes[] = new byte[8];
long next = -1;
boolean start = true;
while (next != 0) {
monitor.checkCanceled();
Address chainLoc = chainStart.add(nextOff);
final long chainValue = DyldChainedPtr.getChainValue(memory, chainLoc, pointerFormat);
long newChainValue = chainValue;
boolean isAuthenticated = DyldChainedPtr.isAuthenticated(pointerFormat, chainValue);
boolean isBound = DyldChainedPtr.isBound(pointerFormat, chainValue);
String symName = null;
if (isAuthenticated && !isBound) {
long offsetFromSharedCacheBase =
DyldChainedPtr.getTarget(pointerFormat, chainValue);
//long diversityData = DyldChainedPtr.getDiversity(pointerFormat, chainValue);
//boolean hasAddressDiversity =
// DyldChainedPtr.hasAddrDiversity(pointerFormat, chainValue);
//long key = DyldChainedPtr.getKey(pointerFormat, chainValue);
newChainValue = imageBaseOffset + offsetFromSharedCacheBase + auth_value_add;
}
else if (!isAuthenticated && isBound) {
int chainOrdinal = (int) DyldChainedPtr.getOrdinal(pointerFormat, chainValue);
long addend = DyldChainedPtr.getAddend(pointerFormat, chainValue);
DyldChainedImports chainedImports = chainHeader.getChainedImports();
DyldChainedImport chainedImport = chainedImports.getChainedImport(chainOrdinal);
//int libOrdinal = chainedImport.getLibOrdinal();
symName = chainedImport.getName();
// lookup the symbol, and then add addend
List<Symbol> globalSymbols = program.getSymbolTable().getGlobalSymbols(symName);
if (globalSymbols.size() == 1) {
newChainValue = globalSymbols.get(0).getAddress().getOffset();
}
newChainValue += addend;
}
else if (isAuthenticated && isBound) {
int chainOrdinal = (int) DyldChainedPtr.getOrdinal(pointerFormat, chainValue);
//long addend = DyldChainedPtr.getAddend(pointerFormat, chainValue);
//long diversityData = DyldChainedPtr.getDiversity(pointerFormat, chainValue);
//boolean hasAddressDiversity =
// DyldChainedPtr.hasAddrDiversity(pointerFormat, chainValue);
//long key = DyldChainedPtr.getKey(pointerFormat, chainValue);
DyldChainedImports chainedImports = chainHeader.getChainedImports();
DyldChainedImport chainedImport = chainedImports.getChainedImport(chainOrdinal);
symName = chainedImport.getName();
// lookup the symbol, and then add addend
List<Symbol> globalSymbols = program.getSymbolTable().getGlobalSymbols(symName);
if (globalSymbols.size() == 1) {
newChainValue = globalSymbols.get(0).getAddress().getOffset();
}
newChainValue = newChainValue + auth_value_add;
}
else {
newChainValue = DyldChainedPtr.getTarget(pointerFormat, chainValue);
newChainValue += imageBaseOffset;
}
if (!start || program.getRelocationTable().getRelocation(chainLoc) == null) {
addRelocationTableEntry(chainLoc,
(start ? 0x8000 : 0x4000) | (isAuthenticated ? 4 : 0) | (isBound ? 2 : 0) | 1,
newChainValue, origBytes, symName);
DyldChainedPtr.setChainValue(memory, chainLoc, pointerFormat, newChainValue);
}
// delay creating data until after memory has been changed
unchainedLocList.add(chainLoc);
start = false;
next = DyldChainedPtr.getNext(pointerFormat, chainValue);
nextOff += next * DyldChainedPtr.getStride(pointerFormat);
}
}
private void addRelocationTableEntry(Address chainLoc, int type, long chainValue,
byte[] origBytes, String name) throws MemoryAccessException {
if (shouldAddRelocationEntries) {
// Add entry to relocation table for the pointer fixup
memory.getBytes(chainLoc, origBytes);
program.getRelocationTable()
.add(chainLoc, type, new long[] { chainValue }, origBytes, name);
}
}
/**
* Fixes up any chained pointers, starting at the given address.
*
* @param chainStart The starting of address of the pointer chain to fix.
* @param nextOffSize The size of the next offset.
* @return A list of addresses where pointer fixes were performed.
* @throws MemoryAccessException if there was a problem reading/writing memory.
*/
private List<Address> processPointerChain(Address chainStart, long nextOffSize)
throws MemoryAccessException {
List<Address> fixedAddresses = new ArrayList<>();
while (!monitor.isCancelled()) {
long chainValue = memory.getLong(chainStart);
fixupPointer(chainStart, chainValue);
fixedAddresses.add(chainStart);
long nextValueOff = ((chainValue >> 51L) & 0x7ffL) * nextOffSize;
if (nextValueOff == 0) {
break;
}
chainStart = chainStart.add(nextValueOff);
}
return fixedAddresses;
}
/**
* Fixes up the pointer at the given address.
*
* @param pointerAddr The address of the pointer to fix.
* @param pointerValue The value at the address of the pointer to fix.
* @throws MemoryAccessException if there was a problem reading/writing memory.
*/
private void fixupPointer(Address pointerAddr, long pointerValue) throws MemoryAccessException {
final long BIT63 = (0x1L << 63);
final long BIT62 = (0x1L << 62);
// Bad chain value
if ((pointerValue & BIT62) != 0) {
// this is a pointer, but is good now
}
long fixedPointerValue = 0;
long fixedPointerType = 0;
// Pointer checked value
if ((pointerValue & BIT63) != 0) {
//long tagType = (pointerValue >> 49L) & 0x3L;
long pacMod = ((pointerValue >> 32) & 0xffff);
fixedPointerType = pacMod;
fixedPointerValue = program.getImageBase().getOffset() + (pointerValue & 0xffffffffL);
}
else {
fixedPointerValue =
((pointerValue << 13) & 0xff00000000000000L) | (pointerValue & 0x7ffffffffffL);
if ((pointerValue & 0x40000000000L) != 0) {
fixedPointerValue |= 0xfffc0000000000L;
}
}
// Add entry to relocation table for the pointer fixup
byte origBytes[] = new byte[8];
memory.getBytes(pointerAddr, origBytes);
program.getRelocationTable()
.add(pointerAddr, (int) fixedPointerType, new long[] { fixedPointerValue },
origBytes, null);
// Fixup the pointer
memory.setLong(pointerAddr, fixedPointerValue);
}
/**
* Convenience class to store information we need about an individual PRELINK Mach-O.
*/
private class PrelinkMachoInfo implements Comparable<PrelinkMachoInfo> {
private class MachoInfo implements Comparable<MachoInfo> {
private Address headerAddr;
private MachHeader header;
private String name;
/**
* Creates a new {@link PrelinkMachoInfo} object with the given parameters.
* Creates a new {@link MachoInfo} object with the given parameters.
*
* @param provider The {@link ByteProvider} that contains the Mach-O's bytes.
* @param offset The offset in the provider to the start of the Mach-O.
* @param headerAddr The Mach-O's header address.
* @param prelink The {@link PrelinkMap PRELINK} information associated with the Mach-O.
* @param name The Mach-O's name.
* @throws Exception If there was a problem handling the Mach-O or PRELINK info.
*/
public PrelinkMachoInfo(ByteProvider provider, long offset, Address headerAddr,
PrelinkMap prelink) throws Exception {
this(provider, offset, headerAddr, "");
if (prelink != null) {
String path = prelink.getPrelinkBundlePath();
if (path != null) {
this.name = new File(path).getName();
}
}
}
public PrelinkMachoInfo(ByteProvider provider, long fileOffset, Address headerAddr,
String kextName) throws Exception {
public MachoInfo(ByteProvider provider, long offset, Address headerAddr,
String name) throws Exception {
this.headerAddr = headerAddr;
this.header = new MachHeader(provider, fileOffset, false);
this.header = new MachHeader(provider, offset, false);
this.header.parse();
this.headerAddr = headerAddr;
this.name = kextName;
this.name = name;
}
/**
* Processes memory blocks for this PRELINK Mach-O.
* Processes memory blocks for this Mach-O.
*
* @throws Exception If there was a problem processing memory blocks for this PRELINK
* Mach-O.
* @throws Exception If there was a problem processing memory blocks for this Mach-O.
* @see MachoPrelinkProgramBuilder#processMemoryBlocks(MachHeader, String, boolean, boolean)
*/
public void processMemoryBlocks() throws Exception {
@ -581,9 +226,9 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
}
/**
* Marks up the PRELINK Mach-O headers.
* Marks up the Mach-O headers.
*
* @throws Exception If there was a problem marking up the PRELINK Mach-O's headers.
* @throws Exception If there was a problem marking up the Mach-O's headers.
* @see MachoPrelinkProgramBuilder#markupHeaders(MachHeader, Address)
*/
public void markupHeaders() throws Exception {
@ -595,21 +240,21 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
}
/**
* Adds an entry to the program tree for this PRELINK Mach-O.
* Adds an entry to the program tree for this Mach-O.
*
* @param next The PRELINK Mach-O that comes directly after this one. Could be null if this
* is the last one.
* @throws Exception If there was a problem adding this PRELINK Mach-O to the program tree.
* @param next The Mach-O that comes directly after this one. Could be null if this is the
* last one.
* @throws Exception If there was a problem adding this Mach-O to the program tree.
*/
public void addToProgramTree(PrelinkMachoInfo next) throws Exception {
public void addToProgramTree(MachoInfo next) throws Exception {
if (!name.isEmpty()) {
ProgramFragment fragment = listing.getDefaultRootModule().createFragment(name);
if (next != null) {
fragment.move(headerAddr, next.headerAddr.subtract(1));
}
else {
// This is the last PRELINK Mach-O, so we'll assume it ends where the section
// that contains it ends.
// This is the last Mach-O, so we'll assume it ends where the section that
// contains it ends.
for (Section section : machoHeader.getAllSections()) {
Address sectionAddr = space.getAddress(section.getAddress());
if (headerAddr.compareTo(sectionAddr) >= 0 &&
@ -622,7 +267,7 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
}
@Override
public int compareTo(PrelinkMachoInfo o) {
public int compareTo(MachoInfo o) {
return headerAddr.compareTo(o.headerAddr);
}
}

View File

@ -76,12 +76,12 @@ public class MachoPrelinkUtils {
*
* @param provider The provider to parse.
* @param monitor A monitor.
* @return A list of discovered {@link PrelinkMap}s. An empty list indicates that the provider
* @return A list of discovered {@link MachoPrelinkMap}s. An empty list indicates that the provider
* did not represent valid Mach-O PRELINK binary.
* @throws IOException if there was an IO-related issue.
* @throws JDOMException if there was a issue parsing the PRELINK XML.
*/
public static List<PrelinkMap> parsePrelinkXml(ByteProvider provider, TaskMonitor monitor)
public static List<MachoPrelinkMap> parsePrelinkXml(ByteProvider provider, TaskMonitor monitor)
throws IOException, JDOMException {
try {
@ -89,39 +89,13 @@ public class MachoPrelinkUtils {
mainHeader.parse(); // make sure first Mach-O header is valid....
monitor.setMessage("Parsing PRELINK XML...");
return new PrelinkParser(mainHeader, provider).parse(monitor);
return new MachoPrelinkParser(mainHeader, provider).parse(monitor);
}
catch (NoPreLinkSectionException | MachException e) {
return Collections.emptyList();
}
}
/**
* Check if the Macho has a DYLD_CHAINED_FIXUPS_COMMAND
*
* @param provider The provider to parse.
* @param monitor A monitor.
* @return A list of discovered {@link PrelinkMap}s. An empty list indicates that the provider
* did not represent valid Mach-O PRELINK binary.
* @throws IOException if there was an IO-related issue.
* @throws JDOMException if there was a issue parsing the PRELINK XML.
*/
public static boolean hasChainedLoadCommand(ByteProvider provider, TaskMonitor monitor)
throws IOException, JDOMException {
try {
MachHeader mainHeader = new MachHeader(provider);
mainHeader.parse(); // make sure first Mach-O header is valid....
DyldChainedFixupsCommand cmd =
mainHeader.getFirstLoadCommand(DyldChainedFixupsCommand.class);
return cmd != null;
}
catch (MachException e) {
return false;
}
}
/**
* Scans the provider looking for PRELINK Mach-O headers.
* <p>
@ -166,7 +140,7 @@ public class MachoPrelinkUtils {
* Forms a bidirectional mapping of PRELINK XML to Mach-O header offset in the given provider.
*
* @param provider The PRELINK Mach-O provider.
* @param prelinkList A list of {@link PrelinkMap}s.
* @param prelinkList A list of {@link MachoPrelinkMap}s.
* @param machoHeaderOffsets A list of provider offsets where PRELINK Mach-O headers start (not
* including the "System" Mach-O at offset 0).
* @param monitor A monitor
@ -174,14 +148,14 @@ public class MachoPrelinkUtils {
* @throws MachException If there was a problem parsing a Mach-O header.
* @throws IOException If there was an IO-related issue mapping PRELINK XML to Mach-O headers.
*/
public static BidiMap<PrelinkMap, Long> matchPrelinkToMachoHeaderOffsets(ByteProvider provider,
List<PrelinkMap> prelinkList, List<Long> machoHeaderOffsets, TaskMonitor monitor)
public static BidiMap<MachoPrelinkMap, Long> matchPrelinkToMachoHeaderOffsets(ByteProvider provider,
List<MachoPrelinkMap> prelinkList, List<Long> machoHeaderOffsets, TaskMonitor monitor)
throws MachException, IOException {
monitor.setMessage("Matching PRELINK to Mach-O headers...");
monitor.initialize(prelinkList.size());
BidiMap<PrelinkMap, Long> map = new DualHashBidiMap<>();
BidiMap<MachoPrelinkMap, Long> map = new DualHashBidiMap<>();
// For pre-iOS 12, we can use the PrelinkExecutableLoadAddr field to match PrelinkMap
// entries to Mach-O offsets. For iOS 12, PrelinkExecutableLoadAddr is gone so we use
@ -199,7 +173,7 @@ public class MachoPrelinkUtils {
maxModuleIndex + 1, machoHeaderOffsets.size()));
}
for (PrelinkMap info : prelinkList) {
for (MachoPrelinkMap info : prelinkList) {
if (monitor.isCancelled()) {
break;
}
@ -222,7 +196,7 @@ public class MachoPrelinkUtils {
MachHeader machoHeader = new MachHeader(provider, 0, true);
machoHeader.parse();
long prelinkStart = MachoPrelinkUtils.getPrelinkStartAddr(machoHeader);
for (PrelinkMap info : prelinkList) {
for (MachoPrelinkMap info : prelinkList) {
if (monitor.isCancelled()) {
break;
}

View File

@ -15,6 +15,8 @@
*/
package ghidra.app.util.opinion;
import static ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr.*;
import java.math.BigInteger;
import java.util.*;
@ -24,6 +26,8 @@ import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.bin.format.macho.commands.*;
import ghidra.app.util.bin.format.macho.commands.dyld.*;
import ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr;
import ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr.DyldChainType;
import ghidra.app.util.bin.format.macho.relocation.*;
import ghidra.app.util.bin.format.macho.threadcommand.ThreadCommand;
import ghidra.app.util.bin.format.objectiveC.ObjectiveC1_Constants;
@ -57,6 +61,7 @@ public class MachoProgramBuilder {
protected Program program;
protected ByteProvider provider;
protected FileBytes fileBytes;
protected boolean shouldAddChainedFixupsRelocations;
protected MessageLog log;
protected TaskMonitor monitor;
protected Memory memory;
@ -69,14 +74,17 @@ public class MachoProgramBuilder {
* @param program The {@link Program} to build up.
* @param provider The {@link ByteProvider} that contains the Mach-O's bytes.
* @param fileBytes Where the Mach-O's bytes came from.
* @param shouldAddChainedFixupsRelocations True if relocations should be added for chained
* fixups; otherwise, false.
* @param log The log.
* @param monitor A cancelable task monitor.
*/
protected MachoProgramBuilder(Program program, ByteProvider provider, FileBytes fileBytes,
MessageLog log, TaskMonitor monitor) {
boolean shouldAddChainedFixupsRelocations, MessageLog log, TaskMonitor monitor) {
this.program = program;
this.provider = provider;
this.fileBytes = fileBytes;
this.shouldAddChainedFixupsRelocations = shouldAddChainedFixupsRelocations;
this.log = log;
this.monitor = monitor;
this.memory = program.getMemory();
@ -90,16 +98,18 @@ public class MachoProgramBuilder {
* @param program The {@link Program} to build up.
* @param provider The {@link ByteProvider} that contains the Mach-O's bytes.
* @param fileBytes Where the Mach-O's bytes came from.
* @param addChainedFixupsRelocations True if relocations should be added for chained fixups;
* otherwise, false.
* @param log The log.
* @param monitor A cancelable task monitor.
* @throws Exception if a problem occurs.
*/
public static void buildProgram(Program program, ByteProvider provider, FileBytes fileBytes,
MessageLog log, TaskMonitor monitor) throws Exception {
MachoProgramBuilder machoProgramBuilder =
new MachoProgramBuilder(program, provider, fileBytes, log, monitor);
boolean addChainedFixupsRelocations, MessageLog log, TaskMonitor monitor)
throws Exception {
MachoProgramBuilder machoProgramBuilder = new MachoProgramBuilder(program, provider,
fileBytes, addChainedFixupsRelocations, log, monitor);
machoProgramBuilder.build();
machoProgramBuilder.doRelocations();
}
protected void build() throws Exception {
@ -123,19 +133,23 @@ public class MachoProgramBuilder {
renameObjMsgSendRtpSymbol();
processUndefinedSymbols();
processAbsoluteSymbols();
}
protected void doRelocations() throws Exception {
processDyldInfo(true);
List<Address> chainedFixups = processChainedFixups();
processDyldInfo(false);
markupHeaders(machoHeader, setupHeaderAddr(machoHeader.getAllSegments()));
markupSections();
processProgramVars();
processSectionRelocations();
processExternalRelocations();
processLocalRelocations();
markupChainedFixups(chainedFixups);
}
private void setImageBase() throws Exception {
/**
* Sets the image base
*
* @throws Exception if there was a problem setting the image base
*/
protected void setImageBase() throws Exception {
Address imageBaseAddr = null;
for (SegmentCommand segment : machoHeader.getAllSegments()) {
if (segment.getFileSize() > 0) {
@ -161,7 +175,7 @@ public class MachoProgramBuilder {
*
* @throws Exception if there was a problem detecting the encrypted block ranges
*/
private void processEncryption() throws Exception {
protected void processEncryption() throws Exception {
monitor.setMessage("Processing encryption...");
for (EncryptedInformationCommand cmd : machoHeader
.getLoadCommands(EncryptedInformationCommand.class)) {
@ -177,7 +191,7 @@ public class MachoProgramBuilder {
*
* @throws Exception If there was a problem discovering or setting the entry point.
*/
private void processEntryPoint() throws Exception {
protected void processEntryPoint() throws Exception {
monitor.setMessage("Processing entry point...");
Address entryPointAddr = null;
@ -389,7 +403,7 @@ public class MachoProgramBuilder {
*
* @throws CancelledException if the operation was cancelled.
*/
private void processUnsupportedLoadCommands() throws CancelledException {
protected void processUnsupportedLoadCommands() throws CancelledException {
monitor.setMessage("Processing unsupported load commands...");
for (LoadCommand loadCommand : machoHeader.getLoadCommands(UnsupportedLoadCommand.class)) {
@ -398,7 +412,7 @@ public class MachoProgramBuilder {
}
}
private void processSymbolTables() throws Exception {
protected void processSymbolTables() throws Exception {
monitor.setMessage("Processing symbol tables...");
List<SymbolTableCommand> commands = machoHeader.getLoadCommands(SymbolTableCommand.class);
for (SymbolTableCommand symbolTableCommand : commands) {
@ -466,7 +480,7 @@ public class MachoProgramBuilder {
*
* @throws Exception if there is a problem
*/
private void processIndirectSymbols() throws Exception {
protected void processIndirectSymbols() throws Exception {
monitor.setMessage("Processing indirect symbols...");
@ -533,7 +547,7 @@ public class MachoProgramBuilder {
}
}
private void setRelocatableProperty() {
protected void setRelocatableProperty() {
Options props = program.getOptions(Program.PROGRAM_INFO);
switch (machoHeader.getFileType()) {
case MachHeaderFileTypes.MH_EXECUTE:
@ -545,7 +559,7 @@ public class MachoProgramBuilder {
}
}
private void processLibraries() {
protected void processLibraries() {
monitor.setMessage("Processing libraries...");
List<LoadCommand> commands = machoHeader.getLoadCommands();
for (LoadCommand command : commands) {
@ -568,7 +582,7 @@ public class MachoProgramBuilder {
}
}
private void processProgramDescription() {
protected void processProgramDescription() {
Options props = program.getOptions(Program.PROGRAM_INFO);
props.setString("Mach-O File Type",
MachHeaderFileTypes.getFileTypeName(machoHeader.getFileType()));
@ -607,7 +621,7 @@ public class MachoProgramBuilder {
}
}
private void processUndefinedSymbols() throws Exception {
protected void processUndefinedSymbols() throws Exception {
monitor.setMessage("Processing undefined symbols...");
List<NList> undefinedSymbols = new ArrayList<>();
@ -670,7 +684,7 @@ public class MachoProgramBuilder {
}
}
private void processAbsoluteSymbols() throws Exception {
protected void processAbsoluteSymbols() throws Exception {
monitor.setMessage("Processing absolute symbols...");
List<NList> absoluteSymbols = new ArrayList<>();
List<LoadCommand> commands = machoHeader.getLoadCommands();
@ -857,6 +871,16 @@ public class MachoProgramBuilder {
DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()),
StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(),
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()),
StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(),
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
}
else if (loadCommand instanceof FileSetEntryCommand) {
FileSetEntryCommand fileSetEntryCommand = (FileSetEntryCommand) loadCommand;
LoadCommandString name = fileSetEntryCommand.getFileSetEntryId();
DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()),
StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(),
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
}
else if (loadCommand instanceof LinkerOptionCommand) {
LinkerOptionCommand linkerOptionCommand = (LinkerOptionCommand) loadCommand;
@ -1435,4 +1459,349 @@ public class MachoProgramBuilder {
}
return originalRelocationBytes;
}
/**
* Fixes up any chained fixups. Relies on the __thread_starts section being present.
*
* @return A list of addresses where chained fixups were performed.
* @throws Exception if there was a problem reading/writing memory.
*/
protected List<Address> processChainedFixups() throws Exception {
List<Address> fixedAddresses = new ArrayList<>();
// if has Chained Fixups load command, use it
List<DyldChainedFixupsCommand> loadCommands =
machoHeader.getLoadCommands(DyldChainedFixupsCommand.class);
for (LoadCommand loadCommand : loadCommands) {
DyldChainedFixupsCommand linkCmd = (DyldChainedFixupsCommand) loadCommand;
DyldChainedFixupHeader chainHeader = linkCmd.getChainHeader();
DyldChainedStartsInImage chainedStartsInImage = chainHeader.getChainedStartsInImage();
DyldChainedStartsInSegment[] chainedStarts = chainedStartsInImage.getChainedStarts();
for (DyldChainedStartsInSegment chainStart : chainedStarts) {
fixedAddresses.addAll(processSegmentPointerChain(chainHeader, chainStart));
}
log.appendMsg("Fixed up " + fixedAddresses.size() + " chained pointers.");
}
// if pointer chains fixed by DyldChainedFixupsCommands, then all finished
if (loadCommands.size() > 0) {
return fixedAddresses;
}
// if has thread_starts use to fixup chained pointers
Section threadStarts = machoHeader.getSection(SegmentNames.SEG_TEXT, "__thread_starts");
if (threadStarts == null) {
return Collections.emptyList();
}
Address threadSectionStart = null;
Address threadSectionEnd = null;
threadSectionStart = space.getAddress(threadStarts.getAddress());
threadSectionEnd = threadSectionStart.add(threadStarts.getSize() - 1);
monitor.setMessage("Fixing up chained pointers...");
long nextOffSize = (memory.getInt(threadSectionStart) & 1) * 4 + 4;
Address chainHead = threadSectionStart.add(4);
while (chainHead.compareTo(threadSectionEnd) < 0 && !monitor.isCancelled()) {
int headStartOffset = memory.getInt(chainHead);
if (headStartOffset == 0xFFFFFFFF || headStartOffset == 0) {
break;
}
Address chainStart = program.getImageBase().add(headStartOffset & 0xffffffffL);
fixedAddresses.addAll(processPointerChain(chainStart, nextOffSize));
chainHead = chainHead.add(4);
}
log.appendMsg("Fixed up " + fixedAddresses.size() + " chained pointers.");
return fixedAddresses;
}
private List<Address> processSegmentPointerChain(DyldChainedFixupHeader chainHeader,
DyldChainedStartsInSegment chainStart)
throws MemoryAccessException, CancelledException {
List<Address> fixedAddresses = new ArrayList<Address>();
long fixedAddressCount = 0;
if (chainStart.getPointerFormat() == 0) {
return fixedAddresses;
}
long dataPageStart = chainStart.getSegmentOffset();
dataPageStart = dataPageStart + program.getImageBase().getOffset();
long pageSize = chainStart.getPageSize();
long pageStartsCount = chainStart.getPageCount();
long authValueAdd = 0;
short[] pageStarts = chainStart.getPage_starts();
short ptrFormatValue = chainStart.getPointerFormat();
DyldChainType ptrFormat = DyldChainType.lookupChainPtr(ptrFormatValue);
monitor.setMessage("Fixing " + ptrFormat.getName() + " chained pointers...");
monitor.setMaximum(pageStartsCount);
for (int index = 0; index < pageStartsCount; index++) {
monitor.checkCanceled();
long page = dataPageStart + (pageSize * index);
monitor.setProgress(index);
int pageEntry = pageStarts[index] & 0xffff;
if (pageEntry == DYLD_CHAINED_PTR_START_NONE) {
continue;
}
List<Address> unchainedLocList = new ArrayList<>(1024);
long pageOffset = pageEntry; // first entry is byte based
switch (ptrFormat) {
case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
processPointerChain(chainHeader, unchainedLocList, ptrFormat, page, pageOffset,
authValueAdd);
break;
// These might work, but have not been fully tested!
case DYLD_CHAINED_PTR_64:
case DYLD_CHAINED_PTR_64_OFFSET:
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
case DYLD_CHAINED_PTR_32:
case DYLD_CHAINED_PTR_32_CACHE:
case DYLD_CHAINED_PTR_32_FIRMWARE:
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
processPointerChain(chainHeader, unchainedLocList, ptrFormat, page, pageOffset,
authValueAdd);
break;
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
default:
log.appendMsg(
"WARNING: Pointer Chain format " + ptrFormat + " not processed yet!");
break;
}
fixedAddressCount += unchainedLocList.size();
fixedAddresses.addAll(unchainedLocList);
}
log.appendMsg(
"Fixed " + fixedAddressCount + " " + ptrFormat.getName() + " chained pointers.");
return fixedAddresses;
}
/**
* Fixes up any chained pointers, starting at the given address.
*
* @param chainHeader fixup header chains
* @param unchainedLocList list of locations that were unchained
* @param pointerFormat format of pointers within this chain
* @param page within data pages that has pointers to be unchained
* @param nextOff offset within the page that is the chain start
* @param auth_value_add value to be added to each chain pointer
*
* @throws MemoryAccessException IO problem reading file
* @throws CancelledException user cancels
*/
private void processPointerChain(DyldChainedFixupHeader chainHeader,
List<Address> unchainedLocList, DyldChainType pointerFormat, long page, long nextOff,
long auth_value_add) throws MemoryAccessException, CancelledException {
long imageBaseOffset = program.getImageBase().getOffset();
Address chainStart = memory.getProgram().getLanguage().getDefaultSpace().getAddress(page);
byte origBytes[] = new byte[8];
long next = -1;
boolean start = true;
while (next != 0) {
monitor.checkCanceled();
Address chainLoc = chainStart.add(nextOff);
final long chainValue = DyldChainedPtr.getChainValue(memory, chainLoc, pointerFormat);
long newChainValue = chainValue;
boolean isAuthenticated = DyldChainedPtr.isAuthenticated(pointerFormat, chainValue);
boolean isBound = DyldChainedPtr.isBound(pointerFormat, chainValue);
String symName = null;
if (isAuthenticated && !isBound) {
long offsetFromSharedCacheBase =
DyldChainedPtr.getTarget(pointerFormat, chainValue);
//long diversityData = DyldChainedPtr.getDiversity(pointerFormat, chainValue);
//boolean hasAddressDiversity =
// DyldChainedPtr.hasAddrDiversity(pointerFormat, chainValue);
//long key = DyldChainedPtr.getKey(pointerFormat, chainValue);
newChainValue = imageBaseOffset + offsetFromSharedCacheBase + auth_value_add;
}
else if (!isAuthenticated && isBound) {
int chainOrdinal = (int) DyldChainedPtr.getOrdinal(pointerFormat, chainValue);
long addend = DyldChainedPtr.getAddend(pointerFormat, chainValue);
DyldChainedImports chainedImports = chainHeader.getChainedImports();
DyldChainedImport chainedImport = chainedImports.getChainedImport(chainOrdinal);
//int libOrdinal = chainedImport.getLibOrdinal();
symName = chainedImport.getName();
// lookup the symbol, and then add addend
List<Symbol> globalSymbols = program.getSymbolTable().getGlobalSymbols(symName);
if (globalSymbols.size() == 1) {
newChainValue = globalSymbols.get(0).getAddress().getOffset();
}
newChainValue += addend;
}
else if (isAuthenticated && isBound) {
int chainOrdinal = (int) DyldChainedPtr.getOrdinal(pointerFormat, chainValue);
//long addend = DyldChainedPtr.getAddend(pointerFormat, chainValue);
//long diversityData = DyldChainedPtr.getDiversity(pointerFormat, chainValue);
//boolean hasAddressDiversity =
// DyldChainedPtr.hasAddrDiversity(pointerFormat, chainValue);
//long key = DyldChainedPtr.getKey(pointerFormat, chainValue);
DyldChainedImports chainedImports = chainHeader.getChainedImports();
DyldChainedImport chainedImport = chainedImports.getChainedImport(chainOrdinal);
symName = chainedImport.getName();
// lookup the symbol, and then add addend
List<Symbol> globalSymbols = program.getSymbolTable().getGlobalSymbols(symName);
if (globalSymbols.size() == 1) {
newChainValue = globalSymbols.get(0).getAddress().getOffset();
}
newChainValue = newChainValue + auth_value_add;
}
else {
newChainValue = DyldChainedPtr.getTarget(pointerFormat, chainValue);
newChainValue += imageBaseOffset;
}
if (!start || program.getRelocationTable().getRelocation(chainLoc) == null) {
addRelocationTableEntry(chainLoc,
(start ? 0x8000 : 0x4000) | (isAuthenticated ? 4 : 0) | (isBound ? 2 : 0) | 1,
newChainValue, origBytes, symName);
DyldChainedPtr.setChainValue(memory, chainLoc, pointerFormat, newChainValue);
}
// delay creating data until after memory has been changed
unchainedLocList.add(chainLoc);
start = false;
next = DyldChainedPtr.getNext(pointerFormat, chainValue);
nextOff += next * DyldChainedPtr.getStride(pointerFormat);
}
}
private void addRelocationTableEntry(Address chainLoc, int type, long chainValue,
byte[] origBytes, String name) throws MemoryAccessException {
if (shouldAddChainedFixupsRelocations) {
// Add entry to relocation table for the pointer fixup
memory.getBytes(chainLoc, origBytes);
program.getRelocationTable()
.add(chainLoc, type, new long[] { chainValue }, origBytes, name);
}
}
/**
* Fixes up any chained pointers, starting at the given address.
*
* @param chainStart The starting of address of the pointer chain to fix.
* @param nextOffSize The size of the next offset.
* @return A list of addresses where pointer fixes were performed.
* @throws MemoryAccessException if there was a problem reading/writing memory.
*/
private List<Address> processPointerChain(Address chainStart, long nextOffSize)
throws MemoryAccessException {
List<Address> fixedAddresses = new ArrayList<>();
while (!monitor.isCancelled()) {
long chainValue = memory.getLong(chainStart);
fixupPointer(chainStart, chainValue);
fixedAddresses.add(chainStart);
long nextValueOff = ((chainValue >> 51L) & 0x7ffL) * nextOffSize;
if (nextValueOff == 0) {
break;
}
chainStart = chainStart.add(nextValueOff);
}
return fixedAddresses;
}
/**
* Fixes up the pointer at the given address.
*
* @param pointerAddr The address of the pointer to fix.
* @param pointerValue The value at the address of the pointer to fix.
* @throws MemoryAccessException if there was a problem reading/writing memory.
*/
private void fixupPointer(Address pointerAddr, long pointerValue) throws MemoryAccessException {
final long BIT63 = (0x1L << 63);
final long BIT62 = (0x1L << 62);
// Bad chain value
if ((pointerValue & BIT62) != 0) {
// this is a pointer, but is good now
}
long fixedPointerValue = 0;
long fixedPointerType = 0;
// Pointer checked value
if ((pointerValue & BIT63) != 0) {
//long tagType = (pointerValue >> 49L) & 0x3L;
long pacMod = ((pointerValue >> 32) & 0xffff);
fixedPointerType = pacMod;
fixedPointerValue = program.getImageBase().getOffset() + (pointerValue & 0xffffffffL);
}
else {
fixedPointerValue =
((pointerValue << 13) & 0xff00000000000000L) | (pointerValue & 0x7ffffffffffL);
if ((pointerValue & 0x40000000000L) != 0) {
fixedPointerValue |= 0xfffc0000000000L;
}
}
// Add entry to relocation table for the pointer fixup
byte origBytes[] = new byte[8];
memory.getBytes(pointerAddr, origBytes);
program.getRelocationTable()
.add(pointerAddr, (int) fixedPointerType, new long[] { fixedPointerValue },
origBytes, null);
// Fixup the pointer
memory.setLong(pointerAddr, fixedPointerValue);
}
/**
* Markup the given {@link List} of chained fixups by creating pointers at their locations,
* if possible
*
* @param chainedFixups The {@link List} of chained fixups to markup
* @throws CancelledException if the operation was cancelled
*/
protected void markupChainedFixups(List<Address> chainedFixups) throws CancelledException {
for (Address addr : chainedFixups) {
monitor.checkCanceled();
try {
listing.createData(addr, Pointer64DataType.dataType);
}
catch (CodeUnitInsertionException e) {
// No worries, something presumably more important was there already
}
}
}
}

View File

@ -26,8 +26,8 @@ import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.bin.format.macho.commands.*;
import ghidra.app.util.bin.format.macho.prelink.PrelinkConstants;
import ghidra.app.util.bin.format.macho.prelink.PrelinkMap;
import ghidra.app.util.bin.format.macho.prelink.MachoPrelinkConstants;
import ghidra.app.util.bin.format.macho.prelink.MachoPrelinkMap;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.*;
import ghidra.formats.gfilesystem.*;
@ -49,19 +49,19 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.exception.CryptoException;
import ghidra.util.task.TaskMonitor;
@FileSystemInfo(type = PrelinkFileSystem.IOS_PRELINK_FSTYPE, description = PrelinkConstants.TITLE, priority = FileSystemInfo.PRIORITY_HIGH, factory = GFileSystemBaseFactory.class)
public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemProgramProvider {
@FileSystemInfo(type = MachoPrelinkFileSystem.IOS_PRELINK_FSTYPE, description = MachoPrelinkConstants.TITLE, priority = FileSystemInfo.PRIORITY_HIGH, factory = GFileSystemBaseFactory.class)
public class MachoPrelinkFileSystem extends GFileSystemBase implements GFileSystemProgramProvider {
public final static String IOS_PRELINK_FSTYPE = "iosprelink";
private final static String SYSTEM_KEXT = "System.kext";
private Map<GFile, PrelinkMap> fileToPrelinkInfoMap = new HashMap<>();
private Map<GFile, MachoPrelinkMap> fileToPrelinkInfoMap = new HashMap<>();
private Map<Long, GFileImpl> unnamedMachoFileMap = new HashMap<>();
private Map<GFile, Long> fileToMachoOffsetMap = new HashMap<>();
private GFileImpl systemKextFile;
private GFileImpl kernelCacheDirectory;
public PrelinkFileSystem(String fileSystemName, ByteProvider provider) {
public MachoPrelinkFileSystem(String fileSystemName, ByteProvider provider) {
super(fileSystemName, provider);
}
@ -89,7 +89,7 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
List<Long> machoHeaderOffsets =
MachoPrelinkUtils.findPrelinkMachoHeaderOffsets(provider, monitor);
try {
List<PrelinkMap> prelinkList = MachoPrelinkUtils.parsePrelinkXml(provider, monitor);
List<MachoPrelinkMap> prelinkList = MachoPrelinkUtils.parsePrelinkXml(provider, monitor);
if (!prelinkList.isEmpty()) {
processPrelinkWithMacho(prelinkList, machoHeaderOffsets, monitor);
}
@ -110,7 +110,7 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
@Override
public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
PrelinkMap info = fileToPrelinkInfoMap.get(file);
MachoPrelinkMap info = fileToPrelinkInfoMap.get(file);
return FileAttributes.of(info != null
? FileAttribute.create(FileAttributeType.COMMENT_ATTR, info.toString())
: null);
@ -181,8 +181,8 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
provider.length() - offset, monitor);
ByteProvider providerWrapper =
new ByteProviderWrapper(provider, offset, provider.length() - offset);
MachoProgramBuilder.buildProgram(program, providerWrapper, fileBytes, new MessageLog(),
monitor);
MachoProgramBuilder.buildProgram(program, providerWrapper, fileBytes, false,
new MessageLog(), monitor);
AbstractProgramLoader.setProgramProperties(program, providerWrapper,
MachoLoader.MACH_O_NAME);
@ -237,22 +237,22 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
* Processes PRELINK and Macho-O offsets in order to map files to their Mach-O offsets in the
* providers.
*
* @param prelinkList The list of discovered {@link PrelinkMap}s.
* @param prelinkList The list of discovered {@link MachoPrelinkMap}s.
* @param machoHeaderOffsets The list of provider offsets where prelinked Mach-O headers start.
* @param monitor A monitor
* @throws IOException if an IO-related problem occurred.
* @throws MachException if there was a problem parsing Mach-O headers.
*/
private void processPrelinkWithMacho(List<PrelinkMap> prelinkList,
private void processPrelinkWithMacho(List<MachoPrelinkMap> prelinkList,
List<Long> machoHeaderOffsets, TaskMonitor monitor) throws IOException, MachException {
monitor.setMessage("Processing PRELINK with found Mach-O headers...");
monitor.initialize(prelinkList.size());
BidiMap<PrelinkMap, Long> map = MachoPrelinkUtils.matchPrelinkToMachoHeaderOffsets(provider,
BidiMap<MachoPrelinkMap, Long> map = MachoPrelinkUtils.matchPrelinkToMachoHeaderOffsets(provider,
prelinkList, machoHeaderOffsets, monitor);
for (PrelinkMap info : map.keySet()) {
for (MachoPrelinkMap info : map.keySet()) {
if (monitor.isCancelled()) {
break;
@ -293,7 +293,7 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
continue;
}
PrelinkMap prelinkMap = fileToPrelinkInfoMap.get(file);
MachoPrelinkMap prelinkMap = fileToPrelinkInfoMap.get(file);
if (prelinkMap == null || prelinkMap.getPrelinkExecutableLoadAddr() == -1) {
continue;
}
@ -331,7 +331,7 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
}
}
private GFileImpl storeFile(GFileImpl file, PrelinkMap info) {
private GFileImpl storeFile(GFileImpl file, MachoPrelinkMap info) {
if (file == null) {
return file;
}
@ -359,7 +359,7 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
}
else if (fileToPrelinkInfoMap.containsKey(asFile) &&
fileToPrelinkInfoMap.get(asFile) != null && file.isDirectory()) {
PrelinkMap value = fileToPrelinkInfoMap.remove(asFile);
MachoPrelinkMap value = fileToPrelinkInfoMap.remove(asFile);
fileToPrelinkInfoMap.put(asDir, value);
Long offset = fileToMachoOffsetMap.remove(asFile);
fileToMachoOffsetMap.put(asDir, offset);
@ -392,7 +392,7 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
private void processKModInfoStructures(List<Long> machoHeaderOffsets, TaskMonitor monitor)
throws IOException {
Map<PrelinkMap, Long> infoToMachoMap = new HashMap<>();
Map<MachoPrelinkMap, Long> infoToMachoMap = new HashMap<>();
kernelCacheDirectory = GFileImpl.fromFilename(this, root, "kernelcache", true, -1, null);

View File

@ -34,7 +34,7 @@ import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.services.ProgramManager;
import ghidra.file.crypto.CryptoKeyFileTemplateWriter;
import ghidra.file.eclipse.AndroidProjectCreator;
import ghidra.file.formats.ios.prelink.PrelinkFileSystem;
import ghidra.file.formats.ios.prelink.MachoPrelinkFileSystem;
import ghidra.file.jad.JadProcessWrapper;
import ghidra.file.jad.JarDecompiler;
import ghidra.formats.gfilesystem.*;
@ -273,7 +273,7 @@ public class FileFormatsPlugin extends Plugin implements FrontEndable {
}
FSBRootNode rootNode = ac.getRootOfSelectedNode();
return rootNode != null && rootNode.getFSRef() != null &&
rootNode.getFSRef().getFilesystem() instanceof PrelinkFileSystem;
rootNode.getFSRef().getFilesystem() instanceof MachoPrelinkFileSystem;
})
.popupMenuPath("Load iOS Kernel")
.popupMenuIcon(ImageManager.iOS)