diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java index 17c212513c..fe8491398d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java @@ -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(); } /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/MachHeaderFileTypes.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/MachHeaderFileTypes.java index b484dc8de4..9978184dd3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/MachHeaderFileTypes.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/MachHeaderFileTypes.java @@ -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); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedFixupHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedFixupHeader.java new file mode 100644 index 0000000000..4a1361fa34 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedFixupHeader.java @@ -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 mach-o/fixup-chains.h + */ +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; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedFixupsCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedFixupsCommand.java new file mode 100644 index 0000000000..da4e8a62ef --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedFixupsCommand.java @@ -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 mach-o/loader.h + */ +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
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; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedImport.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedImport.java new file mode 100644 index 0000000000..68441a94dd --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedImport.java @@ -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 mach-o/fixup-chains.h + */ +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(); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedImports.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedImports.java new file mode 100644 index 0000000000..de4e1e895b --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedImports.java @@ -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 mach-o/fixup-chains.h + */ +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 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); + } + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedStartsInImage.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedStartsInImage.java new file mode 100644 index 0000000000..42ac1a79b7 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedStartsInImage.java @@ -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 mach-o/fixup-chains.h + */ +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 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; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedStartsInSegment.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedStartsInSegment.java new file mode 100644 index 0000000000..effce5f1e7 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedStartsInSegment.java @@ -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 mach-o/fixup-chains.h + */ +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; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicLibraryModule.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicLibraryModule.java index 0e18e3240e..03d4c2df71 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicLibraryModule.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicLibraryModule.java @@ -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 { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/FileSetEntryCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/FileSetEntryCommand.java new file mode 100644 index 0000000000..02aa49405d --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/FileSetEntryCommand.java @@ -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 mach-o/loader.h + */ +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(); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LinkEditDataCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LinkEditDataCommand.java index 0313156e81..09bfbc378d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LinkEditDataCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LinkEditDataCommand.java @@ -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(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommandTypes.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommandTypes.java index 0589739abf..9592f54713 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommandTypes.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommandTypes.java @@ -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); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/NList.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/NList.java index 60d4e39117..bdcbba9c0a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/NList.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/NList.java @@ -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(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/RoutinesCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/RoutinesCommand.java index fed8f7d3fd..85f017250d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/RoutinesCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/RoutinesCommand.java @@ -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(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java index 4939373bc6..7a775a1b7a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java @@ -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(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoState.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoState.java index 633a718779..dd50d7e023 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoState.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoState.java @@ -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 segments = header.getLoadCommands(SegmentCommand.class); SegmentCommand segment = segments.get(segmentIndex); + List fileSetEntries = + header.getLoadCommands(FileSetEntryCommand.class); + for (FileSetEntryCommand fileSetEntryCommand : fileSetEntries) { + if (fileSetEntryCommand.getFileOffset() == segment.getFileOffset()) { + return fileSetEntryCommand.getFileSetEntryName(); + } + } return segment.getSegmentName(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAccelerateInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAccelerateInfo.java index 1a84e13f7d..47e61c5238 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAccelerateInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAccelerateInfo.java @@ -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 launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheAccelerateInfo implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorDof.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorDof.java index 16f2ce5ad7..52b45e5dda 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorDof.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorDof.java @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_accelerator_dof structure. * - * @see launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheAcceleratorDof implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorInitializer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorInitializer.java index ac17fb6aab..1080173ab3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorInitializer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorInitializer.java @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_accelerator_initializer structure. * - * @see launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheAcceleratorInitializer implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheConstants.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheConstants.java deleted file mode 100644 index 2283a40eb2..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheConstants.java +++ /dev/null @@ -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 { - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java index 76be13439b..dc26406469 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java @@ -33,7 +33,7 @@ import ghidra.util.task.TaskMonitor; /** * Represents a dyld_cache_header structure. * - * @see launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ @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 mappingInfoList; private List imageInfoList; - private DyldCacheSlideInfoCommon slideInfo; + private List slideInfoList; private DyldCacheLocalSymbolsInfo localSymbolsInfo; private List branchPoolList; private DyldCacheAccelerateInfo accelerateInfo; private List imageTextInfoList; private DyldArchitecture architecture; + private List 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 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 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(); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfo.java index db1fb6f214..084ebc952f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfo.java @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_image_info structure. * - * @see launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheImageInfo implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfoExtra.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfoExtra.java index ef1eddfba4..b09927e96d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfoExtra.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfoExtra.java @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_image_info_extra structure. * - * @see launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheImageInfoExtra implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageTextInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageTextInfo.java index 2d3deaf36c..a6d08dfec5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageTextInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageTextInfo.java @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_image_text_info structure. * - * @see launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheImageTextInfo implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsEntry.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsEntry.java index e2f7af934f..588db56789 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsEntry.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsEntry.java @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_local_symbols_entry structure. * - * @see launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheLocalSymbolsEntry implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsInfo.java index 6ca6e50551..3226d9cb4b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsInfo.java @@ -40,7 +40,7 @@ import ghidra.util.task.TaskMonitor; /** * Represents a dyld_cache_local_symbols_info structure. * - * @see launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ @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 sortedList = nlistList - .stream() + List 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); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingAndSlideInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingAndSlideInfo.java new file mode 100644 index 0000000000..af101aa0e7 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingAndSlideInfo.java @@ -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 dyld3/shared-cache/dyld_cache_format.h + */ +@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; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingInfo.java index 3be1b925b9..96c62a7731 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingInfo.java @@ -27,7 +27,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_mapping_info structure. * - * @see launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheMappingInfo implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheRangeEntry.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheRangeEntry.java index ee7916402a..221b10fcd9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheRangeEntry.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheRangeEntry.java @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_range_entry structure. * - * @see launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheRangeEntry implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo1.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo1.java index 646e1c7706..4a952ddcda 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo1.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo1.java @@ -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 launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ 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 mappingInfos = dyldCacheHeader.getMappingInfos(); + DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY); + long dataPageStart = dyldCacheMappingInfo.getAddress(); + + List
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); + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo2.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo2.java index ca094eb076..250e1af2e4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo2.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo2.java @@ -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 launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ 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 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
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
(1024); + do { + pageEntry = extraEntries[extraIndex] & 0xffff; + long pageOffset = (pageEntry & CHAIN_OFFSET_MASK) * BYTES_PER_CHAIN_OFFSET; + + List
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
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
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; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo3.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo3.java index 2d98ba0a59..3373e16b08 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo3.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo3.java @@ -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 launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ 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 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
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
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
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; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo4.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo4.java new file mode 100644 index 0000000000..a1cccb97e7 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo4.java @@ -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 dyld3/shared-cache/dyld_cache_format.h + */ +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 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
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
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
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
unchainedLocList = new ArrayList
(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; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfoCommon.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfoCommon.java index 08b750bb10..843b6c128b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfoCommon.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfoCommon.java @@ -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 launch-cache/dyld_cache_format.h + * @see dyld3/shared-cache/dyld_cache_format.h */ -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
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); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldChainedPtr.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldChainedPtr.java new file mode 100644 index 0000000000..40266dac41 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldChainedPtr.java @@ -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 mach-o/fixup-chains.h + */ +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; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/prelink/PrelinkParser.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/prelink/PrelinkParser.java index 0e97b198ca..f9b38df283 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/prelink/PrelinkParser.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/prelink/PrelinkParser.java @@ -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 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 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 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 list, TaskMonitor monitor) { monitor.setMessage("Processing prelink information..."); @@ -250,6 +273,15 @@ public class PrelinkParser { if (trimmed.endsWith(""; } + + int doctypeIndex = trimmed.indexOf("=0) { + int endOfDoctype = trimmed.indexOf('>', doctypeIndex); + if (endOfDoctype >=0) { + trimmed = trimmed.substring(0,doctypeIndex) + trimmed.substring(endOfDoctype+1); + } + } + // debug(bytes); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/threadcommand/ThreadStatePPC.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/threadcommand/ThreadStatePPC.java index 369b98c52f..16da258ddc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/threadcommand/ThreadStatePPC.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/threadcommand/ThreadStatePPC.java @@ -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(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheProgramBuilder.java index 3b2f9de7d5..350da9cec2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheProgramBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheProgramBuilder.java @@ -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 slideInfos = dyldCacheHeader.getSlideInfos(); + for (DyldCacheSlideInfoCommon info : slideInfos) { + int version = info.getVersion(); - List 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
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
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); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java index 3f6b85227b..33dc4d1ad2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java @@ -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 findSupportedLoadSpecs(ByteProvider provider) throws IOException { List 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 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