Merge remote-tracking branch

'origin/GP-398_emteere_FixesForKernelandDyldCache' (Closes #2487, Closes
#3572)
This commit is contained in:
Ryan Kurtz 2021-11-02 15:45:20 -04:00
commit e8550982f3
42 changed files with 3119 additions and 456 deletions

View file

@ -507,12 +507,9 @@ public class BinaryReader {
* @exception IOException if an I/O error occurs
*/
public String readAsciiString(long index, int length) throws IOException {
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < length; ++i) {
byte b = provider.readByte(index++);
buffer.append((char) (b & 0x00FF));
}
return buffer.toString().trim();
byte[] readBytes = provider.readBytes(index, length);
String str = new String(readBytes);
return str.trim();
}
/**

View file

@ -45,6 +45,8 @@ public final class MachHeaderFileTypes {
public final static int MH_DSYM = 0xa;
/** x86_64 kexts */
public final static int MH_KEXT_BUNDLE = 0xb;
/** kernel cache fileset **/
public final static int MH_FILESET = 0xc;
public final static String getFileTypeName(int fileType) {
Field [] fields = MachHeaderFileTypes.class.getDeclaredFields();
@ -77,6 +79,7 @@ public final class MachHeaderFileTypes {
case MH_DYLIB_STUB: return "Shared Library Stub for Static Linking Only";
case MH_DSYM: return "Companion file with only debug sections";
case MH_KEXT_BUNDLE: return "x86 64 Kernel Extension";
case MH_FILESET: return "Kernel Cache Fileset";
}
return "Unrecognized file type: 0x"+Integer.toHexString(fileType);
}

View file

@ -0,0 +1,134 @@
/* ###
* 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 java.io.IOException;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* Represents a dyld_chained_fixups_header structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/include/mach-o/fixup-chains.h.auto.html">mach-o/fixup-chains.h</a>
*/
public class DyldChainedFixupHeader implements StructConverter {
private int fixups_version; // 0
private int starts_offset; // offset of dyld_chained_starts_in_image in chain_data
private int imports_offset; // offset of imports table in chain_data
private int symbols_offset; // offset of symbol strings in chain_data
private int imports_count; // number of imported symbol names
private int imports_format; // DYLD_CHAINED_IMPORT*
private int symbols_format; // 0 => uncompressed, 1 => zlib compressed
DyldChainedStartsInImage chainedStartsInImage;
DyldChainedImports chainedImports;
static DyldChainedFixupHeader createDyldChainedFixupHeader(
FactoryBundledWithBinaryReader reader) throws IOException {
DyldChainedFixupHeader dyldChainedFixupHeader =
(DyldChainedFixupHeader) reader.getFactory().create(DyldChainedFixupHeader.class);
dyldChainedFixupHeader.initDyldChainedFixupHeader(reader);
return dyldChainedFixupHeader;
}
/**
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
*/
public DyldChainedFixupHeader() {
}
private void initDyldChainedFixupHeader(FactoryBundledWithBinaryReader reader)
throws IOException {
long ptrIndex = reader.getPointerIndex();
fixups_version = reader.readNextInt();
starts_offset = reader.readNextInt();
imports_offset = reader.readNextInt();
symbols_offset = reader.readNextInt();
imports_count = reader.readNextInt();
imports_format = reader.readNextInt();
symbols_format = reader.readNextInt();
reader.setPointerIndex(ptrIndex + starts_offset);
chainedStartsInImage = DyldChainedStartsInImage.createDyldChainedStartsInImage(reader);
reader.setPointerIndex(ptrIndex + imports_offset);
chainedImports = DyldChainedImports.createDyldChainedImports(reader, this);
reader.setPointerIndex(ptrIndex + symbols_offset);
chainedImports.initSymbols(reader, this);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_chained_fixups_header", 0);
struct.add(DWORD, "fixups_version", null);
struct.add(DWORD, "starts_offset", null);
struct.add(DWORD, "imports_offset", null);
struct.add(DWORD, "symbols_offset", null);
struct.add(DWORD, "imports_count", null);
struct.add(DWORD, "imports_format", null);
struct.add(DWORD, "symbols_format", null);
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
public int getFixups_version() {
return fixups_version;
}
public int getStarts_offset() {
return starts_offset;
}
public int getImports_offset() {
return imports_offset;
}
public int getSymbols_offset() {
return symbols_offset;
}
public int getImports_count() {
return imports_count;
}
public int getImports_format() {
return imports_format;
}
public int getSymbols_format() {
return symbols_format;
}
public boolean isCompress() {
return symbols_format != 0;
}
public DyldChainedStartsInImage getChainedStartsInImage() {
return chainedStartsInImage;
}
public DyldChainedImports getChainedImports() {
return chainedImports;
}
}

View file

@ -0,0 +1,114 @@
/* ###
* 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 java.io.IOException;
import java.util.List;
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.flatapi.FlatProgramAPI;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.ProgramModule;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a LC_DYLD_CHAINED_FIXUPS command.
*
* @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 DyldChainedFixupsCommand extends LinkEditDataCommand {
private DyldChainedFixupHeader chainHeader;
static LinkEditDataCommand createDyldChainedFixupsCommand(FactoryBundledWithBinaryReader reader)
throws IOException {
DyldChainedFixupsCommand command =
(DyldChainedFixupsCommand) reader.getFactory().create(DyldChainedFixupsCommand.class);
command.initLinkEditDataCommand(reader);
long ptrIndex = reader.getPointerIndex();
reader.setPointerIndex(command.getDataOffset());
command.chainHeader = DyldChainedFixupHeader.createDyldChainedFixupHeader(reader);
reader.setPointerIndex(ptrIndex);
return command;
}
/**
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
*/
public DyldChainedFixupsCommand() {
}
@Override
public String getCommandName() {
return "dyld_chained_fixups_command";
}
@Override
public void markup(MachHeader header, FlatProgramAPI api, Address baseAddress, boolean isBinary,
ProgramModule parentModule, TaskMonitor monitor, MessageLog log) {
updateMonitor(monitor);
try {
if (isBinary) {
super.markup(header, api, baseAddress, isBinary, parentModule, monitor, log);
List<Address> addrs =
api.getCurrentProgram().getMemory().locateAddressesForFileOffset(
getDataOffset());
if (addrs.size() <= 0) {
throw new Exception("Chain Header does not exist in program");
}
Address dyldChainedHeader = addrs.get(0);
markupChainedFixupHeader(header, api, dyldChainedHeader, parentModule, monitor);
}
}
catch (Exception e) {
log.appendMsg("Unable to create " + getCommandName());
log.appendException(e);
}
}
private void markupChainedFixupHeader(MachHeader header, FlatProgramAPI api,
Address baseAddress, ProgramModule parentModule, TaskMonitor monitor)
throws DuplicateNameException, IOException, CodeUnitInsertionException, Exception {
DataType cHeader = chainHeader.toDataType();
api.createData(baseAddress, cHeader);
Address segsAddr = baseAddress.add(chainHeader.getStarts_offset());
DyldChainedStartsInImage chainedStartsInImage = chainHeader.getChainedStartsInImage();
int[] seg_info_offset = chainedStartsInImage.getSeg_info_offset();
DyldChainedStartsInSegment[] chainedStarts = chainedStartsInImage.getChainedStarts();
for (int i = 0; i < chainedStarts.length; i++) {
DyldChainedStartsInSegment startsInSeg = chainedStarts[i];
DataType dataType = startsInSeg.toDataType();
api.createData(segsAddr.add(seg_info_offset[i]), dataType);
}
}
public DyldChainedFixupHeader getChainHeader() {
return chainHeader;
}
}

View file

@ -0,0 +1,149 @@
/* ###
* 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 java.io.IOException;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* Represents a dyld_chained_import structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/include/mach-o/fixup-chains.h.auto.html">mach-o/fixup-chains.h</a>
*/
public class DyldChainedImport implements StructConverter {
private static final int DYLD_CHAINED_IMPORT = 1;
private static final int DYLD_CHAINED_IMPORT_ADDEND = 2;
private static final int DYLD_CHAINED_IMPORT_ADDEND64 = 3;
private int imports_format;
private int lib_ordinal;
private boolean weak_import;
private long name_offset;
private long addend;
private String symbolName;
static DyldChainedImport createDyldChainedImport(FactoryBundledWithBinaryReader reader,
DyldChainedFixupHeader cfh, int imports_format) throws IOException {
DyldChainedImport dyldChainedImport =
(DyldChainedImport) reader.getFactory().create(DyldChainedImport.class);
dyldChainedImport.initDyldChainedImport(reader, cfh, imports_format);
return dyldChainedImport;
}
/**
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
*/
public DyldChainedImport() {
}
private void initDyldChainedImport(FactoryBundledWithBinaryReader reader,
DyldChainedFixupHeader cfh, int format) throws IOException {
this.imports_format = format;
switch (format) {
case DYLD_CHAINED_IMPORT: {
int ival = reader.readNextInt();
lib_ordinal = ival & 0xff;
weak_import = ((ival >> 8) & 1) == 1;
name_offset = (ival >> 9 & 0x7fffff);
break;
}
case DYLD_CHAINED_IMPORT_ADDEND: {
int ival = reader.readNextInt();
lib_ordinal = ival & 0xff;
weak_import = ((ival >> 8) & 1) == 1;
name_offset = (ival >> 9 & 0x7fffff);
addend = reader.readNextInt();
break;
}
case DYLD_CHAINED_IMPORT_ADDEND64: {
long ival = reader.readNextLong();
lib_ordinal = (int) (ival & 0xffff);
weak_import = ((ival >> 8) & 1) == 1;
name_offset = (ival >> 32 & 0xffffffff);
addend = reader.readNextLong();
break;
}
default:
throw new IOException("Bad Chained import format: " + format);
}
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType dt = new StructureDataType("dyld_chained_import", 0);
try {
switch (imports_format) {
case DYLD_CHAINED_IMPORT:
dt.addBitField(DWORD, 8, "lib_ordinal", "ordinal in imports");
dt.addBitField(DWORD, 1, "weak_import", null);
dt.addBitField(DWORD, 23, "name_offset", null);
break;
case DYLD_CHAINED_IMPORT_ADDEND:
dt.addBitField(DWORD, 8, "lib_ordinal", "ordinal in imports");
dt.addBitField(DWORD, 1, "weak_import", null);
dt.addBitField(DWORD, 23, "name_offset", null);
dt.add(DWORD, "addend", null);
break;
case DYLD_CHAINED_IMPORT_ADDEND64:
dt.addBitField(QWORD, 16, "lib_ordinal", "ordinal in imports");
dt.addBitField(QWORD, 1, "weak_import", null);
dt.addBitField(QWORD, 15, "reserved", null);
dt.addBitField(QWORD, 32, "name_offset", null);
dt.add(QWORD, "addend", null);
break;
default:
throw new IOException("Bad Chained import format: " + imports_format);
}
}
catch (InvalidDataTypeException exc) {
// ignore
}
dt.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return dt;
}
public int getLibOrdinal() {
return lib_ordinal;
}
public boolean isWeakImport() {
return weak_import;
}
public long getNameOffset() {
return name_offset;
}
public long getAddend() {
return addend;
}
public String getName() {
return symbolName;
}
public void initString(FactoryBundledWithBinaryReader reader) throws IOException {
symbolName = reader.readNextNullTerminatedAsciiString();
}
}

View file

@ -0,0 +1,106 @@
/* ###
* 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 java.io.IOException;
import java.util.ArrayList;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.DuplicateNameException;
/**
* Represents a dyld_chained_import array.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/include/mach-o/fixup-chains.h.auto.html">mach-o/fixup-chains.h</a>
*/
public class DyldChainedImports implements StructConverter {
private int imports_count;
private int imports_format;
private long imports_offset;
private DyldChainedImport chainedImports[];
static DyldChainedImports createDyldChainedImports(FactoryBundledWithBinaryReader reader,
DyldChainedFixupHeader cfh) throws IOException {
DyldChainedImports dyldChainedImports =
(DyldChainedImports) reader.getFactory().create(DyldChainedImports.class);
dyldChainedImports.initDyldChainedStartsInImage(reader, cfh);
return dyldChainedImports;
}
/**
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
*/
public DyldChainedImports() {
}
private void initDyldChainedStartsInImage(FactoryBundledWithBinaryReader reader,
DyldChainedFixupHeader cfh) throws IOException {
long ptrIndex = reader.getPointerIndex();
imports_offset = ptrIndex;
this.imports_count = cfh.getImports_count();
this.imports_format = cfh.getImports_format();
ArrayList<DyldChainedImport> starts = new ArrayList<>();
for (int i = 0; i < imports_count; i++) {
starts.add(DyldChainedImport.createDyldChainedImport(reader, cfh, imports_format));
}
chainedImports = starts.toArray(DyldChainedImport[]::new);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
DataType chainedImportDt = chainedImports[0].toDataType();
DataType dt =
new ArrayDataType(chainedImportDt, imports_count, chainedImportDt.getLength());
return dt;
}
public int getImportsCount() {
return imports_count;
}
public long getImportsOffset() {
return imports_offset;
}
public DyldChainedImport[] getChainedImports() {
return chainedImports;
}
public DyldChainedImport getChainedImport(int ordinal) {
if (ordinal < 0 || ordinal >= imports_count) {
return null;
}
return chainedImports[ordinal];
}
public void initSymbols(FactoryBundledWithBinaryReader reader,
DyldChainedFixupHeader dyldChainedFixupHeader) throws IOException {
long ptrIndex = reader.getPointerIndex();
for (DyldChainedImport dyldChainedImport : chainedImports) {
reader.setPointerIndex(ptrIndex + dyldChainedImport.getNameOffset());
dyldChainedImport.initString(reader);
}
}
}

View file

@ -0,0 +1,92 @@
/* ###
* 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 java.io.IOException;
import java.util.ArrayList;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* Represents a dyld_chained_starts_in_image structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/include/mach-o/fixup-chains.h.auto.html">mach-o/fixup-chains.h</a>
*/
public class DyldChainedStartsInImage implements StructConverter {
private int seg_count; // count of segment chain starts
private int seg_info_offset[];
private DyldChainedStartsInSegment chainedStarts[];
static DyldChainedStartsInImage createDyldChainedStartsInImage(
FactoryBundledWithBinaryReader reader) throws IOException {
DyldChainedStartsInImage dyldChainedStartsInImage =
(DyldChainedStartsInImage) reader.getFactory().create(DyldChainedStartsInImage.class);
dyldChainedStartsInImage.initDyldChainedStartsInImage(reader);
return dyldChainedStartsInImage;
}
/**
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
*/
public DyldChainedStartsInImage() {
}
private void initDyldChainedStartsInImage(FactoryBundledWithBinaryReader reader)
throws IOException {
long ptrIndex = reader.getPointerIndex();
seg_count = reader.readNextInt();
seg_info_offset = reader.readNextIntArray(seg_count);
ArrayList<DyldChainedStartsInSegment> starts = new ArrayList<>();
for (int off : seg_info_offset) {
reader.setPointerIndex(ptrIndex + off);
starts.add(DyldChainedStartsInSegment.createDyldChainedFixupHeader(reader));
}
chainedStarts = starts.toArray(DyldChainedStartsInSegment[]::new);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_chained_starts_in_image", 0);
struct.add(DWORD, "seg_count", null);
struct.add(new ArrayDataType(DWORD, seg_count, 1), "seg_info_offset", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
public int getSeg_count() {
return seg_count;
}
public int[] getSeg_info_offset() {
return seg_info_offset;
}
public DyldChainedStartsInSegment[] getChainedStarts() {
return chainedStarts;
}
}

View file

@ -0,0 +1,115 @@
/* ###
* 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 java.io.IOException;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* Represents a dyld_chained_starts_in_segment structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/include/mach-o/fixup-chains.h.auto.html">mach-o/fixup-chains.h</a>
*/
public class DyldChainedStartsInSegment implements StructConverter {
private int size; // size of this (amount kernel needs to copy)
private short page_size; // 0x1000 or 0x4000
private short pointer_format; // DYLD_CHAINED_PTR_*
private long segment_offset; // offset in memory to start of segment
private int max_valid_pointer; // for 32-bit OS, any value beyond this is not a pointer
private short page_count; // how many pages are in array
private short page_starts[]; // each entry is offset in each page of first element in chain
private short chain_starts[]; // TODO: used for some 32-bit formats with multiple starts per page
static DyldChainedStartsInSegment createDyldChainedFixupHeader(
FactoryBundledWithBinaryReader reader) throws IOException {
DyldChainedStartsInSegment dyldChainedFixupHeader =
(DyldChainedStartsInSegment) reader.getFactory().create(
DyldChainedStartsInSegment.class);
dyldChainedFixupHeader.initDyldChainedStartsInSegment(reader);
return dyldChainedFixupHeader;
}
/**
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
*/
public DyldChainedStartsInSegment() {
}
private void initDyldChainedStartsInSegment(FactoryBundledWithBinaryReader reader)
throws IOException {
size = reader.readNextInt();
page_size = reader.readNextShort();
pointer_format = reader.readNextShort();
segment_offset = reader.readNextLong();
max_valid_pointer = reader.readNextInt();
page_count = reader.readNextShort();
page_starts = reader.readNextShortArray(page_count);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_chained_starts_in_segment", 0);
struct.add(DWORD, "size", null);
struct.add(WORD, "page_size", null);
struct.add(WORD, "pointer_format", null);
struct.add(QWORD, "segment_offset", null);
struct.add(DWORD, "max_valid_pointer", null);
struct.add(WORD, "page_count", null);
struct.add(new ArrayDataType(WORD, page_count, 1), "page_starts", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
public int getSize() {
return size;
}
public short getPageSize() {
return page_size;
}
public short getPointerFormat() {
return pointer_format;
}
public long getSegmentOffset() {
return segment_offset;
}
public int getMaxValidPointer() {
return max_valid_pointer;
}
public short getPageCount() {
return page_count;
}
public short[] getPage_starts() {
return page_starts;
}
public short[] getChain_starts() {
return chain_starts;
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -70,7 +69,7 @@ public class DynamicLibraryModule implements StructConverter {
iinit_iterm = reader.readNextInt();
ninit_nterm = reader.readNextInt();
if (is32bit) {
objc_module_info_addr = reader.readNextInt() & 0xffffffffL;
objc_module_info_addr = reader.readNextUnsignedInt();
objc_module_info_size = reader.readNextInt();
}
else {

View file

@ -0,0 +1,145 @@
/* ###
* 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 java.io.IOException;
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.flatapi.FlatProgramAPI;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.ProgramModule;
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>
*/
public class FileSetEntryCommand extends LoadCommand {
private long vmaddr;
private long fileoff;
private String entryName;
private long unknown;
boolean is32bit;
public static FileSetEntryCommand createFileSetEntryCommand(
FactoryBundledWithBinaryReader reader, boolean is32bit) throws IOException {
FileSetEntryCommand filesetEntryCommand =
(FileSetEntryCommand) reader.getFactory().create(FileSetEntryCommand.class);
filesetEntryCommand.initFileSetEntryCommand(reader, is32bit);
return filesetEntryCommand;
}
/**
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
*/
public FileSetEntryCommand() {
}
private void initFileSetEntryCommand(FactoryBundledWithBinaryReader 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;
}
public long getVMaddress() {
return vmaddr;
}
public long getFileOffset() {
return fileoff;
}
public long getUnknown() {
return unknown;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
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.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
@Override
public String getCommandName() {
return "fileset_entry_command";
}
@Override
public void markup(MachHeader header, FlatProgramAPI api, Address baseAddress, boolean isBinary,
ProgramModule parentModule, TaskMonitor monitor, MessageLog log) {
updateMonitor(monitor);
try {
if (isBinary) {
createFragment(api, baseAddress, parentModule);
Address addr = baseAddress.getNewAddress(getStartIndex());
DataType fileSetEntryDT = toDataType();
api.createData(addr, fileSetEntryDT);
api.setPlateComment(addr, getFileSetEntryName());
}
}
catch (Exception e) {
log.appendMsg("Unable to create " + getCommandName() + " - " + e.getMessage());
}
}
@Override
public String toString() {
return getFileSetEntryName();
}
}

View file

@ -51,7 +51,8 @@ public class LinkEditDataCommand extends LoadCommand {
public LinkEditDataCommand() {
}
private void initLinkEditDataCommand(FactoryBundledWithBinaryReader reader) throws IOException {
protected void initLinkEditDataCommand(FactoryBundledWithBinaryReader reader)
throws IOException {
initLoadCommand(reader);
dataoff = reader.readNextInt();
datasize = reader.readNextInt();

View file

@ -23,13 +23,15 @@ import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
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
*/
public final class LoadCommandTypes {
public static LoadCommand getLoadCommand(FactoryBundledWithBinaryReader reader, MachHeader header) throws IOException, MachException {
public static LoadCommand getLoadCommand(FactoryBundledWithBinaryReader reader,
MachHeader header) throws IOException, MachException {
int type = reader.peekNextInt();
switch (type) {
case LC_SEGMENT: {
@ -47,7 +49,8 @@ public final class LoadCommandTypes {
}
case LC_LOADFVMLIB:
case LC_IDFVMLIB: {
return FixedVirtualMemorySharedLibraryCommand.createFixedVirtualMemorySharedLibraryCommand(reader);
return FixedVirtualMemorySharedLibraryCommand.createFixedVirtualMemorySharedLibraryCommand(
reader);
}
case LC_IDENT: {
return IdentCommand.createIdentCommand(reader);
@ -62,9 +65,9 @@ public final class LoadCommandTypes {
return DynamicSymbolTableCommand.createDynamicSymbolTableCommand(reader, header);
}
case LC_LOAD_DYLIB:
case LC_ID_DYLIB:
case LC_ID_DYLIB:
case LC_LOAD_UPWARD_DYLIB:
case LC_DYLD_ENVIRONMENT:{
case LC_DYLD_ENVIRONMENT: {
return DynamicLibraryCommand.createDynamicLibraryCommand(reader);
}
case LC_LOAD_DYLINKER:
@ -112,7 +115,7 @@ public final class LoadCommandTypes {
}
case LC_CODE_SIGNATURE:
case LC_SEGMENT_SPLIT_INFO:
case LC_FUNCTION_STARTS:
case LC_FUNCTION_STARTS:
case LC_DATA_IN_CODE:
case LC_OPTIMIZATION_HINT:
case LC_DYLIB_CODE_SIGN_DRS: {
@ -149,7 +152,18 @@ public final class LoadCommandTypes {
case LC_LINKER_OPTIONS: {
return LinkerOptionCommand.createLinkerOptionCommand(reader);
}
case LC_DYLD_EXPORTS_TRIE:
return LinkEditDataCommand.createLinkEditDataCommand(reader);
case LC_DYLD_CHAINED_FIXUPS:
return DyldChainedFixupsCommand.createDyldChainedFixupsCommand(reader);
case LC_FILESET_ENTRY:
return FileSetEntryCommand.createFileSetEntryCommand(reader, header.is32bit());
default: {
Msg.warn(header, "Unsupported load command" + Integer.toHexString(type));
return UnsupportedLoadCommand.createUnsupportedLoadCommand(reader, type);
}
}
@ -261,6 +275,12 @@ public final class LoadCommandTypes {
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 **/
public final static int LC_DYLD_EXPORTS_TRIE = 0x33 | LC_REQ_DYLD;
/** used with LinkeditDataCommand **/
public final static int LC_DYLD_CHAINED_FIXUPS = 0x34 | LC_REQ_DYLD;
/** used with fileset_entry_command **/
public final static int LC_FILESET_ENTRY = 0x35 | LC_REQ_DYLD;
//@formatter:on
/**
@ -268,22 +288,22 @@ public final class LoadCommandTypes {
* @param type the load command type
* @return a string for 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_" ) ) {
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 ) {
Integer value = (Integer) field.get(null);
if (type == value) {
return field.getName();
}
}
catch ( Exception e ) {
catch (Exception e) {
break;
}
}
}
return "Unknown load command type: " + Integer.toHexString( type );
return "Unknown load command type: " + Integer.toHexString(type);
}
}

View file

@ -61,7 +61,7 @@ public class NList implements StructConverter {
n_sect = reader.readNextByte();
n_desc = reader.readNextShort();
if (is32bit) {
n_value = reader.readNextInt() & 0xffffffffL;
n_value = reader.readNextUnsignedInt();
}
else {
n_value = reader.readNextLong();

View file

@ -62,14 +62,14 @@ public class RoutinesCommand extends LoadCommand {
initLoadCommand(reader);
this.is32bit = is32bit;
if (is32bit) {
init_address = reader.readNextInt() & 0xffffffffL;
init_module = reader.readNextInt() & 0xffffffffL;
reserved1 = reader.readNextInt() & 0xffffffffL;
reserved2 = reader.readNextInt() & 0xffffffffL;
reserved3 = reader.readNextInt() & 0xffffffffL;
reserved4 = reader.readNextInt() & 0xffffffffL;
reserved5 = reader.readNextInt() & 0xffffffffL;
reserved6 = reader.readNextInt() & 0xffffffffL;
init_address = reader.readNextUnsignedInt();
init_module = reader.readNextUnsignedInt();
reserved1 = reader.readNextUnsignedInt();
reserved2 = reader.readNextUnsignedInt();
reserved3 = reader.readNextUnsignedInt();
reserved4 = reader.readNextUnsignedInt();
reserved5 = reader.readNextUnsignedInt();
reserved6 = reader.readNextUnsignedInt();
}
else {
init_address = reader.readNextLong();

View file

@ -71,10 +71,10 @@ public class SegmentCommand extends LoadCommand {
segname = reader.readNextAsciiString(MachConstants.NAME_LENGTH);
if (is32bit) {
vmaddr = reader.readNextInt() & 0xffffffffL;
vmsize = reader.readNextInt() & 0xffffffffL;
fileoff = reader.readNextInt() & 0xffffffffL;
filesize = reader.readNextInt() & 0xffffffffL;
vmaddr = reader.readNextUnsignedInt();
vmsize = reader.readNextUnsignedInt();
fileoff = reader.readNextUnsignedInt();
filesize = reader.readNextUnsignedInt();
}
else {
vmaddr = reader.readNextLong();

View file

@ -23,7 +23,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolIterator;
import ghidra.util.*;
import ghidra.util.DataConverter;
import ghidra.util.task.TaskMonitor;
abstract public class AbstractDyldInfoState {
@ -143,6 +143,13 @@ abstract public class AbstractDyldInfoState {
protected String getSegmentName() {
List<SegmentCommand> segments = header.getLoadCommands(SegmentCommand.class);
SegmentCommand segment = segments.get(segmentIndex);
List<FileSetEntryCommand> fileSetEntries =
header.getLoadCommands(FileSetEntryCommand.class);
for (FileSetEntryCommand fileSetEntryCommand : fileSetEntries) {
if (fileSetEntryCommand.getFileOffset() == segment.getFileOffset()) {
return fileSetEntryCommand.getFileSetEntryName();
}
}
return segment.getSegmentName();
}

View file

@ -34,9 +34,9 @@ import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
/**
* Represents a dyld_cache_accelerate_info structure.
* Represents a dyld_cache_accelerator_info structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
@SuppressWarnings("unused")
public class DyldCacheAccelerateInfo implements StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* Represents a dyld_cache_accelerator_dof structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
@SuppressWarnings("unused")
public class DyldCacheAcceleratorDof implements StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* Represents a dyld_cache_accelerator_initializer structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
@SuppressWarnings("unused")
public class DyldCacheAcceleratorInitializer implements StructConverter {

View file

@ -1,20 +0,0 @@
/* ###
* 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.dyld;
public final class DyldCacheConstants {
}

View file

@ -33,7 +33,7 @@ import ghidra.util.task.TaskMonitor;
/**
* Represents a dyld_cache_header structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
@SuppressWarnings("unused")
public class DyldCacheHeader implements StructConverter {
@ -58,18 +58,49 @@ public class DyldCacheHeader implements StructConverter {
private long accelerateInfoSize;
private long imagesTextOffset;
private long imagesTextCount;
private long patchInfoAddr;
private long patchInfoSize;
private long otherImageGroupAddrUnused; // unused
private long otherImageGroupSizeUnused; // unused
private long progClosuresAddr;
private long progClosuresSize;
private long progClosuresTrieAddr;
private long progClosuresTrieSize;
private int platform;
private int dyld_info;
private int formatVersion; // Extracted from dyld_info
private boolean dylibsExpectedOnDisk; // Extracted from dyld_info
private boolean simulator; // Extracted from dyld_info
private boolean locallyBuiltCache; // Extracted from dyld_info
private boolean builtFromChainedFixups; // Extracted from dyld_info
private int padding; // Extracted from dyld_info
private long sharedRegionStart;
private long sharedRegionSize;
private long maxSlide;
private long dylibsImageArrayAddr;
private long dylibsImageArraySize;
private long dylibsTrieAddr;
private long dylibsTrieSize;
private long otherImageArrayAddr;
private long otherImageArraySize;
private long otherTrieAddr;
private long otherTrieSize;
private int mappingWithSlideOffset;
private int mappingWithSlideCount;
private int headerType;
private int headerSize;
private BinaryReader reader;
private long baseAddress;
private List<DyldCacheMappingInfo> mappingInfoList;
private List<DyldCacheImageInfo> imageInfoList;
private DyldCacheSlideInfoCommon slideInfo;
private List<DyldCacheSlideInfoCommon> slideInfoList;
private DyldCacheLocalSymbolsInfo localSymbolsInfo;
private List<Long> branchPoolList;
private DyldCacheAccelerateInfo accelerateInfo;
private List<DyldCacheImageTextInfo> imageTextInfoList;
private DyldArchitecture architecture;
private List<DyldCacheMappingAndSlideInfo> cacheMappingAndSlideInfoList;
/**
* Create a new {@link DyldCacheHeader}.
@ -79,9 +110,10 @@ public class DyldCacheHeader implements StructConverter {
*/
public DyldCacheHeader(BinaryReader reader) throws IOException {
this.reader = reader;
long startIndex = reader.getPointerIndex();
// ------ HEADER 1 ---------
headerType = 1; // https://opensource.apple.com/source/dyld/dyld-95.3/launch-cache/dyld_cache_format.h.auto.html
// HEADER 1: https://opensource.apple.com/source/dyld/dyld-95.3/launch-cache/dyld_cache_format.h.auto.html
headerType = 1;
magic = reader.readNextByteArray(16);
mappingOffset = reader.readNextInt();
mappingCount = reader.readNextInt();
@ -89,37 +121,39 @@ public class DyldCacheHeader implements StructConverter {
imagesCount = reader.readNextInt();
dyldBaseAddress = reader.readNextLong();
// ------ HEADER 2 ---------
if (mappingOffset > 0x28) {
headerType = 2; // https://opensource.apple.com/source/dyld/dyld-195.5/launch-cache/dyld_cache_format.h.auto.html
// HEADER 2: https://opensource.apple.com/source/dyld/dyld-195.5/launch-cache/dyld_cache_format.h.auto.html
if (reader.getPointerIndex() < mappingOffset) {
headerType = 2;
codeSignatureOffset = reader.readNextLong();
codeSignatureSize = reader.readNextLong();
}
if (reader.getPointerIndex() < mappingOffset) {
slideInfoOffset = reader.readNextLong();
slideInfoSize = reader.readNextLong();
}
// ------ HEADER 3 ---------
if (mappingOffset > 0x48) {
headerType = 3; // No header file for this version (without the following UUID), but there are images of this version
// HEADER 3: No header file for this version (without the following UUID), but there are images of this version
if (reader.getPointerIndex() < mappingOffset) {
headerType = 3;
localSymbolsOffset = reader.readNextLong();
localSymbolsSize = reader.readNextLong();
}
// ------ HEADER 4 ---------
if (mappingOffset > 0x58) {
headerType = 4; // https://opensource.apple.com/source/dyld/dyld-239.3/launch-cache/dyld_cache_format.h.auto.html
// HEADER 4: https://opensource.apple.com/source/dyld/dyld-239.3/launch-cache/dyld_cache_format.h.auto.html
if (reader.getPointerIndex() < mappingOffset) {
headerType = 4;
uuid = reader.readNextByteArray(16);
}
// ------ HEADER 5 ---------
if (mappingOffset > 0x68) {
headerType = 5; // https://opensource.apple.com/source/dyld/dyld-360.14/launch-cache/dyld_cache_format.h.auto.html
// HEADER 5: https://opensource.apple.com/source/dyld/dyld-360.14/launch-cache/dyld_cache_format.h.auto.html
if (reader.getPointerIndex() < mappingOffset) {
headerType = 5;
cacheType = reader.readNextLong();
}
// ------ HEADER 6 ---------
if (mappingOffset > 0x70) {
headerType = 6; // https://opensource.apple.com/source/dyld/dyld-421.1/launch-cache/dyld_cache_format.h.auto.html
// HEADER 6: https://opensource.apple.com/source/dyld/dyld-421.1/launch-cache/dyld_cache_format.h.auto.html
if (reader.getPointerIndex() < mappingOffset) {
headerType = 6;
branchPoolsOffset = reader.readNextInt();
branchPoolsCount = reader.readNextInt();
accelerateInfoAddr = reader.readNextLong();
@ -128,10 +162,94 @@ public class DyldCacheHeader implements StructConverter {
imagesTextCount = reader.readNextLong();
}
// HEADER 7: https://opensource.apple.com/source/dyld/dyld-832.7.1/dyld3/shared-cache/dyld_cache_format.h.auto.html
if (reader.getPointerIndex() < mappingOffset) {
headerType = 7;
}
if (reader.getPointerIndex() < mappingOffset) {
patchInfoAddr = reader.readNextLong(); // (unslid) address of dyld_cache_patch_info
}
if (reader.getPointerIndex() < mappingOffset) {
patchInfoSize = reader.readNextLong(); // Size of all of the patch information pointed to via the dyld_cache_patch_info
}
if (reader.getPointerIndex() < mappingOffset) {
otherImageGroupAddrUnused = reader.readNextLong(); // unused
}
if (reader.getPointerIndex() < mappingOffset) {
otherImageGroupSizeUnused = reader.readNextLong(); // unused
}
if (reader.getPointerIndex() < mappingOffset) {
progClosuresAddr = reader.readNextLong(); // (unslid) address of list of program launch closures
}
if (reader.getPointerIndex() < mappingOffset) {
progClosuresSize = reader.readNextLong(); // size of list of program launch closures
}
if (reader.getPointerIndex() < mappingOffset) {
progClosuresTrieAddr = reader.readNextLong(); // (unslid) address of trie of indexes into program launch closures
}
if (reader.getPointerIndex() < mappingOffset) {
progClosuresTrieSize = reader.readNextLong(); // size of trie of indexes into program launch closures
}
if (reader.getPointerIndex() < mappingOffset) {
platform = reader.readNextInt(); // platform number (macOS=1, etc)
}
if (reader.getPointerIndex() < mappingOffset) {
dyld_info = reader.readNextInt();
formatVersion = dyld_info & 0xff; // dyld3::closure::kFormatVersion
dylibsExpectedOnDisk = (dyld_info >>> 8 & 1) == 1; // dyld should expect the dylib exists on disk and to compare inode/mtime to see if cache is valid
simulator = (dyld_info >>> 9 & 1) == 1; // for simulator of specified platform
locallyBuiltCache = (dyld_info >> 10 & 1) == 1; // 0 for B&I built cache, 1 for locally built cache
builtFromChainedFixups = (dyld_info >> 11 & 1) == 1; // some dylib in cache was built using chained fixups, so patch tables must be used for overrides
padding = (dyld_info >> 12) & 0xfffff; // TBD
}
if (reader.getPointerIndex() < mappingOffset) {
sharedRegionStart = reader.readNextLong(); // base load address of cache if not slid
}
if (reader.getPointerIndex() < mappingOffset) {
sharedRegionSize = reader.readNextLong(); // overall size of region cache can be mapped into
}
if (reader.getPointerIndex() < mappingOffset) {
maxSlide = reader.readNextLong(); // runtime slide of cache can be between zero and this value
}
if (reader.getPointerIndex() < mappingOffset) {
dylibsImageArrayAddr = reader.readNextLong(); // (unslid) address of ImageArray for dylibs in this cache
}
if (reader.getPointerIndex() < mappingOffset) {
dylibsImageArraySize = reader.readNextLong(); // size of ImageArray for dylibs in this cache
}
if (reader.getPointerIndex() < mappingOffset) {
dylibsTrieAddr = reader.readNextLong(); // (unslid) address of trie of indexes of all cached dylibs
}
if (reader.getPointerIndex() < mappingOffset) {
dylibsTrieSize = reader.readNextLong(); // size of trie of cached dylib paths
}
if (reader.getPointerIndex() < mappingOffset) {
otherImageArrayAddr = reader.readNextLong(); // (unslid) address of ImageArray for dylibs and bundles with dlopen closures
}
if (reader.getPointerIndex() < mappingOffset) {
otherImageArraySize = reader.readNextLong(); // size of ImageArray for dylibs and bundles with dlopen closures
}
if (reader.getPointerIndex() < mappingOffset) {
otherTrieAddr = reader.readNextLong(); // (unslid) address of trie of indexes of all dylibs and bundles with dlopen closures
}
if (reader.getPointerIndex() < mappingOffset) {
otherTrieSize = reader.readNextLong(); // size of trie of dylibs and bundles with dlopen closures
}
if (reader.getPointerIndex() < mappingOffset) {
mappingWithSlideOffset = reader.readNextInt(); // file offset to first dyld_cache_mapping_and_slide_info
}
if (reader.getPointerIndex() < mappingOffset) {
mappingWithSlideCount = reader.readNextInt(); // number of dyld_cache_mapping_and_slide_info entries
}
headerSize = (int) (reader.getPointerIndex() - startIndex);
baseAddress = reader.readLong(mappingOffset);
architecture = DyldArchitecture.getArchitecture(new String(magic).trim());
mappingInfoList = new ArrayList<>(mappingCount);
cacheMappingAndSlideInfoList = new ArrayList<>(mappingWithSlideCount);
slideInfoList = new ArrayList<>();
imageInfoList = new ArrayList<>(imagesCount);
branchPoolList = new ArrayList<>(branchPoolsCount);
imageTextInfoList = new ArrayList<>();
@ -151,9 +269,6 @@ public class DyldCacheHeader implements StructConverter {
parseMappingInfo(log, monitor);
parseImageInfo(log, monitor);
}
if (headerType >= 2) {
parseSlideInfo(log, monitor);
}
if (headerType >= 3) {
if (parseSymbols) {
parseLocalSymbolsInfo(log, monitor);
@ -163,6 +278,56 @@ public class DyldCacheHeader implements StructConverter {
parseBranchPools(log, monitor);
parseImageTextInfo(log, monitor);
}
parseCacheMappingSlideInfo(log, monitor);
if (haSlideInfo()) {
parseSlideInfos(log, monitor);
}
}
private void parseSlideInfos(MessageLog log, TaskMonitor monitor) throws CancelledException {
if (slideInfoOffset != 0) {
DyldCacheSlideInfoCommon slideInfo = parseSlideInfo(slideInfoOffset, log, monitor);
if (slideInfo != null) {
slideInfoList.add(slideInfo);
}
}
else if (cacheMappingAndSlideInfoList.size() > 0) {
// last section contains the real slide infos
int listLen = cacheMappingAndSlideInfoList.size();
DyldCacheMappingAndSlideInfo linkEditInfo =
cacheMappingAndSlideInfoList.get(listLen - 1);
for (DyldCacheMappingAndSlideInfo info : cacheMappingAndSlideInfoList) {
if (info.getSlideInfoFileOffset() == 0) {
continue;
}
long offsetInEditRegion =
info.getSlideInfoFileOffset() - linkEditInfo.getSlideInfoFileOffset();
DyldCacheSlideInfoCommon slideInfo =
parseSlideInfo(info.getSlideInfoFileOffset(), log, monitor);
slideInfoList.add(slideInfo);
}
}
}
private void parseCacheMappingSlideInfo(MessageLog log, TaskMonitor monitor)
throws CancelledException {
monitor.setMessage("Parsing DYLD cache mapping and slide info...");
monitor.initialize(mappingWithSlideCount);
try {
if (mappingWithSlideCount <= 0) {
return;
}
reader.setPointerIndex(mappingWithSlideOffset);
for (int i = 0; i < mappingWithSlideCount; ++i) {
cacheMappingAndSlideInfoList.add(new DyldCacheMappingAndSlideInfo(reader));
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (IOException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to parse dyld_cache_mapping_info.");
}
}
/**
@ -209,6 +374,9 @@ public class DyldCacheHeader implements StructConverter {
markupAcceleratorInfo(program, space, monitor, log);
markupImageTextInfo(program, space, monitor, log);
}
if (mappingWithSlideOffset >= 0) {
markupCacheMappingSlideInfo(program, space, log, monitor);
}
}
/**
@ -266,6 +434,15 @@ public class DyldCacheHeader implements StructConverter {
return imageInfoList;
}
/**
* Gets the {@link List} of {@link DyldCacheMappingAndSlideInfo}s. Requires header to have been parsed.
*
* @return The {@link List} of {@link DyldCacheMappingAndSlideInfo}s
*/
public List<DyldCacheMappingAndSlideInfo> getCacheMappingAndSlideInfos() {
return cacheMappingAndSlideInfoList;
}
/**
* Gets the {@link DyldCacheLocalSymbolsInfo}.
*
@ -274,28 +451,14 @@ public class DyldCacheHeader implements StructConverter {
public DyldCacheLocalSymbolsInfo getLocalSymbolsInfo() {
return localSymbolsInfo;
}
/**
* Gets the {@link DyldCacheSlideInfoCommon}.
* Gets the {@link List} of {@link DyldCacheSlideInfoCommon}s.
*
* @return the {@link DyldCacheSlideInfoCommon}. Common, or particular version
* @return the {@link List} of {@link DyldCacheSlideInfoCommon}s.
*/
public DyldCacheSlideInfoCommon getSlideInfo() {
return slideInfo;
}
/**
* @return slideInfoOffset
*/
public long getSlideInfoOffset() {
return slideInfoOffset;
}
/**
* @return slideInfoSize
*/
public long getSlideInfoSize() {
return slideInfoSize;
public List<DyldCacheSlideInfoCommon> getSlideInfos() {
return slideInfoList;
}
/**
@ -319,47 +482,86 @@ public class DyldCacheHeader implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_header", 0);
if (headerType >= 1) {
struct.add(new ArrayDataType(ASCII, 16, 1), "magic", "e.g. \"dyld_v0 i386\"");
struct.add(DWORD, "mappingOffset", "file offset to first dyld_cache_mapping_info");
struct.add(DWORD, "mappingCount", "number of dyld_cache_mapping_info entries");
struct.add(DWORD, "imagesOffset", "file offset to first dyld_cache_image_info");
struct.add(DWORD, "imagesCount", "number of dyld_cache_image_info entries");
struct.add(QWORD, "dyldBaseAddress", "base address of dyld when cache was built");
}
if (headerType >= 2) {
struct.add(QWORD, "codeSignatureOffset", "file offset of code signature blob");
struct.add(QWORD, "codeSignatureSize",
"size of code signature blob (zero means to end of file)");
struct.add(QWORD, "slideInfoOffset", "file offset of kernel slid info");
struct.add(QWORD, "slideInfoSize", "size of kernel slid info");
}
if (headerType >= 3) {
struct.add(QWORD, "localSymbolsOffset",
"file offset of where local symbols are stored");
struct.add(QWORD, "localSymbolsSize", "size of local symbols information");
}
if (headerType >= 4) {
struct.add(new ArrayDataType(BYTE, 16, 1), "uuid",
"unique value for each shared cache file");
}
if (headerType >= 5) {
struct.add(QWORD, "cacheType", "0 for development, 1 for production");
}
if (headerType >= 6) {
struct.add(DWORD, "branchPoolsOffset",
"file offset to table of uint64_t pool addresses");
struct.add(DWORD, "branchPoolsCount", "number of uint64_t entries");
struct.add(QWORD, "accelerateInfoAddr", "(unslid) address of optimization info");
struct.add(QWORD, "accelerateInfoSize", "size of optimization info");
struct.add(QWORD, "imagesTextOffset",
"file offset to first dyld_cache_image_text_info");
struct.add(QWORD, "imagesTextCount", "number of dyld_cache_image_text_info entries");
}
// @formatter:off
// headerType 1
//
addHeaderField(struct, new ArrayDataType(ASCII, 16, 1), "magic","e.g. \"dyld_v0 i386\"");
addHeaderField(struct, DWORD, "mappingOffset","file offset to first dyld_cache_mapping_info");
addHeaderField(struct, DWORD, "mappingCount", "number of dyld_cache_mapping_info entries");
addHeaderField(struct, DWORD, "imagesOffset", "file offset to first dyld_cache_image_info");
addHeaderField(struct, DWORD, "imagesCount", "number of dyld_cache_image_info entries");
addHeaderField(struct, QWORD, "dyldBaseAddress","base address of dyld when cache was built");
// headerType 2
//
addHeaderField(struct, QWORD, "codeSignatureOffset", "file offset of code signature blob");
addHeaderField(struct, QWORD, "codeSignatureSize","size of code signature blob (zero means to end of file)");
addHeaderField(struct, QWORD, "slideInfoOffset", "file offset of kernel slid info");
addHeaderField(struct, QWORD, "slideInfoSize", "size of kernel slid info");
// headerType 3
//
addHeaderField(struct, QWORD, "localSymbolsOffset","file offset of where local symbols are stored");
addHeaderField(struct, QWORD, "localSymbolsSize", "size of local symbols information");
// headerType 4
//
addHeaderField(struct, new ArrayDataType(BYTE, 16, 1), "uuid","unique value for each shared cache file");
// headerType 5
//
addHeaderField(struct, QWORD, "cacheType", "0 for development, 1 for production");
// headerType 6
//
addHeaderField(struct, DWORD, "branchPoolsOffset","file offset to table of uint64_t pool addresses");
addHeaderField(struct, DWORD, "branchPoolsCount", "number of uint64_t entries");
addHeaderField(struct, QWORD, "accelerateInfoAddr","(unslid) address of optimization info");
addHeaderField(struct, QWORD, "accelerateInfoSize", "size of optimization info");
addHeaderField(struct, QWORD, "imagesTextOffset","file offset to first dyld_cache_image_text_info");
addHeaderField(struct, QWORD, "imagesTextCount","number of dyld_cache_image_text_info entries");
// headerType 7
//
addHeaderField(struct, QWORD, "patchInfoAddr", "(unslid) address of dyld_cache_patch_info");
addHeaderField(struct, QWORD, "patchInfoSize", "Size of all of the patch information pointed to via the dyld_cache_patch_info");
addHeaderField(struct, QWORD, "otherImageGroupAddrUnused", "unused");
addHeaderField(struct, QWORD, "otherImageGroupSizeUnused", "unused");
addHeaderField(struct, QWORD, "progClosuresAddr", "(unslid) address of list of program launch closures");
addHeaderField(struct, QWORD, "progClosuresSize", "size of list of program launch closures");
addHeaderField(struct, QWORD, "progClosuresTrieAddr", "(unslid) address of trie of indexes into program launch closures");
addHeaderField(struct, QWORD, "progClosuresTrieSize", "size of trie of indexes into program launch closures");
addHeaderField(struct, DWORD, "platform", "platform number (macOS=1, etc)");
addHeaderField(struct, DWORD, "dyld_info", "");
addHeaderField(struct, QWORD, "sharedRegionStart", "base load address of cache if not slid");
addHeaderField(struct, QWORD, "sharedRegionSize", "overall size of region cache can be mapped into");
addHeaderField(struct, QWORD, "maxSlide","runtime slide of cache can be between zero and this value");
addHeaderField(struct, QWORD, "dylibsImageArrayAddr","(unslid) address of ImageArray for dylibs in this cache");
addHeaderField(struct, QWORD, "dylibsImageArraySize","size of ImageArray for dylibs in this cache");
addHeaderField(struct, QWORD, "dylibsTrieAddr","(unslid) address of trie of indexes of all cached dylibs");
addHeaderField(struct, QWORD, "dylibsTrieSize", "size of trie of cached dylib paths");
addHeaderField(struct, QWORD, "otherImageArrayAddr","(unslid) address of ImageArray for dylibs and bundles with dlopen closures");
addHeaderField(struct, QWORD, "otherImageArraySize","size of ImageArray for dylibs and bundles with dlopen closures");
addHeaderField(struct, QWORD, "otherTrieAddr","(unslid) address of trie of indexes of all dylibs and bundles with dlopen closures");
addHeaderField(struct, QWORD, "otherTrieSize","size of trie of dylibs and bundles with dlopen closures");
addHeaderField(struct, DWORD, "mappingWithSlideOffset","file offset to first dyld_cache_mapping_and_slide_info");
addHeaderField(struct, DWORD, "mappingWithSlideCount","number of dyld_cache_mapping_and_slide_info entries");
// @formatter:on
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
private void addHeaderField(StructureDataType struct, DataType dt, String fieldname,
String comment) {
if (headerSize > struct.getLength()) {
struct.add(dt, fieldname, comment);
}
}
private void parseMappingInfo(MessageLog log, TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Parsing DYLD mapping info...");
monitor.initialize(mappingCount);
@ -397,35 +599,11 @@ public class DyldCacheHeader implements StructConverter {
}
}
private void parseSlideInfo(MessageLog log, TaskMonitor monitor) throws CancelledException {
if (slideInfoOffset == 0) {
return;
}
monitor.setMessage("Parsing DYLD slide info...");
monitor.initialize(1);
try {
reader.setPointerIndex(slideInfoOffset);
slideInfo = new DyldCacheSlideInfoCommon(reader);
reader.setPointerIndex(slideInfoOffset);
switch (slideInfo.getVersion()) {
case 1:
slideInfo = new DyldCacheSlideInfo1(reader);
break;
case 2:
slideInfo = new DyldCacheSlideInfo2(reader);
break;
case 3:
slideInfo = new DyldCacheSlideInfo3(reader);
break;
default:
throw new IOException();
}
monitor.incrementProgress(1);
}
catch (IOException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to parse dyld_cache_slide_info.");
}
private DyldCacheSlideInfoCommon parseSlideInfo(long offset, MessageLog log,
TaskMonitor monitor) throws CancelledException {
DyldCacheSlideInfoCommon slideInfo =
DyldCacheSlideInfoCommon.parseSlideInfo(reader, offset, log, monitor);
return slideInfo;
}
private void parseLocalSymbolsInfo(MessageLog log, TaskMonitor monitor)
@ -543,6 +721,26 @@ public class DyldCacheHeader implements StructConverter {
}
}
private void markupCacheMappingSlideInfo(Program program, AddressSpace space, MessageLog log,
TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Marking up DYLD cache mapping and slide info...");
monitor.initialize(cacheMappingAndSlideInfoList.size());
try {
Address addr = fileOffsetToAddr(mappingWithSlideOffset, program, space);
for (DyldCacheMappingAndSlideInfo mappingInfo : cacheMappingAndSlideInfoList) {
Data d = DataUtilities.createData(program, addr, mappingInfo.toDataType(), -1,
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
addr = addr.add(d.getLength());
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (CodeUnitInsertionException | DuplicateNameException | IOException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to markup dyld_cache_mapping_info.");
}
}
private void markupImageInfo(Program program, AddressSpace space, TaskMonitor monitor,
MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD image info...");
@ -570,8 +768,9 @@ public class DyldCacheHeader implements StructConverter {
monitor.initialize(1);
try {
String size = "0x" + Long.toHexString(codeSignatureSize);
program.getListing().setComment(fileOffsetToAddr(codeSignatureOffset, program, space),
CodeUnit.PLATE_COMMENT, "Code Signature (" + size + " bytes)");
program.getListing()
.setComment(fileOffsetToAddr(codeSignatureOffset, program, space),
CodeUnit.PLATE_COMMENT, "Code Signature (" + size + " bytes)");
monitor.incrementProgress(1);
}
catch (IllegalArgumentException e) {
@ -585,10 +784,12 @@ public class DyldCacheHeader implements StructConverter {
monitor.setMessage("Marking up DYLD slide info...");
monitor.initialize(1);
try {
if (slideInfo != null) {
Address addr = fileOffsetToAddr(slideInfoOffset, program, space);
DataUtilities.createData(program, addr, slideInfo.toDataType(), -1, false,
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
if (slideInfoList.size() > 0) {
for (DyldCacheSlideInfoCommon info : slideInfoList) {
Address addr = fileOffsetToAddr(info.getSlideInfoOffset(), program, space);
DataUtilities.createData(program, addr, info.toDataType(), -1, false,
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
}
}
monitor.incrementProgress(1);
}
@ -666,8 +867,8 @@ public class DyldCacheHeader implements StructConverter {
for (DyldCacheImageTextInfo imageTextInfo : imageTextInfoList) {
Data d = DataUtilities.createData(program, addr, imageTextInfo.toDataType(), -1,
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
program.getListing().setComment(addr, CodeUnit.EOL_COMMENT,
imageTextInfo.getPath());
program.getListing()
.setComment(addr, CodeUnit.EOL_COMMENT, imageTextInfo.getPath());
addr = addr.add(d.getLength());
monitor.checkCanceled();
monitor.incrementProgress(1);
@ -713,4 +914,33 @@ public class DyldCacheHeader implements StructConverter {
return null;
}
/**
* Checks to see if any slide info exists
*
* @return True if any slide info exists; otherwise, false
*/
public boolean haSlideInfo() {
if (slideInfoSize != 0) {
// this is no longer used, but if non-zero, is older format and has slide-info
return true;
}
if (headerType > 6) {
for (DyldCacheMappingAndSlideInfo info : cacheMappingAndSlideInfoList) {
if (info.getSlideInfoFileSize() != 0) {
return true;
}
}
}
return false;
}
/**
* Get the original unslid load address. This is found in the first mapping infos.
*
* @return the original unslid load address
*/
public long unslidLoadAddress() {
return mappingInfoList.get(0).getAddress();
}
}

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* Represents a dyld_cache_image_info structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
@SuppressWarnings("unused")
public class DyldCacheImageInfo implements StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* Represents a dyld_cache_image_info_extra structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
@SuppressWarnings("unused")
public class DyldCacheImageInfoExtra implements StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* Represents a dyld_cache_image_text_info structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
@SuppressWarnings("unused")
public class DyldCacheImageTextInfo implements StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* Represents a dyld_cache_local_symbols_entry structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
@SuppressWarnings("unused")
public class DyldCacheLocalSymbolsEntry implements StructConverter {

View file

@ -40,7 +40,7 @@ import ghidra.util.task.TaskMonitor;
/**
* Represents a dyld_cache_local_symbols_info structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
@SuppressWarnings("unused")
public class DyldCacheLocalSymbolsInfo implements StructConverter {
@ -159,8 +159,7 @@ public class DyldCacheLocalSymbolsInfo implements StructConverter {
monitor.incrementProgress(1);
}
// sort the entries by the index in the string table, so don't jump around reading
List<NList> sortedList = nlistList
.stream()
List<NList> sortedList = nlistList.stream()
.sorted((o1, o2) -> Integer.compare(o1.getStringTableIndex(),
o2.getStringTableIndex()))
.collect(Collectors.toList());
@ -202,8 +201,7 @@ public class DyldCacheLocalSymbolsInfo implements StructConverter {
try {
Address addr = localSymbolsInfoAddr.add(nlistOffset);
for (NList nlist : nlistList) {
Data d = DataUtilities.createData(program, addr, nlist.toDataType(), -1, false,
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
Data d = program.getListing().createData(addr, nlist.toDataType());
addr = addr.add(d.getLength());
monitor.checkCanceled();
monitor.incrementProgress(1);
@ -221,8 +219,7 @@ public class DyldCacheLocalSymbolsInfo implements StructConverter {
try {
Address addr = localSymbolsInfoAddr.add(entriesOffset);
for (DyldCacheLocalSymbolsEntry localSymbolsEntry : localSymbolsEntryList) {
Data d = DataUtilities.createData(program, addr, localSymbolsEntry.toDataType(), -1,
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
Data d = program.getListing().createData(addr, localSymbolsEntry.toDataType());
addr = addr.add(d.getLength());
monitor.checkCanceled();
monitor.incrementProgress(1);

View file

@ -0,0 +1,172 @@
/* ###
* 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.dyld;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.bin.format.macho.commands.SegmentConstants;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* Represents a dyld_cache_mapping_and_slide_info structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
@SuppressWarnings("unused")
public class DyldCacheMappingAndSlideInfo implements StructConverter {
public static long DYLD_CACHE_MAPPING_AUTH_DATA = 1 << 3L;
public static long DYLD_CACHE_MAPPING_DIRTY_DATA = 1 << 1L;
public static long DYLD_CACHE_MAPPING_CONST_DATA = 1 << 2L;
private long address;
private long size;
private long fileOffset;
private long slideInfoFileOffset;
private long slideInfoFileSize;
private long flags;
private int maxProt;
private int initProt;
/**
* Create a new {@link DyldCacheImageInfo}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD mapping info
* @throws IOException if there was an IO-related problem creating the DYLD mapping info
*/
public DyldCacheMappingAndSlideInfo(BinaryReader reader) throws IOException {
address = reader.readNextLong();
size = reader.readNextLong();
fileOffset = reader.readNextLong();
slideInfoFileOffset = reader.readNextLong();
slideInfoFileSize = reader.readNextLong();
flags = reader.readNextLong();
maxProt = reader.readNextInt();
initProt = reader.readNextInt();
}
/**
* Gets the address of the start of the mapping.
*
* @return The address of the start of the mapping
*/
public long getAddress() {
return address;
}
/**
* Gets the size of the mapping.
*
* @return The size of the mapping
*/
public long getSize() {
return size;
}
/**
* Gets the file offset of the start of the mapping.
*
* @return The file offset of the start of the mapping
*/
public long getFileOffset() {
return fileOffset;
}
/**
* Get slide info file offset
*
* @return slide info file offset
*/
public long getSlideInfoFileOffset() {
return slideInfoFileOffset;
}
/**
* Get slide info file size
*
* @return slide info file size
*/
public long getSlideInfoFileSize() {
return slideInfoFileSize;
}
/**
* Get slide info flags
*
* @return slide info flags
*/
public long getFlags() {
return flags;
}
public boolean isAuthData() {
return (flags & DYLD_CACHE_MAPPING_AUTH_DATA) != 0;
}
public boolean isDirtyData() {
return (flags & DYLD_CACHE_MAPPING_DIRTY_DATA) != 0;
}
public boolean isConstData() {
return (flags & DYLD_CACHE_MAPPING_CONST_DATA) != 0;
}
/**
* Returns true if the initial protections include READ.
*
* @return true if the initial protections include READ
*/
public boolean isRead() {
return (initProt & SegmentConstants.PROTECTION_R) != 0;
}
/**
* Returns true if the initial protections include WRITE.
*
* @return true if the initial protections include WRITE
*/
public boolean isWrite() {
return (initProt & SegmentConstants.PROTECTION_W) != 0;
}
/**
* Returns true if the initial protections include EXECUTE.
*
* @return true if the initial protections include EXECUTE
*/
public boolean isExecute() {
return (initProt & SegmentConstants.PROTECTION_X) != 0;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_mapping_and_slide_info", 0);
struct.add(QWORD, "address", "");
struct.add(QWORD, "size", "");
struct.add(QWORD, "fileOffset", "");
struct.add(QWORD, "slideInfoFileOffset", "");
struct.add(QWORD, "slideInfoFileSize", "");
struct.add(QWORD, "flags", "");
struct.add(DWORD, "maxProt", "");
struct.add(DWORD, "initProt", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
}

View file

@ -27,7 +27,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* Represents a dyld_cache_mapping_info structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
@SuppressWarnings("unused")
public class DyldCacheMappingInfo implements StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* Represents a dyld_cache_range_entry structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
@SuppressWarnings("unused")
public class DyldCacheRangeEntry implements StructConverter {

View file

@ -16,16 +16,25 @@
package ghidra.app.util.bin.format.macho.dyld;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a dyld_cache_slide_info structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
@ -35,6 +44,9 @@ public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
private int entries_count;
private int entries_size;
private short toc[];
private byte bits[][];
public int getTocOffset() {
return toc_offset;
}
@ -55,6 +67,14 @@ public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
return entries_size;
}
public short[] getToc() {
return toc;
}
public byte[][] getEntries() {
return bits;
}
/**
* Create a new {@link DyldCacheSlideInfo1}.
*
@ -63,11 +83,22 @@ public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
*/
public DyldCacheSlideInfo1(BinaryReader reader) throws IOException {
super(reader);
long startIndex = reader.getPointerIndex() - 4; // version # already read
toc_offset = reader.readNextInt();
toc_count = reader.readNextInt();
entries_offset = reader.readNextInt();
entries_count = reader.readNextInt();
entries_size = reader.readNextInt();
reader.setPointerIndex(startIndex + toc_offset);
toc = reader.readNextShortArray(toc_count);
reader.setPointerIndex(startIndex + entries_offset);
bits = new byte[entries_count][];
for (int i = 0; i < entries_count; i++) {
bits[i] = reader.readNextByteArray(entries_size);
}
}
@Override
@ -79,7 +110,85 @@ public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
struct.add(DWORD, "entries_offset", "");
struct.add(DWORD, "entries_count", "");
struct.add(DWORD, "entries_size", "");
if (toc_offset > 0x18) {
struct.add(new ArrayDataType(ByteDataType.dataType, toc_offset - 0x18, -1),
"tocAlignment", "");
}
struct.add(new ArrayDataType(WordDataType.dataType, toc_count, -1), "toc", "");
if (entries_offset > (toc_offset + (toc_count * 2))) {
struct.add(new ArrayDataType(ByteDataType.dataType,
entries_offset - (toc_offset + (toc_count * 2)), -1), "entriesAlignment", "");
}
struct.add(new ArrayDataType(new ArrayDataType(ByteDataType.dataType, entries_size, -1),
entries_count, -1), "entries", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
@Override
public void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
boolean addRelocations, MessageLog log, TaskMonitor monitor)
throws MemoryAccessException, CancelledException {
Memory memory = program.getMemory();
List<DyldCacheMappingInfo> mappingInfos = dyldCacheHeader.getMappingInfos();
DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY);
long dataPageStart = dyldCacheMappingInfo.getAddress();
List<Address> unchainedLocList = new ArrayList<>(1024);
byte origBytes[] = new byte[8];
monitor.setMessage("Fixing V1 chained data page pointers...");
monitor.setMaximum(entries_count);
// V1 pointers currently don't need to be fixed, unless the pointers the
// dyld is slid from its preferred location.
for (int tocIndex = 0; tocIndex < toc_count; tocIndex++) {
monitor.checkCanceled();
int entryIndex = (toc[tocIndex]) & 0xFFFF;
if (entryIndex > entries_count || entryIndex > bits.length) {
log.appendMsg("Entry too big! [" + tocIndex + "] " + entryIndex + " " +
entries_count + " " + bits.length);
continue;
}
byte entry[] = bits[entryIndex];
long page = dataPageStart + (4096L * tocIndex);
for (int pageEntriesIndex = 0; pageEntriesIndex < 128; ++pageEntriesIndex) {
long prtEntryBitmap = entry[pageEntriesIndex] & 0xffL;
if (prtEntryBitmap != 0) {
for (int bitMapIndex = 0; bitMapIndex < 8; ++bitMapIndex) {
if ((prtEntryBitmap & (1L << bitMapIndex)) != 0) {
long loc = (page + pageEntriesIndex * 8 * 4 + bitMapIndex * 4);
Address addr =
memory.getProgram().getLanguage().getDefaultSpace().getAddress(loc);
long origValue = memory.getLong(addr);
long value = origValue /* + slide */ ;
// not actually changing bytes, so not really a relocation, but a relocate-able place
if (addRelocations) {
addRelocationTableEntry(program, addr, 0x1000, value, origBytes,
null);
}
//memory.setLong(addr, value);
unchainedLocList.add(addr);
}
}
}
}
monitor.setProgress(tocIndex);
}
createChainPointers(program, unchainedLocList, monitor);
}
}

View file

@ -16,19 +16,31 @@
package ghidra.app.util.bin.format.macho.dyld;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a dyld_cache_slide_info2 structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
private static final int DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE = 0x4000;
private static final int DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA = 0x8000;
private int page_size;
private int page_starts_offset;
private int page_starts_count;
@ -40,23 +52,23 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
private short page_extras_entries[];
public long getPageSize() {
return ((long)page_size) & 0xffffffff;
return Integer.toUnsignedLong(page_size);
}
public long getPageStartsOffset() {
return ((long) page_starts_offset) & 0xffffffff;
return Integer.toUnsignedLong(page_starts_offset);
}
public long getPageStartsCount() {
return ((long) page_starts_count) & 0xffffffff;
return Integer.toUnsignedLong(page_starts_count);
}
public long getPageExtrasOffset() {
return ((long) page_extras_offset) & 0xffffffff;
return Integer.toUnsignedLong(page_extras_offset);
}
public long getPageExtrasCount() {
return ((long) page_extras_count) & 0xffffffff;
return Integer.toUnsignedLong(page_extras_count);
}
public long getDeltaMask() {
@ -66,15 +78,15 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
public long getValueAdd() {
return value_add;
}
public short[] getPageStartsEntries() {
public short[] getPageStarts() {
return page_starts_entries;
}
public short[] getPageExtrasEntries() {
public short[] getPageExtras() {
return page_extras_entries;
}
/**
* Create a new {@link DyldCacheSlideInfo2}.
*
@ -108,4 +120,127 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
@Override
public void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
boolean addRelocations, MessageLog log, TaskMonitor monitor)
throws MemoryAccessException, CancelledException {
long fixedAddressCount = 0;
List<DyldCacheMappingInfo> mappingInfos = dyldCacheHeader.getMappingInfos();
DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY);
long dataPageStart = dyldCacheMappingInfo.getAddress();
long pageSize = getPageSize();
long pageStartsCount = getPageStartsCount();
long deltaMask = getDeltaMask();
long deltaShift = Long.numberOfTrailingZeros(deltaMask);
long valueAdd = getValueAdd();
short[] pageEntries = getPageStarts();
short[] extraEntries = getPageExtras();
monitor.setMessage("Fixing V2 chained data page pointers...");
monitor.setMaximum(pageStartsCount);
for (int index = 0; index < pageStartsCount; index++) {
monitor.checkCanceled();
long page = dataPageStart + (pageSize * index);
monitor.setProgress(index);
int pageEntry = pageEntries[index] & 0xffff;
if (pageEntry == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE) {
continue;
}
List<Address> unchainedLocList;
if ((pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) != 0) {
// go into extras and process list of chain entries for the same page
int extraIndex = (pageEntry & CHAIN_OFFSET_MASK);
unchainedLocList = new ArrayList<Address>(1024);
do {
pageEntry = extraEntries[extraIndex] & 0xffff;
long pageOffset = (pageEntry & CHAIN_OFFSET_MASK) * BYTES_PER_CHAIN_OFFSET;
List<Address> subLocList;
subLocList = processPointerChain2(program, page, pageOffset, deltaMask,
deltaShift, valueAdd, addRelocations, monitor);
unchainedLocList.addAll(subLocList);
extraIndex++;
}
while ((pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0);
}
else {
long pageOffset = pageEntry * BYTES_PER_CHAIN_OFFSET;
unchainedLocList = processPointerChain2(program, page, pageOffset, deltaMask,
deltaShift, valueAdd, addRelocations, monitor);
}
fixedAddressCount += unchainedLocList.size();
createChainPointers(program, unchainedLocList, monitor);
}
log.appendMsg("Fixed " + fixedAddressCount + " chained pointers.");
}
/**
* Fixes up any chained pointers, starting at the given address.
*
* @param program the program
* @param page within data pages that has pointers to be unchained
* @param nextOff offset within the page that is the chain start
* @param deltaMask delta offset mask for each value
* @param deltaShift shift needed for the deltaMask to extract the next offset
* @param valueAdd value to be added to each chain pointer
*
* @throws MemoryAccessException IO problem reading file
* @throws CancelledException user cancels
*/
private List<Address> processPointerChain2(Program program, long page, long nextOff,
long deltaMask, long deltaShift, long valueAdd, boolean addRelocations,
TaskMonitor monitor) throws MemoryAccessException, CancelledException {
// TODO: should the image base be used to perform the ASLR slide on the pointers.
// currently image is kept at it's initial location with no ASLR.
Address chainStart = program.getLanguage().getDefaultSpace().getAddress(page);
Memory memory = program.getMemory();
List<Address> unchainedLocList = new ArrayList<>(1024);
byte origBytes[] = new byte[8];
long valueMask = 0xffffffffffffffffL >>> (64 - deltaShift);
long delta = -1;
while (delta != 0) {
monitor.checkCanceled();
Address chainLoc = chainStart.add(nextOff);
long chainValue = memory.getLong(chainLoc);
delta = (chainValue & deltaMask) >> deltaShift;
chainValue = chainValue & valueMask;
if (chainValue != 0) {
chainValue += valueAdd;
// chainValue += slideAmount - if we were sliding
}
if (addRelocations) {
addRelocationTableEntry(program, chainLoc, 2, chainValue, origBytes, null);
}
memory.setLong(chainLoc, chainValue);
// delay creating data until after memory has been changed
unchainedLocList.add(chainLoc);
nextOff += (delta * 4);
}
return unchainedLocList;
}
}

View file

@ -16,19 +16,30 @@
package ghidra.app.util.bin.format.macho.dyld;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a dyld_cache_slide_info3 structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
private static final int DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE = 0xFFFF;
private int page_size;
private int page_starts_count;
private long auth_value_add;
@ -60,6 +71,7 @@ public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
super(reader);
page_size = reader.readNextInt();
page_starts_count = reader.readNextInt();
int pad = reader.readNextInt();
auth_value_add = reader.readNextLong();
page_starts = reader.readNextShortArray(page_starts_count);
}
@ -70,9 +82,135 @@ public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
struct.add(DWORD, "version", "");
struct.add(DWORD, "page_size", "");
struct.add(DWORD, "page_starts_count", "");
struct.add(DWORD, "pad", "");
struct.add(QWORD, "auth_value_add", "");
struct.add(new ArrayDataType(WORD, page_starts_count, 1), "page_starts", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
@Override
public void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
boolean addRelocations, MessageLog log, TaskMonitor monitor)
throws MemoryAccessException, CancelledException {
long fixedAddressCount = 0;
List<DyldCacheMappingAndSlideInfo> mappingInfos =
dyldCacheHeader.getCacheMappingAndSlideInfos();
if (mappingInfos.size() <= DATA_PAGE_MAP_ENTRY) {
return;
}
DyldCacheMappingAndSlideInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY); // default
for (DyldCacheMappingAndSlideInfo cacheSlideInfo : mappingInfos) {
if (cacheSlideInfo.getSlideInfoFileOffset() == getSlideInfoOffset()) {
dyldCacheMappingInfo = cacheSlideInfo;
break;
}
}
long dataPageStart = dyldCacheMappingInfo.getAddress();
long pageSize = getPageSize();
long pageStartsCount = getPageStartsCount();
long authValueAdd = getAuthValueAdd();
short[] pageStarts = getPageStarts();
monitor.setMessage("Fixing V3 chained data page 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_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE) {
continue;
}
long pageOffset = (pageEntry / 8) * 8; // first entry byte based
List<Address> unchainedLocList;
unchainedLocList = processPointerChain3(program, page, pageOffset, authValueAdd,
addRelocations, monitor);
fixedAddressCount += unchainedLocList.size();
createChainPointers(program, unchainedLocList, monitor);
}
log.appendMsg("Fixed " + fixedAddressCount + " chained pointers.");
monitor.setMessage("Created " + fixedAddressCount + " chained pointers");
}
/**
* Fixes up any chained pointers, starting at the given address.
*
* @param program the program
* @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
*
* @return list of locations that were unchained
*
* @throws MemoryAccessException IO problem reading file
* @throws CancelledException user cancels
*/
private List<Address> processPointerChain3(Program program, long page, long nextOff,
long auth_value_add, boolean addRelocation, TaskMonitor monitor)
throws MemoryAccessException, CancelledException {
// TODO: should the image base be used to perform the ASLR slide on the pointers.
// currently image is kept at it's initial location with no ASLR.
Address chainStart = program.getLanguage().getDefaultSpace().getAddress(page);
Memory memory = program.getMemory();
List<Address> unchainedLocList = new ArrayList<>(1024);
byte origBytes[] = new byte[8];
long delta = -1;
while (delta != 0) {
monitor.checkCanceled();
Address chainLoc = chainStart.add(nextOff);
long chainValue = memory.getLong(chainLoc);
// if authenticated pointer
boolean isAuthenticated = chainValue >>> 63 != 0;
delta = (chainValue & (0x7FFL << 51L)) >> 51L;
if (isAuthenticated) {
long offsetFromSharedCacheBase = chainValue & 0xFFFFFFFFL;
//long diversityData = (chainValue >> 32L) & 0xFFFFL;
//long hasAddressDiversity = (chainValue >> 48L) & 0x1L;
//long key = (chainValue >> 49L) & 0x3L;
chainValue = offsetFromSharedCacheBase + auth_value_add;
}
else {
long top8Bits = chainValue & 0x0007F80000000000L;
long bottom43Bits = chainValue & 0x000007FFFFFFFFFFL;
chainValue = (top8Bits << 13) | bottom43Bits;
// chainValue += slideAmount - if we were sliding
}
if (addRelocation) {
addRelocationTableEntry(program, chainLoc, 3 * (isAuthenticated ? -1 : 1),
chainValue, origBytes, null);
}
memory.setLong(chainLoc, chainValue);
// delay creating data until after memory has been changed
unchainedLocList.add(chainLoc);
nextOff += delta * 8;
}
return unchainedLocList;
}
}

View file

@ -0,0 +1,266 @@
/* ###
* 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.dyld;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a dyld_cache_slide_info3 structure.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
public class DyldCacheSlideInfo4 extends DyldCacheSlideInfoCommon {
private static final int DYLD_CACHE_SLIDE4_PAGE_NO_REBASE = 0xFFFF;
private static final int DYLD_CACHE_SLIDE4_PAGE_INDEX = 0x7FFF;
private static final int DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA = 0x8000;
private static final int DYLD_CACHE_SLIDE4_PAGE_EXTRA_END = 0x8000;
private static final int HEADERSIZE4 = 40;
private int page_size; // currently 4096 (may also be 16384)
private int page_starts_offset;
private int page_starts_count;
private int page_extras_offset;
private int page_extras_count;
private long delta_mask; // which (contiguous) set of bits contains the delta to the next rebase location (0xC0000000)
private long value_add; // base address of cache
private short page_starts[];
private short page_extras[];
public int getPageSize() {
return page_size;
}
public int getPageStartsOffset() {
return page_starts_offset;
}
public int getPageStartsCount() {
return page_starts_count;
}
public int getPageExtrasOffset() {
return page_extras_offset;
}
public int getPageExtrasCount() {
return page_extras_count;
}
public long getDeltaMask() {
return delta_mask;
}
public long getValueAdd() {
return value_add;
}
public short[] getPageStarts() {
return page_starts;
}
public short[] getPageExtras() {
return page_extras;
}
/**
* Create a new {@link DyldCacheSlideInfo3}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info 3
* @throws IOException if there was an IO-related problem creating the DYLD slide info 3
*/
public DyldCacheSlideInfo4(BinaryReader reader) throws IOException {
super(reader);
page_size = reader.readNextInt();
page_starts_offset = reader.readNextInt();
page_starts_count = reader.readNextInt();
page_extras_offset = reader.readNextInt();
page_extras_count = reader.readNextInt();
delta_mask = reader.readNextLong();
value_add = reader.readNextLong();
reader.setPointerIndex(page_starts_offset);
page_starts = reader.readNextShortArray(page_starts_count);
reader.setPointerIndex(page_extras_offset);
page_extras = reader.readNextShortArray(page_extras_count);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_slide_info4", 0);
struct.add(DWORD, "version", "");
struct.add(DWORD, "page_size", "");
struct.add(DWORD, "page_starts_offset", "");
struct.add(DWORD, "page_starts_count", "");
struct.add(DWORD, "page_extras_offset", "");
struct.add(DWORD, "page_extras_count", "");
struct.add(QWORD, "delta_mask", "");
struct.add(QWORD, "value_add", "");
if (page_starts_offset == HEADERSIZE4) {
struct.add(new ArrayDataType(WORD, page_starts_count, 1), "page_starts", "");
}
if (page_extras_offset == (HEADERSIZE4 + page_starts_count * 2)) {
struct.add(new ArrayDataType(WORD, page_extras_count, 1), "page_extras", "");
}
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
@Override
public void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
boolean addRelocations, MessageLog log, TaskMonitor monitor)
throws MemoryAccessException, CancelledException {
long fixedAddressCount = 0;
List<DyldCacheMappingInfo> mappingInfos = dyldCacheHeader.getMappingInfos();
DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY);
long dataPageStart = dyldCacheMappingInfo.getAddress();
long pageSize = getPageSize();
long pageStartsCount = getPageStartsCount();
long deltaMask = getDeltaMask();
long deltaShift = Long.numberOfTrailingZeros(deltaMask);
long valueAdd = getValueAdd();
short[] pageEntries = getPageStarts();
short[] extraEntries = getPageExtras();
monitor.setMessage("Fixing V4 chained data page pointers...");
monitor.setMaximum(pageStartsCount);
for (int index = 0; index < pageStartsCount; index++) {
monitor.checkCanceled();
long page = dataPageStart + (pageSize * index);
monitor.setProgress(index);
int pageEntry = pageEntries[index] & 0xffff;
if (pageEntry == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE) {
continue;
}
List<Address> unchainedLocList;
if ((pageEntry & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA) != 0) {
// go into extras and process list of chain entries for the same page
int extraIndex = (pageEntry & CHAIN_OFFSET_MASK);
unchainedLocList = new ArrayList<>(1024);
do {
pageEntry = extraEntries[extraIndex] & 0xffff;
long pageOffset = (pageEntry & CHAIN_OFFSET_MASK) * BYTES_PER_CHAIN_OFFSET;
List<Address> subLocList;
subLocList = processPointerChain4(program, page, pageOffset, deltaMask,
deltaShift, valueAdd, addRelocations, monitor);
unchainedLocList.addAll(subLocList);
extraIndex++;
}
while ((pageEntry & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA) == 0);
}
else {
long pageOffset = pageEntry * BYTES_PER_CHAIN_OFFSET;
unchainedLocList = processPointerChain4(program, page, pageOffset, deltaMask,
deltaShift, valueAdd, addRelocations, monitor);
}
fixedAddressCount += unchainedLocList.size();
createChainPointers(program, unchainedLocList, monitor);
}
log.appendMsg("Fixed " + fixedAddressCount + " chained pointers.");
}
/**
* Fixes up any chained pointers, starting at the given address.
*
* @param unchainedLocList list of locations that were unchained
* @param page within data pages that has pointers to be unchained
* @param nextOff offset within the page that is the chain start
* @param deltaMask delta offset mask for each value
* @param deltaShift shift needed for the deltaMask to extract the next offset
* @param valueAdd value to be added to each chain pointer
*
* @throws MemoryAccessException IO problem reading file
* @throws CancelledException user cancels
*/
private List<Address> processPointerChain4(Program program, long page, long nextOff,
long deltaMask, long deltaShift, long valueAdd, boolean addRelocations,
TaskMonitor monitor) throws MemoryAccessException, CancelledException {
// TODO: should the image base be used to perform the ASLR slide on the pointers.
// currently image is kept at it's initial location with no ASLR.
Address chainStart = program.getLanguage().getDefaultSpace().getAddress(page);
Memory memory = program.getMemory();
List<Address> unchainedLocList = new ArrayList<Address>(1024);
byte origBytes[] = new byte[4];
int valueMask = 0xffffffff >>> (32 - deltaShift);
long delta = -1;
while (delta != 0) {
monitor.checkCanceled();
Address chainLoc = chainStart.add(nextOff);
int chainValue = memory.getInt(chainLoc);
delta = (chainValue & deltaMask) >> deltaShift;
chainValue = chainValue & valueMask;
if ((chainValue & 0xFFFF8000) == 0) {
// small positive non-pointer, use as-is
}
else if ((chainValue & 0x3FFF8000) == 0x3FFF8000) {
chainValue |= 0xC0000000;
}
else {
chainValue += valueAdd;
// chainValue += slideAmount - if we were sliding
}
if (addRelocations) {
addRelocationTableEntry(program, chainLoc, 4, chainValue, origBytes, null);
}
memory.setInt(chainLoc, chainValue);
// delay creating data until after memory has been changed
unchainedLocList.add(chainLoc);
nextOff += (delta * 4);
}
return unchainedLocList;
}
}

View file

@ -16,23 +16,85 @@
package ghidra.app.util.bin.format.macho.dyld;
import java.io.IOException;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Class for representing the common components of the various dyld_cache_slide_info structures.
* The intent is for the the full dyld_cache_slide_info structures to extend this and add their
* specific parts.
*
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
*/
public class DyldCacheSlideInfoCommon implements StructConverter {
public abstract class DyldCacheSlideInfoCommon implements StructConverter {
public static final int DATA_PAGE_MAP_ENTRY = 1;
public static final int BYTES_PER_CHAIN_OFFSET = 4;
public static final int CHAIN_OFFSET_MASK = 0x3fff;
/**
* Parses the slide info
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info
* @param slideInfoOffset The offset of the slide info to parse
* @param log The log
* @param monitor A cancelable task monitor
* @return The slide info object
*/
public static DyldCacheSlideInfoCommon parseSlideInfo(BinaryReader reader, long slideInfoOffset,
MessageLog log, TaskMonitor monitor) {
if (slideInfoOffset == 0) {
return null;
}
DyldCacheSlideInfoCommon returnedSlideInfo = null;
monitor.setMessage("Parsing DYLD slide info...");
monitor.initialize(1);
try {
reader.setPointerIndex(slideInfoOffset);
int version = reader.readNextInt();
reader.setPointerIndex(slideInfoOffset);
switch (version) {
case 1:
returnedSlideInfo = new DyldCacheSlideInfo1(reader);
break;
case 2:
returnedSlideInfo = new DyldCacheSlideInfo2(reader);
break;
case 3:
returnedSlideInfo = new DyldCacheSlideInfo3(reader);
break;
case 4:
returnedSlideInfo = new DyldCacheSlideInfo4(reader);
break;
default:
throw new IOException();
}
monitor.incrementProgress(1);
}
catch (IOException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to parse dyld_cache_slide_info.");
return null;
}
returnedSlideInfo.slideInfoOffset = slideInfoOffset;
return returnedSlideInfo;
}
protected int version;
protected long slideInfoOffset;
/**
* Create a new {@link DyldCacheSlideInfoCommon}.
@ -53,6 +115,54 @@ public class DyldCacheSlideInfoCommon implements StructConverter {
return version;
}
/**
* Return the original slide info offset
*
* @return the original slide info offset
*/
public long getSlideInfoOffset() {
return slideInfoOffset;
}
public abstract void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
boolean addRelocations, MessageLog log, TaskMonitor monitor)
throws MemoryAccessException, CancelledException;
protected void addRelocationTableEntry(Program program, Address chainLoc, int type,
long chainValue, byte[] origBytes, String name) throws MemoryAccessException {
// Add entry to relocation table for the pointer fixup
program.getMemory().getBytes(chainLoc, origBytes);
program.getRelocationTable()
.add(chainLoc, type, new long[] { chainValue }, origBytes, name);
}
/**
* Create pointers at each fixed chain location.
*
* @param program The program
* @param unchainedLocList Address list of fixed pointer locations
* @param monitor A cancelable task monitor
*
* @throws CancelledException if the user cancels
*/
protected void createChainPointers(Program program, List<Address> unchainedLocList,
TaskMonitor monitor) throws CancelledException {
int numFixedLocations = unchainedLocList.size();
monitor.setMessage("Fixed " + numFixedLocations + " chained pointers. Creating Pointers");
// Create pointers at any fixed-up addresses
for (Address addr : unchainedLocList) {
monitor.checkCanceled();
try {
program.getListing().createData(addr, Pointer64DataType.dataType);
}
catch (CodeUnitInsertionException e) {
// No worries, something presumably more important was there already
}
}
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_slide_info", 0);

View file

@ -0,0 +1,405 @@
/* ###
* 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.dyld;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
/**
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/include/mach-o/fixup-chains.h.auto.html">mach-o/fixup-chains.h</a>
*/
public class DyldChainedPtr {
public enum DyldChainType {
DYLD_CHAINED_PTR_ARM64E(1), // stride 8, unauth target is vmaddr
DYLD_CHAINED_PTR_64(2), // target is vmaddr
DYLD_CHAINED_PTR_32(3),
DYLD_CHAINED_PTR_32_CACHE(4),
DYLD_CHAINED_PTR_32_FIRMWARE(5),
DYLD_CHAINED_PTR_64_OFFSET(6), // target is vm offset
DYLD_CHAINED_PTR_ARM64E_KERNEL(7), // stride 4, unauth target is vm offset
DYLD_CHAINED_PTR_64_KERNEL_CACHE(8),
DYLD_CHAINED_PTR_ARM64E_USERLAND(9), // stride 8, unauth target is vm offset
DYLD_CHAINED_PTR_ARM64E_FIRMWARE(10), // stride 4, unauth target is vmaddr
DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE(11), // stride 1, x86_64 kernel caches
DYLD_CHAINED_PTR_ARM64E_USERLAND24(12), // stride 8, unauth target is vm offset, 24-bit bind
DYLD_CHAINED_PTR_TYPE_UNKNOWN(-1);
private final int val;
private final String name;
private DyldChainType(int v) {
val = v;
name = this.name().substring("DYLD_CHAINED_".length());
}
public static DyldChainType lookupChainPtr(int val) {
switch (val) {
case 1:
return DYLD_CHAINED_PTR_ARM64E;
case 2:
return DYLD_CHAINED_PTR_64;
case 3:
return DYLD_CHAINED_PTR_32;
case 4:
return DYLD_CHAINED_PTR_32_CACHE;
case 5:
return DYLD_CHAINED_PTR_32_FIRMWARE;
case 6:
return DYLD_CHAINED_PTR_64_OFFSET;
case 7:
return DYLD_CHAINED_PTR_ARM64E_KERNEL;
case 8:
return DYLD_CHAINED_PTR_64_KERNEL_CACHE;
case 9:
return DYLD_CHAINED_PTR_ARM64E_USERLAND;
case 10:
return DYLD_CHAINED_PTR_ARM64E_FIRMWARE;
case 11:
return DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE;
case 12:
return DYLD_CHAINED_PTR_ARM64E_USERLAND24;
}
return DYLD_CHAINED_PTR_TYPE_UNKNOWN;
}
public int getValue() {
return val;
}
public String getName() {
return name;
}
}
public static final int DYLD_CHAINED_PTR_START_NONE = 0xFFFF;
public static final int DYLD_CHAINED_PTR_START_MULTI = 0x8000;
public static final int DYLD_CHAINED_PTR_START_LAST = 0x8000;
public static long getStride(DyldChainType ptrFormat) {
switch (ptrFormat) {
case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
return 8;
case DYLD_CHAINED_PTR_64:
case DYLD_CHAINED_PTR_64_OFFSET:
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
case DYLD_CHAINED_PTR_32:
case DYLD_CHAINED_PTR_32_CACHE:
case DYLD_CHAINED_PTR_32_FIRMWARE:
return 4;
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
return 1;
default:
return 1;
}
}
public static void setChainValue(Memory memory, Address chainLoc, DyldChainType ptrFormat,
long value) throws MemoryAccessException {
switch (ptrFormat) {
case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
case DYLD_CHAINED_PTR_64:
case DYLD_CHAINED_PTR_64_OFFSET:
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
memory.setLong(chainLoc, value);
break;
case DYLD_CHAINED_PTR_32:
case DYLD_CHAINED_PTR_32_CACHE:
case DYLD_CHAINED_PTR_32_FIRMWARE:
memory.setInt(chainLoc, (int) (value & 0xFFFFFFFFL));
break;
default:
break;
}
}
public static long getChainValue(Memory memory, Address chainLoc, DyldChainType ptrFormat)
throws MemoryAccessException {
switch (ptrFormat) {
case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
case DYLD_CHAINED_PTR_64:
case DYLD_CHAINED_PTR_64_OFFSET:
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
return memory.getLong(chainLoc);
case DYLD_CHAINED_PTR_32:
case DYLD_CHAINED_PTR_32_CACHE:
case DYLD_CHAINED_PTR_32_FIRMWARE:
return memory.getInt(chainLoc) & 0xFFFFFFFFL;
default:
return 0;
}
}
public static boolean isBound(DyldChainType ptrFormat, long chainValue) {
switch (ptrFormat) {
case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
return ((chainValue >>> 62) & 1) != 0;
case DYLD_CHAINED_PTR_64:
case DYLD_CHAINED_PTR_64_OFFSET:
return ((chainValue >>> 63) & 1) != 0;
case DYLD_CHAINED_PTR_32:
return ((chainValue >>> 31) & 1) != 0;
// Never bound
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
case DYLD_CHAINED_PTR_32_CACHE:
case DYLD_CHAINED_PTR_32_FIRMWARE:
default:
return false;
}
}
public static boolean isAuthenticated(DyldChainType ptrFormat, long chainValue) {
switch (ptrFormat) {
case DYLD_CHAINED_PTR_64:
case DYLD_CHAINED_PTR_64_OFFSET:
case DYLD_CHAINED_PTR_32:
case DYLD_CHAINED_PTR_32_CACHE:
case DYLD_CHAINED_PTR_32_FIRMWARE:
return false;
default:
break;
}
boolean isAuthenticated = ((chainValue >>> 63) & 1) != 0;
return isAuthenticated;
}
public static long getDiversity(DyldChainType ptrFormat, long chainValue) {
if (!isAuthenticated(ptrFormat, chainValue)) {
return 0;
}
long diversityData = (chainValue >> 32L) & 0xFFFFL;
return diversityData;
}
public static boolean hasAddrDiversity(DyldChainType ptrFormat, long chainValue) {
if (!isAuthenticated(ptrFormat, chainValue)) {
return false;
}
return ((chainValue >> 48L) & 0x1L) == 1;
}
public static long getKey(DyldChainType ptrFormat, long chainValue) {
if (!isAuthenticated(ptrFormat, chainValue)) {
return 0;
}
return (chainValue >> 49L) & 0x3L;
}
public static long getTarget(DyldChainType ptrFormat, long chainValue) {
long target = 0;
if (isBound(ptrFormat, chainValue)) {
return -1;
}
if (isAuthenticated(ptrFormat, chainValue)) {
switch (ptrFormat) {
case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
return chainValue & 0x000000FFFFFFFFL;
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
return (chainValue & 0x3FFFFFFFL); // 30 bits
default:
break;
}
}
switch (ptrFormat) {
case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
long top8Bits = (chainValue >> 43) & 0xFFL;
long bottom43Bits = chainValue & 0x000007FFFFFFFFFFL;
// Hack! Top bits don't matter and are a pointer tag
if (top8Bits == 0x80) {
top8Bits = 0;
}
target = (top8Bits << 56) | bottom43Bits;
break;
case DYLD_CHAINED_PTR_64:
case DYLD_CHAINED_PTR_64_OFFSET:
top8Bits = (chainValue >> 36) & 0xFFL;
long bottom36Bits = chainValue & 0x00000FFFFFFFFFL;
// Hack! Top bits don't matter and are a pointer tag
if (top8Bits == 0x80) {
top8Bits = 0;
}
target = (top8Bits << 56) | bottom36Bits;
break;
case DYLD_CHAINED_PTR_32:
target = (chainValue & 0x3FFFFFL); // 26 bits
break;
case DYLD_CHAINED_PTR_32_CACHE:
target = (chainValue & 0x3FFFFFFFL); // 30 bits
break;
case DYLD_CHAINED_PTR_32_FIRMWARE:
target = (chainValue & 0x3FFFFFL); // 26 bits
break;
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
target = (chainValue & 0x3FFFFFFFL); // 30 bits
break;
default:
return 0;
}
return target;
}
public static long getAddend(DyldChainType ptrFormat, long chainValue) {
if (!isBound(ptrFormat, chainValue)) {
return 0;
}
switch (ptrFormat) {
case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
long addend = (chainValue >> 32L) & 0x7FFFFL;
addend = ((addend & 0x40000) != 0 ? (addend | 0xFFFFFFFFFFFC0000L) : addend);
return addend;
case DYLD_CHAINED_PTR_64:
case DYLD_CHAINED_PTR_64_OFFSET:
return (chainValue >> 24) & 0xFFL;
case DYLD_CHAINED_PTR_32:
return (chainValue >> 20) & 0x3FL; // 6 bits
default:
return 0;
}
}
public static long getOrdinal(DyldChainType ptrFormat, long chainValue) {
long ordinal = -1;
if (!isBound(ptrFormat, chainValue)) {
return -1;
}
switch (ptrFormat) {
case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
ordinal = (int) (chainValue & 0xFFFFL);
break;
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
case DYLD_CHAINED_PTR_64:
case DYLD_CHAINED_PTR_64_OFFSET:
ordinal = (int) (chainValue & 0xFFFFFFL);
break;
case DYLD_CHAINED_PTR_32:
ordinal = (int) (chainValue & 0xFFFFFL);
break;
// Never Ordinal
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
case DYLD_CHAINED_PTR_32_CACHE:
case DYLD_CHAINED_PTR_32_FIRMWARE:
break;
default:
break;
}
return ordinal;
}
public static long getNext(DyldChainType ptrFormat, long chainValue) {
long next = 1;
switch (ptrFormat) {
case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
next = (chainValue & (0x7FFL << 51L)) >> 51L; // 11-bits
break;
case DYLD_CHAINED_PTR_64:
case DYLD_CHAINED_PTR_64_OFFSET:
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
next = (chainValue & (0xFFFL << 52L)) >> 52L; // 12 bits
break;
case DYLD_CHAINED_PTR_32:
next = (chainValue & (0x1FL << 26L)) >> 26L; // 5 bits
break;
// Never bound
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
next = 0;
case DYLD_CHAINED_PTR_32_CACHE:
next = (chainValue & (0x3L << 30L)) >> 30L; // 2 bits
break;
case DYLD_CHAINED_PTR_32_FIRMWARE:
next = (chainValue & (0x3FL << 26L)) >> 26L; // 6 bits
break;
default:
break;
}
return next;
}
}

View file

@ -74,24 +74,47 @@ public class PrelinkParser {
break;
}
Element element = (Element) iterator.next();
if (element.getName().equals(TAG_KEY)) {
String value = element.getValue();
if (value.equals(PrelinkConstants.kPrelinkPersonalitiesKey)) {
Element arrayElement = (Element) iterator.next();
if (arrayElement.getChildren().size() == 0) {
//should be empty...
}
}
else if (value.equals(PrelinkConstants.kPrelinkInfoDictionaryKey)) {
Element arrayElement = (Element) iterator.next();
process(arrayElement.getChildren(), list, monitor);
}
if (element.getName().equals(TAG_DICT)) {
// top level is <dict> entry
processTopDict(monitor, list, element);
}
else if (element.getName().equals(TAG_KEY)) {
processKey(monitor, list, iterator, element);
}
}
}
return list;
}
private void processTopDict(TaskMonitor monitor, List<PrelinkMap> list,
Element dictRootElement) {
Iterator<?> iterator = dictRootElement.getChildren().iterator();
while (iterator.hasNext()) {
if (monitor.isCancelled()) {
break;
}
Element element = (Element) iterator.next();
if (element.getName().equals(TAG_KEY)) {
processKey(monitor, list, iterator, element);
}
}
}
private void processKey(TaskMonitor monitor, List<PrelinkMap> list, Iterator<?> iterator,
Element element) {
String value = element.getValue();
if (value.equals(PrelinkConstants.kPrelinkPersonalitiesKey)) {
Element arrayElement = (Element) iterator.next();
if (arrayElement.getChildren().size() == 0) {
//should be empty...
}
}
else if (value.equals(PrelinkConstants.kPrelinkInfoDictionaryKey)) {
Element arrayElement = (Element) iterator.next();
process(arrayElement.getChildren(), list, monitor);
}
}
private void process(List<?> children, List<PrelinkMap> list, TaskMonitor monitor) {
monitor.setMessage("Processing prelink information...");
@ -250,6 +273,15 @@ public class PrelinkParser {
if (trimmed.endsWith("</4.2</shoneOS<")) {//this is a wank-around the malformed XML found in 4.2.x firmwares
trimmed = trimmed.substring(0, trimmed.length() - 15) + "</array></dict>";
}
int doctypeIndex = trimmed.indexOf("<!DOCTYPE");
if (doctypeIndex >=0) {
int endOfDoctype = trimmed.indexOf('>', doctypeIndex);
if (endOfDoctype >=0) {
trimmed = trimmed.substring(0,doctypeIndex) + trimmed.substring(endOfDoctype+1);
}
}
// <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
debug(bytes);

View file

@ -140,7 +140,7 @@ public class ThreadStatePPC extends ThreadState {
private long read(FactoryBundledWithBinaryReader reader, boolean is32bit) throws IOException {
if (is32bit) {
return reader.readNextInt() & 0xffffffffL;
return reader.readNextUnsignedInt();
}
return reader.readNextLong();
}

View file

@ -31,13 +31,12 @@ import ghidra.app.util.importer.MessageLogContinuesFactory;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.Pointer64DataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
@ -45,12 +44,6 @@ import ghidra.util.task.TaskMonitor;
*/
public class DyldCacheProgramBuilder extends MachoProgramBuilder {
private static final int DATA_PAGE_MAP_ENTRY = 1;
private static final int BYTES_PER_CHAIN_OFFSET = 4;
private static final int CHAIN_OFFSET_MASK = 0x3fff;
private static final int DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE = 0x4000;
private static final int DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA = 0x8000;
protected DyldCacheHeader dyldCacheHeader;
private boolean shouldProcessSymbols;
private boolean shouldCreateDylibSections;
@ -116,7 +109,6 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
markupHeaders();
markupBranchIslands();
createSymbols();
processDylibs();
}
@ -215,9 +207,10 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
for (NList nlist : localSymbolsInfo.getNList()) {
if (!nlist.getString().trim().isEmpty()) {
try {
program.getSymbolTable().createLabel(space.getAddress(nlist.getValue()),
SymbolUtilities.replaceInvalidChars(nlist.getString(), true),
program.getGlobalNamespace(), SourceType.IMPORTED);
program.getSymbolTable()
.createLabel(space.getAddress(nlist.getValue()),
SymbolUtilities.replaceInvalidChars(nlist.getString(), true),
program.getGlobalNamespace(), SourceType.IMPORTED);
}
catch (Exception e) {
log.appendMsg(e.getMessage() + " " + nlist.getString());
@ -236,135 +229,13 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
* @throws CancelledException if user cancels
*/
private void fixPageChains() throws MemoryAccessException, CancelledException {
long fixedAddressCount = 0;
// locate slide Info
DyldCacheSlideInfoCommon slideInfo = dyldCacheHeader.getSlideInfo();
if (slideInfo == null || !(slideInfo instanceof DyldCacheSlideInfo2)) {
log.appendMsg("No compatible slide info version");
return;
}
DyldCacheSlideInfo2 slideInfo2 = (DyldCacheSlideInfo2) slideInfo;
List<DyldCacheSlideInfoCommon> slideInfos = dyldCacheHeader.getSlideInfos();
for (DyldCacheSlideInfoCommon info : slideInfos) {
int version = info.getVersion();
List<DyldCacheMappingInfo> mappingInfos = dyldCacheHeader.getMappingInfos();
DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY);
long dataPageStart = dyldCacheMappingInfo.getAddress();
long pageSize = slideInfo2.getPageSize();
long pageStartsCount = slideInfo2.getPageStartsCount();
long deltaMask = slideInfo2.getDeltaMask();
long deltaShift = Long.numberOfTrailingZeros(deltaMask);
long valueAdd = slideInfo2.getValueAdd();
short[] pageEntries = slideInfo2.getPageStartsEntries();
short[] extraEntries = slideInfo2.getPageExtrasEntries();
monitor.setMessage("Fixing chained data page pointers...");
monitor.setMaximum(pageStartsCount);
for (int index = 0; index < pageStartsCount; index++) {
monitor.checkCanceled();
long page = dataPageStart + (pageSize * index);
monitor.setProgress(index);
int pageEntry = pageEntries[index] & 0xffff;
if (pageEntry == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE) {
continue;
}
List<Address> unchainedLocList = new ArrayList<>(1024);
if ((pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) != 0) {
// go into extras and process list of chain entries for the same page
int extraIndex = (pageEntry & CHAIN_OFFSET_MASK);
do {
pageEntry = extraEntries[extraIndex] & 0xffff;
long pageOffset = (pageEntry & CHAIN_OFFSET_MASK) * BYTES_PER_CHAIN_OFFSET;
processPointerChain(unchainedLocList, page, pageOffset, deltaMask, deltaShift,
valueAdd);
extraIndex++;
}
while ((pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0);
}
else {
long pageOffset = pageEntry * BYTES_PER_CHAIN_OFFSET;
processPointerChain(unchainedLocList, page, pageOffset, deltaMask, deltaShift,
valueAdd);
}
fixedAddressCount += unchainedLocList.size();
unchainedLocList.forEach(entry -> {
// create a pointer at the fixed up chain pointer location
try {
// don't use data utilities. does too much extra checking work
listing.createData(entry, Pointer64DataType.dataType);
}
catch (CodeUnitInsertionException e) {
// No worries, something presumably more important was there already
}
});
}
log.appendMsg("Fixed " + fixedAddressCount + " chained pointers. Creating Pointers");
monitor.setMessage("Created " + fixedAddressCount + " chained pointers");
}
/**
* Fixes up any chained pointers, starting at the given address.
*
* @param unchainedLocList list of locations that were unchained
* @param page within data pages that has pointers to be unchained
* @param nextOff offset within the page that is the chain start
* @param deltaMask delta offset mask for each value
* @param deltaShift shift needed for the deltaMask to extract the next offset
* @param valueAdd value to be added to each chain pointer
*
* @throws MemoryAccessException IO problem reading file
* @throws CancelledException user cancels
*/
private void processPointerChain(List<Address> unchainedLocList, long page, long nextOff,
long deltaMask, long deltaShift, long valueAdd)
throws MemoryAccessException, CancelledException {
// TODO: should the image base be used to perform the ASLR slide on the pointers.
// currently image is kept at it's initial location with no ASLR.
Address chainStart = memory.getProgram().getLanguage().getDefaultSpace().getAddress(page);
byte origBytes[] = new byte[8];
long valueMask = 0xffffffffffffffffL >>> (64 - deltaShift);
long delta = -1;
while (delta != 0) {
monitor.checkCanceled();
Address chainLoc = chainStart.add(nextOff);
long chainValue = memory.getLong(chainLoc);
delta = (chainValue & deltaMask) >> deltaShift;
chainValue = chainValue & valueMask;
if (chainValue != 0) {
chainValue += valueAdd;
}
if (shouldAddRelocationEntries) {
// Add entry to relocation table for the pointer fixup
memory.getBytes(chainLoc, origBytes);
program.getRelocationTable().add(chainLoc, 1, new long[] { chainValue }, origBytes,
null);
}
memory.setLong(chainLoc, chainValue);
// delay creating data until after memory has been changed
unchainedLocList.add(chainLoc);
nextOff += (delta * 4);
log.appendMsg("Fixing page chains version: " + version);
info.fixPageChains(program, dyldCacheHeader, shouldAddRelocationEntries, log, monitor);
}
}
@ -406,7 +277,12 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
DyldCacheMachoInfo curr = iter.next();
do {
DyldCacheMachoInfo next = iter.hasNext() ? iter.next() : null;
curr.addToProgramTree(next);
try {
curr.addToProgramTree(next);
}
catch (DuplicateNameException exc) {
log.appendException(exc);
}
curr = next;
monitor.checkCanceled();
monitor.incrementProgress(1);

View file

@ -20,8 +20,7 @@ import java.io.IOException;
import java.util.*;
import generic.continues.RethrowContinuesFactory;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.Option;
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;
@ -42,6 +41,13 @@ 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";
/** Default value for loader option add relocation entries */
static final boolean ADD_RELOCATION_ENTRIES_OPTION_DEFAULT = true;
@Override
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
List<LoadSpec> loadSpecs = new ArrayList<>();
@ -83,16 +89,22 @@ 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,
log, monitor);
}
else {
MachoProgramBuilder.buildProgram(program, provider, fileBytes, log, monitor);
shouldAddRelocationEntries(options), log, monitor);
return;
}
MachoProgramBuilder.buildProgram(program, provider, fileBytes, log, monitor);
}
catch (IOException e) {
throw e;
@ -107,6 +119,11 @@ 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);
}
/**
* Overrides the default implementation to account for Universal Binary (UBI) files.
* These must be specially parsed to find the internal file matching the current architecture.

View file

@ -15,27 +15,30 @@
*/
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.MachHeader;
import ghidra.app.util.bin.format.macho.Section;
import ghidra.app.util.bin.format.macho.commands.SegmentNames;
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.importer.MessageLog;
import ghidra.app.util.importer.MessageLogContinuesFactory;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataUtilities;
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.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
/**
@ -45,6 +48,8 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
private List<PrelinkMap> prelinkList;
private boolean shouldAddRelocationEntries;
/**
* Creates a new {@link MachoPrelinkProgramBuilder} based on the given information.
*
@ -52,14 +57,16 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
* @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 log The log.
* @param monitor A cancelable task monitor.
*/
protected MachoPrelinkProgramBuilder(Program program, ByteProvider provider,
FileBytes fileBytes, List<PrelinkMap> prelinkList, MessageLog log,
TaskMonitor monitor) {
FileBytes fileBytes, List<PrelinkMap> prelinkList, boolean shouldAddRelocationEntries,
MessageLog log, TaskMonitor monitor) {
super(program, provider, fileBytes, log, monitor);
this.prelinkList = prelinkList;
this.shouldAddRelocationEntries = shouldAddRelocationEntries;
}
/**
@ -69,14 +76,16 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
* @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 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, MessageLog log, TaskMonitor monitor) throws Exception {
List<PrelinkMap> prelinkList, boolean addRelocationEntries, MessageLog log,
TaskMonitor monitor) throws Exception {
MachoPrelinkProgramBuilder machoPrelinkProgramBuilder = new MachoPrelinkProgramBuilder(
program, provider, fileBytes, prelinkList, log, monitor);
program, provider, fileBytes, prelinkList, addRelocationEntries, log, monitor);
machoPrelinkProgramBuilder.build();
}
@ -85,41 +94,91 @@ 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();
loadSectionRelocations();
loadExternalRelocations();
loadLocalRelocations();
}
protected void fixPreLinkAddresses() throws MemoryAccessException, CancelledException,
Exception, IOException, MachException {
// Fixup any chained pointers
List<Address> fixedAddresses = fixupChainedPointers();
// 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;
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
}
}
}
// Match PRELINK information to the Mach-O's we've found
BidiMap<PrelinkMap, Long> map = MachoPrelinkUtils.matchPrelinkToMachoHeaderOffsets(provider,
prelinkList, machoHeaderOffsets, monitor);
protected void processPreLinkMachoInfo() throws Exception, IOException, MachException {
List<PrelinkMachoInfo> prelinkMachoInfoList = new ArrayList<>();
// 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));
// 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 {
prelinkStartAddr = space.getAddress(prelinkStart);
}
// Create an "info" object for each PRELINK Mach-O, which will make processing them easier
List<PrelinkMachoInfo> prelinkMachoInfoList = new ArrayList<>();
for (Long machoHeaderOffset : machoHeaderOffsets) {
prelinkMachoInfoList.add(new PrelinkMachoInfo(provider, machoHeaderOffset,
prelinkStartAddr.add(machoHeaderOffset - machoHeaderOffsets.get(0)),
map.getKey(machoHeaderOffset)));
// 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
@ -137,17 +196,6 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
monitor.incrementProgress(1);
}
// Create pointers at any fixed-up addresses
fixedAddresses.forEach(addr -> {
try {
DataUtilities.createData(program, addr, Pointer64DataType.dataType, -1, false,
DataUtilities.ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA);
}
catch (CodeUnitInsertionException e) {
// No worries, something presumably more important was there already
}
});
}
@Override
@ -162,18 +210,44 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
* @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 {
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();
}
monitor.setMessage("Fixing up chained pointers...");
Address threadSectionStart = null;
Address threadSectionEnd = null;
threadSectionStart = space.getAddress(threadStarts.getAddress());
threadSectionEnd = threadSectionStart.add(threadStarts.getSize() - 1);
List<Address> fixedAddresses = new ArrayList<>();
Address threadSectionStart = space.getAddress(threadStarts.getAddress());
Address 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);
@ -193,6 +267,195 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
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 / 8) * DyldChainedPtr.getStride(ptrFormat); // first entry 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 (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.
*
@ -259,8 +522,9 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
// 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);
program.getRelocationTable()
.add(pointerAddr, (int) fixedPointerType, new long[] { fixedPointerValue },
origBytes, null);
// Fixup the pointer
memory.setLong(pointerAddr, fixedPointerValue);
@ -286,12 +550,7 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
*/
public PrelinkMachoInfo(ByteProvider provider, long offset, Address headerAddr,
PrelinkMap prelink) throws Exception {
this.headerAddr = headerAddr;
this.header = MachHeader.createMachHeader(MessageLogContinuesFactory.create(log),
provider, offset);
this.header.parse();
this.headerAddr = headerAddr;
this.name = "";
this(provider, offset, headerAddr, "");
if (prelink != null) {
String path = prelink.getPrelinkBundlePath();
@ -301,6 +560,16 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
}
}
public PrelinkMachoInfo(ByteProvider provider, long fileOffset, Address headerAddr,
String kextName) throws Exception {
this.headerAddr = headerAddr;
this.header = MachHeader.createMachHeader(MessageLogContinuesFactory.create(log),
provider, fileOffset, false);
this.header.parse();
this.headerAddr = headerAddr;
this.name = kextName;
}
/**
* Processes memory blocks for this PRELINK Mach-O.
*

View file

@ -27,8 +27,7 @@ import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.ByteProviderWrapper;
import ghidra.app.util.bin.format.macho.MachException;
import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.bin.format.macho.commands.SegmentCommand;
import ghidra.app.util.bin.format.macho.commands.SegmentNames;
import ghidra.app.util.bin.format.macho.commands.*;
import ghidra.app.util.bin.format.macho.prelink.*;
import ghidra.util.Msg;
import ghidra.util.task.TaskMonitor;
@ -64,6 +63,33 @@ public class MachoPrelinkUtils {
}
}
/**
* 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 =
MachHeader.createMachHeader(RethrowContinuesFactory.INSTANCE, 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>

View file

@ -100,6 +100,7 @@ public class MachoProgramBuilder {
MachoProgramBuilder machoProgramBuilder =
new MachoProgramBuilder(program, provider, fileBytes, log, monitor);
machoProgramBuilder.build();
machoProgramBuilder.doRelocations();
}
protected void build() throws Exception {
@ -122,7 +123,10 @@ public class MachoProgramBuilder {
renameObjMsgSendRtpSymbol();
processUndefinedSymbols();
processAbsoluteSymbols();
processDyldInfo();
}
protected void doRelocations() throws Exception {
processDyldInfo(true);
markupHeaders(machoHeader, setupHeaderAddr(machoHeader.getAllSegments()));
markupSections();
processProgramVars();
@ -488,8 +492,8 @@ public class MachoProgramBuilder {
String name = generateValidName(symbol.getString());
if (name != null && name.length() > 0) {
try {
program.getSymbolTable().createLabel(startAddr, name, namespace,
SourceType.IMPORTED);
program.getSymbolTable()
.createLabel(startAddr, name, namespace, SourceType.IMPORTED);
}
catch (Exception e) {
log.appendMsg("Unable to create indirect symbol " + name);
@ -570,8 +574,9 @@ public class MachoProgramBuilder {
symbol.setName(ObjectiveC1_Constants.OBJC_MSG_SEND_RTP_NAME, SourceType.IMPORTED);
}
else {
program.getSymbolTable().createLabel(address,
ObjectiveC1_Constants.OBJC_MSG_SEND_RTP_NAME, SourceType.IMPORTED);
program.getSymbolTable()
.createLabel(address, ObjectiveC1_Constants.OBJC_MSG_SEND_RTP_NAME,
SourceType.IMPORTED);
}
}
@ -597,8 +602,8 @@ public class MachoProgramBuilder {
continue;
}
if (symbol.isTypeUndefined()) {
List<Symbol> globalSymbols = program.getSymbolTable().getLabelOrFunctionSymbols(
symbol.getString(), null);
List<Symbol> globalSymbols = program.getSymbolTable()
.getLabelOrFunctionSymbols(symbol.getString(), null);
if (globalSymbols.isEmpty()) {//IF IT DOES NOT ALREADY EXIST...
undefinedSymbols.add(symbol);
}
@ -688,7 +693,7 @@ public class MachoProgramBuilder {
}
}
private void processDyldInfo() {
protected void processDyldInfo(boolean doClassic) {
List<DyldInfoCommand> commands = machoHeader.getLoadCommands(DyldInfoCommand.class);
for (DyldInfoCommand command : commands) {
if (command.getBindSize() > 0) {
@ -712,6 +717,10 @@ public class MachoProgramBuilder {
//}
}
if (!doClassic) {
return;
}
//then we are use the old school binding technique.
//this only still appears in powerpc
if (commands.size() == 0) {
@ -854,7 +863,7 @@ public class MachoProgramBuilder {
* @param segments A {@link Collection} of {@link SegmentCommand Mach-O segments}
* @return The {@link Address} of {@link MachHeader} in memory
*/
private Address setupHeaderAddr(Collection<SegmentCommand> segments)
protected Address setupHeaderAddr(Collection<SegmentCommand> segments)
throws AddressOverflowException {
Address headerAddr = null;
long lowestFileOffset = Long.MAX_VALUE;
@ -876,7 +885,7 @@ public class MachoProgramBuilder {
return headerBlock.getStart();
}
private void markupSections() throws Exception {
protected void markupSections() throws Exception {
monitor.setMessage("Processing section markup...");
@ -949,9 +958,9 @@ public class MachoProgramBuilder {
/**
* See crt.c from opensource.apple.com
*/
private void processProgramVars() {
if (program.getLanguage().getProcessor() == Processor.findOrPossiblyCreateProcessor(
"PowerPC")) {
protected void processProgramVars() {
if (program.getLanguage().getProcessor() == Processor
.findOrPossiblyCreateProcessor("PowerPC")) {
return;
}
@ -1023,7 +1032,7 @@ public class MachoProgramBuilder {
}
}
private void loadSectionRelocations() {
protected void loadSectionRelocations() {
monitor.setMessage("Processing relocation table...");
@ -1084,22 +1093,24 @@ public class MachoProgramBuilder {
}
}
program.getRelocationTable().add(address, relocationInfo.getType(),
new long[] { relocationInfo.getValue(), relocationInfo.getLength(),
relocationInfo.isPcRelocated() ? 1 : 0, relocationInfo.isExternal() ? 1 : 0,
relocationInfo.isScattered() ? 1 : 0 },
origBytes, relocation.getTargetDescription());
program.getRelocationTable()
.add(address, relocationInfo.getType(),
new long[] { relocationInfo.getValue(), relocationInfo.getLength(),
relocationInfo.isPcRelocated() ? 1 : 0,
relocationInfo.isExternal() ? 1 : 0,
relocationInfo.isScattered() ? 1 : 0 },
origBytes, relocation.getTargetDescription());
}
}
}
private void handleRelocationError(Address address, String message) {
program.getBookmarkManager().setBookmark(address, BookmarkType.ERROR, "Relocations",
message);
program.getBookmarkManager()
.setBookmark(address, BookmarkType.ERROR, "Relocations", message);
log.appendMsg(message);
}
private void loadExternalRelocations() {
protected void loadExternalRelocations() {
monitor.setMessage("Processing external relocations...");
@ -1120,7 +1131,7 @@ public class MachoProgramBuilder {
}
}
private void loadLocalRelocations() {
protected void loadLocalRelocations() {
monitor.setMessage("Processing local relocations...");
@ -1182,8 +1193,9 @@ public class MachoProgramBuilder {
byte[] originalRelocationBytes = getOriginalRelocationBytes(relocation, relocationAddress);
program.getRelocationTable().add(relocationAddress, relocation.getType(),
relocation.toValues(), originalRelocationBytes, null);
program.getRelocationTable()
.add(relocationAddress, relocation.getType(), relocation.toValues(),
originalRelocationBytes, null);
}
private void addLibrary(String library) {
@ -1226,7 +1238,11 @@ public class MachoProgramBuilder {
length = listing.getDataAt(address).getLength();
}
catch (Exception e) {
log.appendException(e);
// don't worry about exceptions
// may have already been created, by relocation, or chain pointers
if (!(datatype instanceof Pointer)) {
log.appendException(e);
}
return;
}
if (datatype instanceof Pointer) {
@ -1298,8 +1314,9 @@ public class MachoProgramBuilder {
private void markAsThumb(Address address)
throws ContextChangeException, AddressOverflowException {
if (!program.getLanguage().getProcessor().equals(
Processor.findOrPossiblyCreateProcessor("ARM"))) {
if (!program.getLanguage()
.getProcessor()
.equals(Processor.findOrPossiblyCreateProcessor("ARM"))) {
return;
}
if ((address.getOffset() & 1) == 1) {
@ -1327,8 +1344,9 @@ public class MachoProgramBuilder {
try {
MemoryBlock memoryBlock = memory.getBlock(reference.getToAddress());
Namespace namespace = createNamespace(memoryBlock.getName());
program.getSymbolTable().createLabel(reference.getToAddress(),
fromSymbol.getName(), namespace, SourceType.IMPORTED);
program.getSymbolTable()
.createLabel(reference.getToAddress(), fromSymbol.getName(), namespace,
SourceType.IMPORTED);
}
catch (Exception e) {
//log.appendMsg("Unable to create lazy pointer symbol " + fromSymbol.getName() + " at " + reference.getToAddress());
@ -1344,8 +1362,9 @@ public class MachoProgramBuilder {
private Namespace createNamespace(String namespaceName) {
try {
return program.getSymbolTable().createNameSpace(program.getGlobalNamespace(),
namespaceName, SourceType.IMPORTED);
return program.getSymbolTable()
.createNameSpace(program.getGlobalNamespace(), namespaceName,
SourceType.IMPORTED);
}
catch (DuplicateNameException | InvalidInputException e) {
Namespace namespace =