mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-02-16 07:30:16 +00:00
GP-2071: Refactoring Mach-O things
This commit is contained in:
parent
2474de38d0
commit
ac0d7f6d43
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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>
|
||||
*/
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
}
|
@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user