mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-09-19 10:11:55 +00:00
Merge remote-tracking branch
'origin/GP-398_emteere_FixesForKernelandDyldCache' (Closes #2487, Closes #3572)
This commit is contained in:
commit
e8550982f3
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.macho.commands;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||
import ghidra.app.util.bin.format.macho.MachConstants;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* Represents a dyld_chained_fixups_header structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/include/mach-o/fixup-chains.h.auto.html">mach-o/fixup-chains.h</a>
|
||||
*/
|
||||
public class DyldChainedFixupHeader implements StructConverter {
|
||||
|
||||
private int fixups_version; // 0
|
||||
private int starts_offset; // offset of dyld_chained_starts_in_image in chain_data
|
||||
private int imports_offset; // offset of imports table in chain_data
|
||||
private int symbols_offset; // offset of symbol strings in chain_data
|
||||
private int imports_count; // number of imported symbol names
|
||||
private int imports_format; // DYLD_CHAINED_IMPORT*
|
||||
private int symbols_format; // 0 => uncompressed, 1 => zlib compressed
|
||||
|
||||
DyldChainedStartsInImage chainedStartsInImage;
|
||||
DyldChainedImports chainedImports;
|
||||
|
||||
static DyldChainedFixupHeader createDyldChainedFixupHeader(
|
||||
FactoryBundledWithBinaryReader reader) throws IOException {
|
||||
DyldChainedFixupHeader dyldChainedFixupHeader =
|
||||
(DyldChainedFixupHeader) reader.getFactory().create(DyldChainedFixupHeader.class);
|
||||
dyldChainedFixupHeader.initDyldChainedFixupHeader(reader);
|
||||
return dyldChainedFixupHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
|
||||
*/
|
||||
public DyldChainedFixupHeader() {
|
||||
}
|
||||
|
||||
private void initDyldChainedFixupHeader(FactoryBundledWithBinaryReader reader)
|
||||
throws IOException {
|
||||
long ptrIndex = reader.getPointerIndex();
|
||||
|
||||
fixups_version = reader.readNextInt();
|
||||
starts_offset = reader.readNextInt();
|
||||
imports_offset = reader.readNextInt();
|
||||
symbols_offset = reader.readNextInt();
|
||||
imports_count = reader.readNextInt();
|
||||
imports_format = reader.readNextInt();
|
||||
symbols_format = reader.readNextInt();
|
||||
|
||||
reader.setPointerIndex(ptrIndex + starts_offset);
|
||||
chainedStartsInImage = DyldChainedStartsInImage.createDyldChainedStartsInImage(reader);
|
||||
|
||||
reader.setPointerIndex(ptrIndex + imports_offset);
|
||||
chainedImports = DyldChainedImports.createDyldChainedImports(reader, this);
|
||||
|
||||
reader.setPointerIndex(ptrIndex + symbols_offset);
|
||||
chainedImports.initSymbols(reader, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType("dyld_chained_fixups_header", 0);
|
||||
struct.add(DWORD, "fixups_version", null);
|
||||
struct.add(DWORD, "starts_offset", null);
|
||||
struct.add(DWORD, "imports_offset", null);
|
||||
struct.add(DWORD, "symbols_offset", null);
|
||||
struct.add(DWORD, "imports_count", null);
|
||||
struct.add(DWORD, "imports_format", null);
|
||||
struct.add(DWORD, "symbols_format", null);
|
||||
|
||||
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
|
||||
return struct;
|
||||
}
|
||||
|
||||
public int getFixups_version() {
|
||||
return fixups_version;
|
||||
}
|
||||
|
||||
public int getStarts_offset() {
|
||||
return starts_offset;
|
||||
}
|
||||
|
||||
public int getImports_offset() {
|
||||
return imports_offset;
|
||||
}
|
||||
|
||||
public int getSymbols_offset() {
|
||||
return symbols_offset;
|
||||
}
|
||||
|
||||
public int getImports_count() {
|
||||
return imports_count;
|
||||
}
|
||||
|
||||
public int getImports_format() {
|
||||
return imports_format;
|
||||
}
|
||||
|
||||
public int getSymbols_format() {
|
||||
return symbols_format;
|
||||
}
|
||||
|
||||
public boolean isCompress() {
|
||||
return symbols_format != 0;
|
||||
}
|
||||
|
||||
public DyldChainedStartsInImage getChainedStartsInImage() {
|
||||
return chainedStartsInImage;
|
||||
}
|
||||
|
||||
public DyldChainedImports getChainedImports() {
|
||||
return chainedImports;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.macho.commands;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||
import ghidra.app.util.bin.format.macho.MachHeader;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.flatapi.FlatProgramAPI;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.ProgramModule;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Represents a LC_DYLD_CHAINED_FIXUPS command.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/xnu/xnu-7195.81.3/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
|
||||
*/
|
||||
public class DyldChainedFixupsCommand extends LinkEditDataCommand {
|
||||
|
||||
private DyldChainedFixupHeader chainHeader;
|
||||
|
||||
static LinkEditDataCommand createDyldChainedFixupsCommand(FactoryBundledWithBinaryReader reader)
|
||||
throws IOException {
|
||||
DyldChainedFixupsCommand command =
|
||||
(DyldChainedFixupsCommand) reader.getFactory().create(DyldChainedFixupsCommand.class);
|
||||
command.initLinkEditDataCommand(reader);
|
||||
|
||||
long ptrIndex = reader.getPointerIndex();
|
||||
reader.setPointerIndex(command.getDataOffset());
|
||||
command.chainHeader = DyldChainedFixupHeader.createDyldChainedFixupHeader(reader);
|
||||
reader.setPointerIndex(ptrIndex);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
|
||||
*/
|
||||
public DyldChainedFixupsCommand() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "dyld_chained_fixups_command";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markup(MachHeader header, FlatProgramAPI api, Address baseAddress, boolean isBinary,
|
||||
ProgramModule parentModule, TaskMonitor monitor, MessageLog log) {
|
||||
updateMonitor(monitor);
|
||||
try {
|
||||
if (isBinary) {
|
||||
super.markup(header, api, baseAddress, isBinary, parentModule, monitor, log);
|
||||
|
||||
List<Address> addrs =
|
||||
api.getCurrentProgram().getMemory().locateAddressesForFileOffset(
|
||||
getDataOffset());
|
||||
if (addrs.size() <= 0) {
|
||||
throw new Exception("Chain Header does not exist in program");
|
||||
}
|
||||
Address dyldChainedHeader = addrs.get(0);
|
||||
|
||||
markupChainedFixupHeader(header, api, dyldChainedHeader, parentModule, monitor);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.appendMsg("Unable to create " + getCommandName());
|
||||
log.appendException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void markupChainedFixupHeader(MachHeader header, FlatProgramAPI api,
|
||||
Address baseAddress, ProgramModule parentModule, TaskMonitor monitor)
|
||||
throws DuplicateNameException, IOException, CodeUnitInsertionException, Exception {
|
||||
DataType cHeader = chainHeader.toDataType();
|
||||
api.createData(baseAddress, cHeader);
|
||||
|
||||
Address segsAddr = baseAddress.add(chainHeader.getStarts_offset());
|
||||
|
||||
DyldChainedStartsInImage chainedStartsInImage = chainHeader.getChainedStartsInImage();
|
||||
int[] seg_info_offset = chainedStartsInImage.getSeg_info_offset();
|
||||
|
||||
DyldChainedStartsInSegment[] chainedStarts = chainedStartsInImage.getChainedStarts();
|
||||
for (int i = 0; i < chainedStarts.length; i++) {
|
||||
DyldChainedStartsInSegment startsInSeg = chainedStarts[i];
|
||||
DataType dataType = startsInSeg.toDataType();
|
||||
|
||||
api.createData(segsAddr.add(seg_info_offset[i]), dataType);
|
||||
}
|
||||
}
|
||||
|
||||
public DyldChainedFixupHeader getChainHeader() {
|
||||
return chainHeader;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.macho.commands;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||
import ghidra.app.util.bin.format.macho.MachConstants;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* Represents a dyld_chained_import structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/include/mach-o/fixup-chains.h.auto.html">mach-o/fixup-chains.h</a>
|
||||
*/
|
||||
public class DyldChainedImport implements StructConverter {
|
||||
private static final int DYLD_CHAINED_IMPORT = 1;
|
||||
private static final int DYLD_CHAINED_IMPORT_ADDEND = 2;
|
||||
private static final int DYLD_CHAINED_IMPORT_ADDEND64 = 3;
|
||||
|
||||
private int imports_format;
|
||||
private int lib_ordinal;
|
||||
private boolean weak_import;
|
||||
private long name_offset;
|
||||
private long addend;
|
||||
private String symbolName;
|
||||
|
||||
static DyldChainedImport createDyldChainedImport(FactoryBundledWithBinaryReader reader,
|
||||
DyldChainedFixupHeader cfh, int imports_format) throws IOException {
|
||||
DyldChainedImport dyldChainedImport =
|
||||
(DyldChainedImport) reader.getFactory().create(DyldChainedImport.class);
|
||||
dyldChainedImport.initDyldChainedImport(reader, cfh, imports_format);
|
||||
return dyldChainedImport;
|
||||
}
|
||||
|
||||
/**
|
||||
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
|
||||
*/
|
||||
public DyldChainedImport() {
|
||||
}
|
||||
|
||||
private void initDyldChainedImport(FactoryBundledWithBinaryReader reader,
|
||||
DyldChainedFixupHeader cfh, int format) throws IOException {
|
||||
|
||||
this.imports_format = format;
|
||||
switch (format) {
|
||||
case DYLD_CHAINED_IMPORT: {
|
||||
int ival = reader.readNextInt();
|
||||
lib_ordinal = ival & 0xff;
|
||||
weak_import = ((ival >> 8) & 1) == 1;
|
||||
name_offset = (ival >> 9 & 0x7fffff);
|
||||
break;
|
||||
}
|
||||
case DYLD_CHAINED_IMPORT_ADDEND: {
|
||||
int ival = reader.readNextInt();
|
||||
lib_ordinal = ival & 0xff;
|
||||
weak_import = ((ival >> 8) & 1) == 1;
|
||||
name_offset = (ival >> 9 & 0x7fffff);
|
||||
addend = reader.readNextInt();
|
||||
break;
|
||||
}
|
||||
case DYLD_CHAINED_IMPORT_ADDEND64: {
|
||||
long ival = reader.readNextLong();
|
||||
lib_ordinal = (int) (ival & 0xffff);
|
||||
weak_import = ((ival >> 8) & 1) == 1;
|
||||
name_offset = (ival >> 32 & 0xffffffff);
|
||||
addend = reader.readNextLong();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IOException("Bad Chained import format: " + format);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType dt = new StructureDataType("dyld_chained_import", 0);
|
||||
|
||||
try {
|
||||
switch (imports_format) {
|
||||
case DYLD_CHAINED_IMPORT:
|
||||
dt.addBitField(DWORD, 8, "lib_ordinal", "ordinal in imports");
|
||||
dt.addBitField(DWORD, 1, "weak_import", null);
|
||||
dt.addBitField(DWORD, 23, "name_offset", null);
|
||||
break;
|
||||
case DYLD_CHAINED_IMPORT_ADDEND:
|
||||
dt.addBitField(DWORD, 8, "lib_ordinal", "ordinal in imports");
|
||||
dt.addBitField(DWORD, 1, "weak_import", null);
|
||||
dt.addBitField(DWORD, 23, "name_offset", null);
|
||||
dt.add(DWORD, "addend", null);
|
||||
break;
|
||||
case DYLD_CHAINED_IMPORT_ADDEND64:
|
||||
dt.addBitField(QWORD, 16, "lib_ordinal", "ordinal in imports");
|
||||
dt.addBitField(QWORD, 1, "weak_import", null);
|
||||
dt.addBitField(QWORD, 15, "reserved", null);
|
||||
dt.addBitField(QWORD, 32, "name_offset", null);
|
||||
dt.add(QWORD, "addend", null);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Bad Chained import format: " + imports_format);
|
||||
}
|
||||
}
|
||||
catch (InvalidDataTypeException exc) {
|
||||
// ignore
|
||||
}
|
||||
dt.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
|
||||
return dt;
|
||||
}
|
||||
|
||||
public int getLibOrdinal() {
|
||||
return lib_ordinal;
|
||||
}
|
||||
|
||||
public boolean isWeakImport() {
|
||||
return weak_import;
|
||||
}
|
||||
|
||||
public long getNameOffset() {
|
||||
return name_offset;
|
||||
}
|
||||
|
||||
public long getAddend() {
|
||||
return addend;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return symbolName;
|
||||
}
|
||||
|
||||
public void initString(FactoryBundledWithBinaryReader reader) throws IOException {
|
||||
symbolName = reader.readNextNullTerminatedAsciiString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.macho.commands;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||
import ghidra.program.model.data.ArrayDataType;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* Represents a dyld_chained_import array.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/include/mach-o/fixup-chains.h.auto.html">mach-o/fixup-chains.h</a>
|
||||
*/
|
||||
public class DyldChainedImports implements StructConverter {
|
||||
|
||||
private int imports_count;
|
||||
private int imports_format;
|
||||
private long imports_offset;
|
||||
private DyldChainedImport chainedImports[];
|
||||
|
||||
static DyldChainedImports createDyldChainedImports(FactoryBundledWithBinaryReader reader,
|
||||
DyldChainedFixupHeader cfh) throws IOException {
|
||||
DyldChainedImports dyldChainedImports =
|
||||
(DyldChainedImports) reader.getFactory().create(DyldChainedImports.class);
|
||||
dyldChainedImports.initDyldChainedStartsInImage(reader, cfh);
|
||||
return dyldChainedImports;
|
||||
}
|
||||
|
||||
/**
|
||||
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
|
||||
*/
|
||||
public DyldChainedImports() {
|
||||
}
|
||||
|
||||
private void initDyldChainedStartsInImage(FactoryBundledWithBinaryReader reader,
|
||||
DyldChainedFixupHeader cfh) throws IOException {
|
||||
|
||||
long ptrIndex = reader.getPointerIndex();
|
||||
imports_offset = ptrIndex;
|
||||
|
||||
this.imports_count = cfh.getImports_count();
|
||||
this.imports_format = cfh.getImports_format();
|
||||
|
||||
ArrayList<DyldChainedImport> starts = new ArrayList<>();
|
||||
for (int i = 0; i < imports_count; i++) {
|
||||
starts.add(DyldChainedImport.createDyldChainedImport(reader, cfh, imports_format));
|
||||
}
|
||||
chainedImports = starts.toArray(DyldChainedImport[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
DataType chainedImportDt = chainedImports[0].toDataType();
|
||||
DataType dt =
|
||||
new ArrayDataType(chainedImportDt, imports_count, chainedImportDt.getLength());
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
public int getImportsCount() {
|
||||
return imports_count;
|
||||
}
|
||||
|
||||
public long getImportsOffset() {
|
||||
return imports_offset;
|
||||
}
|
||||
|
||||
public DyldChainedImport[] getChainedImports() {
|
||||
return chainedImports;
|
||||
}
|
||||
|
||||
public DyldChainedImport getChainedImport(int ordinal) {
|
||||
if (ordinal < 0 || ordinal >= imports_count) {
|
||||
return null;
|
||||
}
|
||||
return chainedImports[ordinal];
|
||||
}
|
||||
|
||||
public void initSymbols(FactoryBundledWithBinaryReader reader,
|
||||
DyldChainedFixupHeader dyldChainedFixupHeader) throws IOException {
|
||||
long ptrIndex = reader.getPointerIndex();
|
||||
|
||||
for (DyldChainedImport dyldChainedImport : chainedImports) {
|
||||
reader.setPointerIndex(ptrIndex + dyldChainedImport.getNameOffset());
|
||||
dyldChainedImport.initString(reader);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.macho.commands;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||
import ghidra.app.util.bin.format.macho.MachConstants;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* Represents a dyld_chained_starts_in_image structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/include/mach-o/fixup-chains.h.auto.html">mach-o/fixup-chains.h</a>
|
||||
*/
|
||||
public class DyldChainedStartsInImage implements StructConverter {
|
||||
|
||||
private int seg_count; // count of segment chain starts
|
||||
private int seg_info_offset[];
|
||||
|
||||
private DyldChainedStartsInSegment chainedStarts[];
|
||||
|
||||
static DyldChainedStartsInImage createDyldChainedStartsInImage(
|
||||
FactoryBundledWithBinaryReader reader) throws IOException {
|
||||
DyldChainedStartsInImage dyldChainedStartsInImage =
|
||||
(DyldChainedStartsInImage) reader.getFactory().create(DyldChainedStartsInImage.class);
|
||||
dyldChainedStartsInImage.initDyldChainedStartsInImage(reader);
|
||||
return dyldChainedStartsInImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
|
||||
*/
|
||||
public DyldChainedStartsInImage() {
|
||||
}
|
||||
|
||||
private void initDyldChainedStartsInImage(FactoryBundledWithBinaryReader reader)
|
||||
throws IOException {
|
||||
|
||||
long ptrIndex = reader.getPointerIndex();
|
||||
|
||||
seg_count = reader.readNextInt();
|
||||
seg_info_offset = reader.readNextIntArray(seg_count);
|
||||
|
||||
ArrayList<DyldChainedStartsInSegment> starts = new ArrayList<>();
|
||||
for (int off : seg_info_offset) {
|
||||
|
||||
reader.setPointerIndex(ptrIndex + off);
|
||||
starts.add(DyldChainedStartsInSegment.createDyldChainedFixupHeader(reader));
|
||||
}
|
||||
chainedStarts = starts.toArray(DyldChainedStartsInSegment[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType("dyld_chained_starts_in_image", 0);
|
||||
|
||||
struct.add(DWORD, "seg_count", null);
|
||||
struct.add(new ArrayDataType(DWORD, seg_count, 1), "seg_info_offset", "");
|
||||
|
||||
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
|
||||
return struct;
|
||||
}
|
||||
|
||||
public int getSeg_count() {
|
||||
return seg_count;
|
||||
}
|
||||
|
||||
public int[] getSeg_info_offset() {
|
||||
return seg_info_offset;
|
||||
}
|
||||
|
||||
public DyldChainedStartsInSegment[] getChainedStarts() {
|
||||
return chainedStarts;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.macho.commands;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||
import ghidra.app.util.bin.format.macho.MachConstants;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* Represents a dyld_chained_starts_in_segment structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/include/mach-o/fixup-chains.h.auto.html">mach-o/fixup-chains.h</a>
|
||||
*/
|
||||
public class DyldChainedStartsInSegment implements StructConverter {
|
||||
|
||||
private int size; // size of this (amount kernel needs to copy)
|
||||
private short page_size; // 0x1000 or 0x4000
|
||||
private short pointer_format; // DYLD_CHAINED_PTR_*
|
||||
private long segment_offset; // offset in memory to start of segment
|
||||
private int max_valid_pointer; // for 32-bit OS, any value beyond this is not a pointer
|
||||
private short page_count; // how many pages are in array
|
||||
private short page_starts[]; // each entry is offset in each page of first element in chain
|
||||
private short chain_starts[]; // TODO: used for some 32-bit formats with multiple starts per page
|
||||
|
||||
static DyldChainedStartsInSegment createDyldChainedFixupHeader(
|
||||
FactoryBundledWithBinaryReader reader) throws IOException {
|
||||
DyldChainedStartsInSegment dyldChainedFixupHeader =
|
||||
(DyldChainedStartsInSegment) reader.getFactory().create(
|
||||
DyldChainedStartsInSegment.class);
|
||||
dyldChainedFixupHeader.initDyldChainedStartsInSegment(reader);
|
||||
return dyldChainedFixupHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
|
||||
*/
|
||||
public DyldChainedStartsInSegment() {
|
||||
}
|
||||
|
||||
private void initDyldChainedStartsInSegment(FactoryBundledWithBinaryReader reader)
|
||||
throws IOException {
|
||||
size = reader.readNextInt();
|
||||
page_size = reader.readNextShort();
|
||||
pointer_format = reader.readNextShort();
|
||||
segment_offset = reader.readNextLong();
|
||||
max_valid_pointer = reader.readNextInt();
|
||||
page_count = reader.readNextShort();
|
||||
|
||||
page_starts = reader.readNextShortArray(page_count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType("dyld_chained_starts_in_segment", 0);
|
||||
struct.add(DWORD, "size", null);
|
||||
struct.add(WORD, "page_size", null);
|
||||
struct.add(WORD, "pointer_format", null);
|
||||
struct.add(QWORD, "segment_offset", null);
|
||||
struct.add(DWORD, "max_valid_pointer", null);
|
||||
struct.add(WORD, "page_count", null);
|
||||
struct.add(new ArrayDataType(WORD, page_count, 1), "page_starts", "");
|
||||
|
||||
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
|
||||
return struct;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public short getPageSize() {
|
||||
return page_size;
|
||||
}
|
||||
|
||||
public short getPointerFormat() {
|
||||
return pointer_format;
|
||||
}
|
||||
|
||||
public long getSegmentOffset() {
|
||||
return segment_offset;
|
||||
}
|
||||
|
||||
public int getMaxValidPointer() {
|
||||
return max_valid_pointer;
|
||||
}
|
||||
|
||||
public short getPageCount() {
|
||||
return page_count;
|
||||
}
|
||||
|
||||
public short[] getPage_starts() {
|
||||
return page_starts;
|
||||
}
|
||||
|
||||
public short[] getChain_starts() {
|
||||
return chain_starts;
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.macho.commands;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||
import ghidra.app.util.bin.format.macho.MachConstants;
|
||||
import ghidra.app.util.bin.format.macho.MachHeader;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.flatapi.FlatProgramAPI;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.ProgramModule;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Represents a kext_command
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/xnu/xnu-7195.60.75/EXTERNAL_HEADERS/mach-o/loader.h.auto.html">mach-o/loader.h</a>
|
||||
*/
|
||||
public class FileSetEntryCommand extends LoadCommand {
|
||||
|
||||
private long vmaddr;
|
||||
private long fileoff;
|
||||
private String entryName;
|
||||
private long unknown;
|
||||
|
||||
boolean is32bit;
|
||||
|
||||
public static FileSetEntryCommand createFileSetEntryCommand(
|
||||
FactoryBundledWithBinaryReader reader, boolean is32bit) throws IOException {
|
||||
FileSetEntryCommand filesetEntryCommand =
|
||||
(FileSetEntryCommand) reader.getFactory().create(FileSetEntryCommand.class);
|
||||
filesetEntryCommand.initFileSetEntryCommand(reader, is32bit);
|
||||
return filesetEntryCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
|
||||
*/
|
||||
public FileSetEntryCommand() {
|
||||
}
|
||||
|
||||
private void initFileSetEntryCommand(FactoryBundledWithBinaryReader reader, boolean is32bit)
|
||||
throws IOException {
|
||||
initLoadCommand(reader);
|
||||
this.is32bit = is32bit;
|
||||
|
||||
if (is32bit) {
|
||||
vmaddr = reader.readNextUnsignedInt();
|
||||
fileoff = reader.readNextUnsignedInt();
|
||||
unknown = reader.readNextUnsignedInt();
|
||||
}
|
||||
else {
|
||||
vmaddr = reader.readNextLong();
|
||||
fileoff = reader.readNextLong();
|
||||
unknown = reader.readNextLong();
|
||||
}
|
||||
|
||||
int stringSize = this.getCommandSize() - (8 + 3 * (is32bit ? 4 : 8));
|
||||
entryName = reader.readNextAsciiString(stringSize);
|
||||
}
|
||||
|
||||
public String getFileSetEntryName() {
|
||||
return entryName;
|
||||
}
|
||||
|
||||
public long getVMaddress() {
|
||||
return vmaddr;
|
||||
}
|
||||
|
||||
public long getFileOffset() {
|
||||
return fileoff;
|
||||
}
|
||||
|
||||
public long getUnknown() {
|
||||
return unknown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType(getCommandName(), 0);
|
||||
struct.add(DWORD, "cmd", null);
|
||||
struct.add(DWORD, "cmdsize", null);
|
||||
|
||||
if (is32bit) {
|
||||
struct.add(DWORD, "vmaddr", null);
|
||||
struct.add(DWORD, "fileoff", null);
|
||||
struct.add(DWORD, "unknown", null);
|
||||
}
|
||||
else {
|
||||
struct.add(QWORD, "vmaddr", null);
|
||||
struct.add(QWORD, "fileoff", null);
|
||||
struct.add(QWORD, "unknown", null);
|
||||
}
|
||||
int stringSize = getCommandSize() - (8 + 3 * (is32bit ? 4 : 8));
|
||||
struct.add(new StringDataType(), stringSize, "fileSetEntryname", null);
|
||||
|
||||
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
|
||||
return struct;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "fileset_entry_command";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markup(MachHeader header, FlatProgramAPI api, Address baseAddress, boolean isBinary,
|
||||
ProgramModule parentModule, TaskMonitor monitor, MessageLog log) {
|
||||
updateMonitor(monitor);
|
||||
try {
|
||||
if (isBinary) {
|
||||
createFragment(api, baseAddress, parentModule);
|
||||
Address addr = baseAddress.getNewAddress(getStartIndex());
|
||||
DataType fileSetEntryDT = toDataType();
|
||||
api.createData(addr, fileSetEntryDT);
|
||||
api.setPlateComment(addr, getFileSetEntryName());
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.appendMsg("Unable to create " + getCommandName() + " - " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getFileSetEntryName();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -23,7 +23,7 @@ import ghidra.program.model.address.*;
|
|||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolIterator;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.DataConverter;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
abstract public class AbstractDyldInfoState {
|
||||
|
@ -143,6 +143,13 @@ abstract public class AbstractDyldInfoState {
|
|||
protected String getSegmentName() {
|
||||
List<SegmentCommand> segments = header.getLoadCommands(SegmentCommand.class);
|
||||
SegmentCommand segment = segments.get(segmentIndex);
|
||||
List<FileSetEntryCommand> fileSetEntries =
|
||||
header.getLoadCommands(FileSetEntryCommand.class);
|
||||
for (FileSetEntryCommand fileSetEntryCommand : fileSetEntries) {
|
||||
if (fileSetEntryCommand.getFileOffset() == segment.getFileOffset()) {
|
||||
return fileSetEntryCommand.getFileSetEntryName();
|
||||
}
|
||||
}
|
||||
return segment.getSegmentName();
|
||||
}
|
||||
|
||||
|
|
|
@ -34,9 +34,9 @@ import ghidra.util.exception.*;
|
|||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Represents a dyld_cache_accelerate_info structure.
|
||||
* Represents a dyld_cache_accelerator_info structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheAccelerateInfo implements StructConverter {
|
||||
|
|
|
@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
/**
|
||||
* Represents a dyld_cache_accelerator_dof structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheAcceleratorDof implements StructConverter {
|
||||
|
|
|
@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
/**
|
||||
* Represents a dyld_cache_accelerator_initializer structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheAcceleratorInitializer implements StructConverter {
|
||||
|
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -33,7 +33,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
/**
|
||||
* Represents a dyld_cache_header structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheHeader implements StructConverter {
|
||||
|
@ -58,18 +58,49 @@ public class DyldCacheHeader implements StructConverter {
|
|||
private long accelerateInfoSize;
|
||||
private long imagesTextOffset;
|
||||
private long imagesTextCount;
|
||||
private long patchInfoAddr;
|
||||
private long patchInfoSize;
|
||||
private long otherImageGroupAddrUnused; // unused
|
||||
private long otherImageGroupSizeUnused; // unused
|
||||
private long progClosuresAddr;
|
||||
private long progClosuresSize;
|
||||
private long progClosuresTrieAddr;
|
||||
private long progClosuresTrieSize;
|
||||
private int platform;
|
||||
private int dyld_info;
|
||||
private int formatVersion; // Extracted from dyld_info
|
||||
private boolean dylibsExpectedOnDisk; // Extracted from dyld_info
|
||||
private boolean simulator; // Extracted from dyld_info
|
||||
private boolean locallyBuiltCache; // Extracted from dyld_info
|
||||
private boolean builtFromChainedFixups; // Extracted from dyld_info
|
||||
private int padding; // Extracted from dyld_info
|
||||
private long sharedRegionStart;
|
||||
private long sharedRegionSize;
|
||||
private long maxSlide;
|
||||
private long dylibsImageArrayAddr;
|
||||
private long dylibsImageArraySize;
|
||||
private long dylibsTrieAddr;
|
||||
private long dylibsTrieSize;
|
||||
private long otherImageArrayAddr;
|
||||
private long otherImageArraySize;
|
||||
private long otherTrieAddr;
|
||||
private long otherTrieSize;
|
||||
private int mappingWithSlideOffset;
|
||||
private int mappingWithSlideCount;
|
||||
|
||||
private int headerType;
|
||||
private int headerSize;
|
||||
private BinaryReader reader;
|
||||
private long baseAddress;
|
||||
private List<DyldCacheMappingInfo> mappingInfoList;
|
||||
private List<DyldCacheImageInfo> imageInfoList;
|
||||
private DyldCacheSlideInfoCommon slideInfo;
|
||||
private List<DyldCacheSlideInfoCommon> slideInfoList;
|
||||
private DyldCacheLocalSymbolsInfo localSymbolsInfo;
|
||||
private List<Long> branchPoolList;
|
||||
private DyldCacheAccelerateInfo accelerateInfo;
|
||||
private List<DyldCacheImageTextInfo> imageTextInfoList;
|
||||
private DyldArchitecture architecture;
|
||||
private List<DyldCacheMappingAndSlideInfo> cacheMappingAndSlideInfoList;
|
||||
|
||||
/**
|
||||
* Create a new {@link DyldCacheHeader}.
|
||||
|
@ -79,9 +110,10 @@ public class DyldCacheHeader implements StructConverter {
|
|||
*/
|
||||
public DyldCacheHeader(BinaryReader reader) throws IOException {
|
||||
this.reader = reader;
|
||||
long startIndex = reader.getPointerIndex();
|
||||
|
||||
// ------ HEADER 1 ---------
|
||||
headerType = 1; // https://opensource.apple.com/source/dyld/dyld-95.3/launch-cache/dyld_cache_format.h.auto.html
|
||||
// HEADER 1: https://opensource.apple.com/source/dyld/dyld-95.3/launch-cache/dyld_cache_format.h.auto.html
|
||||
headerType = 1;
|
||||
magic = reader.readNextByteArray(16);
|
||||
mappingOffset = reader.readNextInt();
|
||||
mappingCount = reader.readNextInt();
|
||||
|
@ -89,37 +121,39 @@ public class DyldCacheHeader implements StructConverter {
|
|||
imagesCount = reader.readNextInt();
|
||||
dyldBaseAddress = reader.readNextLong();
|
||||
|
||||
// ------ HEADER 2 ---------
|
||||
if (mappingOffset > 0x28) {
|
||||
headerType = 2; // https://opensource.apple.com/source/dyld/dyld-195.5/launch-cache/dyld_cache_format.h.auto.html
|
||||
// HEADER 2: https://opensource.apple.com/source/dyld/dyld-195.5/launch-cache/dyld_cache_format.h.auto.html
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
headerType = 2;
|
||||
codeSignatureOffset = reader.readNextLong();
|
||||
codeSignatureSize = reader.readNextLong();
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
slideInfoOffset = reader.readNextLong();
|
||||
slideInfoSize = reader.readNextLong();
|
||||
}
|
||||
|
||||
// ------ HEADER 3 ---------
|
||||
if (mappingOffset > 0x48) {
|
||||
headerType = 3; // No header file for this version (without the following UUID), but there are images of this version
|
||||
// HEADER 3: No header file for this version (without the following UUID), but there are images of this version
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
headerType = 3;
|
||||
localSymbolsOffset = reader.readNextLong();
|
||||
localSymbolsSize = reader.readNextLong();
|
||||
}
|
||||
|
||||
// ------ HEADER 4 ---------
|
||||
if (mappingOffset > 0x58) {
|
||||
headerType = 4; // https://opensource.apple.com/source/dyld/dyld-239.3/launch-cache/dyld_cache_format.h.auto.html
|
||||
// HEADER 4: https://opensource.apple.com/source/dyld/dyld-239.3/launch-cache/dyld_cache_format.h.auto.html
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
headerType = 4;
|
||||
uuid = reader.readNextByteArray(16);
|
||||
}
|
||||
|
||||
// ------ HEADER 5 ---------
|
||||
if (mappingOffset > 0x68) {
|
||||
headerType = 5; // https://opensource.apple.com/source/dyld/dyld-360.14/launch-cache/dyld_cache_format.h.auto.html
|
||||
// HEADER 5: https://opensource.apple.com/source/dyld/dyld-360.14/launch-cache/dyld_cache_format.h.auto.html
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
headerType = 5;
|
||||
cacheType = reader.readNextLong();
|
||||
}
|
||||
|
||||
// ------ HEADER 6 ---------
|
||||
if (mappingOffset > 0x70) {
|
||||
headerType = 6; // https://opensource.apple.com/source/dyld/dyld-421.1/launch-cache/dyld_cache_format.h.auto.html
|
||||
// HEADER 6: https://opensource.apple.com/source/dyld/dyld-421.1/launch-cache/dyld_cache_format.h.auto.html
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
headerType = 6;
|
||||
branchPoolsOffset = reader.readNextInt();
|
||||
branchPoolsCount = reader.readNextInt();
|
||||
accelerateInfoAddr = reader.readNextLong();
|
||||
|
@ -128,10 +162,94 @@ public class DyldCacheHeader implements StructConverter {
|
|||
imagesTextCount = reader.readNextLong();
|
||||
}
|
||||
|
||||
// HEADER 7: https://opensource.apple.com/source/dyld/dyld-832.7.1/dyld3/shared-cache/dyld_cache_format.h.auto.html
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
headerType = 7;
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
patchInfoAddr = reader.readNextLong(); // (unslid) address of dyld_cache_patch_info
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
patchInfoSize = reader.readNextLong(); // Size of all of the patch information pointed to via the dyld_cache_patch_info
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
otherImageGroupAddrUnused = reader.readNextLong(); // unused
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
otherImageGroupSizeUnused = reader.readNextLong(); // unused
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
progClosuresAddr = reader.readNextLong(); // (unslid) address of list of program launch closures
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
progClosuresSize = reader.readNextLong(); // size of list of program launch closures
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
progClosuresTrieAddr = reader.readNextLong(); // (unslid) address of trie of indexes into program launch closures
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
progClosuresTrieSize = reader.readNextLong(); // size of trie of indexes into program launch closures
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
platform = reader.readNextInt(); // platform number (macOS=1, etc)
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
dyld_info = reader.readNextInt();
|
||||
formatVersion = dyld_info & 0xff; // dyld3::closure::kFormatVersion
|
||||
dylibsExpectedOnDisk = (dyld_info >>> 8 & 1) == 1; // dyld should expect the dylib exists on disk and to compare inode/mtime to see if cache is valid
|
||||
simulator = (dyld_info >>> 9 & 1) == 1; // for simulator of specified platform
|
||||
locallyBuiltCache = (dyld_info >> 10 & 1) == 1; // 0 for B&I built cache, 1 for locally built cache
|
||||
builtFromChainedFixups = (dyld_info >> 11 & 1) == 1; // some dylib in cache was built using chained fixups, so patch tables must be used for overrides
|
||||
padding = (dyld_info >> 12) & 0xfffff; // TBD
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
sharedRegionStart = reader.readNextLong(); // base load address of cache if not slid
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
sharedRegionSize = reader.readNextLong(); // overall size of region cache can be mapped into
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
maxSlide = reader.readNextLong(); // runtime slide of cache can be between zero and this value
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
dylibsImageArrayAddr = reader.readNextLong(); // (unslid) address of ImageArray for dylibs in this cache
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
dylibsImageArraySize = reader.readNextLong(); // size of ImageArray for dylibs in this cache
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
dylibsTrieAddr = reader.readNextLong(); // (unslid) address of trie of indexes of all cached dylibs
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
dylibsTrieSize = reader.readNextLong(); // size of trie of cached dylib paths
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
otherImageArrayAddr = reader.readNextLong(); // (unslid) address of ImageArray for dylibs and bundles with dlopen closures
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
otherImageArraySize = reader.readNextLong(); // size of ImageArray for dylibs and bundles with dlopen closures
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
otherTrieAddr = reader.readNextLong(); // (unslid) address of trie of indexes of all dylibs and bundles with dlopen closures
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
otherTrieSize = reader.readNextLong(); // size of trie of dylibs and bundles with dlopen closures
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
mappingWithSlideOffset = reader.readNextInt(); // file offset to first dyld_cache_mapping_and_slide_info
|
||||
}
|
||||
if (reader.getPointerIndex() < mappingOffset) {
|
||||
mappingWithSlideCount = reader.readNextInt(); // number of dyld_cache_mapping_and_slide_info entries
|
||||
}
|
||||
|
||||
headerSize = (int) (reader.getPointerIndex() - startIndex);
|
||||
|
||||
baseAddress = reader.readLong(mappingOffset);
|
||||
architecture = DyldArchitecture.getArchitecture(new String(magic).trim());
|
||||
|
||||
mappingInfoList = new ArrayList<>(mappingCount);
|
||||
cacheMappingAndSlideInfoList = new ArrayList<>(mappingWithSlideCount);
|
||||
slideInfoList = new ArrayList<>();
|
||||
imageInfoList = new ArrayList<>(imagesCount);
|
||||
branchPoolList = new ArrayList<>(branchPoolsCount);
|
||||
imageTextInfoList = new ArrayList<>();
|
||||
|
@ -151,9 +269,6 @@ public class DyldCacheHeader implements StructConverter {
|
|||
parseMappingInfo(log, monitor);
|
||||
parseImageInfo(log, monitor);
|
||||
}
|
||||
if (headerType >= 2) {
|
||||
parseSlideInfo(log, monitor);
|
||||
}
|
||||
if (headerType >= 3) {
|
||||
if (parseSymbols) {
|
||||
parseLocalSymbolsInfo(log, monitor);
|
||||
|
@ -163,6 +278,56 @@ public class DyldCacheHeader implements StructConverter {
|
|||
parseBranchPools(log, monitor);
|
||||
parseImageTextInfo(log, monitor);
|
||||
}
|
||||
parseCacheMappingSlideInfo(log, monitor);
|
||||
if (haSlideInfo()) {
|
||||
parseSlideInfos(log, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseSlideInfos(MessageLog log, TaskMonitor monitor) throws CancelledException {
|
||||
if (slideInfoOffset != 0) {
|
||||
DyldCacheSlideInfoCommon slideInfo = parseSlideInfo(slideInfoOffset, log, monitor);
|
||||
if (slideInfo != null) {
|
||||
slideInfoList.add(slideInfo);
|
||||
}
|
||||
}
|
||||
else if (cacheMappingAndSlideInfoList.size() > 0) {
|
||||
// last section contains the real slide infos
|
||||
int listLen = cacheMappingAndSlideInfoList.size();
|
||||
DyldCacheMappingAndSlideInfo linkEditInfo =
|
||||
cacheMappingAndSlideInfoList.get(listLen - 1);
|
||||
for (DyldCacheMappingAndSlideInfo info : cacheMappingAndSlideInfoList) {
|
||||
if (info.getSlideInfoFileOffset() == 0) {
|
||||
continue;
|
||||
}
|
||||
long offsetInEditRegion =
|
||||
info.getSlideInfoFileOffset() - linkEditInfo.getSlideInfoFileOffset();
|
||||
DyldCacheSlideInfoCommon slideInfo =
|
||||
parseSlideInfo(info.getSlideInfoFileOffset(), log, monitor);
|
||||
slideInfoList.add(slideInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseCacheMappingSlideInfo(MessageLog log, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
monitor.setMessage("Parsing DYLD cache mapping and slide info...");
|
||||
monitor.initialize(mappingWithSlideCount);
|
||||
try {
|
||||
if (mappingWithSlideCount <= 0) {
|
||||
return;
|
||||
}
|
||||
reader.setPointerIndex(mappingWithSlideOffset);
|
||||
for (int i = 0; i < mappingWithSlideCount; ++i) {
|
||||
cacheMappingAndSlideInfoList.add(new DyldCacheMappingAndSlideInfo(reader));
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
|
||||
"Failed to parse dyld_cache_mapping_info.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -209,6 +374,9 @@ public class DyldCacheHeader implements StructConverter {
|
|||
markupAcceleratorInfo(program, space, monitor, log);
|
||||
markupImageTextInfo(program, space, monitor, log);
|
||||
}
|
||||
if (mappingWithSlideOffset >= 0) {
|
||||
markupCacheMappingSlideInfo(program, space, log, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -266,6 +434,15 @@ public class DyldCacheHeader implements StructConverter {
|
|||
return imageInfoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link List} of {@link DyldCacheMappingAndSlideInfo}s. Requires header to have been parsed.
|
||||
*
|
||||
* @return The {@link List} of {@link DyldCacheMappingAndSlideInfo}s
|
||||
*/
|
||||
public List<DyldCacheMappingAndSlideInfo> getCacheMappingAndSlideInfos() {
|
||||
return cacheMappingAndSlideInfoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link DyldCacheLocalSymbolsInfo}.
|
||||
*
|
||||
|
@ -274,28 +451,14 @@ public class DyldCacheHeader implements StructConverter {
|
|||
public DyldCacheLocalSymbolsInfo getLocalSymbolsInfo() {
|
||||
return localSymbolsInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the {@link DyldCacheSlideInfoCommon}.
|
||||
* Gets the {@link List} of {@link DyldCacheSlideInfoCommon}s.
|
||||
*
|
||||
* @return the {@link DyldCacheSlideInfoCommon}. Common, or particular version
|
||||
* @return the {@link List} of {@link DyldCacheSlideInfoCommon}s.
|
||||
*/
|
||||
public DyldCacheSlideInfoCommon getSlideInfo() {
|
||||
return slideInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return slideInfoOffset
|
||||
*/
|
||||
public long getSlideInfoOffset() {
|
||||
return slideInfoOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return slideInfoSize
|
||||
*/
|
||||
public long getSlideInfoSize() {
|
||||
return slideInfoSize;
|
||||
public List<DyldCacheSlideInfoCommon> getSlideInfos() {
|
||||
return slideInfoList;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -319,47 +482,86 @@ public class DyldCacheHeader implements StructConverter {
|
|||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType("dyld_cache_header", 0);
|
||||
if (headerType >= 1) {
|
||||
struct.add(new ArrayDataType(ASCII, 16, 1), "magic", "e.g. \"dyld_v0 i386\"");
|
||||
struct.add(DWORD, "mappingOffset", "file offset to first dyld_cache_mapping_info");
|
||||
struct.add(DWORD, "mappingCount", "number of dyld_cache_mapping_info entries");
|
||||
struct.add(DWORD, "imagesOffset", "file offset to first dyld_cache_image_info");
|
||||
struct.add(DWORD, "imagesCount", "number of dyld_cache_image_info entries");
|
||||
struct.add(QWORD, "dyldBaseAddress", "base address of dyld when cache was built");
|
||||
}
|
||||
if (headerType >= 2) {
|
||||
struct.add(QWORD, "codeSignatureOffset", "file offset of code signature blob");
|
||||
struct.add(QWORD, "codeSignatureSize",
|
||||
"size of code signature blob (zero means to end of file)");
|
||||
struct.add(QWORD, "slideInfoOffset", "file offset of kernel slid info");
|
||||
struct.add(QWORD, "slideInfoSize", "size of kernel slid info");
|
||||
}
|
||||
if (headerType >= 3) {
|
||||
struct.add(QWORD, "localSymbolsOffset",
|
||||
"file offset of where local symbols are stored");
|
||||
struct.add(QWORD, "localSymbolsSize", "size of local symbols information");
|
||||
}
|
||||
if (headerType >= 4) {
|
||||
struct.add(new ArrayDataType(BYTE, 16, 1), "uuid",
|
||||
"unique value for each shared cache file");
|
||||
}
|
||||
if (headerType >= 5) {
|
||||
struct.add(QWORD, "cacheType", "0 for development, 1 for production");
|
||||
}
|
||||
if (headerType >= 6) {
|
||||
struct.add(DWORD, "branchPoolsOffset",
|
||||
"file offset to table of uint64_t pool addresses");
|
||||
struct.add(DWORD, "branchPoolsCount", "number of uint64_t entries");
|
||||
struct.add(QWORD, "accelerateInfoAddr", "(unslid) address of optimization info");
|
||||
struct.add(QWORD, "accelerateInfoSize", "size of optimization info");
|
||||
struct.add(QWORD, "imagesTextOffset",
|
||||
"file offset to first dyld_cache_image_text_info");
|
||||
struct.add(QWORD, "imagesTextCount", "number of dyld_cache_image_text_info entries");
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
|
||||
// headerType 1
|
||||
//
|
||||
addHeaderField(struct, new ArrayDataType(ASCII, 16, 1), "magic","e.g. \"dyld_v0 i386\"");
|
||||
addHeaderField(struct, DWORD, "mappingOffset","file offset to first dyld_cache_mapping_info");
|
||||
addHeaderField(struct, DWORD, "mappingCount", "number of dyld_cache_mapping_info entries");
|
||||
addHeaderField(struct, DWORD, "imagesOffset", "file offset to first dyld_cache_image_info");
|
||||
addHeaderField(struct, DWORD, "imagesCount", "number of dyld_cache_image_info entries");
|
||||
addHeaderField(struct, QWORD, "dyldBaseAddress","base address of dyld when cache was built");
|
||||
|
||||
// headerType 2
|
||||
//
|
||||
addHeaderField(struct, QWORD, "codeSignatureOffset", "file offset of code signature blob");
|
||||
addHeaderField(struct, QWORD, "codeSignatureSize","size of code signature blob (zero means to end of file)");
|
||||
addHeaderField(struct, QWORD, "slideInfoOffset", "file offset of kernel slid info");
|
||||
addHeaderField(struct, QWORD, "slideInfoSize", "size of kernel slid info");
|
||||
|
||||
// headerType 3
|
||||
//
|
||||
addHeaderField(struct, QWORD, "localSymbolsOffset","file offset of where local symbols are stored");
|
||||
addHeaderField(struct, QWORD, "localSymbolsSize", "size of local symbols information");
|
||||
|
||||
// headerType 4
|
||||
//
|
||||
addHeaderField(struct, new ArrayDataType(BYTE, 16, 1), "uuid","unique value for each shared cache file");
|
||||
|
||||
// headerType 5
|
||||
//
|
||||
addHeaderField(struct, QWORD, "cacheType", "0 for development, 1 for production");
|
||||
|
||||
// headerType 6
|
||||
//
|
||||
addHeaderField(struct, DWORD, "branchPoolsOffset","file offset to table of uint64_t pool addresses");
|
||||
addHeaderField(struct, DWORD, "branchPoolsCount", "number of uint64_t entries");
|
||||
addHeaderField(struct, QWORD, "accelerateInfoAddr","(unslid) address of optimization info");
|
||||
addHeaderField(struct, QWORD, "accelerateInfoSize", "size of optimization info");
|
||||
addHeaderField(struct, QWORD, "imagesTextOffset","file offset to first dyld_cache_image_text_info");
|
||||
addHeaderField(struct, QWORD, "imagesTextCount","number of dyld_cache_image_text_info entries");
|
||||
|
||||
// headerType 7
|
||||
//
|
||||
addHeaderField(struct, QWORD, "patchInfoAddr", "(unslid) address of dyld_cache_patch_info");
|
||||
addHeaderField(struct, QWORD, "patchInfoSize", "Size of all of the patch information pointed to via the dyld_cache_patch_info");
|
||||
addHeaderField(struct, QWORD, "otherImageGroupAddrUnused", "unused");
|
||||
addHeaderField(struct, QWORD, "otherImageGroupSizeUnused", "unused");
|
||||
addHeaderField(struct, QWORD, "progClosuresAddr", "(unslid) address of list of program launch closures");
|
||||
addHeaderField(struct, QWORD, "progClosuresSize", "size of list of program launch closures");
|
||||
addHeaderField(struct, QWORD, "progClosuresTrieAddr", "(unslid) address of trie of indexes into program launch closures");
|
||||
addHeaderField(struct, QWORD, "progClosuresTrieSize", "size of trie of indexes into program launch closures");
|
||||
addHeaderField(struct, DWORD, "platform", "platform number (macOS=1, etc)");
|
||||
addHeaderField(struct, DWORD, "dyld_info", "");
|
||||
addHeaderField(struct, QWORD, "sharedRegionStart", "base load address of cache if not slid");
|
||||
addHeaderField(struct, QWORD, "sharedRegionSize", "overall size of region cache can be mapped into");
|
||||
addHeaderField(struct, QWORD, "maxSlide","runtime slide of cache can be between zero and this value");
|
||||
addHeaderField(struct, QWORD, "dylibsImageArrayAddr","(unslid) address of ImageArray for dylibs in this cache");
|
||||
addHeaderField(struct, QWORD, "dylibsImageArraySize","size of ImageArray for dylibs in this cache");
|
||||
addHeaderField(struct, QWORD, "dylibsTrieAddr","(unslid) address of trie of indexes of all cached dylibs");
|
||||
addHeaderField(struct, QWORD, "dylibsTrieSize", "size of trie of cached dylib paths");
|
||||
addHeaderField(struct, QWORD, "otherImageArrayAddr","(unslid) address of ImageArray for dylibs and bundles with dlopen closures");
|
||||
addHeaderField(struct, QWORD, "otherImageArraySize","size of ImageArray for dylibs and bundles with dlopen closures");
|
||||
addHeaderField(struct, QWORD, "otherTrieAddr","(unslid) address of trie of indexes of all dylibs and bundles with dlopen closures");
|
||||
addHeaderField(struct, QWORD, "otherTrieSize","size of trie of dylibs and bundles with dlopen closures");
|
||||
addHeaderField(struct, DWORD, "mappingWithSlideOffset","file offset to first dyld_cache_mapping_and_slide_info");
|
||||
addHeaderField(struct, DWORD, "mappingWithSlideCount","number of dyld_cache_mapping_and_slide_info entries");
|
||||
|
||||
// @formatter:on
|
||||
|
||||
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
|
||||
return struct;
|
||||
}
|
||||
|
||||
private void addHeaderField(StructureDataType struct, DataType dt, String fieldname,
|
||||
String comment) {
|
||||
if (headerSize > struct.getLength()) {
|
||||
struct.add(dt, fieldname, comment);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseMappingInfo(MessageLog log, TaskMonitor monitor) throws CancelledException {
|
||||
monitor.setMessage("Parsing DYLD mapping info...");
|
||||
monitor.initialize(mappingCount);
|
||||
|
@ -397,35 +599,11 @@ public class DyldCacheHeader implements StructConverter {
|
|||
}
|
||||
}
|
||||
|
||||
private void parseSlideInfo(MessageLog log, TaskMonitor monitor) throws CancelledException {
|
||||
if (slideInfoOffset == 0) {
|
||||
return;
|
||||
}
|
||||
monitor.setMessage("Parsing DYLD slide info...");
|
||||
monitor.initialize(1);
|
||||
try {
|
||||
reader.setPointerIndex(slideInfoOffset);
|
||||
slideInfo = new DyldCacheSlideInfoCommon(reader);
|
||||
reader.setPointerIndex(slideInfoOffset);
|
||||
switch (slideInfo.getVersion()) {
|
||||
case 1:
|
||||
slideInfo = new DyldCacheSlideInfo1(reader);
|
||||
break;
|
||||
case 2:
|
||||
slideInfo = new DyldCacheSlideInfo2(reader);
|
||||
break;
|
||||
case 3:
|
||||
slideInfo = new DyldCacheSlideInfo3(reader);
|
||||
break;
|
||||
default:
|
||||
throw new IOException();
|
||||
}
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
|
||||
"Failed to parse dyld_cache_slide_info.");
|
||||
}
|
||||
private DyldCacheSlideInfoCommon parseSlideInfo(long offset, MessageLog log,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
DyldCacheSlideInfoCommon slideInfo =
|
||||
DyldCacheSlideInfoCommon.parseSlideInfo(reader, offset, log, monitor);
|
||||
return slideInfo;
|
||||
}
|
||||
|
||||
private void parseLocalSymbolsInfo(MessageLog log, TaskMonitor monitor)
|
||||
|
@ -543,6 +721,26 @@ public class DyldCacheHeader implements StructConverter {
|
|||
}
|
||||
}
|
||||
|
||||
private void markupCacheMappingSlideInfo(Program program, AddressSpace space, MessageLog log,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
monitor.setMessage("Marking up DYLD cache mapping and slide info...");
|
||||
monitor.initialize(cacheMappingAndSlideInfoList.size());
|
||||
try {
|
||||
Address addr = fileOffsetToAddr(mappingWithSlideOffset, program, space);
|
||||
for (DyldCacheMappingAndSlideInfo mappingInfo : cacheMappingAndSlideInfoList) {
|
||||
Data d = DataUtilities.createData(program, addr, mappingInfo.toDataType(), -1,
|
||||
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
addr = addr.add(d.getLength());
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
}
|
||||
catch (CodeUnitInsertionException | DuplicateNameException | IOException e) {
|
||||
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
|
||||
"Failed to markup dyld_cache_mapping_info.");
|
||||
}
|
||||
}
|
||||
|
||||
private void markupImageInfo(Program program, AddressSpace space, TaskMonitor monitor,
|
||||
MessageLog log) throws CancelledException {
|
||||
monitor.setMessage("Marking up DYLD image info...");
|
||||
|
@ -570,8 +768,9 @@ public class DyldCacheHeader implements StructConverter {
|
|||
monitor.initialize(1);
|
||||
try {
|
||||
String size = "0x" + Long.toHexString(codeSignatureSize);
|
||||
program.getListing().setComment(fileOffsetToAddr(codeSignatureOffset, program, space),
|
||||
CodeUnit.PLATE_COMMENT, "Code Signature (" + size + " bytes)");
|
||||
program.getListing()
|
||||
.setComment(fileOffsetToAddr(codeSignatureOffset, program, space),
|
||||
CodeUnit.PLATE_COMMENT, "Code Signature (" + size + " bytes)");
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
|
@ -585,10 +784,12 @@ public class DyldCacheHeader implements StructConverter {
|
|||
monitor.setMessage("Marking up DYLD slide info...");
|
||||
monitor.initialize(1);
|
||||
try {
|
||||
if (slideInfo != null) {
|
||||
Address addr = fileOffsetToAddr(slideInfoOffset, program, space);
|
||||
DataUtilities.createData(program, addr, slideInfo.toDataType(), -1, false,
|
||||
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
if (slideInfoList.size() > 0) {
|
||||
for (DyldCacheSlideInfoCommon info : slideInfoList) {
|
||||
Address addr = fileOffsetToAddr(info.getSlideInfoOffset(), program, space);
|
||||
DataUtilities.createData(program, addr, info.toDataType(), -1, false,
|
||||
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
}
|
||||
}
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
|
@ -666,8 +867,8 @@ public class DyldCacheHeader implements StructConverter {
|
|||
for (DyldCacheImageTextInfo imageTextInfo : imageTextInfoList) {
|
||||
Data d = DataUtilities.createData(program, addr, imageTextInfo.toDataType(), -1,
|
||||
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
program.getListing().setComment(addr, CodeUnit.EOL_COMMENT,
|
||||
imageTextInfo.getPath());
|
||||
program.getListing()
|
||||
.setComment(addr, CodeUnit.EOL_COMMENT, imageTextInfo.getPath());
|
||||
addr = addr.add(d.getLength());
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
|
@ -713,4 +914,33 @@ public class DyldCacheHeader implements StructConverter {
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if any slide info exists
|
||||
*
|
||||
* @return True if any slide info exists; otherwise, false
|
||||
*/
|
||||
public boolean haSlideInfo() {
|
||||
if (slideInfoSize != 0) {
|
||||
// this is no longer used, but if non-zero, is older format and has slide-info
|
||||
return true;
|
||||
}
|
||||
if (headerType > 6) {
|
||||
for (DyldCacheMappingAndSlideInfo info : cacheMappingAndSlideInfoList) {
|
||||
if (info.getSlideInfoFileSize() != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original unslid load address. This is found in the first mapping infos.
|
||||
*
|
||||
* @return the original unslid load address
|
||||
*/
|
||||
public long unslidLoadAddress() {
|
||||
return mappingInfoList.get(0).getAddress();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
/**
|
||||
* Represents a dyld_cache_image_info structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheImageInfo implements StructConverter {
|
||||
|
|
|
@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
/**
|
||||
* Represents a dyld_cache_image_info_extra structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheImageInfoExtra implements StructConverter {
|
||||
|
|
|
@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
/**
|
||||
* Represents a dyld_cache_image_text_info structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheImageTextInfo implements StructConverter {
|
||||
|
|
|
@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
/**
|
||||
* Represents a dyld_cache_local_symbols_entry structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheLocalSymbolsEntry implements StructConverter {
|
||||
|
|
|
@ -40,7 +40,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
/**
|
||||
* Represents a dyld_cache_local_symbols_info structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheLocalSymbolsInfo implements StructConverter {
|
||||
|
@ -159,8 +159,7 @@ public class DyldCacheLocalSymbolsInfo implements StructConverter {
|
|||
monitor.incrementProgress(1);
|
||||
}
|
||||
// sort the entries by the index in the string table, so don't jump around reading
|
||||
List<NList> sortedList = nlistList
|
||||
.stream()
|
||||
List<NList> sortedList = nlistList.stream()
|
||||
.sorted((o1, o2) -> Integer.compare(o1.getStringTableIndex(),
|
||||
o2.getStringTableIndex()))
|
||||
.collect(Collectors.toList());
|
||||
|
@ -202,8 +201,7 @@ public class DyldCacheLocalSymbolsInfo implements StructConverter {
|
|||
try {
|
||||
Address addr = localSymbolsInfoAddr.add(nlistOffset);
|
||||
for (NList nlist : nlistList) {
|
||||
Data d = DataUtilities.createData(program, addr, nlist.toDataType(), -1, false,
|
||||
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
Data d = program.getListing().createData(addr, nlist.toDataType());
|
||||
addr = addr.add(d.getLength());
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
|
@ -221,8 +219,7 @@ public class DyldCacheLocalSymbolsInfo implements StructConverter {
|
|||
try {
|
||||
Address addr = localSymbolsInfoAddr.add(entriesOffset);
|
||||
for (DyldCacheLocalSymbolsEntry localSymbolsEntry : localSymbolsEntryList) {
|
||||
Data d = DataUtilities.createData(program, addr, localSymbolsEntry.toDataType(), -1,
|
||||
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
Data d = program.getListing().createData(addr, localSymbolsEntry.toDataType());
|
||||
addr = addr.add(d.getLength());
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.macho.dyld;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.macho.MachConstants;
|
||||
import ghidra.app.util.bin.format.macho.commands.SegmentConstants;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* Represents a dyld_cache_mapping_and_slide_info structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheMappingAndSlideInfo implements StructConverter {
|
||||
|
||||
public static long DYLD_CACHE_MAPPING_AUTH_DATA = 1 << 3L;
|
||||
public static long DYLD_CACHE_MAPPING_DIRTY_DATA = 1 << 1L;
|
||||
public static long DYLD_CACHE_MAPPING_CONST_DATA = 1 << 2L;
|
||||
|
||||
private long address;
|
||||
private long size;
|
||||
private long fileOffset;
|
||||
private long slideInfoFileOffset;
|
||||
private long slideInfoFileSize;
|
||||
private long flags;
|
||||
private int maxProt;
|
||||
private int initProt;
|
||||
|
||||
/**
|
||||
* Create a new {@link DyldCacheImageInfo}.
|
||||
*
|
||||
* @param reader A {@link BinaryReader} positioned at the start of a DYLD mapping info
|
||||
* @throws IOException if there was an IO-related problem creating the DYLD mapping info
|
||||
*/
|
||||
public DyldCacheMappingAndSlideInfo(BinaryReader reader) throws IOException {
|
||||
address = reader.readNextLong();
|
||||
size = reader.readNextLong();
|
||||
fileOffset = reader.readNextLong();
|
||||
slideInfoFileOffset = reader.readNextLong();
|
||||
slideInfoFileSize = reader.readNextLong();
|
||||
flags = reader.readNextLong();
|
||||
maxProt = reader.readNextInt();
|
||||
initProt = reader.readNextInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address of the start of the mapping.
|
||||
*
|
||||
* @return The address of the start of the mapping
|
||||
*/
|
||||
public long getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the mapping.
|
||||
*
|
||||
* @return The size of the mapping
|
||||
*/
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file offset of the start of the mapping.
|
||||
*
|
||||
* @return The file offset of the start of the mapping
|
||||
*/
|
||||
public long getFileOffset() {
|
||||
return fileOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get slide info file offset
|
||||
*
|
||||
* @return slide info file offset
|
||||
*/
|
||||
public long getSlideInfoFileOffset() {
|
||||
return slideInfoFileOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get slide info file size
|
||||
*
|
||||
* @return slide info file size
|
||||
*/
|
||||
public long getSlideInfoFileSize() {
|
||||
return slideInfoFileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get slide info flags
|
||||
*
|
||||
* @return slide info flags
|
||||
*/
|
||||
public long getFlags() {
|
||||
return flags;
|
||||
}
|
||||
|
||||
public boolean isAuthData() {
|
||||
return (flags & DYLD_CACHE_MAPPING_AUTH_DATA) != 0;
|
||||
}
|
||||
|
||||
public boolean isDirtyData() {
|
||||
return (flags & DYLD_CACHE_MAPPING_DIRTY_DATA) != 0;
|
||||
}
|
||||
|
||||
public boolean isConstData() {
|
||||
return (flags & DYLD_CACHE_MAPPING_CONST_DATA) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the initial protections include READ.
|
||||
*
|
||||
* @return true if the initial protections include READ
|
||||
*/
|
||||
public boolean isRead() {
|
||||
return (initProt & SegmentConstants.PROTECTION_R) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the initial protections include WRITE.
|
||||
*
|
||||
* @return true if the initial protections include WRITE
|
||||
*/
|
||||
public boolean isWrite() {
|
||||
return (initProt & SegmentConstants.PROTECTION_W) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the initial protections include EXECUTE.
|
||||
*
|
||||
* @return true if the initial protections include EXECUTE
|
||||
*/
|
||||
public boolean isExecute() {
|
||||
return (initProt & SegmentConstants.PROTECTION_X) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType("dyld_cache_mapping_and_slide_info", 0);
|
||||
struct.add(QWORD, "address", "");
|
||||
struct.add(QWORD, "size", "");
|
||||
struct.add(QWORD, "fileOffset", "");
|
||||
struct.add(QWORD, "slideInfoFileOffset", "");
|
||||
struct.add(QWORD, "slideInfoFileSize", "");
|
||||
struct.add(QWORD, "flags", "");
|
||||
struct.add(DWORD, "maxProt", "");
|
||||
struct.add(DWORD, "initProt", "");
|
||||
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
|
||||
return struct;
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
/**
|
||||
* Represents a dyld_cache_mapping_info structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheMappingInfo implements StructConverter {
|
||||
|
|
|
@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
/**
|
||||
* Represents a dyld_cache_range_entry structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheRangeEntry implements StructConverter {
|
||||
|
|
|
@ -16,16 +16,25 @@
|
|||
package ghidra.app.util.bin.format.macho.dyld;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.macho.MachConstants;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Represents a dyld_cache_slide_info structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
|
||||
|
||||
|
@ -35,6 +44,9 @@ public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
|
|||
private int entries_count;
|
||||
private int entries_size;
|
||||
|
||||
private short toc[];
|
||||
private byte bits[][];
|
||||
|
||||
public int getTocOffset() {
|
||||
return toc_offset;
|
||||
}
|
||||
|
@ -55,6 +67,14 @@ public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
|
|||
return entries_size;
|
||||
}
|
||||
|
||||
public short[] getToc() {
|
||||
return toc;
|
||||
}
|
||||
|
||||
public byte[][] getEntries() {
|
||||
return bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link DyldCacheSlideInfo1}.
|
||||
*
|
||||
|
@ -63,11 +83,22 @@ public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
|
|||
*/
|
||||
public DyldCacheSlideInfo1(BinaryReader reader) throws IOException {
|
||||
super(reader);
|
||||
long startIndex = reader.getPointerIndex() - 4; // version # already read
|
||||
|
||||
toc_offset = reader.readNextInt();
|
||||
toc_count = reader.readNextInt();
|
||||
entries_offset = reader.readNextInt();
|
||||
entries_count = reader.readNextInt();
|
||||
entries_size = reader.readNextInt();
|
||||
|
||||
reader.setPointerIndex(startIndex + toc_offset);
|
||||
toc = reader.readNextShortArray(toc_count);
|
||||
|
||||
reader.setPointerIndex(startIndex + entries_offset);
|
||||
bits = new byte[entries_count][];
|
||||
for (int i = 0; i < entries_count; i++) {
|
||||
bits[i] = reader.readNextByteArray(entries_size);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,7 +110,85 @@ public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
|
|||
struct.add(DWORD, "entries_offset", "");
|
||||
struct.add(DWORD, "entries_count", "");
|
||||
struct.add(DWORD, "entries_size", "");
|
||||
if (toc_offset > 0x18) {
|
||||
struct.add(new ArrayDataType(ByteDataType.dataType, toc_offset - 0x18, -1),
|
||||
"tocAlignment", "");
|
||||
}
|
||||
struct.add(new ArrayDataType(WordDataType.dataType, toc_count, -1), "toc", "");
|
||||
if (entries_offset > (toc_offset + (toc_count * 2))) {
|
||||
struct.add(new ArrayDataType(ByteDataType.dataType,
|
||||
entries_offset - (toc_offset + (toc_count * 2)), -1), "entriesAlignment", "");
|
||||
}
|
||||
struct.add(new ArrayDataType(new ArrayDataType(ByteDataType.dataType, entries_size, -1),
|
||||
entries_count, -1), "entries", "");
|
||||
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
|
||||
return struct;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
|
||||
boolean addRelocations, MessageLog log, TaskMonitor monitor)
|
||||
throws MemoryAccessException, CancelledException {
|
||||
|
||||
Memory memory = program.getMemory();
|
||||
|
||||
List<DyldCacheMappingInfo> mappingInfos = dyldCacheHeader.getMappingInfos();
|
||||
DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY);
|
||||
long dataPageStart = dyldCacheMappingInfo.getAddress();
|
||||
|
||||
List<Address> unchainedLocList = new ArrayList<>(1024);
|
||||
|
||||
byte origBytes[] = new byte[8];
|
||||
|
||||
monitor.setMessage("Fixing V1 chained data page pointers...");
|
||||
|
||||
monitor.setMaximum(entries_count);
|
||||
|
||||
// V1 pointers currently don't need to be fixed, unless the pointers the
|
||||
// dyld is slid from its preferred location.
|
||||
for (int tocIndex = 0; tocIndex < toc_count; tocIndex++) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
int entryIndex = (toc[tocIndex]) & 0xFFFF;
|
||||
if (entryIndex > entries_count || entryIndex > bits.length) {
|
||||
log.appendMsg("Entry too big! [" + tocIndex + "] " + entryIndex + " " +
|
||||
entries_count + " " + bits.length);
|
||||
continue;
|
||||
}
|
||||
|
||||
byte entry[] = bits[entryIndex];
|
||||
|
||||
long page = dataPageStart + (4096L * tocIndex);
|
||||
for (int pageEntriesIndex = 0; pageEntriesIndex < 128; ++pageEntriesIndex) {
|
||||
long prtEntryBitmap = entry[pageEntriesIndex] & 0xffL;
|
||||
|
||||
if (prtEntryBitmap != 0) {
|
||||
for (int bitMapIndex = 0; bitMapIndex < 8; ++bitMapIndex) {
|
||||
if ((prtEntryBitmap & (1L << bitMapIndex)) != 0) {
|
||||
long loc = (page + pageEntriesIndex * 8 * 4 + bitMapIndex * 4);
|
||||
Address addr =
|
||||
memory.getProgram().getLanguage().getDefaultSpace().getAddress(loc);
|
||||
long origValue = memory.getLong(addr);
|
||||
|
||||
long value = origValue /* + slide */ ;
|
||||
|
||||
// not actually changing bytes, so not really a relocation, but a relocate-able place
|
||||
if (addRelocations) {
|
||||
addRelocationTableEntry(program, addr, 0x1000, value, origBytes,
|
||||
null);
|
||||
}
|
||||
//memory.setLong(addr, value);
|
||||
|
||||
unchainedLocList.add(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
monitor.setProgress(tocIndex);
|
||||
}
|
||||
|
||||
createChainPointers(program, unchainedLocList, monitor);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,19 +16,31 @@
|
|||
package ghidra.app.util.bin.format.macho.dyld;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.macho.MachConstants;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Represents a dyld_cache_slide_info2 structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
|
||||
|
||||
private static final int DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE = 0x4000;
|
||||
private static final int DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA = 0x8000;
|
||||
|
||||
private int page_size;
|
||||
private int page_starts_offset;
|
||||
private int page_starts_count;
|
||||
|
@ -40,23 +52,23 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
|
|||
private short page_extras_entries[];
|
||||
|
||||
public long getPageSize() {
|
||||
return ((long)page_size) & 0xffffffff;
|
||||
return Integer.toUnsignedLong(page_size);
|
||||
}
|
||||
|
||||
public long getPageStartsOffset() {
|
||||
return ((long) page_starts_offset) & 0xffffffff;
|
||||
return Integer.toUnsignedLong(page_starts_offset);
|
||||
}
|
||||
|
||||
public long getPageStartsCount() {
|
||||
return ((long) page_starts_count) & 0xffffffff;
|
||||
return Integer.toUnsignedLong(page_starts_count);
|
||||
}
|
||||
|
||||
public long getPageExtrasOffset() {
|
||||
return ((long) page_extras_offset) & 0xffffffff;
|
||||
return Integer.toUnsignedLong(page_extras_offset);
|
||||
}
|
||||
|
||||
public long getPageExtrasCount() {
|
||||
return ((long) page_extras_count) & 0xffffffff;
|
||||
return Integer.toUnsignedLong(page_extras_count);
|
||||
}
|
||||
|
||||
public long getDeltaMask() {
|
||||
|
@ -66,15 +78,15 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
|
|||
public long getValueAdd() {
|
||||
return value_add;
|
||||
}
|
||||
|
||||
public short[] getPageStartsEntries() {
|
||||
|
||||
public short[] getPageStarts() {
|
||||
return page_starts_entries;
|
||||
}
|
||||
|
||||
public short[] getPageExtrasEntries() {
|
||||
|
||||
public short[] getPageExtras() {
|
||||
return page_extras_entries;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link DyldCacheSlideInfo2}.
|
||||
*
|
||||
|
@ -108,4 +120,127 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
|
|||
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
|
||||
return struct;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
|
||||
boolean addRelocations, MessageLog log, TaskMonitor monitor)
|
||||
throws MemoryAccessException, CancelledException {
|
||||
|
||||
long fixedAddressCount = 0;
|
||||
|
||||
List<DyldCacheMappingInfo> mappingInfos = dyldCacheHeader.getMappingInfos();
|
||||
DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY);
|
||||
long dataPageStart = dyldCacheMappingInfo.getAddress();
|
||||
long pageSize = getPageSize();
|
||||
long pageStartsCount = getPageStartsCount();
|
||||
|
||||
long deltaMask = getDeltaMask();
|
||||
long deltaShift = Long.numberOfTrailingZeros(deltaMask);
|
||||
long valueAdd = getValueAdd();
|
||||
|
||||
short[] pageEntries = getPageStarts();
|
||||
short[] extraEntries = getPageExtras();
|
||||
|
||||
monitor.setMessage("Fixing V2 chained data page pointers...");
|
||||
|
||||
monitor.setMaximum(pageStartsCount);
|
||||
for (int index = 0; index < pageStartsCount; index++) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
long page = dataPageStart + (pageSize * index);
|
||||
|
||||
monitor.setProgress(index);
|
||||
|
||||
int pageEntry = pageEntries[index] & 0xffff;
|
||||
if (pageEntry == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Address> unchainedLocList;
|
||||
|
||||
if ((pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) != 0) {
|
||||
// go into extras and process list of chain entries for the same page
|
||||
int extraIndex = (pageEntry & CHAIN_OFFSET_MASK);
|
||||
unchainedLocList = new ArrayList<Address>(1024);
|
||||
do {
|
||||
pageEntry = extraEntries[extraIndex] & 0xffff;
|
||||
long pageOffset = (pageEntry & CHAIN_OFFSET_MASK) * BYTES_PER_CHAIN_OFFSET;
|
||||
|
||||
List<Address> subLocList;
|
||||
subLocList = processPointerChain2(program, page, pageOffset, deltaMask,
|
||||
deltaShift, valueAdd, addRelocations, monitor);
|
||||
unchainedLocList.addAll(subLocList);
|
||||
extraIndex++;
|
||||
}
|
||||
while ((pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0);
|
||||
}
|
||||
else {
|
||||
long pageOffset = pageEntry * BYTES_PER_CHAIN_OFFSET;
|
||||
|
||||
unchainedLocList = processPointerChain2(program, page, pageOffset, deltaMask,
|
||||
deltaShift, valueAdd, addRelocations, monitor);
|
||||
}
|
||||
|
||||
fixedAddressCount += unchainedLocList.size();
|
||||
|
||||
createChainPointers(program, unchainedLocList, monitor);
|
||||
}
|
||||
|
||||
log.appendMsg("Fixed " + fixedAddressCount + " chained pointers.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes up any chained pointers, starting at the given address.
|
||||
*
|
||||
* @param program the program
|
||||
* @param page within data pages that has pointers to be unchained
|
||||
* @param nextOff offset within the page that is the chain start
|
||||
* @param deltaMask delta offset mask for each value
|
||||
* @param deltaShift shift needed for the deltaMask to extract the next offset
|
||||
* @param valueAdd value to be added to each chain pointer
|
||||
*
|
||||
* @throws MemoryAccessException IO problem reading file
|
||||
* @throws CancelledException user cancels
|
||||
*/
|
||||
private List<Address> processPointerChain2(Program program, long page, long nextOff,
|
||||
long deltaMask, long deltaShift, long valueAdd, boolean addRelocations,
|
||||
TaskMonitor monitor) throws MemoryAccessException, CancelledException {
|
||||
|
||||
// TODO: should the image base be used to perform the ASLR slide on the pointers.
|
||||
// currently image is kept at it's initial location with no ASLR.
|
||||
Address chainStart = program.getLanguage().getDefaultSpace().getAddress(page);
|
||||
Memory memory = program.getMemory();
|
||||
List<Address> unchainedLocList = new ArrayList<>(1024);
|
||||
|
||||
byte origBytes[] = new byte[8];
|
||||
|
||||
long valueMask = 0xffffffffffffffffL >>> (64 - deltaShift);
|
||||
|
||||
long delta = -1;
|
||||
while (delta != 0) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
Address chainLoc = chainStart.add(nextOff);
|
||||
long chainValue = memory.getLong(chainLoc);
|
||||
|
||||
delta = (chainValue & deltaMask) >> deltaShift;
|
||||
chainValue = chainValue & valueMask;
|
||||
if (chainValue != 0) {
|
||||
chainValue += valueAdd;
|
||||
// chainValue += slideAmount - if we were sliding
|
||||
}
|
||||
if (addRelocations) {
|
||||
addRelocationTableEntry(program, chainLoc, 2, chainValue, origBytes, null);
|
||||
}
|
||||
|
||||
memory.setLong(chainLoc, chainValue);
|
||||
|
||||
// delay creating data until after memory has been changed
|
||||
unchainedLocList.add(chainLoc);
|
||||
|
||||
nextOff += (delta * 4);
|
||||
}
|
||||
|
||||
return unchainedLocList;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,19 +16,30 @@
|
|||
package ghidra.app.util.bin.format.macho.dyld;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.macho.MachConstants;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Represents a dyld_cache_slide_info3 structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
|
||||
|
||||
private static final int DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE = 0xFFFF;
|
||||
|
||||
private int page_size;
|
||||
private int page_starts_count;
|
||||
private long auth_value_add;
|
||||
|
@ -60,6 +71,7 @@ public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
|
|||
super(reader);
|
||||
page_size = reader.readNextInt();
|
||||
page_starts_count = reader.readNextInt();
|
||||
int pad = reader.readNextInt();
|
||||
auth_value_add = reader.readNextLong();
|
||||
page_starts = reader.readNextShortArray(page_starts_count);
|
||||
}
|
||||
|
@ -70,9 +82,135 @@ public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
|
|||
struct.add(DWORD, "version", "");
|
||||
struct.add(DWORD, "page_size", "");
|
||||
struct.add(DWORD, "page_starts_count", "");
|
||||
struct.add(DWORD, "pad", "");
|
||||
struct.add(QWORD, "auth_value_add", "");
|
||||
struct.add(new ArrayDataType(WORD, page_starts_count, 1), "page_starts", "");
|
||||
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
|
||||
return struct;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
|
||||
boolean addRelocations, MessageLog log, TaskMonitor monitor)
|
||||
throws MemoryAccessException, CancelledException {
|
||||
long fixedAddressCount = 0;
|
||||
|
||||
List<DyldCacheMappingAndSlideInfo> mappingInfos =
|
||||
dyldCacheHeader.getCacheMappingAndSlideInfos();
|
||||
|
||||
if (mappingInfos.size() <= DATA_PAGE_MAP_ENTRY) {
|
||||
return;
|
||||
}
|
||||
|
||||
DyldCacheMappingAndSlideInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY); // default
|
||||
for (DyldCacheMappingAndSlideInfo cacheSlideInfo : mappingInfos) {
|
||||
if (cacheSlideInfo.getSlideInfoFileOffset() == getSlideInfoOffset()) {
|
||||
dyldCacheMappingInfo = cacheSlideInfo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
long dataPageStart = dyldCacheMappingInfo.getAddress();
|
||||
long pageSize = getPageSize();
|
||||
long pageStartsCount = getPageStartsCount();
|
||||
|
||||
long authValueAdd = getAuthValueAdd();
|
||||
|
||||
short[] pageStarts = getPageStarts();
|
||||
|
||||
monitor.setMessage("Fixing V3 chained data page pointers...");
|
||||
|
||||
monitor.setMaximum(pageStartsCount);
|
||||
for (int index = 0; index < pageStartsCount; index++) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
long page = dataPageStart + (pageSize * index);
|
||||
|
||||
monitor.setProgress(index);
|
||||
|
||||
int pageEntry = pageStarts[index] & 0xffff;
|
||||
if (pageEntry == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long pageOffset = (pageEntry / 8) * 8; // first entry byte based
|
||||
|
||||
List<Address> unchainedLocList;
|
||||
unchainedLocList = processPointerChain3(program, page, pageOffset, authValueAdd,
|
||||
addRelocations, monitor);
|
||||
|
||||
fixedAddressCount += unchainedLocList.size();
|
||||
|
||||
createChainPointers(program, unchainedLocList, monitor);
|
||||
}
|
||||
|
||||
log.appendMsg("Fixed " + fixedAddressCount + " chained pointers.");
|
||||
|
||||
monitor.setMessage("Created " + fixedAddressCount + " chained pointers");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes up any chained pointers, starting at the given address.
|
||||
*
|
||||
* @param program the program
|
||||
* @param page within data pages that has pointers to be unchained
|
||||
* @param nextOff offset within the page that is the chain start
|
||||
* @param auth_value_add value to be added to each chain pointer
|
||||
*
|
||||
* @return list of locations that were unchained
|
||||
*
|
||||
* @throws MemoryAccessException IO problem reading file
|
||||
* @throws CancelledException user cancels
|
||||
*/
|
||||
private List<Address> processPointerChain3(Program program, long page, long nextOff,
|
||||
long auth_value_add, boolean addRelocation, TaskMonitor monitor)
|
||||
throws MemoryAccessException, CancelledException {
|
||||
// TODO: should the image base be used to perform the ASLR slide on the pointers.
|
||||
// currently image is kept at it's initial location with no ASLR.
|
||||
Address chainStart = program.getLanguage().getDefaultSpace().getAddress(page);
|
||||
Memory memory = program.getMemory();
|
||||
|
||||
List<Address> unchainedLocList = new ArrayList<>(1024);
|
||||
|
||||
byte origBytes[] = new byte[8];
|
||||
|
||||
long delta = -1;
|
||||
while (delta != 0) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
Address chainLoc = chainStart.add(nextOff);
|
||||
long chainValue = memory.getLong(chainLoc);
|
||||
|
||||
// if authenticated pointer
|
||||
boolean isAuthenticated = chainValue >>> 63 != 0;
|
||||
delta = (chainValue & (0x7FFL << 51L)) >> 51L;
|
||||
|
||||
if (isAuthenticated) {
|
||||
long offsetFromSharedCacheBase = chainValue & 0xFFFFFFFFL;
|
||||
//long diversityData = (chainValue >> 32L) & 0xFFFFL;
|
||||
//long hasAddressDiversity = (chainValue >> 48L) & 0x1L;
|
||||
//long key = (chainValue >> 49L) & 0x3L;
|
||||
chainValue = offsetFromSharedCacheBase + auth_value_add;
|
||||
}
|
||||
else {
|
||||
long top8Bits = chainValue & 0x0007F80000000000L;
|
||||
long bottom43Bits = chainValue & 0x000007FFFFFFFFFFL;
|
||||
chainValue = (top8Bits << 13) | bottom43Bits;
|
||||
// chainValue += slideAmount - if we were sliding
|
||||
}
|
||||
|
||||
if (addRelocation) {
|
||||
addRelocationTableEntry(program, chainLoc, 3 * (isAuthenticated ? -1 : 1),
|
||||
chainValue, origBytes, null);
|
||||
}
|
||||
memory.setLong(chainLoc, chainValue);
|
||||
|
||||
// delay creating data until after memory has been changed
|
||||
unchainedLocList.add(chainLoc);
|
||||
|
||||
nextOff += delta * 8;
|
||||
}
|
||||
|
||||
return unchainedLocList;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,266 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.macho.dyld;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.macho.MachConstants;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Represents a dyld_cache_slide_info3 structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
public class DyldCacheSlideInfo4 extends DyldCacheSlideInfoCommon {
|
||||
|
||||
private static final int DYLD_CACHE_SLIDE4_PAGE_NO_REBASE = 0xFFFF;
|
||||
private static final int DYLD_CACHE_SLIDE4_PAGE_INDEX = 0x7FFF;
|
||||
private static final int DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA = 0x8000;
|
||||
private static final int DYLD_CACHE_SLIDE4_PAGE_EXTRA_END = 0x8000;
|
||||
|
||||
private static final int HEADERSIZE4 = 40;
|
||||
private int page_size; // currently 4096 (may also be 16384)
|
||||
private int page_starts_offset;
|
||||
private int page_starts_count;
|
||||
private int page_extras_offset;
|
||||
private int page_extras_count;
|
||||
private long delta_mask; // which (contiguous) set of bits contains the delta to the next rebase location (0xC0000000)
|
||||
private long value_add; // base address of cache
|
||||
|
||||
private short page_starts[];
|
||||
private short page_extras[];
|
||||
|
||||
public int getPageSize() {
|
||||
return page_size;
|
||||
}
|
||||
|
||||
public int getPageStartsOffset() {
|
||||
return page_starts_offset;
|
||||
}
|
||||
|
||||
public int getPageStartsCount() {
|
||||
return page_starts_count;
|
||||
}
|
||||
|
||||
public int getPageExtrasOffset() {
|
||||
return page_extras_offset;
|
||||
}
|
||||
|
||||
public int getPageExtrasCount() {
|
||||
return page_extras_count;
|
||||
}
|
||||
|
||||
public long getDeltaMask() {
|
||||
return delta_mask;
|
||||
}
|
||||
|
||||
public long getValueAdd() {
|
||||
return value_add;
|
||||
}
|
||||
|
||||
public short[] getPageStarts() {
|
||||
return page_starts;
|
||||
}
|
||||
|
||||
public short[] getPageExtras() {
|
||||
return page_extras;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link DyldCacheSlideInfo3}.
|
||||
*
|
||||
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info 3
|
||||
* @throws IOException if there was an IO-related problem creating the DYLD slide info 3
|
||||
*/
|
||||
public DyldCacheSlideInfo4(BinaryReader reader) throws IOException {
|
||||
super(reader);
|
||||
page_size = reader.readNextInt();
|
||||
page_starts_offset = reader.readNextInt();
|
||||
page_starts_count = reader.readNextInt();
|
||||
page_extras_offset = reader.readNextInt();
|
||||
page_extras_count = reader.readNextInt();
|
||||
delta_mask = reader.readNextLong();
|
||||
value_add = reader.readNextLong();
|
||||
reader.setPointerIndex(page_starts_offset);
|
||||
page_starts = reader.readNextShortArray(page_starts_count);
|
||||
reader.setPointerIndex(page_extras_offset);
|
||||
page_extras = reader.readNextShortArray(page_extras_count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType("dyld_cache_slide_info4", 0);
|
||||
struct.add(DWORD, "version", "");
|
||||
struct.add(DWORD, "page_size", "");
|
||||
struct.add(DWORD, "page_starts_offset", "");
|
||||
struct.add(DWORD, "page_starts_count", "");
|
||||
struct.add(DWORD, "page_extras_offset", "");
|
||||
struct.add(DWORD, "page_extras_count", "");
|
||||
struct.add(QWORD, "delta_mask", "");
|
||||
struct.add(QWORD, "value_add", "");
|
||||
|
||||
if (page_starts_offset == HEADERSIZE4) {
|
||||
struct.add(new ArrayDataType(WORD, page_starts_count, 1), "page_starts", "");
|
||||
}
|
||||
if (page_extras_offset == (HEADERSIZE4 + page_starts_count * 2)) {
|
||||
struct.add(new ArrayDataType(WORD, page_extras_count, 1), "page_extras", "");
|
||||
}
|
||||
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
|
||||
return struct;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
|
||||
boolean addRelocations, MessageLog log, TaskMonitor monitor)
|
||||
throws MemoryAccessException, CancelledException {
|
||||
long fixedAddressCount = 0;
|
||||
|
||||
List<DyldCacheMappingInfo> mappingInfos = dyldCacheHeader.getMappingInfos();
|
||||
DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY);
|
||||
long dataPageStart = dyldCacheMappingInfo.getAddress();
|
||||
long pageSize = getPageSize();
|
||||
long pageStartsCount = getPageStartsCount();
|
||||
|
||||
long deltaMask = getDeltaMask();
|
||||
long deltaShift = Long.numberOfTrailingZeros(deltaMask);
|
||||
long valueAdd = getValueAdd();
|
||||
|
||||
short[] pageEntries = getPageStarts();
|
||||
short[] extraEntries = getPageExtras();
|
||||
|
||||
monitor.setMessage("Fixing V4 chained data page pointers...");
|
||||
|
||||
monitor.setMaximum(pageStartsCount);
|
||||
for (int index = 0; index < pageStartsCount; index++) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
long page = dataPageStart + (pageSize * index);
|
||||
|
||||
monitor.setProgress(index);
|
||||
|
||||
int pageEntry = pageEntries[index] & 0xffff;
|
||||
if (pageEntry == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Address> unchainedLocList;
|
||||
|
||||
if ((pageEntry & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA) != 0) {
|
||||
// go into extras and process list of chain entries for the same page
|
||||
int extraIndex = (pageEntry & CHAIN_OFFSET_MASK);
|
||||
unchainedLocList = new ArrayList<>(1024);
|
||||
do {
|
||||
pageEntry = extraEntries[extraIndex] & 0xffff;
|
||||
long pageOffset = (pageEntry & CHAIN_OFFSET_MASK) * BYTES_PER_CHAIN_OFFSET;
|
||||
|
||||
List<Address> subLocList;
|
||||
subLocList = processPointerChain4(program, page, pageOffset, deltaMask,
|
||||
deltaShift, valueAdd, addRelocations, monitor);
|
||||
unchainedLocList.addAll(subLocList);
|
||||
extraIndex++;
|
||||
}
|
||||
while ((pageEntry & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA) == 0);
|
||||
}
|
||||
else {
|
||||
long pageOffset = pageEntry * BYTES_PER_CHAIN_OFFSET;
|
||||
|
||||
unchainedLocList = processPointerChain4(program, page, pageOffset, deltaMask,
|
||||
deltaShift, valueAdd, addRelocations, monitor);
|
||||
}
|
||||
|
||||
fixedAddressCount += unchainedLocList.size();
|
||||
|
||||
createChainPointers(program, unchainedLocList, monitor);
|
||||
}
|
||||
|
||||
log.appendMsg("Fixed " + fixedAddressCount + " chained pointers.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes up any chained pointers, starting at the given address.
|
||||
*
|
||||
* @param unchainedLocList list of locations that were unchained
|
||||
* @param page within data pages that has pointers to be unchained
|
||||
* @param nextOff offset within the page that is the chain start
|
||||
* @param deltaMask delta offset mask for each value
|
||||
* @param deltaShift shift needed for the deltaMask to extract the next offset
|
||||
* @param valueAdd value to be added to each chain pointer
|
||||
*
|
||||
* @throws MemoryAccessException IO problem reading file
|
||||
* @throws CancelledException user cancels
|
||||
*/
|
||||
private List<Address> processPointerChain4(Program program, long page, long nextOff,
|
||||
long deltaMask, long deltaShift, long valueAdd, boolean addRelocations,
|
||||
TaskMonitor monitor) throws MemoryAccessException, CancelledException {
|
||||
|
||||
// TODO: should the image base be used to perform the ASLR slide on the pointers.
|
||||
// currently image is kept at it's initial location with no ASLR.
|
||||
Address chainStart = program.getLanguage().getDefaultSpace().getAddress(page);
|
||||
Memory memory = program.getMemory();
|
||||
|
||||
List<Address> unchainedLocList = new ArrayList<Address>(1024);
|
||||
|
||||
byte origBytes[] = new byte[4];
|
||||
|
||||
int valueMask = 0xffffffff >>> (32 - deltaShift);
|
||||
|
||||
long delta = -1;
|
||||
while (delta != 0) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
Address chainLoc = chainStart.add(nextOff);
|
||||
int chainValue = memory.getInt(chainLoc);
|
||||
|
||||
delta = (chainValue & deltaMask) >> deltaShift;
|
||||
chainValue = chainValue & valueMask;
|
||||
if ((chainValue & 0xFFFF8000) == 0) {
|
||||
// small positive non-pointer, use as-is
|
||||
}
|
||||
else if ((chainValue & 0x3FFF8000) == 0x3FFF8000) {
|
||||
chainValue |= 0xC0000000;
|
||||
}
|
||||
else {
|
||||
chainValue += valueAdd;
|
||||
// chainValue += slideAmount - if we were sliding
|
||||
}
|
||||
|
||||
if (addRelocations) {
|
||||
addRelocationTableEntry(program, chainLoc, 4, chainValue, origBytes, null);
|
||||
}
|
||||
|
||||
memory.setInt(chainLoc, chainValue);
|
||||
|
||||
// delay creating data until after memory has been changed
|
||||
unchainedLocList.add(chainLoc);
|
||||
|
||||
nextOff += (delta * 4);
|
||||
}
|
||||
|
||||
return unchainedLocList;
|
||||
}
|
||||
}
|
|
@ -16,23 +16,85 @@
|
|||
package ghidra.app.util.bin.format.macho.dyld;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.macho.MachConstants;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Class for representing the common components of the various dyld_cache_slide_info structures.
|
||||
* The intent is for the the full dyld_cache_slide_info structures to extend this and add their
|
||||
* specific parts.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/dyld3/shared-cache/dyld_cache_format.h.auto.html">dyld3/shared-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
public class DyldCacheSlideInfoCommon implements StructConverter {
|
||||
public abstract class DyldCacheSlideInfoCommon implements StructConverter {
|
||||
|
||||
public static final int DATA_PAGE_MAP_ENTRY = 1;
|
||||
public static final int BYTES_PER_CHAIN_OFFSET = 4;
|
||||
public static final int CHAIN_OFFSET_MASK = 0x3fff;
|
||||
|
||||
/**
|
||||
* Parses the slide info
|
||||
*
|
||||
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info
|
||||
* @param slideInfoOffset The offset of the slide info to parse
|
||||
* @param log The log
|
||||
* @param monitor A cancelable task monitor
|
||||
* @return The slide info object
|
||||
*/
|
||||
public static DyldCacheSlideInfoCommon parseSlideInfo(BinaryReader reader, long slideInfoOffset,
|
||||
MessageLog log, TaskMonitor monitor) {
|
||||
if (slideInfoOffset == 0) {
|
||||
return null;
|
||||
}
|
||||
DyldCacheSlideInfoCommon returnedSlideInfo = null;
|
||||
|
||||
monitor.setMessage("Parsing DYLD slide info...");
|
||||
monitor.initialize(1);
|
||||
try {
|
||||
reader.setPointerIndex(slideInfoOffset);
|
||||
int version = reader.readNextInt();
|
||||
reader.setPointerIndex(slideInfoOffset);
|
||||
switch (version) {
|
||||
case 1:
|
||||
returnedSlideInfo = new DyldCacheSlideInfo1(reader);
|
||||
break;
|
||||
case 2:
|
||||
returnedSlideInfo = new DyldCacheSlideInfo2(reader);
|
||||
break;
|
||||
case 3:
|
||||
returnedSlideInfo = new DyldCacheSlideInfo3(reader);
|
||||
break;
|
||||
case 4:
|
||||
returnedSlideInfo = new DyldCacheSlideInfo4(reader);
|
||||
break;
|
||||
default:
|
||||
throw new IOException();
|
||||
}
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
|
||||
"Failed to parse dyld_cache_slide_info.");
|
||||
return null;
|
||||
}
|
||||
returnedSlideInfo.slideInfoOffset = slideInfoOffset;
|
||||
return returnedSlideInfo;
|
||||
}
|
||||
|
||||
protected int version;
|
||||
protected long slideInfoOffset;
|
||||
|
||||
/**
|
||||
* Create a new {@link DyldCacheSlideInfoCommon}.
|
||||
|
@ -53,6 +115,54 @@ public class DyldCacheSlideInfoCommon implements StructConverter {
|
|||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original slide info offset
|
||||
*
|
||||
* @return the original slide info offset
|
||||
*/
|
||||
public long getSlideInfoOffset() {
|
||||
return slideInfoOffset;
|
||||
}
|
||||
|
||||
public abstract void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
|
||||
boolean addRelocations, MessageLog log, TaskMonitor monitor)
|
||||
throws MemoryAccessException, CancelledException;
|
||||
|
||||
protected void addRelocationTableEntry(Program program, Address chainLoc, int type,
|
||||
long chainValue, byte[] origBytes, String name) throws MemoryAccessException {
|
||||
// Add entry to relocation table for the pointer fixup
|
||||
program.getMemory().getBytes(chainLoc, origBytes);
|
||||
program.getRelocationTable()
|
||||
.add(chainLoc, type, new long[] { chainValue }, origBytes, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create pointers at each fixed chain location.
|
||||
*
|
||||
* @param program The program
|
||||
* @param unchainedLocList Address list of fixed pointer locations
|
||||
* @param monitor A cancelable task monitor
|
||||
*
|
||||
* @throws CancelledException if the user cancels
|
||||
*/
|
||||
protected void createChainPointers(Program program, List<Address> unchainedLocList,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
int numFixedLocations = unchainedLocList.size();
|
||||
|
||||
monitor.setMessage("Fixed " + numFixedLocations + " chained pointers. Creating Pointers");
|
||||
|
||||
// Create pointers at any fixed-up addresses
|
||||
for (Address addr : unchainedLocList) {
|
||||
monitor.checkCanceled();
|
||||
try {
|
||||
program.getListing().createData(addr, Pointer64DataType.dataType);
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
// No worries, something presumably more important was there already
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType("dyld_cache_slide_info", 0);
|
||||
|
|
|
@ -0,0 +1,405 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.macho.dyld;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
|
||||
/**
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-852.2/include/mach-o/fixup-chains.h.auto.html">mach-o/fixup-chains.h</a>
|
||||
*/
|
||||
public class DyldChainedPtr {
|
||||
|
||||
public enum DyldChainType {
|
||||
DYLD_CHAINED_PTR_ARM64E(1), // stride 8, unauth target is vmaddr
|
||||
DYLD_CHAINED_PTR_64(2), // target is vmaddr
|
||||
DYLD_CHAINED_PTR_32(3),
|
||||
DYLD_CHAINED_PTR_32_CACHE(4),
|
||||
DYLD_CHAINED_PTR_32_FIRMWARE(5),
|
||||
DYLD_CHAINED_PTR_64_OFFSET(6), // target is vm offset
|
||||
DYLD_CHAINED_PTR_ARM64E_KERNEL(7), // stride 4, unauth target is vm offset
|
||||
DYLD_CHAINED_PTR_64_KERNEL_CACHE(8),
|
||||
DYLD_CHAINED_PTR_ARM64E_USERLAND(9), // stride 8, unauth target is vm offset
|
||||
DYLD_CHAINED_PTR_ARM64E_FIRMWARE(10), // stride 4, unauth target is vmaddr
|
||||
DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE(11), // stride 1, x86_64 kernel caches
|
||||
DYLD_CHAINED_PTR_ARM64E_USERLAND24(12), // stride 8, unauth target is vm offset, 24-bit bind
|
||||
DYLD_CHAINED_PTR_TYPE_UNKNOWN(-1);
|
||||
|
||||
private final int val;
|
||||
private final String name;
|
||||
|
||||
private DyldChainType(int v) {
|
||||
val = v;
|
||||
name = this.name().substring("DYLD_CHAINED_".length());
|
||||
}
|
||||
|
||||
public static DyldChainType lookupChainPtr(int val) {
|
||||
switch (val) {
|
||||
case 1:
|
||||
return DYLD_CHAINED_PTR_ARM64E;
|
||||
case 2:
|
||||
return DYLD_CHAINED_PTR_64;
|
||||
case 3:
|
||||
return DYLD_CHAINED_PTR_32;
|
||||
case 4:
|
||||
return DYLD_CHAINED_PTR_32_CACHE;
|
||||
case 5:
|
||||
return DYLD_CHAINED_PTR_32_FIRMWARE;
|
||||
case 6:
|
||||
return DYLD_CHAINED_PTR_64_OFFSET;
|
||||
case 7:
|
||||
return DYLD_CHAINED_PTR_ARM64E_KERNEL;
|
||||
case 8:
|
||||
return DYLD_CHAINED_PTR_64_KERNEL_CACHE;
|
||||
case 9:
|
||||
return DYLD_CHAINED_PTR_ARM64E_USERLAND;
|
||||
case 10:
|
||||
return DYLD_CHAINED_PTR_ARM64E_FIRMWARE;
|
||||
case 11:
|
||||
return DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE;
|
||||
case 12:
|
||||
return DYLD_CHAINED_PTR_ARM64E_USERLAND24;
|
||||
}
|
||||
return DYLD_CHAINED_PTR_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return val;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
public static final int DYLD_CHAINED_PTR_START_NONE = 0xFFFF;
|
||||
public static final int DYLD_CHAINED_PTR_START_MULTI = 0x8000;
|
||||
public static final int DYLD_CHAINED_PTR_START_LAST = 0x8000;
|
||||
|
||||
public static long getStride(DyldChainType ptrFormat) {
|
||||
switch (ptrFormat) {
|
||||
case DYLD_CHAINED_PTR_ARM64E:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
|
||||
return 8;
|
||||
case DYLD_CHAINED_PTR_64:
|
||||
case DYLD_CHAINED_PTR_64_OFFSET:
|
||||
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
|
||||
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
|
||||
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
|
||||
case DYLD_CHAINED_PTR_32:
|
||||
case DYLD_CHAINED_PTR_32_CACHE:
|
||||
case DYLD_CHAINED_PTR_32_FIRMWARE:
|
||||
return 4;
|
||||
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
|
||||
return 1;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setChainValue(Memory memory, Address chainLoc, DyldChainType ptrFormat,
|
||||
long value) throws MemoryAccessException {
|
||||
switch (ptrFormat) {
|
||||
case DYLD_CHAINED_PTR_ARM64E:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
|
||||
case DYLD_CHAINED_PTR_64:
|
||||
case DYLD_CHAINED_PTR_64_OFFSET:
|
||||
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
|
||||
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
|
||||
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
|
||||
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
|
||||
memory.setLong(chainLoc, value);
|
||||
break;
|
||||
|
||||
case DYLD_CHAINED_PTR_32:
|
||||
case DYLD_CHAINED_PTR_32_CACHE:
|
||||
case DYLD_CHAINED_PTR_32_FIRMWARE:
|
||||
memory.setInt(chainLoc, (int) (value & 0xFFFFFFFFL));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static long getChainValue(Memory memory, Address chainLoc, DyldChainType ptrFormat)
|
||||
throws MemoryAccessException {
|
||||
switch (ptrFormat) {
|
||||
case DYLD_CHAINED_PTR_ARM64E:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
|
||||
case DYLD_CHAINED_PTR_64:
|
||||
case DYLD_CHAINED_PTR_64_OFFSET:
|
||||
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
|
||||
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
|
||||
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
|
||||
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
|
||||
return memory.getLong(chainLoc);
|
||||
|
||||
case DYLD_CHAINED_PTR_32:
|
||||
case DYLD_CHAINED_PTR_32_CACHE:
|
||||
case DYLD_CHAINED_PTR_32_FIRMWARE:
|
||||
return memory.getInt(chainLoc) & 0xFFFFFFFFL;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isBound(DyldChainType ptrFormat, long chainValue) {
|
||||
|
||||
switch (ptrFormat) {
|
||||
case DYLD_CHAINED_PTR_ARM64E:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
|
||||
return ((chainValue >>> 62) & 1) != 0;
|
||||
|
||||
case DYLD_CHAINED_PTR_64:
|
||||
case DYLD_CHAINED_PTR_64_OFFSET:
|
||||
return ((chainValue >>> 63) & 1) != 0;
|
||||
|
||||
case DYLD_CHAINED_PTR_32:
|
||||
return ((chainValue >>> 31) & 1) != 0;
|
||||
|
||||
// Never bound
|
||||
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
|
||||
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
|
||||
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
|
||||
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
|
||||
case DYLD_CHAINED_PTR_32_CACHE:
|
||||
case DYLD_CHAINED_PTR_32_FIRMWARE:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isAuthenticated(DyldChainType ptrFormat, long chainValue) {
|
||||
switch (ptrFormat) {
|
||||
case DYLD_CHAINED_PTR_64:
|
||||
case DYLD_CHAINED_PTR_64_OFFSET:
|
||||
case DYLD_CHAINED_PTR_32:
|
||||
case DYLD_CHAINED_PTR_32_CACHE:
|
||||
case DYLD_CHAINED_PTR_32_FIRMWARE:
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
boolean isAuthenticated = ((chainValue >>> 63) & 1) != 0;
|
||||
|
||||
return isAuthenticated;
|
||||
}
|
||||
|
||||
public static long getDiversity(DyldChainType ptrFormat, long chainValue) {
|
||||
if (!isAuthenticated(ptrFormat, chainValue)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long diversityData = (chainValue >> 32L) & 0xFFFFL;
|
||||
|
||||
return diversityData;
|
||||
}
|
||||
|
||||
public static boolean hasAddrDiversity(DyldChainType ptrFormat, long chainValue) {
|
||||
if (!isAuthenticated(ptrFormat, chainValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((chainValue >> 48L) & 0x1L) == 1;
|
||||
}
|
||||
|
||||
public static long getKey(DyldChainType ptrFormat, long chainValue) {
|
||||
if (!isAuthenticated(ptrFormat, chainValue)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (chainValue >> 49L) & 0x3L;
|
||||
}
|
||||
|
||||
public static long getTarget(DyldChainType ptrFormat, long chainValue) {
|
||||
|
||||
long target = 0;
|
||||
if (isBound(ptrFormat, chainValue)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (isAuthenticated(ptrFormat, chainValue)) {
|
||||
switch (ptrFormat) {
|
||||
case DYLD_CHAINED_PTR_ARM64E:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
|
||||
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
|
||||
return chainValue & 0x000000FFFFFFFFL;
|
||||
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
|
||||
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
|
||||
return (chainValue & 0x3FFFFFFFL); // 30 bits
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (ptrFormat) {
|
||||
case DYLD_CHAINED_PTR_ARM64E:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
|
||||
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
|
||||
long top8Bits = (chainValue >> 43) & 0xFFL;
|
||||
long bottom43Bits = chainValue & 0x000007FFFFFFFFFFL;
|
||||
// Hack! Top bits don't matter and are a pointer tag
|
||||
if (top8Bits == 0x80) {
|
||||
top8Bits = 0;
|
||||
}
|
||||
target = (top8Bits << 56) | bottom43Bits;
|
||||
break;
|
||||
|
||||
case DYLD_CHAINED_PTR_64:
|
||||
case DYLD_CHAINED_PTR_64_OFFSET:
|
||||
top8Bits = (chainValue >> 36) & 0xFFL;
|
||||
long bottom36Bits = chainValue & 0x00000FFFFFFFFFL;
|
||||
// Hack! Top bits don't matter and are a pointer tag
|
||||
if (top8Bits == 0x80) {
|
||||
top8Bits = 0;
|
||||
}
|
||||
target = (top8Bits << 56) | bottom36Bits;
|
||||
break;
|
||||
|
||||
case DYLD_CHAINED_PTR_32:
|
||||
target = (chainValue & 0x3FFFFFL); // 26 bits
|
||||
break;
|
||||
|
||||
case DYLD_CHAINED_PTR_32_CACHE:
|
||||
target = (chainValue & 0x3FFFFFFFL); // 30 bits
|
||||
break;
|
||||
|
||||
case DYLD_CHAINED_PTR_32_FIRMWARE:
|
||||
target = (chainValue & 0x3FFFFFL); // 26 bits
|
||||
break;
|
||||
|
||||
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
|
||||
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
|
||||
target = (chainValue & 0x3FFFFFFFL); // 30 bits
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
public static long getAddend(DyldChainType ptrFormat, long chainValue) {
|
||||
|
||||
if (!isBound(ptrFormat, chainValue)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (ptrFormat) {
|
||||
case DYLD_CHAINED_PTR_ARM64E:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
|
||||
long addend = (chainValue >> 32L) & 0x7FFFFL;
|
||||
addend = ((addend & 0x40000) != 0 ? (addend | 0xFFFFFFFFFFFC0000L) : addend);
|
||||
return addend;
|
||||
|
||||
case DYLD_CHAINED_PTR_64:
|
||||
case DYLD_CHAINED_PTR_64_OFFSET:
|
||||
return (chainValue >> 24) & 0xFFL;
|
||||
|
||||
case DYLD_CHAINED_PTR_32:
|
||||
return (chainValue >> 20) & 0x3FL; // 6 bits
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static long getOrdinal(DyldChainType ptrFormat, long chainValue) {
|
||||
|
||||
long ordinal = -1;
|
||||
if (!isBound(ptrFormat, chainValue)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (ptrFormat) {
|
||||
case DYLD_CHAINED_PTR_ARM64E:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
|
||||
ordinal = (int) (chainValue & 0xFFFFL);
|
||||
break;
|
||||
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
|
||||
case DYLD_CHAINED_PTR_64:
|
||||
case DYLD_CHAINED_PTR_64_OFFSET:
|
||||
ordinal = (int) (chainValue & 0xFFFFFFL);
|
||||
break;
|
||||
|
||||
case DYLD_CHAINED_PTR_32:
|
||||
ordinal = (int) (chainValue & 0xFFFFFL);
|
||||
break;
|
||||
|
||||
// Never Ordinal
|
||||
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
|
||||
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
|
||||
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
|
||||
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
|
||||
case DYLD_CHAINED_PTR_32_CACHE:
|
||||
case DYLD_CHAINED_PTR_32_FIRMWARE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ordinal;
|
||||
}
|
||||
|
||||
public static long getNext(DyldChainType ptrFormat, long chainValue) {
|
||||
|
||||
long next = 1;
|
||||
|
||||
switch (ptrFormat) {
|
||||
case DYLD_CHAINED_PTR_ARM64E:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
|
||||
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
|
||||
next = (chainValue & (0x7FFL << 51L)) >> 51L; // 11-bits
|
||||
break;
|
||||
case DYLD_CHAINED_PTR_64:
|
||||
case DYLD_CHAINED_PTR_64_OFFSET:
|
||||
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
|
||||
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
|
||||
next = (chainValue & (0xFFFL << 52L)) >> 52L; // 12 bits
|
||||
break;
|
||||
|
||||
case DYLD_CHAINED_PTR_32:
|
||||
next = (chainValue & (0x1FL << 26L)) >> 26L; // 5 bits
|
||||
break;
|
||||
|
||||
// Never bound
|
||||
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
|
||||
next = 0;
|
||||
|
||||
case DYLD_CHAINED_PTR_32_CACHE:
|
||||
next = (chainValue & (0x3L << 30L)) >> 30L; // 2 bits
|
||||
break;
|
||||
|
||||
case DYLD_CHAINED_PTR_32_FIRMWARE:
|
||||
next = (chainValue & (0x3FL << 26L)) >> 26L; // 6 bits
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
}
|
|
@ -74,24 +74,47 @@ public class PrelinkParser {
|
|||
break;
|
||||
}
|
||||
Element element = (Element) iterator.next();
|
||||
if (element.getName().equals(TAG_KEY)) {
|
||||
String value = element.getValue();
|
||||
if (value.equals(PrelinkConstants.kPrelinkPersonalitiesKey)) {
|
||||
Element arrayElement = (Element) iterator.next();
|
||||
if (arrayElement.getChildren().size() == 0) {
|
||||
//should be empty...
|
||||
}
|
||||
}
|
||||
else if (value.equals(PrelinkConstants.kPrelinkInfoDictionaryKey)) {
|
||||
Element arrayElement = (Element) iterator.next();
|
||||
process(arrayElement.getChildren(), list, monitor);
|
||||
}
|
||||
if (element.getName().equals(TAG_DICT)) {
|
||||
// top level is <dict> entry
|
||||
processTopDict(monitor, list, element);
|
||||
}
|
||||
else if (element.getName().equals(TAG_KEY)) {
|
||||
processKey(monitor, list, iterator, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private void processTopDict(TaskMonitor monitor, List<PrelinkMap> list,
|
||||
Element dictRootElement) {
|
||||
Iterator<?> iterator = dictRootElement.getChildren().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (monitor.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
Element element = (Element) iterator.next();
|
||||
if (element.getName().equals(TAG_KEY)) {
|
||||
processKey(monitor, list, iterator, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processKey(TaskMonitor monitor, List<PrelinkMap> list, Iterator<?> iterator,
|
||||
Element element) {
|
||||
String value = element.getValue();
|
||||
if (value.equals(PrelinkConstants.kPrelinkPersonalitiesKey)) {
|
||||
Element arrayElement = (Element) iterator.next();
|
||||
if (arrayElement.getChildren().size() == 0) {
|
||||
//should be empty...
|
||||
}
|
||||
}
|
||||
else if (value.equals(PrelinkConstants.kPrelinkInfoDictionaryKey)) {
|
||||
Element arrayElement = (Element) iterator.next();
|
||||
process(arrayElement.getChildren(), list, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
private void process(List<?> children, List<PrelinkMap> list, TaskMonitor monitor) {
|
||||
monitor.setMessage("Processing prelink information...");
|
||||
|
@ -250,6 +273,15 @@ public class PrelinkParser {
|
|||
if (trimmed.endsWith("</4.2</shoneOS<")) {//this is a wank-around the malformed XML found in 4.2.x firmwares
|
||||
trimmed = trimmed.substring(0, trimmed.length() - 15) + "</array></dict>";
|
||||
}
|
||||
|
||||
int doctypeIndex = trimmed.indexOf("<!DOCTYPE");
|
||||
if (doctypeIndex >=0) {
|
||||
int endOfDoctype = trimmed.indexOf('>', doctypeIndex);
|
||||
if (endOfDoctype >=0) {
|
||||
trimmed = trimmed.substring(0,doctypeIndex) + trimmed.substring(endOfDoctype+1);
|
||||
}
|
||||
}
|
||||
// <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
|
||||
debug(bytes);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -31,13 +31,12 @@ import ghidra.app.util.importer.MessageLogContinuesFactory;
|
|||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.data.Pointer64DataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.symbol.SymbolUtilities;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
|
@ -45,12 +44,6 @@ import ghidra.util.task.TaskMonitor;
|
|||
*/
|
||||
public class DyldCacheProgramBuilder extends MachoProgramBuilder {
|
||||
|
||||
private static final int DATA_PAGE_MAP_ENTRY = 1;
|
||||
private static final int BYTES_PER_CHAIN_OFFSET = 4;
|
||||
private static final int CHAIN_OFFSET_MASK = 0x3fff;
|
||||
private static final int DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE = 0x4000;
|
||||
private static final int DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA = 0x8000;
|
||||
|
||||
protected DyldCacheHeader dyldCacheHeader;
|
||||
private boolean shouldProcessSymbols;
|
||||
private boolean shouldCreateDylibSections;
|
||||
|
@ -116,7 +109,6 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
|
|||
markupHeaders();
|
||||
markupBranchIslands();
|
||||
createSymbols();
|
||||
|
||||
processDylibs();
|
||||
}
|
||||
|
||||
|
@ -215,9 +207,10 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
|
|||
for (NList nlist : localSymbolsInfo.getNList()) {
|
||||
if (!nlist.getString().trim().isEmpty()) {
|
||||
try {
|
||||
program.getSymbolTable().createLabel(space.getAddress(nlist.getValue()),
|
||||
SymbolUtilities.replaceInvalidChars(nlist.getString(), true),
|
||||
program.getGlobalNamespace(), SourceType.IMPORTED);
|
||||
program.getSymbolTable()
|
||||
.createLabel(space.getAddress(nlist.getValue()),
|
||||
SymbolUtilities.replaceInvalidChars(nlist.getString(), true),
|
||||
program.getGlobalNamespace(), SourceType.IMPORTED);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.appendMsg(e.getMessage() + " " + nlist.getString());
|
||||
|
@ -236,135 +229,13 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
|
|||
* @throws CancelledException if user cancels
|
||||
*/
|
||||
private void fixPageChains() throws MemoryAccessException, CancelledException {
|
||||
long fixedAddressCount = 0;
|
||||
|
||||
// locate slide Info
|
||||
DyldCacheSlideInfoCommon slideInfo = dyldCacheHeader.getSlideInfo();
|
||||
if (slideInfo == null || !(slideInfo instanceof DyldCacheSlideInfo2)) {
|
||||
log.appendMsg("No compatible slide info version");
|
||||
return;
|
||||
}
|
||||
DyldCacheSlideInfo2 slideInfo2 = (DyldCacheSlideInfo2) slideInfo;
|
||||
List<DyldCacheSlideInfoCommon> slideInfos = dyldCacheHeader.getSlideInfos();
|
||||
for (DyldCacheSlideInfoCommon info : slideInfos) {
|
||||
int version = info.getVersion();
|
||||
|
||||
List<DyldCacheMappingInfo> mappingInfos = dyldCacheHeader.getMappingInfos();
|
||||
DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY);
|
||||
long dataPageStart = dyldCacheMappingInfo.getAddress();
|
||||
long pageSize = slideInfo2.getPageSize();
|
||||
long pageStartsCount = slideInfo2.getPageStartsCount();
|
||||
|
||||
long deltaMask = slideInfo2.getDeltaMask();
|
||||
long deltaShift = Long.numberOfTrailingZeros(deltaMask);
|
||||
long valueAdd = slideInfo2.getValueAdd();
|
||||
|
||||
short[] pageEntries = slideInfo2.getPageStartsEntries();
|
||||
short[] extraEntries = slideInfo2.getPageExtrasEntries();
|
||||
|
||||
monitor.setMessage("Fixing chained data page pointers...");
|
||||
|
||||
monitor.setMaximum(pageStartsCount);
|
||||
for (int index = 0; index < pageStartsCount; index++) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
long page = dataPageStart + (pageSize * index);
|
||||
|
||||
monitor.setProgress(index);
|
||||
|
||||
int pageEntry = pageEntries[index] & 0xffff;
|
||||
if (pageEntry == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Address> unchainedLocList = new ArrayList<>(1024);
|
||||
|
||||
if ((pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) != 0) {
|
||||
// go into extras and process list of chain entries for the same page
|
||||
int extraIndex = (pageEntry & CHAIN_OFFSET_MASK);
|
||||
do {
|
||||
pageEntry = extraEntries[extraIndex] & 0xffff;
|
||||
long pageOffset = (pageEntry & CHAIN_OFFSET_MASK) * BYTES_PER_CHAIN_OFFSET;
|
||||
|
||||
processPointerChain(unchainedLocList, page, pageOffset, deltaMask, deltaShift,
|
||||
valueAdd);
|
||||
extraIndex++;
|
||||
}
|
||||
while ((pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0);
|
||||
}
|
||||
else {
|
||||
long pageOffset = pageEntry * BYTES_PER_CHAIN_OFFSET;
|
||||
|
||||
processPointerChain(unchainedLocList, page, pageOffset, deltaMask, deltaShift,
|
||||
valueAdd);
|
||||
}
|
||||
|
||||
fixedAddressCount += unchainedLocList.size();
|
||||
unchainedLocList.forEach(entry -> {
|
||||
// create a pointer at the fixed up chain pointer location
|
||||
try {
|
||||
// don't use data utilities. does too much extra checking work
|
||||
listing.createData(entry, Pointer64DataType.dataType);
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
// No worries, something presumably more important was there already
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
log.appendMsg("Fixed " + fixedAddressCount + " chained pointers. Creating Pointers");
|
||||
|
||||
monitor.setMessage("Created " + fixedAddressCount + " chained pointers");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes up any chained pointers, starting at the given address.
|
||||
*
|
||||
* @param unchainedLocList list of locations that were unchained
|
||||
* @param page within data pages that has pointers to be unchained
|
||||
* @param nextOff offset within the page that is the chain start
|
||||
* @param deltaMask delta offset mask for each value
|
||||
* @param deltaShift shift needed for the deltaMask to extract the next offset
|
||||
* @param valueAdd value to be added to each chain pointer
|
||||
*
|
||||
* @throws MemoryAccessException IO problem reading file
|
||||
* @throws CancelledException user cancels
|
||||
*/
|
||||
private void processPointerChain(List<Address> unchainedLocList, long page, long nextOff,
|
||||
long deltaMask, long deltaShift, long valueAdd)
|
||||
throws MemoryAccessException, CancelledException {
|
||||
|
||||
// TODO: should the image base be used to perform the ASLR slide on the pointers.
|
||||
// currently image is kept at it's initial location with no ASLR.
|
||||
Address chainStart = memory.getProgram().getLanguage().getDefaultSpace().getAddress(page);
|
||||
|
||||
byte origBytes[] = new byte[8];
|
||||
|
||||
long valueMask = 0xffffffffffffffffL >>> (64 - deltaShift);
|
||||
|
||||
long delta = -1;
|
||||
while (delta != 0) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
Address chainLoc = chainStart.add(nextOff);
|
||||
long chainValue = memory.getLong(chainLoc);
|
||||
|
||||
delta = (chainValue & deltaMask) >> deltaShift;
|
||||
chainValue = chainValue & valueMask;
|
||||
if (chainValue != 0) {
|
||||
chainValue += valueAdd;
|
||||
}
|
||||
|
||||
if (shouldAddRelocationEntries) {
|
||||
// Add entry to relocation table for the pointer fixup
|
||||
memory.getBytes(chainLoc, origBytes);
|
||||
program.getRelocationTable().add(chainLoc, 1, new long[] { chainValue }, origBytes,
|
||||
null);
|
||||
}
|
||||
|
||||
memory.setLong(chainLoc, chainValue);
|
||||
|
||||
// delay creating data until after memory has been changed
|
||||
unchainedLocList.add(chainLoc);
|
||||
|
||||
nextOff += (delta * 4);
|
||||
log.appendMsg("Fixing page chains version: " + version);
|
||||
info.fixPageChains(program, dyldCacheHeader, shouldAddRelocationEntries, log, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -406,7 +277,12 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
|
|||
DyldCacheMachoInfo curr = iter.next();
|
||||
do {
|
||||
DyldCacheMachoInfo next = iter.hasNext() ? iter.next() : null;
|
||||
curr.addToProgramTree(next);
|
||||
try {
|
||||
curr.addToProgramTree(next);
|
||||
}
|
||||
catch (DuplicateNameException exc) {
|
||||
log.appendException(exc);
|
||||
}
|
||||
curr = next;
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
|
|
|
@ -20,8 +20,7 @@ import java.io.IOException;
|
|||
import java.util.*;
|
||||
|
||||
import generic.continues.RethrowContinuesFactory;
|
||||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.*;
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.format.macho.*;
|
||||
import ghidra.app.util.bin.format.macho.prelink.PrelinkMap;
|
||||
|
@ -42,6 +41,13 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
public final static String MACH_O_NAME = "Mac OS X Mach-O";
|
||||
private static final long MIN_BYTE_LENGTH = 4;
|
||||
|
||||
/** Loader option to add relocation entries for each fixed chain pointer */
|
||||
static final String ADD_RELOCATION_ENTRIES_OPTION_NAME =
|
||||
"Add relocation entries for fixed chain pointers";
|
||||
|
||||
/** Default value for loader option add relocation entries */
|
||||
static final boolean ADD_RELOCATION_ENTRIES_OPTION_DEFAULT = true;
|
||||
|
||||
@Override
|
||||
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
|
||||
List<LoadSpec> loadSpecs = new ArrayList<>();
|
||||
|
@ -83,16 +89,22 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
try {
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||
|
||||
if (MachoPrelinkUtils.hasChainedLoadCommand(provider, monitor)) {
|
||||
MachoPrelinkProgramBuilder.buildProgram(program, provider, fileBytes,
|
||||
Collections.emptyList(), shouldAddRelocationEntries(options), log, monitor);
|
||||
return;
|
||||
}
|
||||
|
||||
// A Mach-O file may contain PRELINK information. If so, we use a special
|
||||
// program builder that knows how to deal with it.
|
||||
List<PrelinkMap> prelinkList = MachoPrelinkUtils.parsePrelinkXml(provider, monitor);
|
||||
if (!prelinkList.isEmpty()) {
|
||||
MachoPrelinkProgramBuilder.buildProgram(program, provider, fileBytes, prelinkList,
|
||||
log, monitor);
|
||||
}
|
||||
else {
|
||||
MachoProgramBuilder.buildProgram(program, provider, fileBytes, log, monitor);
|
||||
shouldAddRelocationEntries(options), log, monitor);
|
||||
return;
|
||||
}
|
||||
|
||||
MachoProgramBuilder.buildProgram(program, provider, fileBytes, log, monitor);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw e;
|
||||
|
@ -107,6 +119,11 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||
return MACH_O_NAME;
|
||||
}
|
||||
|
||||
private boolean shouldAddRelocationEntries(List<Option> options) {
|
||||
return OptionUtils.getOption(ADD_RELOCATION_ENTRIES_OPTION_NAME, options,
|
||||
ADD_RELOCATION_ENTRIES_OPTION_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the default implementation to account for Universal Binary (UBI) files.
|
||||
* These must be specially parsed to find the internal file matching the current architecture.
|
||||
|
|
|
@ -15,27 +15,30 @@
|
|||
*/
|
||||
package ghidra.app.util.opinion;
|
||||
|
||||
import static ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.collections4.BidiMap;
|
||||
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.format.macho.MachHeader;
|
||||
import ghidra.app.util.bin.format.macho.Section;
|
||||
import ghidra.app.util.bin.format.macho.commands.SegmentNames;
|
||||
import ghidra.app.util.bin.format.macho.*;
|
||||
import ghidra.app.util.bin.format.macho.commands.*;
|
||||
import ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr;
|
||||
import ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr.DyldChainType;
|
||||
import ghidra.app.util.bin.format.macho.prelink.PrelinkMap;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.importer.MessageLogContinuesFactory;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataUtilities;
|
||||
import ghidra.program.model.data.Pointer64DataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
|
@ -45,6 +48,8 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
|
|||
|
||||
private List<PrelinkMap> prelinkList;
|
||||
|
||||
private boolean shouldAddRelocationEntries;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MachoPrelinkProgramBuilder} based on the given information.
|
||||
*
|
||||
|
@ -52,14 +57,16 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
|
|||
* @param provider The {@link ByteProvider} that contains the Mach-O's bytes.
|
||||
* @param fileBytes Where the Mach-O's bytes came from.
|
||||
* @param prelinkList Parsed {@link PrelinkMap PRELINK} information.
|
||||
* @param shouldAddRelocationEntries true if relocation records should be created
|
||||
* @param log The log.
|
||||
* @param monitor A cancelable task monitor.
|
||||
*/
|
||||
protected MachoPrelinkProgramBuilder(Program program, ByteProvider provider,
|
||||
FileBytes fileBytes, List<PrelinkMap> prelinkList, MessageLog log,
|
||||
TaskMonitor monitor) {
|
||||
FileBytes fileBytes, List<PrelinkMap> prelinkList, boolean shouldAddRelocationEntries,
|
||||
MessageLog log, TaskMonitor monitor) {
|
||||
super(program, provider, fileBytes, log, monitor);
|
||||
this.prelinkList = prelinkList;
|
||||
this.shouldAddRelocationEntries = shouldAddRelocationEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,14 +76,16 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
|
|||
* @param provider The {@link ByteProvider} that contains the Mach-O's bytes.
|
||||
* @param fileBytes Where the Mach-O's bytes came from.
|
||||
* @param prelinkList Parsed {@link PrelinkMap PRELINK} information.
|
||||
* @param addRelocationEntries true if relocation records should be added
|
||||
* @param log The log.
|
||||
* @param monitor A cancelable task monitor.
|
||||
* @throws Exception if a problem occurs.
|
||||
*/
|
||||
public static void buildProgram(Program program, ByteProvider provider, FileBytes fileBytes,
|
||||
List<PrelinkMap> prelinkList, MessageLog log, TaskMonitor monitor) throws Exception {
|
||||
List<PrelinkMap> prelinkList, boolean addRelocationEntries, MessageLog log,
|
||||
TaskMonitor monitor) throws Exception {
|
||||
MachoPrelinkProgramBuilder machoPrelinkProgramBuilder = new MachoPrelinkProgramBuilder(
|
||||
program, provider, fileBytes, prelinkList, log, monitor);
|
||||
program, provider, fileBytes, prelinkList, addRelocationEntries, log, monitor);
|
||||
machoPrelinkProgramBuilder.build();
|
||||
}
|
||||
|
||||
|
@ -85,41 +94,91 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
|
|||
|
||||
// We want to handle the start of the Mach-O normally. It represents the System.kext.
|
||||
super.build();
|
||||
|
||||
|
||||
// fixup any slide or headers before markup or regular relocation
|
||||
fixPreLinkAddresses();
|
||||
|
||||
doRelocations();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRelocations() throws Exception {
|
||||
processDyldInfo(false);
|
||||
markupHeaders(machoHeader, setupHeaderAddr(machoHeader.getAllSegments()));
|
||||
markupSections();
|
||||
processProgramVars();
|
||||
loadSectionRelocations();
|
||||
loadExternalRelocations();
|
||||
loadLocalRelocations();
|
||||
}
|
||||
|
||||
protected void fixPreLinkAddresses() throws MemoryAccessException, CancelledException,
|
||||
Exception, IOException, MachException {
|
||||
// Fixup any chained pointers
|
||||
List<Address> fixedAddresses = fixupChainedPointers();
|
||||
|
||||
// The rest of the Mach-O's live in the memory segments that the System.kext already
|
||||
// defined. Therefore, we really just want to go through and do additional markup on them
|
||||
// since they are already loaded in.
|
||||
List<Long> machoHeaderOffsets =
|
||||
MachoPrelinkUtils.findPrelinkMachoHeaderOffsets(provider, monitor);
|
||||
if (machoHeaderOffsets.isEmpty()) {
|
||||
return;
|
||||
processPreLinkMachoInfo();
|
||||
|
||||
// Create pointers at any fixed-up addresses, that don't have header data created at them
|
||||
for (Address addr : fixedAddresses) {
|
||||
monitor.checkCanceled();
|
||||
try {
|
||||
program.getListing().createData(addr, Pointer64DataType.dataType);
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
// No worries, something presumably more important was there already
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Match PRELINK information to the Mach-O's we've found
|
||||
BidiMap<PrelinkMap, Long> map = MachoPrelinkUtils.matchPrelinkToMachoHeaderOffsets(provider,
|
||||
prelinkList, machoHeaderOffsets, monitor);
|
||||
protected void processPreLinkMachoInfo() throws Exception, IOException, MachException {
|
||||
List<PrelinkMachoInfo> prelinkMachoInfoList = new ArrayList<>();
|
||||
|
||||
// Determine the starting address of the PRELINK Mach-O's
|
||||
long prelinkStart = MachoPrelinkUtils.getPrelinkStartAddr(machoHeader);
|
||||
Address prelinkStartAddr = null;
|
||||
if (prelinkStart == 0) {
|
||||
// Probably iOS 12, which doesn't define a proper __PRELINK_TEXT segment.
|
||||
// Assume the file offset is the same as the offset from image base.
|
||||
prelinkStartAddr = program.getImageBase().add(machoHeaderOffsets.get(0));
|
||||
// if has fileSetEntryCommands, that tells where the prelinked headers are
|
||||
List<FileSetEntryCommand> fileSetEntries =
|
||||
machoHeader.getLoadCommands(FileSetEntryCommand.class);
|
||||
if (fileSetEntries != null && fileSetEntries.size() > 0) {
|
||||
for (FileSetEntryCommand fileSetEntryCommand : fileSetEntries) {
|
||||
prelinkMachoInfoList
|
||||
.add(new PrelinkMachoInfo(provider, fileSetEntryCommand.getFileOffset(),
|
||||
space.getAddress(fileSetEntryCommand.getVMaddress()),
|
||||
fileSetEntryCommand.getFileSetEntryName()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
prelinkStartAddr = space.getAddress(prelinkStart);
|
||||
}
|
||||
|
||||
// Create an "info" object for each PRELINK Mach-O, which will make processing them easier
|
||||
List<PrelinkMachoInfo> prelinkMachoInfoList = new ArrayList<>();
|
||||
for (Long machoHeaderOffset : machoHeaderOffsets) {
|
||||
prelinkMachoInfoList.add(new PrelinkMachoInfo(provider, machoHeaderOffset,
|
||||
prelinkStartAddr.add(machoHeaderOffset - machoHeaderOffsets.get(0)),
|
||||
map.getKey(machoHeaderOffset)));
|
||||
// The rest of the Mach-O's live in the memory segments that the System.kext already
|
||||
// defined. Therefore, we really just want to go through and do additional markup on them
|
||||
// since they are already loaded in.
|
||||
List<Long> machoHeaderOffsets =
|
||||
MachoPrelinkUtils.findPrelinkMachoHeaderOffsets(provider, monitor);
|
||||
if (machoHeaderOffsets.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Match PRELINK information to the Mach-O's we've found
|
||||
BidiMap<PrelinkMap, Long> map = MachoPrelinkUtils.matchPrelinkToMachoHeaderOffsets(
|
||||
provider, prelinkList, machoHeaderOffsets, monitor);
|
||||
|
||||
// Determine the starting address of the PRELINK Mach-O's
|
||||
long prelinkStart = MachoPrelinkUtils.getPrelinkStartAddr(machoHeader);
|
||||
Address prelinkStartAddr = null;
|
||||
if (prelinkStart == 0) {
|
||||
// Probably iOS 12, which doesn't define a proper __PRELINK_TEXT segment.
|
||||
// Assume the file offset is the same as the offset from image base.
|
||||
prelinkStartAddr = program.getImageBase().add(machoHeaderOffsets.get(0));
|
||||
}
|
||||
else {
|
||||
prelinkStartAddr = space.getAddress(prelinkStart);
|
||||
}
|
||||
|
||||
// Create an "info" object for each PRELINK Mach-O, which will make processing them easier
|
||||
|
||||
for (Long machoHeaderOffset : machoHeaderOffsets) {
|
||||
prelinkMachoInfoList.add(new PrelinkMachoInfo(provider, machoHeaderOffset,
|
||||
prelinkStartAddr.add(machoHeaderOffset - machoHeaderOffsets.get(0)),
|
||||
map.getKey(machoHeaderOffset)));
|
||||
}
|
||||
}
|
||||
|
||||
// Process each PRELINK Mach-O
|
||||
|
@ -137,17 +196,6 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
|
|||
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
|
||||
// Create pointers at any fixed-up addresses
|
||||
fixedAddresses.forEach(addr -> {
|
||||
try {
|
||||
DataUtilities.createData(program, addr, Pointer64DataType.dataType, -1, false,
|
||||
DataUtilities.ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA);
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
// No worries, something presumably more important was there already
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -162,18 +210,44 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
|
|||
* @return A list of addresses where pointer fixes were performed.
|
||||
* @throws MemoryAccessException if there was a problem reading/writing memory.
|
||||
*/
|
||||
private List<Address> fixupChainedPointers() throws MemoryAccessException {
|
||||
private List<Address> fixupChainedPointers() throws MemoryAccessException, CancelledException {
|
||||
|
||||
List<Address> fixedAddresses = new ArrayList<>();
|
||||
|
||||
// if has Chained Fixups load command, use it
|
||||
List<DyldChainedFixupsCommand> loadCommands =
|
||||
machoHeader.getLoadCommands(DyldChainedFixupsCommand.class);
|
||||
for (LoadCommand loadCommand : loadCommands) {
|
||||
DyldChainedFixupsCommand linkCmd = (DyldChainedFixupsCommand) loadCommand;
|
||||
|
||||
DyldChainedFixupHeader chainHeader = linkCmd.getChainHeader();
|
||||
|
||||
DyldChainedStartsInImage chainedStartsInImage = chainHeader.getChainedStartsInImage();
|
||||
|
||||
DyldChainedStartsInSegment[] chainedStarts = chainedStartsInImage.getChainedStarts();
|
||||
for (DyldChainedStartsInSegment chainStart : chainedStarts) {
|
||||
fixedAddresses.addAll(processSegmentPointerChain(chainHeader, chainStart));
|
||||
}
|
||||
log.appendMsg("Fixed up " + fixedAddresses.size() + " chained pointers.");
|
||||
}
|
||||
|
||||
// if pointer chains fixed by DyldChainedFixupsCommands, then all finished
|
||||
if (loadCommands.size() > 0) {
|
||||
return fixedAddresses;
|
||||
}
|
||||
|
||||
// if has thread_starts use to fixup chained pointers
|
||||
Section threadStarts = machoHeader.getSection(SegmentNames.SEG_TEXT, "__thread_starts");
|
||||
if (threadStarts == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
monitor.setMessage("Fixing up chained pointers...");
|
||||
Address threadSectionStart = null;
|
||||
Address threadSectionEnd = null;
|
||||
threadSectionStart = space.getAddress(threadStarts.getAddress());
|
||||
threadSectionEnd = threadSectionStart.add(threadStarts.getSize() - 1);
|
||||
|
||||
List<Address> fixedAddresses = new ArrayList<>();
|
||||
Address threadSectionStart = space.getAddress(threadStarts.getAddress());
|
||||
Address threadSectionEnd = threadSectionStart.add(threadStarts.getSize() - 1);
|
||||
monitor.setMessage("Fixing up chained pointers...");
|
||||
|
||||
long nextOffSize = (memory.getInt(threadSectionStart) & 1) * 4 + 4;
|
||||
Address chainHead = threadSectionStart.add(4);
|
||||
|
@ -193,6 +267,195 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
|
|||
return fixedAddresses;
|
||||
}
|
||||
|
||||
private List<Address> processSegmentPointerChain(DyldChainedFixupHeader chainHeader,
|
||||
DyldChainedStartsInSegment chainStart)
|
||||
throws MemoryAccessException, CancelledException {
|
||||
|
||||
List<Address> fixedAddresses = new ArrayList<Address>();
|
||||
long fixedAddressCount = 0;
|
||||
|
||||
if (chainStart.getPointerFormat() == 0) {
|
||||
return fixedAddresses;
|
||||
}
|
||||
|
||||
long dataPageStart = chainStart.getSegmentOffset();
|
||||
dataPageStart = dataPageStart + program.getImageBase().getOffset();
|
||||
long pageSize = chainStart.getPageSize();
|
||||
long pageStartsCount = chainStart.getPageCount();
|
||||
|
||||
long authValueAdd = 0;
|
||||
|
||||
short[] pageStarts = chainStart.getPage_starts();
|
||||
|
||||
short ptrFormatValue = chainStart.getPointerFormat();
|
||||
DyldChainType ptrFormat = DyldChainType.lookupChainPtr(ptrFormatValue);
|
||||
|
||||
monitor.setMessage("Fixing " + ptrFormat.getName() + " chained pointers...");
|
||||
|
||||
monitor.setMaximum(pageStartsCount);
|
||||
for (int index = 0; index < pageStartsCount; index++) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
long page = dataPageStart + (pageSize * index);
|
||||
|
||||
monitor.setProgress(index);
|
||||
|
||||
int pageEntry = pageStarts[index] & 0xffff;
|
||||
if (pageEntry == DYLD_CHAINED_PTR_START_NONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Address> unchainedLocList = new ArrayList<>(1024);
|
||||
|
||||
long pageOffset = (pageEntry / 8) * DyldChainedPtr.getStride(ptrFormat); // first entry byte based
|
||||
|
||||
switch (ptrFormat) {
|
||||
case DYLD_CHAINED_PTR_ARM64E:
|
||||
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
|
||||
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
|
||||
processPointerChain(chainHeader, unchainedLocList, ptrFormat, page, pageOffset,
|
||||
authValueAdd);
|
||||
break;
|
||||
|
||||
// These might work, but have not been fully tested!
|
||||
case DYLD_CHAINED_PTR_64:
|
||||
case DYLD_CHAINED_PTR_64_OFFSET:
|
||||
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
|
||||
case DYLD_CHAINED_PTR_32:
|
||||
case DYLD_CHAINED_PTR_32_CACHE:
|
||||
case DYLD_CHAINED_PTR_32_FIRMWARE:
|
||||
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
|
||||
processPointerChain(chainHeader, unchainedLocList, ptrFormat, page, pageOffset,
|
||||
authValueAdd);
|
||||
break;
|
||||
|
||||
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
|
||||
default:
|
||||
log.appendMsg(
|
||||
"WARNING: Pointer Chain format " + ptrFormat + " not processed yet!");
|
||||
break;
|
||||
}
|
||||
|
||||
fixedAddressCount += unchainedLocList.size();
|
||||
|
||||
fixedAddresses.addAll(unchainedLocList);
|
||||
}
|
||||
|
||||
log.appendMsg(
|
||||
"Fixed " + fixedAddressCount + " " + ptrFormat.getName() + " chained pointers.");
|
||||
|
||||
return fixedAddresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes up any chained pointers, starting at the given address.
|
||||
*
|
||||
* @param chainHeader fixup header chains
|
||||
* @param unchainedLocList list of locations that were unchained
|
||||
* @param pointerFormat format of pointers within this chain
|
||||
* @param page within data pages that has pointers to be unchained
|
||||
* @param nextOff offset within the page that is the chain start
|
||||
* @param auth_value_add value to be added to each chain pointer
|
||||
*
|
||||
* @throws MemoryAccessException IO problem reading file
|
||||
* @throws CancelledException user cancels
|
||||
*/
|
||||
private void processPointerChain(DyldChainedFixupHeader chainHeader,
|
||||
List<Address> unchainedLocList, DyldChainType pointerFormat, long page, long nextOff,
|
||||
long auth_value_add) throws MemoryAccessException, CancelledException {
|
||||
|
||||
long imageBaseOffset = program.getImageBase().getOffset();
|
||||
Address chainStart = memory.getProgram().getLanguage().getDefaultSpace().getAddress(page);
|
||||
|
||||
byte origBytes[] = new byte[8];
|
||||
|
||||
long next = -1;
|
||||
boolean start = true;
|
||||
while (next != 0) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
Address chainLoc = chainStart.add(nextOff);
|
||||
final long chainValue = DyldChainedPtr.getChainValue(memory, chainLoc, pointerFormat);
|
||||
long newChainValue = chainValue;
|
||||
|
||||
boolean isAuthenticated = DyldChainedPtr.isAuthenticated(pointerFormat, chainValue);
|
||||
boolean isBound = DyldChainedPtr.isBound(pointerFormat, chainValue);
|
||||
|
||||
String symName = null;
|
||||
|
||||
if (isAuthenticated && !isBound) {
|
||||
long offsetFromSharedCacheBase =
|
||||
DyldChainedPtr.getTarget(pointerFormat, chainValue);
|
||||
//long diversityData = DyldChainedPtr.getDiversity(pointerFormat, chainValue);
|
||||
//boolean hasAddressDiversity =
|
||||
// DyldChainedPtr.hasAddrDiversity(pointerFormat, chainValue);
|
||||
//long key = DyldChainedPtr.getKey(pointerFormat, chainValue);
|
||||
newChainValue = imageBaseOffset + offsetFromSharedCacheBase + auth_value_add;
|
||||
}
|
||||
else if (!isAuthenticated && isBound) {
|
||||
int chainOrdinal = (int) DyldChainedPtr.getOrdinal(pointerFormat, chainValue);
|
||||
long addend = DyldChainedPtr.getAddend(pointerFormat, chainValue);
|
||||
DyldChainedImports chainedImports = chainHeader.getChainedImports();
|
||||
DyldChainedImport chainedImport = chainedImports.getChainedImport(chainOrdinal);
|
||||
//int libOrdinal = chainedImport.getLibOrdinal();
|
||||
symName = chainedImport.getName();
|
||||
// lookup the symbol, and then add addend
|
||||
List<Symbol> globalSymbols = program.getSymbolTable().getGlobalSymbols(symName);
|
||||
if (globalSymbols.size() == 1) {
|
||||
newChainValue = globalSymbols.get(0).getAddress().getOffset();
|
||||
}
|
||||
newChainValue += addend;
|
||||
}
|
||||
else if (isAuthenticated && isBound) {
|
||||
int chainOrdinal = (int) DyldChainedPtr.getOrdinal(pointerFormat, chainValue);
|
||||
//long addend = DyldChainedPtr.getAddend(pointerFormat, chainValue);
|
||||
//long diversityData = DyldChainedPtr.getDiversity(pointerFormat, chainValue);
|
||||
//boolean hasAddressDiversity =
|
||||
// DyldChainedPtr.hasAddrDiversity(pointerFormat, chainValue);
|
||||
//long key = DyldChainedPtr.getKey(pointerFormat, chainValue);
|
||||
|
||||
DyldChainedImports chainedImports = chainHeader.getChainedImports();
|
||||
DyldChainedImport chainedImport = chainedImports.getChainedImport(chainOrdinal);
|
||||
symName = chainedImport.getName();
|
||||
|
||||
// lookup the symbol, and then add addend
|
||||
List<Symbol> globalSymbols = program.getSymbolTable().getGlobalSymbols(symName);
|
||||
if (globalSymbols.size() == 1) {
|
||||
newChainValue = globalSymbols.get(0).getAddress().getOffset();
|
||||
}
|
||||
newChainValue = newChainValue + auth_value_add;
|
||||
}
|
||||
else {
|
||||
newChainValue = DyldChainedPtr.getTarget(pointerFormat, chainValue);
|
||||
newChainValue += imageBaseOffset;
|
||||
}
|
||||
|
||||
if (!start || program.getRelocationTable().getRelocation(chainLoc) == null) {
|
||||
addRelocationTableEntry(chainLoc,
|
||||
(start ? 0x8000 : 0x4000) | (isAuthenticated ? 4 : 0) | (isBound ? 2 : 0) | 1,
|
||||
newChainValue, origBytes, symName);
|
||||
DyldChainedPtr.setChainValue(memory, chainLoc, pointerFormat, newChainValue);
|
||||
}
|
||||
// delay creating data until after memory has been changed
|
||||
unchainedLocList.add(chainLoc);
|
||||
|
||||
start = false;
|
||||
next = DyldChainedPtr.getNext(pointerFormat, chainValue);
|
||||
nextOff += next * DyldChainedPtr.getStride(pointerFormat);
|
||||
}
|
||||
}
|
||||
|
||||
private void addRelocationTableEntry(Address chainLoc, int type, long chainValue,
|
||||
byte[] origBytes, String name) throws MemoryAccessException {
|
||||
if (shouldAddRelocationEntries) {
|
||||
// Add entry to relocation table for the pointer fixup
|
||||
memory.getBytes(chainLoc, origBytes);
|
||||
program.getRelocationTable()
|
||||
.add(chainLoc, type, new long[] { chainValue }, origBytes, name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes up any chained pointers, starting at the given address.
|
||||
*
|
||||
|
@ -259,8 +522,9 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
|
|||
// Add entry to relocation table for the pointer fixup
|
||||
byte origBytes[] = new byte[8];
|
||||
memory.getBytes(pointerAddr, origBytes);
|
||||
program.getRelocationTable().add(pointerAddr, (int) fixedPointerType,
|
||||
new long[] { fixedPointerValue }, origBytes, null);
|
||||
program.getRelocationTable()
|
||||
.add(pointerAddr, (int) fixedPointerType, new long[] { fixedPointerValue },
|
||||
origBytes, null);
|
||||
|
||||
// Fixup the pointer
|
||||
memory.setLong(pointerAddr, fixedPointerValue);
|
||||
|
@ -286,12 +550,7 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
|
|||
*/
|
||||
public PrelinkMachoInfo(ByteProvider provider, long offset, Address headerAddr,
|
||||
PrelinkMap prelink) throws Exception {
|
||||
this.headerAddr = headerAddr;
|
||||
this.header = MachHeader.createMachHeader(MessageLogContinuesFactory.create(log),
|
||||
provider, offset);
|
||||
this.header.parse();
|
||||
this.headerAddr = headerAddr;
|
||||
this.name = "";
|
||||
this(provider, offset, headerAddr, "");
|
||||
|
||||
if (prelink != null) {
|
||||
String path = prelink.getPrelinkBundlePath();
|
||||
|
@ -301,6 +560,16 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
public PrelinkMachoInfo(ByteProvider provider, long fileOffset, Address headerAddr,
|
||||
String kextName) throws Exception {
|
||||
this.headerAddr = headerAddr;
|
||||
this.header = MachHeader.createMachHeader(MessageLogContinuesFactory.create(log),
|
||||
provider, fileOffset, false);
|
||||
this.header.parse();
|
||||
this.headerAddr = headerAddr;
|
||||
this.name = kextName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes memory blocks for this PRELINK Mach-O.
|
||||
*
|
||||
|
|
|
@ -27,8 +27,7 @@ import ghidra.app.util.bin.ByteProvider;
|
|||
import ghidra.app.util.bin.ByteProviderWrapper;
|
||||
import ghidra.app.util.bin.format.macho.MachException;
|
||||
import ghidra.app.util.bin.format.macho.MachHeader;
|
||||
import ghidra.app.util.bin.format.macho.commands.SegmentCommand;
|
||||
import ghidra.app.util.bin.format.macho.commands.SegmentNames;
|
||||
import ghidra.app.util.bin.format.macho.commands.*;
|
||||
import ghidra.app.util.bin.format.macho.prelink.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
@ -64,6 +63,33 @@ public class MachoPrelinkUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Macho has a DYLD_CHAINED_FIXUPS_COMMAND
|
||||
*
|
||||
* @param provider The provider to parse.
|
||||
* @param monitor A monitor.
|
||||
* @return A list of discovered {@link PrelinkMap}s. An empty list indicates that the provider
|
||||
* did not represent valid Mach-O PRELINK binary.
|
||||
* @throws IOException if there was an IO-related issue.
|
||||
* @throws JDOMException if there was a issue parsing the PRELINK XML.
|
||||
*/
|
||||
public static boolean hasChainedLoadCommand(ByteProvider provider, TaskMonitor monitor)
|
||||
throws IOException, JDOMException {
|
||||
|
||||
try {
|
||||
MachHeader mainHeader =
|
||||
MachHeader.createMachHeader(RethrowContinuesFactory.INSTANCE, provider);
|
||||
mainHeader.parse(); // make sure first Mach-O header is valid....
|
||||
|
||||
DyldChainedFixupsCommand cmd =
|
||||
mainHeader.getFirstLoadCommand(DyldChainedFixupsCommand.class);
|
||||
return cmd != null;
|
||||
}
|
||||
catch (MachException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the provider looking for PRELINK Mach-O headers.
|
||||
* <p>
|
||||
|
|
|
@ -100,6 +100,7 @@ public class MachoProgramBuilder {
|
|||
MachoProgramBuilder machoProgramBuilder =
|
||||
new MachoProgramBuilder(program, provider, fileBytes, log, monitor);
|
||||
machoProgramBuilder.build();
|
||||
machoProgramBuilder.doRelocations();
|
||||
}
|
||||
|
||||
protected void build() throws Exception {
|
||||
|
@ -122,7 +123,10 @@ public class MachoProgramBuilder {
|
|||
renameObjMsgSendRtpSymbol();
|
||||
processUndefinedSymbols();
|
||||
processAbsoluteSymbols();
|
||||
processDyldInfo();
|
||||
}
|
||||
|
||||
protected void doRelocations() throws Exception {
|
||||
processDyldInfo(true);
|
||||
markupHeaders(machoHeader, setupHeaderAddr(machoHeader.getAllSegments()));
|
||||
markupSections();
|
||||
processProgramVars();
|
||||
|
@ -488,8 +492,8 @@ public class MachoProgramBuilder {
|
|||
String name = generateValidName(symbol.getString());
|
||||
if (name != null && name.length() > 0) {
|
||||
try {
|
||||
program.getSymbolTable().createLabel(startAddr, name, namespace,
|
||||
SourceType.IMPORTED);
|
||||
program.getSymbolTable()
|
||||
.createLabel(startAddr, name, namespace, SourceType.IMPORTED);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.appendMsg("Unable to create indirect symbol " + name);
|
||||
|
@ -570,8 +574,9 @@ public class MachoProgramBuilder {
|
|||
symbol.setName(ObjectiveC1_Constants.OBJC_MSG_SEND_RTP_NAME, SourceType.IMPORTED);
|
||||
}
|
||||
else {
|
||||
program.getSymbolTable().createLabel(address,
|
||||
ObjectiveC1_Constants.OBJC_MSG_SEND_RTP_NAME, SourceType.IMPORTED);
|
||||
program.getSymbolTable()
|
||||
.createLabel(address, ObjectiveC1_Constants.OBJC_MSG_SEND_RTP_NAME,
|
||||
SourceType.IMPORTED);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -597,8 +602,8 @@ public class MachoProgramBuilder {
|
|||
continue;
|
||||
}
|
||||
if (symbol.isTypeUndefined()) {
|
||||
List<Symbol> globalSymbols = program.getSymbolTable().getLabelOrFunctionSymbols(
|
||||
symbol.getString(), null);
|
||||
List<Symbol> globalSymbols = program.getSymbolTable()
|
||||
.getLabelOrFunctionSymbols(symbol.getString(), null);
|
||||
if (globalSymbols.isEmpty()) {//IF IT DOES NOT ALREADY EXIST...
|
||||
undefinedSymbols.add(symbol);
|
||||
}
|
||||
|
@ -688,7 +693,7 @@ public class MachoProgramBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private void processDyldInfo() {
|
||||
protected void processDyldInfo(boolean doClassic) {
|
||||
List<DyldInfoCommand> commands = machoHeader.getLoadCommands(DyldInfoCommand.class);
|
||||
for (DyldInfoCommand command : commands) {
|
||||
if (command.getBindSize() > 0) {
|
||||
|
@ -712,6 +717,10 @@ public class MachoProgramBuilder {
|
|||
//}
|
||||
}
|
||||
|
||||
if (!doClassic) {
|
||||
return;
|
||||
}
|
||||
|
||||
//then we are use the old school binding technique.
|
||||
//this only still appears in powerpc
|
||||
if (commands.size() == 0) {
|
||||
|
@ -854,7 +863,7 @@ public class MachoProgramBuilder {
|
|||
* @param segments A {@link Collection} of {@link SegmentCommand Mach-O segments}
|
||||
* @return The {@link Address} of {@link MachHeader} in memory
|
||||
*/
|
||||
private Address setupHeaderAddr(Collection<SegmentCommand> segments)
|
||||
protected Address setupHeaderAddr(Collection<SegmentCommand> segments)
|
||||
throws AddressOverflowException {
|
||||
Address headerAddr = null;
|
||||
long lowestFileOffset = Long.MAX_VALUE;
|
||||
|
@ -876,7 +885,7 @@ public class MachoProgramBuilder {
|
|||
return headerBlock.getStart();
|
||||
}
|
||||
|
||||
private void markupSections() throws Exception {
|
||||
protected void markupSections() throws Exception {
|
||||
|
||||
monitor.setMessage("Processing section markup...");
|
||||
|
||||
|
@ -949,9 +958,9 @@ public class MachoProgramBuilder {
|
|||
/**
|
||||
* See crt.c from opensource.apple.com
|
||||
*/
|
||||
private void processProgramVars() {
|
||||
if (program.getLanguage().getProcessor() == Processor.findOrPossiblyCreateProcessor(
|
||||
"PowerPC")) {
|
||||
protected void processProgramVars() {
|
||||
if (program.getLanguage().getProcessor() == Processor
|
||||
.findOrPossiblyCreateProcessor("PowerPC")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1023,7 +1032,7 @@ public class MachoProgramBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private void loadSectionRelocations() {
|
||||
protected void loadSectionRelocations() {
|
||||
|
||||
monitor.setMessage("Processing relocation table...");
|
||||
|
||||
|
@ -1084,22 +1093,24 @@ public class MachoProgramBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
program.getRelocationTable().add(address, relocationInfo.getType(),
|
||||
new long[] { relocationInfo.getValue(), relocationInfo.getLength(),
|
||||
relocationInfo.isPcRelocated() ? 1 : 0, relocationInfo.isExternal() ? 1 : 0,
|
||||
relocationInfo.isScattered() ? 1 : 0 },
|
||||
origBytes, relocation.getTargetDescription());
|
||||
program.getRelocationTable()
|
||||
.add(address, relocationInfo.getType(),
|
||||
new long[] { relocationInfo.getValue(), relocationInfo.getLength(),
|
||||
relocationInfo.isPcRelocated() ? 1 : 0,
|
||||
relocationInfo.isExternal() ? 1 : 0,
|
||||
relocationInfo.isScattered() ? 1 : 0 },
|
||||
origBytes, relocation.getTargetDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRelocationError(Address address, String message) {
|
||||
program.getBookmarkManager().setBookmark(address, BookmarkType.ERROR, "Relocations",
|
||||
message);
|
||||
program.getBookmarkManager()
|
||||
.setBookmark(address, BookmarkType.ERROR, "Relocations", message);
|
||||
log.appendMsg(message);
|
||||
}
|
||||
|
||||
private void loadExternalRelocations() {
|
||||
protected void loadExternalRelocations() {
|
||||
|
||||
monitor.setMessage("Processing external relocations...");
|
||||
|
||||
|
@ -1120,7 +1131,7 @@ public class MachoProgramBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private void loadLocalRelocations() {
|
||||
protected void loadLocalRelocations() {
|
||||
|
||||
monitor.setMessage("Processing local relocations...");
|
||||
|
||||
|
@ -1182,8 +1193,9 @@ public class MachoProgramBuilder {
|
|||
|
||||
byte[] originalRelocationBytes = getOriginalRelocationBytes(relocation, relocationAddress);
|
||||
|
||||
program.getRelocationTable().add(relocationAddress, relocation.getType(),
|
||||
relocation.toValues(), originalRelocationBytes, null);
|
||||
program.getRelocationTable()
|
||||
.add(relocationAddress, relocation.getType(), relocation.toValues(),
|
||||
originalRelocationBytes, null);
|
||||
}
|
||||
|
||||
private void addLibrary(String library) {
|
||||
|
@ -1226,7 +1238,11 @@ public class MachoProgramBuilder {
|
|||
length = listing.getDataAt(address).getLength();
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.appendException(e);
|
||||
// don't worry about exceptions
|
||||
// may have already been created, by relocation, or chain pointers
|
||||
if (!(datatype instanceof Pointer)) {
|
||||
log.appendException(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (datatype instanceof Pointer) {
|
||||
|
@ -1298,8 +1314,9 @@ public class MachoProgramBuilder {
|
|||
|
||||
private void markAsThumb(Address address)
|
||||
throws ContextChangeException, AddressOverflowException {
|
||||
if (!program.getLanguage().getProcessor().equals(
|
||||
Processor.findOrPossiblyCreateProcessor("ARM"))) {
|
||||
if (!program.getLanguage()
|
||||
.getProcessor()
|
||||
.equals(Processor.findOrPossiblyCreateProcessor("ARM"))) {
|
||||
return;
|
||||
}
|
||||
if ((address.getOffset() & 1) == 1) {
|
||||
|
@ -1327,8 +1344,9 @@ public class MachoProgramBuilder {
|
|||
try {
|
||||
MemoryBlock memoryBlock = memory.getBlock(reference.getToAddress());
|
||||
Namespace namespace = createNamespace(memoryBlock.getName());
|
||||
program.getSymbolTable().createLabel(reference.getToAddress(),
|
||||
fromSymbol.getName(), namespace, SourceType.IMPORTED);
|
||||
program.getSymbolTable()
|
||||
.createLabel(reference.getToAddress(), fromSymbol.getName(), namespace,
|
||||
SourceType.IMPORTED);
|
||||
}
|
||||
catch (Exception e) {
|
||||
//log.appendMsg("Unable to create lazy pointer symbol " + fromSymbol.getName() + " at " + reference.getToAddress());
|
||||
|
@ -1344,8 +1362,9 @@ public class MachoProgramBuilder {
|
|||
|
||||
private Namespace createNamespace(String namespaceName) {
|
||||
try {
|
||||
return program.getSymbolTable().createNameSpace(program.getGlobalNamespace(),
|
||||
namespaceName, SourceType.IMPORTED);
|
||||
return program.getSymbolTable()
|
||||
.createNameSpace(program.getGlobalNamespace(), namespaceName,
|
||||
SourceType.IMPORTED);
|
||||
}
|
||||
catch (DuplicateNameException | InvalidInputException e) {
|
||||
Namespace namespace =
|
||||
|
|
Loading…
Reference in a new issue