Merge branch 'GT-2343_ryanmkurtz_DYLD-loader'

This commit is contained in:
Ryan Kurtz 2019-06-04 08:49:45 -04:00
commit 67198eb40f
35 changed files with 2796 additions and 269 deletions

View File

@ -435,21 +435,25 @@ public class BinaryReader {
* @exception IOException if an I/O error occurs
*/
public String readAsciiString(long index) throws IOException {
StringBuffer buffer = new StringBuffer();
while (true) {
if (index == provider.length()) {
// reached the end of the bytes and found no non-ascii data
break;
}
byte b = provider.readByte(index++);
if ((b >= 32) && (b <= 126)) {
buffer.append((char) b);
}
else {
break;
final int BUF_LEN = 1024;
StringBuilder builder = new StringBuilder();
boolean done = false;
while (!done && index < provider.length()) {
long numToRead = Math.min(BUF_LEN, provider.length() - index);
for (byte b : provider.readBytes(index, numToRead)) {
if ((b >= 32) && (b <= 126)) {
builder.append((char) b);
}
else {
done = true;
break;
}
}
index += numToRead;
}
return buffer.toString().trim();
return builder.toString().trim();
}
/**

View File

@ -38,8 +38,8 @@ public class NList implements StructConverter {
private String string;
private boolean is32bit;
static NList createNList(FactoryBundledWithBinaryReader reader,
boolean is32bit, int stringTableOffset) throws IOException {
public static NList createNList(FactoryBundledWithBinaryReader reader,
boolean is32bit, long stringTableOffset) throws IOException {
NList nList = (NList) reader.getFactory().create(NList.class);
nList.initNList(reader, is32bit, stringTableOffset);
return nList;
@ -50,7 +50,8 @@ public class NList implements StructConverter {
*/
public NList() {}
private void initNList(FactoryBundledWithBinaryReader reader, boolean is32bit, int stringTableOffset) throws IOException {
private void initNList(FactoryBundledWithBinaryReader reader, boolean is32bit,
long stringTableOffset) throws IOException {
this.is32bit = is32bit;
n_strx = reader.readNextInt();
@ -64,9 +65,9 @@ public class NList implements StructConverter {
n_value = reader.readNextLong();
}
try {
string = reader.readAsciiString((stringTableOffset + n_strx) & 0xffffffffL);
string = reader.readAsciiString(stringTableOffset + n_strx);
}
catch (IOException e) {
catch (Exception e) {
string = "";
}
}

View File

@ -59,8 +59,8 @@ public class SubClientCommand extends LoadCommand {
* Returns the client name.
* @return the client name
*/
public String getClientName() {
return client.getString();
public LoadCommandString getClientName() {
return client;
}
@Override

View File

@ -55,8 +55,8 @@ public class SubFrameworkCommand extends LoadCommand {
umbrella = LoadCommandString.createLoadCommandString(reader, this);
}
public String getUmbrellaFrameworkName() {
return umbrella.getString();
public LoadCommandString getUmbrellaFrameworkName() {
return umbrella;
}
@Override

View File

@ -55,8 +55,8 @@ public class SubLibraryCommand extends LoadCommand {
sub_library = LoadCommandString.createLoadCommandString(reader, this);
}
public String getSubLibraryName() {
return sub_library.getString();
public LoadCommandString getSubLibraryName() {
return sub_library;
}
@Override

View File

@ -55,8 +55,8 @@ public class SubUmbrellaCommand extends LoadCommand {
sub_umbrella = LoadCommandString.createLoadCommandString(reader, this);
}
public String getSubUmbrellaFrameworkName() {
return sub_umbrella.getString();
public LoadCommandString getSubUmbrellaFrameworkName() {
return sub_umbrella;
}
@Override

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.file.formats.ios.dyldcache;
package ghidra.app.util.bin.format.macho.dyld;
import java.io.IOException;

View File

@ -0,0 +1,363 @@
/* ###
* 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.StructConverter;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
/**
* Represents a dyld_cache_accelerate_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>
*/
@SuppressWarnings("unused")
public class DyldCacheAccelerateInfo implements StructConverter {
private int version;
private int imageExtrasCount;
private int imagesExtrasOffset;
private int bottomUpListOffset;
private int dylibTrieOffset;
private int dylibTrieSize;
private int initializersOffset;
private int initializersCount;
private int dofSectionsOffset;
private int dofSectionsCount;
private int reExportListOffset;
private int reExportCount;
private int depListOffset;
private int depListCount;
private int rangeTableOffset;
private int rangeTableCount;
private long dyldSectionAddr;
private BinaryReader reader;
private List<DyldCacheImageInfoExtra> imageInfoExtraList;
private List<DyldCacheAcceleratorInitializer> acceleratorInitializerList;
private List<DyldCacheAcceleratorDof> acceleratorDofList;
private List<DyldCacheRangeEntry> rangeEntryList;
/**
* Create a new {@link DyldCacheAccelerateInfo}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD accelerate info
* @throws IOException if there was an IO-related problem creating the DYLD accelerate info
*/
public DyldCacheAccelerateInfo(BinaryReader reader) throws IOException {
this.reader = reader;
version = reader.readNextInt();
imageExtrasCount = reader.readNextInt();
imagesExtrasOffset = reader.readNextInt();
bottomUpListOffset = reader.readNextInt();
dylibTrieOffset = reader.readNextInt();
dylibTrieSize = reader.readNextInt();
initializersOffset = reader.readNextInt();
initializersCount = reader.readNextInt();
dofSectionsOffset = reader.readNextInt();
dofSectionsCount = reader.readNextInt();
reExportListOffset = reader.readNextInt();
reExportCount = reader.readNextInt();
depListOffset = reader.readNextInt();
depListCount = reader.readNextInt();
rangeTableOffset = reader.readNextInt();
rangeTableCount = reader.readNextInt();
dyldSectionAddr = reader.readNextLong();
imageInfoExtraList = new ArrayList<>(imageExtrasCount);
acceleratorInitializerList = new ArrayList<>(initializersCount);
acceleratorDofList = new ArrayList<>(dofSectionsCount);
rangeEntryList = new ArrayList<>(rangeTableCount);
}
/**
* Parses the structures referenced by this {@link DyldCacheAccelerateInfo}.
*
* @param program The {@link Program} to parse.
* @param accelerateInfoAddr The {@link Address} of the {@link DyldCacheAccelerateInfo}
* @param log The log
* @param monitor A cancellable task monitor
* @throws CancelledException if the user cancelled the operation
*/
public void parse(Program program, Address accelerateInfoAddr, MessageLog log,
TaskMonitor monitor) throws CancelledException {
parseImageInfoExtra(program, accelerateInfoAddr, log, monitor);
parseAcceleratorInitializer(program, accelerateInfoAddr, log, monitor);
parseAcceleratorDof(program, accelerateInfoAddr, log, monitor);
parseRangeEntry(program, accelerateInfoAddr, log, monitor);
}
/**
* Marks up this {@link DyldCacheAccelerateInfo} with data structures and comments.
*
* @param program The {@link Program} to mark up
* @param accelerateInfoAddr The {@link Address} of the {@link DyldCacheAccelerateInfo}
* @param monitor A cancellable task monitor
* @param log The log
* @throws CancelledException if the user cancelled the operation
*/
public void markup(Program program, Address accelerateInfoAddr, TaskMonitor monitor,
MessageLog log) throws CancelledException {
markupImageInfoExtra(program, accelerateInfoAddr, monitor, log);
markupAcceleratorInitializer(program, accelerateInfoAddr, monitor, log);
markupAcceleratorDof(program, accelerateInfoAddr, monitor, log);
markupReExportList(program, accelerateInfoAddr, monitor, log);
markupDependencies(program, accelerateInfoAddr, monitor, log);
markupRangeEntry(program, accelerateInfoAddr, monitor, log);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_accelerate_info", 0);
struct.add(DWORD, "version", "currently 1");
struct.add(DWORD, "imageExtrasCount", "does not include aliases");
struct.add(DWORD, "imagesExtrasOffset",
"offset into this chunk of first dyld_cache_image_info_extra");
struct.add(DWORD, "bottomUpListOffset",
"offset into this chunk to start of 16-bit array of sorted image indexes");
struct.add(DWORD, "dylibTrieOffset",
"offset into this chunk to start of trie containing all dylib paths");
struct.add(DWORD, "dylibTrieSize", "size of trie containing all dylib paths");
struct.add(DWORD, "initializersOffset",
"offset into this chunk to start of initializers list");
struct.add(DWORD, "initializersCount", "size of initializers list");
struct.add(DWORD, "dofSectionsOffset",
"offset into this chunk to start of DOF (DTrace object format) sections list");
struct.add(DWORD, "dofSectionsCount", "size of DOF (DTrace object format sections list)");
struct.add(DWORD, "reExportListOffset",
"offset into this chunk to start of 16-bit array of re-exports");
struct.add(DWORD, "reExportCount", "size of re-exports");
struct.add(DWORD, "depListOffset",
"offset into this chunk to start of 16-bit array of dependencies (0x8000 bit set if upward)");
struct.add(DWORD, "depListCount", "size of dependencies");
struct.add(DWORD, "rangeTableOffset", "offset into this chunk to start of ss");
struct.add(DWORD, "rangeTableCount", "size of dependencies");
struct.add(QWORD, "dyldSectionAddr", "address of libdyld's __dyld section in unslid cache");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
private void parseImageInfoExtra(Program program, Address accelerateInfoAddr, MessageLog log,
TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Parsing DYLD image image info extras...");
monitor.initialize(imageExtrasCount);
reader.setPointerIndex(imagesExtrasOffset);
try {
for (int i = 0; i < imageExtrasCount; ++i) {
imageInfoExtraList.add(new DyldCacheImageInfoExtra(reader));
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (IOException e) {
log.appendMsg(DyldCacheAccelerateInfo.class.getSimpleName(),
"Failed to parse dyld_cache_image_info_extra.");
}
}
private void parseAcceleratorInitializer(Program program, Address accelerateInfoAddr,
MessageLog log, TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Parsing DYLD accelerator initializers...");
monitor.initialize(initializersCount);
reader.setPointerIndex(initializersOffset);
try {
for (int i = 0; i < initializersCount; ++i) {
acceleratorInitializerList.add(new DyldCacheAcceleratorInitializer(reader));
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (IOException e) {
log.appendMsg(DyldCacheAccelerateInfo.class.getSimpleName(),
"Failed to parse dyld_cache_accelerator_initializer.");
}
}
private void parseAcceleratorDof(Program program, Address accelerateInfoAddr, MessageLog log,
TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Parsing DYLD DOF sections...");
monitor.initialize(dofSectionsCount);
reader.setPointerIndex(dofSectionsOffset);
try {
for (int i = 0; i < dofSectionsCount; ++i) {
acceleratorDofList.add(new DyldCacheAcceleratorDof(reader));
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (IOException e) {
log.appendMsg(DyldCacheAccelerateInfo.class.getSimpleName(),
"Failed to parse dyld_cache_accelerator_dof.");
}
}
private void parseRangeEntry(Program program, Address accelerateInfoAddr, MessageLog log,
TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Parsing DYLD range entries...");
monitor.initialize(rangeTableCount);
reader.setPointerIndex(rangeTableOffset);
try {
for (int i = 0; i < rangeTableCount; ++i) {
rangeEntryList.add(new DyldCacheRangeEntry(reader));
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (IOException e) {
log.appendMsg(DyldCacheAccelerateInfo.class.getSimpleName(),
"Failed to parse dyld_cache_range_entry.");
}
}
private void markupImageInfoExtra(Program program, Address accelerateInfoAddr,
TaskMonitor monitor, MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD image info extras...");
monitor.initialize(imageInfoExtraList.size());
try {
Address addr = accelerateInfoAddr.add(imagesExtrasOffset);
for (DyldCacheImageInfoExtra imageInfoExtra : imageInfoExtraList) {
Data d = DataUtilities.createData(program, addr, imageInfoExtra.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(DyldCacheAccelerateInfo.class.getSimpleName(),
"Failed to markup dyld_cache_image_info_extra.");
}
}
private void markupAcceleratorInitializer(Program program, Address accelerateInfoAddr,
TaskMonitor monitor, MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD accelerator initializers...");
monitor.initialize(acceleratorInitializerList.size());
try {
Address addr = accelerateInfoAddr.add(initializersOffset);
for (DyldCacheAcceleratorInitializer initializer : acceleratorInitializerList) {
Data d = DataUtilities.createData(program, addr, initializer.toDataType(), -1,
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
Address funcAddr = program.getImageBase().add(initializer.getFunctionsOffset());
try {
program.getFunctionManager().createFunction(null, funcAddr,
new AddressSet(funcAddr), SourceType.ANALYSIS);
}
catch (OverlappingFunctionException | InvalidInputException e) {
// Function already created...skip
}
addr = addr.add(d.getLength());
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (CodeUnitInsertionException | DuplicateNameException | IOException e) {
log.appendMsg(DyldCacheAccelerateInfo.class.getSimpleName(),
"Failed to markup dyld_cache_accelerator_initializer.");
}
}
private void markupAcceleratorDof(Program program, Address accelerateInfoAddr,
TaskMonitor monitor, MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD DOF sections...");
monitor.initialize(acceleratorDofList.size());
try {
Address addr = accelerateInfoAddr.add(dofSectionsOffset);
for (DyldCacheAcceleratorDof dof : acceleratorDofList) {
Data d = DataUtilities.createData(program, addr, dof.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(DyldCacheAccelerateInfo.class.getSimpleName(),
"Failed to markup dyld_cache_accelerator_dof.");
}
}
private void markupReExportList(Program program, Address accelerateInfoAddr,
TaskMonitor monitor, MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD re-exports...");
monitor.initialize(1);
try {
Address addr = accelerateInfoAddr.add(reExportListOffset);
DataType dt = new ArrayDataType(WORD, reExportCount, WORD.getLength());
DataUtilities.createData(program, addr, dt, -1, false,
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
program.getListing().setComment(addr, CodeUnit.EOL_COMMENT, "re-exports");
monitor.incrementProgress(1);
}
catch (CodeUnitInsertionException e) {
log.appendMsg(DyldCacheAccelerateInfo.class.getSimpleName(),
"Failed to markup reExportList.");
}
}
private void markupDependencies(Program program, Address accelerateInfoAddr,
TaskMonitor monitor, MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD dependencies...");
monitor.initialize(1);
try {
Address addr = accelerateInfoAddr.add(depListOffset);
DataType dt = new ArrayDataType(WORD, depListCount, WORD.getLength());
DataUtilities.createData(program, addr, dt, -1, false,
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
program.getListing().setComment(addr, CodeUnit.EOL_COMMENT, "dependencies");
monitor.incrementProgress(1);
}
catch (CodeUnitInsertionException e) {
log.appendMsg(DyldCacheAccelerateInfo.class.getSimpleName(),
"Failed to markup dependences.");
}
}
private void markupRangeEntry(Program program, Address accelerateInfoAddr, TaskMonitor monitor,
MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD range entries...");
monitor.initialize(rangeEntryList.size());
try {
Address addr = accelerateInfoAddr.add(rangeTableOffset);
for (DyldCacheRangeEntry rangeEntry : rangeEntryList) {
Data d = DataUtilities.createData(program, addr, rangeEntry.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(DyldCacheAccelerateInfo.class.getSimpleName(),
"Failed to markup dyld_cache_range_entry.");
}
}
}

View File

@ -0,0 +1,59 @@
/* ###
* 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.program.model.data.*;
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>
*/
@SuppressWarnings("unused")
public class DyldCacheAcceleratorDof implements StructConverter {
private long sectionAddress;
private int sectionSize;
private int imageIndex;
/**
* Create a new {@link DyldCacheAcceleratorDof}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD accelerator DOF
* @throws IOException if there was an IO-related problem creating the DYLD accelerator DOF
*/
public DyldCacheAcceleratorDof(BinaryReader reader) throws IOException {
sectionAddress = reader.readNextLong();
sectionSize = reader.readNextInt();
imageIndex = reader.readNextInt();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_accelerator_dof", 0);
struct.add(QWORD, "sectionAddress", "");
struct.add(DWORD, "sectionSize", "");
struct.add(DWORD, "imageIndex", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
}

View File

@ -0,0 +1,68 @@
/* ###
* 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.program.model.data.*;
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>
*/
@SuppressWarnings("unused")
public class DyldCacheAcceleratorInitializer implements StructConverter {
private int functionsOffset;
private int imageIndex;
/**
* Create a new {@link DyldCacheAcceleratorInitializer}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD accelerator
* initializer
* @throws IOException if there was an IO-related problem creating the DYLD accelerator
* initializer
*/
public DyldCacheAcceleratorInitializer(BinaryReader reader) throws IOException {
functionsOffset = reader.readNextInt();
imageIndex = reader.readNextInt();
}
/**
* Gets the functions offset, which is an address offset from the start of the cache mapping.
*
* @return The functions offset, which is an address offset from the start of the cache
* mapping
*/
public int getFunctionsOffset() {
return functionsOffset;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_accelerator_initializer", 0);
struct.add(DWORD, "functionsOffset", "");
struct.add(DWORD, "imageIndex", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
}

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.file.formats.ios.dyldcache;
package ghidra.app.util.bin.format.macho.dyld;
public final class DyldCacheConstants {

View File

@ -0,0 +1,675 @@
/* ###
* 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.*;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
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>
*/
@SuppressWarnings("unused")
public class DyldCacheHeader implements StructConverter {
private byte[] magic;
private int mappingOffset;
private int mappingCount;
private int imagesOffset;
private int imagesCount;
private long dyldBaseAddress;
private long codeSignatureOffset;
private long codeSignatureSize;
private long slideInfoOffset;
private long slideInfoSize;
private long localSymbolsOffset;
private long localSymbolsSize;
private byte[] uuid;
private long cacheType;
private int branchPoolsOffset;
private int branchPoolsCount;
private long accelerateInfoAddr;
private long accelerateInfoSize;
private long imagesTextOffset;
private long imagesTextCount;
private int headerType;
private BinaryReader reader;
private long baseAddress;
private List<DyldCacheMappingInfo> mappingInfoList;
private List<DyldCacheImageInfo> imageInfoList;
private DyldCacheSlideInfoCommon slideInfo;
private DyldCacheLocalSymbolsInfo localSymbolsInfo;
private List<Long> branchPoolList;
private DyldCacheAccelerateInfo accelerateInfo;
private List<DyldCacheImageTextInfo> imageTextInfoList;
private DyldArchitecture architecture;
/**
* Create a new {@link DyldCacheHeader}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD cache header
* @throws IOException if there was an IO-related problem creating the DYLD cache header
*/
public DyldCacheHeader(BinaryReader reader) throws IOException {
this.reader = reader;
// ------ HEADER 1 ---------
headerType = 1; // https://opensource.apple.com/source/dyld/dyld-95.3/launch-cache/dyld_cache_format.h.auto.html
magic = reader.readNextByteArray(16);
mappingOffset = reader.readNextInt();
mappingCount = reader.readNextInt();
imagesOffset = reader.readNextInt();
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
codeSignatureOffset = reader.readNextLong();
codeSignatureSize = reader.readNextLong();
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
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
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
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
branchPoolsOffset = reader.readNextInt();
branchPoolsCount = reader.readNextInt();
accelerateInfoAddr = reader.readNextLong();
accelerateInfoSize = reader.readNextLong();
imagesTextOffset = reader.readNextLong();
imagesTextCount = reader.readNextLong();
}
baseAddress = reader.readLong(mappingOffset);
architecture = DyldArchitecture.getArchitecture(new String(magic).trim());
mappingInfoList = new ArrayList<>(mappingCount);
imageInfoList = new ArrayList<>(imagesCount);
branchPoolList = new ArrayList<>(branchPoolsCount);
imageTextInfoList = new ArrayList<>();
}
/**
* Parses the structures referenced by this {@link DyldCacheHeader} from a file.
*
* @param parseSymbols True if symbols should be parsed (could be very slow); otherwise, false
* @param log The log
* @param monitor A cancellable task monitor
* @throws CancelledException if the user cancelled the operation
*/
public void parseFromFile(boolean parseSymbols, MessageLog log, TaskMonitor monitor)
throws CancelledException {
if (headerType >= 1) {
parseMappingInfo(log, monitor);
parseImageInfo(log, monitor);
}
if (headerType >= 2) {
parseSlideInfo(log, monitor);
}
if (headerType >= 3) {
if (parseSymbols) {
parseLocalSymbolsInfo(log, monitor);
}
}
if (headerType >= 6) {
parseBranchPools(log, monitor);
parseImageTextInfo(log, monitor);
}
}
/**
* Parses the structures referenced by this {@link DyldCacheHeader} from memory.
*
* @param program The {@link Program} whose memory to parse
* @param space The {@link Program}'s {@link AddressSpace}
* @param log The log
* @param monitor A cancellable task monitor
* @throws CancelledException if the user cancelled the operation
*/
public void parseFromMemory(Program program, AddressSpace space, MessageLog log,
TaskMonitor monitor) throws CancelledException {
if (headerType >= 6) {
parseAcceleratorInfo(program, space, log, monitor);
}
}
/**
* Marks up this {@link DyldCacheHeader} with data structures and comments.
*
* @param program The {@link Program} to mark up
* @param space The {@link Program}'s {@link AddressSpace}
* @param monitor A cancellable task monitor
* @param log The log
* @throws CancelledException if the user cancelled the operation
*/
public void markup(Program program, AddressSpace space, TaskMonitor monitor, MessageLog log)
throws CancelledException {
if (headerType >= 1) {
markupHeader(program, space, monitor, log);
markupMappingInfo(program, space, monitor, log);
markupImageInfo(program, space, monitor, log);
}
if (headerType >= 2) {
markupCodeSignature(program, space, monitor, log);
markupSlideInfo(program, space, monitor, log);
}
if (headerType >= 3) {
markupLocalSymbolsInfo(program, space, monitor, log);
}
if (headerType >= 6) {
markupBranchPools(program, space, monitor, log);
markupAcceleratorInfo(program, space, monitor, log);
markupImageTextInfo(program, space, monitor, log);
}
}
/**
* Gets the base address of the DYLD cache. This is where the cache should be loaded in
* memory.
*
* @return The base address of the DYLD cache
*/
public long getBaseAddress() {
return baseAddress;
}
/**
* Gets the magic bytes, which contain version information.
*
* @return The magic bytes
*/
public byte[] getMagic() {
return magic;
}
/**
* Gets the {@link List} of {@link DyldCacheMappingInfo}s. Requires header to have been parsed.
*
* @return The {@link List} of {@link DyldCacheMappingInfo}s
*/
public List<DyldCacheMappingInfo> getMappingInfos() {
return mappingInfoList;
}
/**
* Gets the file offset to first {@link DyldCacheImageInfo}.
*
* @return The file offset to first {@link DyldCacheImageInfo}
*/
public int getImagesOffset() {
return imagesOffset;
}
/**
* Gets the number of {@link DyldCacheImageInfo}s.
*
* @return The number of {@link DyldCacheImageInfo}s
*/
public int getImagesCount() {
return imagesCount;
}
/**
* Gets the {@link List} of {@link DyldCacheImageInfo}s. Requires header to have been parsed.
*
* @return The {@link List} of {@link DyldCacheImageInfo}s
*/
public List<DyldCacheImageInfo> getImageInfos() {
return imageInfoList;
}
/**
* Gets the {@link DyldCacheLocalSymbolsInfo}.
*
* @return The {@link DyldCacheLocalSymbolsInfo}. Could be be null if it didn't parse.
*/
public DyldCacheLocalSymbolsInfo getLocalSymbolsInfo() {
return localSymbolsInfo;
}
/**
* Gets the {@link List} of branch pool address. Requires header to have been parsed.
*
* @return The {@link List} of branch pool address
*/
public List<Long> getBranchPoolAddresses() {
return branchPoolList;
}
/**
* Gets architecture information.
*
* @return architecture information
*/
public DyldArchitecture getArchitecture() {
return architecture;
}
@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");
}
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
private void parseMappingInfo(MessageLog log, TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Parsing DYLD mapping info...");
monitor.initialize(mappingCount);
try {
reader.setPointerIndex(mappingOffset);
for (int i = 0; i < mappingCount; ++i) {
mappingInfoList.add(new DyldCacheMappingInfo(reader));
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (IOException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to parse dyld_cache_mapping_info.");
}
}
private void parseImageInfo(MessageLog log, TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Parsing DYLD image info...");
monitor.initialize(imagesCount);
try {
reader.setPointerIndex(imagesOffset);
for (int i = 0; i < imagesCount; ++i) {
imageInfoList.add(new DyldCacheImageInfo(reader));
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (IOException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to parse dyld_cache_image_info.");
}
}
private void parseSlideInfo(MessageLog log, TaskMonitor monitor) throws CancelledException {
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 void parseLocalSymbolsInfo(MessageLog log, TaskMonitor monitor)
throws CancelledException {
monitor.setMessage("Parsing DYLD local symbols info...");
monitor.initialize(1);
try {
reader.setPointerIndex(localSymbolsOffset);
localSymbolsInfo = new DyldCacheLocalSymbolsInfo(reader, architecture);
localSymbolsInfo.parse(log, monitor);
monitor.incrementProgress(1);
}
catch (IOException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to parse dyld_cache_local_symbols_info.");
}
}
private void parseBranchPools(MessageLog log, TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Parsing DYLD branch pool addresses...");
monitor.initialize(branchPoolsCount);
try {
reader.setPointerIndex(branchPoolsOffset);
for (int i = 0; i < branchPoolsCount; ++i) {
branchPoolList.add(reader.readNextLong());
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (IOException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(), "Failed to parse pool addresses.");
}
}
private void parseImageTextInfo(MessageLog log, TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Parsing DYLD image text info...");
monitor.initialize(imagesTextCount);
try {
reader.setPointerIndex(imagesTextOffset);
for (int i = 0; i < imagesTextCount; ++i) {
imageTextInfoList.add(new DyldCacheImageTextInfo(reader));
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (IOException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to parse dyld_cache_image_text_info.");
}
}
private void parseAcceleratorInfo(Program program, AddressSpace space, MessageLog log,
TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Parsing DYLD accelerateor info...");
monitor.initialize(imagesTextCount);
try {
Address addr = space.getAddress(accelerateInfoAddr);
ByteProvider bytes = new MemoryByteProvider(program.getMemory(), addr);
BinaryReader memoryReader =
new BinaryReader(bytes, !program.getLanguage().isBigEndian());
accelerateInfo = new DyldCacheAccelerateInfo(memoryReader);
accelerateInfo.parse(program, addr, log, monitor);
monitor.incrementProgress(1);
}
catch (IOException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to parse dyld_cache_accelerator_info.");
}
}
private void markupHeader(Program program, AddressSpace space, TaskMonitor monitor,
MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD header...");
monitor.initialize(1);
try {
DataUtilities.createData(program, program.getImageBase(), toDataType(), -1, false,
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
monitor.incrementProgress(1);
}
catch (CodeUnitInsertionException | DuplicateNameException | IOException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to markup dyld_cache_header.");
}
}
private void markupMappingInfo(Program program, AddressSpace space, TaskMonitor monitor,
MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD mapping info...");
monitor.initialize(mappingInfoList.size());
try {
Address addr = fileOffsetToAddr(mappingOffset, program, space);
for (DyldCacheMappingInfo mappingInfo : mappingInfoList) {
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...");
monitor.initialize(imageInfoList.size());
try {
Address addr = fileOffsetToAddr(imagesOffset, program, space);
for (DyldCacheImageInfo imageInfo : imageInfoList) {
Data d = DataUtilities.createData(program, addr, imageInfo.toDataType(), -1, false,
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
program.getListing().setComment(addr, CodeUnit.EOL_COMMENT, imageInfo.getPath());
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_image_info.");
}
}
private void markupCodeSignature(Program program, AddressSpace space, TaskMonitor monitor,
MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD code signature...");
monitor.initialize(1);
try {
String size = "0x" + Long.toHexString(codeSignatureSize);
program.getListing().setComment(fileOffsetToAddr(codeSignatureOffset, program, space),
CodeUnit.PLATE_COMMENT, "Code Signature (" + size + " bytes)");
monitor.incrementProgress(1);
}
catch (IllegalArgumentException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to markup code signature.");
}
}
private void markupSlideInfo(Program program, AddressSpace space, TaskMonitor monitor,
MessageLog log) throws CancelledException {
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);
}
monitor.incrementProgress(1);
}
catch (CodeUnitInsertionException | DuplicateNameException | IOException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to markup dyld_cache_slide_info.");
}
}
private void markupLocalSymbolsInfo(Program program, AddressSpace space, TaskMonitor monitor,
MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD local symbols info...");
monitor.initialize(1);
try {
if (localSymbolsInfo != null) {
Address addr = fileOffsetToAddr(localSymbolsOffset, program, space);
DataUtilities.createData(program, addr, localSymbolsInfo.toDataType(), -1, false,
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
localSymbolsInfo.markup(program, addr, monitor, log);
}
monitor.incrementProgress(1);
}
catch (CodeUnitInsertionException | DuplicateNameException | IOException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to markup dyld_cache_local_symbols_info.");
}
}
private void markupBranchPools(Program program, AddressSpace space, TaskMonitor monitor,
MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD branch pool addresses...");
monitor.initialize(branchPoolList.size());
try {
Address addr = fileOffsetToAddr(branchPoolsOffset, program, space);
for (int i = 0; i < branchPoolList.size(); i++) {
Data d = DataUtilities.createData(program, addr, Pointer64DataType.dataType,
Pointer64DataType.dataType.getLength(), false,
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
addr = addr.add(d.getLength());
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (CodeUnitInsertionException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to markup branch pool addresses.");
}
}
private void markupAcceleratorInfo(Program program, AddressSpace space, TaskMonitor monitor,
MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD accelerator info...");
monitor.initialize(1);
try {
if (accelerateInfo != null) {
Address addr = space.getAddress(accelerateInfoAddr);
DataUtilities.createData(program, addr, accelerateInfo.toDataType(), -1, false,
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
accelerateInfo.markup(program, addr, monitor, log);
}
monitor.incrementProgress(1);
}
catch (CodeUnitInsertionException | DuplicateNameException | IOException e) {
log.appendMsg(DyldCacheHeader.class.getSimpleName(),
"Failed to markup dyld_cache_accelerator_info.");
}
}
private void markupImageTextInfo(Program program, AddressSpace space, TaskMonitor monitor,
MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD image text info...");
monitor.initialize(imageTextInfoList.size());
try {
Address addr = fileOffsetToAddr(imagesTextOffset, program, space);
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());
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_image_text_info.");
}
}
/**
* Gets the given file offset's corresponding memory address.
*
* @param offset The file offset
* @param program The {@link Program}
* @param space The {@link AddressSpace}
* @return The given file offset's corresponding memory address. Could be null if it doesn't
* have one.
*/
private Address fileOffsetToAddr(long offset, Program program, AddressSpace space) {
// First check the memory that was supposed to get mapped in
for (DyldCacheMappingInfo mappingInfo : mappingInfoList) {
if (offset >= mappingInfo.getFileOffset() &&
offset < mappingInfo.getFileOffset() + mappingInfo.getSize()) {
return space.getAddress(
mappingInfo.getAddress() + (offset - mappingInfo.getFileOffset()));
}
}
// Now check the special memory block that contains bytes that weren't supposed to get
// mapped in to memory
AddressSpace fileSpace = program.getAddressFactory().getAddressSpace("FILE");
if (fileSpace != null) {
try {
return fileSpace.getAddress(offset);
}
catch (AddressOutOfBoundsException e) {
return null;
}
}
return null;
}
}

View File

@ -0,0 +1,87 @@
/* ###
* 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.program.model.data.*;
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>
*/
@SuppressWarnings("unused")
public class DyldCacheImageInfo implements StructConverter {
private long address;
private long modTime;
private long inode;
private int pathFileOffset;
private int pad;
private String path;
/**
* Create a new {@link DyldCacheImageInfo}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD image info
* @throws IOException if there was an IO-related problem creating the DYLD image info
*/
public DyldCacheImageInfo(BinaryReader reader) throws IOException {
address = reader.readNextLong();
modTime = reader.readNextLong();
inode = reader.readNextLong();
pathFileOffset = reader.readNextInt();
pad = reader.readNextInt();
path = reader.readAsciiString(pathFileOffset);
}
/**
* Gets the address the start of the image.
*
* @return The address of the start of the image
*/
public long getAddress() {
return address;
}
/**
* Gets the path of the image.
*
* @return The path of the image
*/
public String getPath() {
return path;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_image_info", 0);
struct.add(QWORD, "address", "");
struct.add(QWORD, "modTime", "");
struct.add(QWORD, "inode", "");
struct.add(DWORD, "pathFileOffset", "");
struct.add(DWORD, "pad", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
}

View File

@ -0,0 +1,68 @@
/* ###
* 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.program.model.data.*;
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>
*/
@SuppressWarnings("unused")
public class DyldCacheImageInfoExtra implements StructConverter {
private long exportsTrieAddr;
private long weakBindingsAddr;
private int exportsTrieSize;
private int weakBindingsSize;
private int dependentsStartArrayIndex;
private int reExportsStartArrayIndex;
/**
* Create a new {@link DyldCacheImageInfoExtra}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD image info extra
* @throws IOException if there was an IO-related problem creating the DYLD image info extra
*/
public DyldCacheImageInfoExtra(BinaryReader reader) throws IOException {
exportsTrieAddr = reader.readNextLong();
weakBindingsAddr = reader.readNextLong();
exportsTrieSize = reader.readNextInt();
weakBindingsSize = reader.readNextInt();
dependentsStartArrayIndex = reader.readNextInt();
reExportsStartArrayIndex = reader.readNextInt();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_image_info_extra", 0);
struct.add(QWORD, "exportsTrieAddr", "");
struct.add(QWORD, "weakBindingsAddr", "");
struct.add(DWORD, "exportsTrieSize", "");
struct.add(DWORD, "weakBindingsSize", "");
struct.add(DWORD, "dependentsStartArrayIndex", "");
struct.add(DWORD, "reExportsStartArrayIndex", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
}

View File

@ -0,0 +1,76 @@
/* ###
* 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.program.model.data.*;
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>
*/
@SuppressWarnings("unused")
public class DyldCacheImageTextInfo implements StructConverter {
private byte[] uuid;
private long loadAddress;
private int textSegmentSize;
private int pathOffset;
private String path;
/**
* Create a new {@link DyldCacheImageTextInfo}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD image text info
* @throws IOException if there was an IO-related problem creating the DYLD image text info
*/
public DyldCacheImageTextInfo(BinaryReader reader) throws IOException {
uuid = reader.readNextByteArray(16);
loadAddress = reader.readNextLong();
textSegmentSize = reader.readNextInt();
pathOffset = reader.readNextInt();
path = reader.readAsciiString(pathOffset);
}
/**
* Gets the path of the image text.
*
* @return The path of the image text.
*/
public String getPath() {
return path;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_image_text_info", 0);
struct.add(new ArrayDataType(BYTE, 16, 1), "uuid", "");
struct.add(QWORD, "loadAddress", "");
struct.add(DWORD, "textSegmentSize", "");
struct.add(DWORD, "pathOffset", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
}

View File

@ -0,0 +1,59 @@
/* ###
* 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.program.model.data.*;
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>
*/
@SuppressWarnings("unused")
public class DyldCacheLocalSymbolsEntry implements StructConverter {
private int dylibOffset;
private int nlistStartIndex;
private int nlistCount;
/**
* Create a new {@link DyldCacheLocalSymbolsEntry}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD local symbols entry
* @throws IOException if there was an IO-related problem creating the DYLD local symbols entry
*/
public DyldCacheLocalSymbolsEntry(BinaryReader reader) throws IOException {
dylibOffset = reader.readNextInt();
nlistStartIndex = reader.readNextInt();
nlistCount = reader.readNextInt();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_local_symbols_entry", 0);
struct.add(DWORD, "dylibOffset", "");
struct.add(DWORD, "nlistStartIndex", "");
struct.add(DWORD, "nlistCount", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
}

View File

@ -0,0 +1,221 @@
/* ###
* 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 generic.continues.RethrowContinuesFactory;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
import ghidra.app.util.bin.format.macho.CpuTypes;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.bin.format.macho.commands.NList;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
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>
*/
@SuppressWarnings("unused")
public class DyldCacheLocalSymbolsInfo implements StructConverter {
private int nlistOffset;
private int nlistCount;
private int stringsOffset;
private int stringsSize;
private int entriesOffset;
private int entriesCount;
private BinaryReader reader;
private long startIndex;
private List<NList> nlistList;
private List<DyldCacheLocalSymbolsEntry> localSymbolsEntryList;
private boolean is32bit;
/**
* Create a new {@link DyldCacheLocalSymbolsInfo}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD local symbols info
* @param architecture The {@link DyldArchitecture}
* @throws IOException if there was an IO-related problem creating the DYLD local symbols info
*/
public DyldCacheLocalSymbolsInfo(BinaryReader reader, DyldArchitecture architecture)
throws IOException {
this.reader = reader;
this.startIndex = reader.getPointerIndex();
nlistOffset = reader.readNextInt();
nlistCount = reader.readNextInt();
stringsOffset = reader.readNextInt();
stringsSize = reader.readNextInt();
entriesOffset = reader.readNextInt();
entriesCount = reader.readNextInt();
nlistList = new ArrayList<>(nlistCount);
localSymbolsEntryList = new ArrayList<>(entriesCount);
is32bit = !(architecture.getCpuType() == CpuTypes.CPU_TYPE_ARM_64 &&
architecture.getCpuType() == CpuTypes.CPU_TYPE_X86_64);
}
/**
* Parses the structures referenced by this {@link DyldCacheLocalSymbolsInfo}.
*
* @param log The log
* @param monitor A cancellable task monitor
* @throws CancelledException if the user cancelled the operation
*/
public void parse(MessageLog log, TaskMonitor monitor) throws CancelledException {
parseNList(log, monitor);
parseLocalSymbols(log, monitor);
}
/**
* Marks up this {@link DyldCacheLocalSymbolsInfo} with data structures and comments.
*
* @param program The {@link Program} to mark up
* @param localSymbolsInfoAddr The {@link Address} of the {@link DyldCacheLocalSymbolsInfo}
* @param monitor A cancellable task monitor
* @param log The log
* @throws CancelledException if the user cancelled the operation
*/
public void markup(Program program, Address localSymbolsInfoAddr, TaskMonitor monitor,
MessageLog log) throws CancelledException {
markupNList(program, localSymbolsInfoAddr, monitor, log);
markupLocalSymbols(program, localSymbolsInfoAddr, monitor, log);
}
/**
* Gets the {@link List} of {@link NList}.
*
* @return The {@link List} of {@link NList}
*/
public List<NList> getNList() {
return nlistList;
}
/**
* Gets the {@link List} of {@link DyldCacheLocalSymbolsEntry}s.
*
* @return The {@link List} of {@link DyldCacheLocalSymbolsEntry}
*/
public List<DyldCacheLocalSymbolsEntry> getLocalSymbols() {
return localSymbolsEntryList;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_local_symbols_info", 0);
struct.add(DWORD, "nlistOffset", "offset into this chunk of nlist entries");
struct.add(DWORD, "nlistCount", "count of nlist entries");
struct.add(DWORD, "stringsOffset", "offset into this chunk of string pool");
struct.add(DWORD, "stringsSize", "byte count of string pool");
struct.add(DWORD, "entriesOffset",
"offset into this chunk of array of dyld_cache_local_symbols_entry ");
struct.add(DWORD, "entriesCount",
"number of elements in dyld_cache_local_symbols_entry array");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
private void parseNList(MessageLog log, TaskMonitor monitor)
throws CancelledException {
FactoryBundledWithBinaryReader nListReader = new FactoryBundledWithBinaryReader(
RethrowContinuesFactory.INSTANCE, reader.getByteProvider(), reader.isLittleEndian());
monitor.setMessage("Parsing DYLD nlist symbol table...");
monitor.initialize(nlistCount);
nListReader.setPointerIndex(startIndex + nlistOffset);
try {
for (int i = 0; i < nlistCount; ++i) {
nlistList.add(NList.createNList(nListReader, is32bit, startIndex + stringsOffset));
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (IOException e) {
log.appendMsg(DyldCacheAccelerateInfo.class.getSimpleName(), "Failed to parse nlist.");
}
}
private void parseLocalSymbols(MessageLog log, TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Parsing DYLD local symbol entries...");
monitor.initialize(entriesCount);
reader.setPointerIndex(startIndex + entriesOffset);
try {
for (int i = 0; i < entriesCount; ++i) {
localSymbolsEntryList.add(new DyldCacheLocalSymbolsEntry(reader));
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (IOException e) {
log.appendMsg(DyldCacheAccelerateInfo.class.getSimpleName(),
"Failed to parse dyld_cache_local_symbols_entry.");
}
}
private void markupNList(Program program, Address localSymbolsInfoAddr, TaskMonitor monitor,
MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD nlist symbol table...");
monitor.initialize(nlistCount);
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);
addr = addr.add(d.getLength());
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (CodeUnitInsertionException | DuplicateNameException | IOException e) {
log.appendMsg(DyldCacheAccelerateInfo.class.getSimpleName(), "Failed to markup nlist.");
}
}
private void markupLocalSymbols(Program program, Address localSymbolsInfoAddr,
TaskMonitor monitor, MessageLog log) throws CancelledException {
monitor.setMessage("Marking up DYLD local symbol entries...");
monitor.initialize(entriesCount);
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);
addr = addr.add(d.getLength());
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
catch (CodeUnitInsertionException | DuplicateNameException | IOException e) {
log.appendMsg(DyldCacheAccelerateInfo.class.getSimpleName(),
"Failed to markup dyld_cache_local_symbols_entry.");
}
}
}

View File

@ -0,0 +1,120 @@
/* ###
* 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_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>
*/
@SuppressWarnings("unused")
public class DyldCacheMappingInfo implements StructConverter {
private long address;
private long size;
private long fileOffset;
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 DyldCacheMappingInfo(BinaryReader reader) throws IOException {
address = reader.readNextLong();
size = reader.readNextLong();
fileOffset = 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;
}
/**
* 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_info", 0);
struct.add(QWORD, "address", "");
struct.add(QWORD, "size", "");
struct.add(QWORD, "fileOffset", "");
struct.add(DWORD, "maxProt", "");
struct.add(DWORD, "initProt", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
}

View File

@ -0,0 +1,59 @@
/* ###
* 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.program.model.data.*;
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>
*/
@SuppressWarnings("unused")
public class DyldCacheRangeEntry implements StructConverter {
private long startAddress;
private int size;
private int imageIndex;
/**
* Create a new {@link DyldCacheRangeEntry}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD range entry
* @throws IOException if there was an IO-related problem creating the DYLD range entry
*/
public DyldCacheRangeEntry(BinaryReader reader) throws IOException {
startAddress = reader.readNextLong();
size = reader.readNextInt();
imageIndex = reader.readNextInt();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_range_entry", 0);
struct.add(QWORD, "startAddress", "");
struct.add(DWORD, "size", "");
struct.add(DWORD, "imageIndex", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
}

View File

@ -0,0 +1,66 @@
/* ###
* 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.format.macho.MachConstants;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* 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>
*/
@SuppressWarnings("unused")
public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
private int toc_offset;
private int toc_count;
private int entries_offset;
private int entries_count;
private int entries_size;
/**
* Create a new {@link DyldCacheSlideInfo1}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info 1
* @throws IOException if there was an IO-related problem creating the DYLD slide info 1
*/
public DyldCacheSlideInfo1(BinaryReader reader) throws IOException {
super(reader);
toc_offset = reader.readNextInt();
toc_count = reader.readNextInt();
entries_offset = reader.readNextInt();
entries_count = reader.readNextInt();
entries_size = reader.readNextInt();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_slide_info", 0);
struct.add(DWORD, "version", "");
struct.add(DWORD, "toc_offset", "");
struct.add(DWORD, "toc_count", "");
struct.add(DWORD, "entries_offset", "");
struct.add(DWORD, "entries_count", "");
struct.add(DWORD, "entries_size", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
}

View File

@ -0,0 +1,72 @@
/* ###
* 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.format.macho.MachConstants;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* 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>
*/
@SuppressWarnings("unused")
public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
private int page_size;
private int page_starts_offset;
private int page_starts_count;
private int page_extras_offset;
private int page_extras_count;
private long delta_mask;
private long value_add;
/**
* Create a new {@link DyldCacheSlideInfo2}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info 2
* @throws IOException if there was an IO-related problem creating the DYLD slide info 2
*/
public DyldCacheSlideInfo2(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();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_slide_info2", 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", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
}

View File

@ -0,0 +1,63 @@
/* ###
* 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.format.macho.MachConstants;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* 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>
*/
@SuppressWarnings("unused")
public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
private int page_size;
private int page_starts_count;
private long auth_value_add;
private short page_starts[];
/**
* 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 DyldCacheSlideInfo3(BinaryReader reader) throws IOException {
super(reader);
page_size = reader.readNextInt();
page_starts_count = reader.readNextInt();
auth_value_add = reader.readNextLong();
page_starts = reader.readNextShortArray(page_starts_count);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_slide_info3", 0);
struct.add(DWORD, "version", "");
struct.add(DWORD, "page_size", "");
struct.add(DWORD, "page_starts_count", "");
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;
}
}

View File

@ -0,0 +1,63 @@
/* ###
* 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.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* 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>
*/
public class DyldCacheSlideInfoCommon implements StructConverter {
protected int version;
/**
* Create a new {@link DyldCacheSlideInfoCommon}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info
* @throws IOException if there was an IO-related problem creating the DYLD slide info
*/
public DyldCacheSlideInfoCommon(BinaryReader reader) throws IOException {
version = reader.readNextInt();
}
/**
* Gets the version of the DYLD slide info.
*
* @return The version of the DYLD slide info.
*/
public int getVersion() {
return version;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_slide_info", 0);
struct.add(DWORD, "version", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
}

View File

@ -0,0 +1,142 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.opinion;
import java.io.IOException;
import java.util.*;
import ghidra.app.util.Option;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.macho.dyld.DyldArchitecture;
import ghidra.app.util.bin.format.macho.dyld.DyldCacheHeader;
import ghidra.app.util.importer.MemoryConflictHandler;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.model.DomainObject;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* A {@link Loader} for DYLD shared cache files.
*/
public class DyldCacheLoader extends AbstractLibrarySupportLoader {
public final static String DYLD_CACHE_NAME = "DYLD Cache";
/** Loader option to process symbols*/
static final String PROCESS_SYMBOLS_OPTION_NAME = "Process symbols";
/** Default value for loader option to process symbols */
static final boolean PROCESS_SYMBOLS_OPTION_DEFAULT = true;
/** Loader option to create memory blocks for DYLIB sections */
static final String CREATE_DYLIB_SECTIONS_OPTION_NAME =
"Create DYLIB section memory blocks (slow!)";
/** Default value for loader option to create memory blocks for DYLIB sections */
static final boolean CREATE_DYLIB_SECTIONS_OPTION_DEFAULT = false;
@Override
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
List<LoadSpec> loadSpecs = new ArrayList<>();
if (!DyldCacheUtils.isDyldCache(provider)) {
return loadSpecs;
}
try {
DyldCacheHeader header = new DyldCacheHeader(new BinaryReader(provider, true));
DyldArchitecture architecture = header.getArchitecture();
if (architecture != null) {
List<QueryResult> results =
QueryOpinionService.query(getName(), architecture.getProcessor(), null);
for (QueryResult result : results) {
loadSpecs.add(new LoadSpec(this, header.getBaseAddress(), result));
}
if (loadSpecs.isEmpty()) {
loadSpecs.add(new LoadSpec(this, header.getBaseAddress(), true));
}
}
}
catch (IOException e) {
// It's not what we expect, so don't consider it
}
return loadSpecs;
}
@Override
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program, MemoryConflictHandler handler, TaskMonitor monitor, MessageLog log)
throws IOException {
try {
DyldCacheProgramBuilder.buildProgram(program, provider, shouldProcessSymbols(options),
shouldCreateDylibSections(options), log, handler, monitor);
}
catch (CancelledException e) {
return;
}
catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
}
@Override
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
DomainObject domainObject, boolean loadIntoProgram) {
List<Option> list =
super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram);
if (!loadIntoProgram) {
list.add(new Option(PROCESS_SYMBOLS_OPTION_NAME, PROCESS_SYMBOLS_OPTION_DEFAULT,
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-processSymbols"));
list.add(
new Option(CREATE_DYLIB_SECTIONS_OPTION_NAME, CREATE_DYLIB_SECTIONS_OPTION_DEFAULT,
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-createDylibSections"));
}
return list;
}
private boolean shouldProcessSymbols(List<Option> options) {
if (options != null) {
for (Option option : options) {
String optName = option.getName();
if (optName.equals(PROCESS_SYMBOLS_OPTION_NAME)) {
return (Boolean) option.getValue();
}
}
}
return PROCESS_SYMBOLS_OPTION_DEFAULT;
}
private boolean shouldCreateDylibSections(List<Option> options) {
if (options != null) {
for (Option option : options) {
String optName = option.getName();
if (optName.equals(CREATE_DYLIB_SECTIONS_OPTION_NAME)) {
return (Boolean) option.getValue();
}
}
}
return CREATE_DYLIB_SECTIONS_OPTION_DEFAULT;
}
@Override
public String getName() {
return DYLD_CACHE_NAME;
}
}

View File

@ -0,0 +1,357 @@
/* ###
* 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.opinion;
import java.io.File;
import java.io.IOException;
import java.util.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.macho.MachException;
import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.bin.format.macho.commands.NList;
import ghidra.app.util.bin.format.macho.dyld.*;
import ghidra.app.util.importer.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.task.TaskMonitor;
/**
* Builds up a DYLD Cache {@link Program} by parsing the DYLD Cache headers.
*/
public class DyldCacheProgramBuilder extends MachoProgramBuilder {
protected DyldCacheHeader dyldCacheHeader;
private boolean shouldProcessSymbols;
private boolean shouldCreateDylibSections;
/**
* Creates a new {@link DyldCacheProgramBuilder} based on the given information.
*
* @param program The {@link Program} to build up
* @param provider The {@link ByteProvider} that contains the DYLD Cache bytes
* @param shouldProcessSymbols True if symbols should be processed; otherwise, false
* @param shouldCreateDylibSections True if memory blocks should be created for DYLIB sections;
* otherwise, false
* @param log The log
* @param memoryConflictHandler How to handle memory conflicts that may occur
* @param monitor A cancelable task monitor
*/
protected DyldCacheProgramBuilder(Program program, ByteProvider provider,
boolean shouldProcessSymbols, boolean shouldCreateDylibSections, MessageLog log,
MemoryConflictHandler memoryConflictHandler, TaskMonitor monitor) {
super(program, provider, log, memoryConflictHandler, monitor);
this.shouldProcessSymbols = shouldProcessSymbols;
this.shouldCreateDylibSections = shouldCreateDylibSections;
}
/**
* Builds up a DYLD Cache {@link Program}.
*
* @param program The {@link Program} to build up
* @param provider The {@link ByteProvider} that contains the DYLD Cache's bytes
* @param shouldProcessSymbols True if symbols should be processed; otherwise, false
* @param shouldCreateDylibSections True if memory blocks should be created for DYLIB sections;
* otherwise, false
* @param log The log
* @param memoryConflictHandler How to handle memory conflicts that may occur
* @param monitor A cancelable task monitor
* @throws Exception if a problem occurs
*/
public static void buildProgram(Program program, ByteProvider provider,
boolean shouldProcessSymbols, boolean shouldCreateDylibSections, MessageLog log,
MemoryConflictHandler memoryConflictHandler, TaskMonitor monitor) throws Exception {
DyldCacheProgramBuilder dyldCacheProgramBuilder = new DyldCacheProgramBuilder(program,
provider, shouldProcessSymbols, shouldCreateDylibSections, log, memoryConflictHandler,
monitor);
dyldCacheProgramBuilder.build();
}
@Override
protected void build() throws Exception {
monitor.setMessage("Parsing DYLD Cache header ...");
monitor.initialize(1);
dyldCacheHeader = new DyldCacheHeader(new BinaryReader(provider, true));
dyldCacheHeader.parseFromFile(shouldProcessSymbols, log, monitor);
monitor.incrementProgress(1);
try {
setDyldCacheImageBase();
processDyldCacheMemoryBlocks();
markupHeaders();
markupBranchIslands();
createSymbols();
processDylibs();
}
finally {
if (mbu != null) {
mbu.dispose();
mbu = null;
}
}
}
/**
* Sets the program's image base.
*
* @throws Exception if there was problem setting the program's image base
*/
private void setDyldCacheImageBase() throws Exception {
monitor.setMessage("Setting image base...");
monitor.initialize(1);
program.setImageBase(space.getAddress(dyldCacheHeader.getBaseAddress()), true);
monitor.incrementProgress(1);
}
/**
* Processes the DYLD Cache's memory mappings and creates memory blocks for them.
*
* @throws Exception if there was a problem creating the memory blocks
*/
private void processDyldCacheMemoryBlocks() throws Exception {
List<DyldCacheMappingInfo> mappingInfos = dyldCacheHeader.getMappingInfos();
monitor.setMessage("Processing DYLD mapped memory blocks...");
monitor.initialize(mappingInfos.size());
long endOfMappedOffset = 0;
for (DyldCacheMappingInfo mappingInfo : mappingInfos) {
long offset = mappingInfo.getFileOffset();
long size = mappingInfo.getSize();
mbu.createInitializedBlock("DYLD", space.getAddress(mappingInfo.getAddress()),
provider.getInputStream(offset), size, "", "", mappingInfo.isRead(),
mappingInfo.isWrite(), mappingInfo.isExecute(), monitor);
if (offset + size > endOfMappedOffset) {
endOfMappedOffset = offset + size;
}
monitor.checkCanceled();
monitor.incrementProgress(1);
}
if (endOfMappedOffset < provider.length()) {
monitor.setMessage("Processing DYLD unmapped memory block...");
mbu.createOverlayBlock("FILE", AddressSpace.OTHER_SPACE.getAddress(endOfMappedOffset),
provider.getInputStream(endOfMappedOffset), provider.length() - endOfMappedOffset,
"Useful bytes that don't get mapped into memory", "", false, false, false, monitor);
}
}
/**
* Marks up the DYLD Cache headers.
*
* @throws Exception if there was a problem marking up the headers
*/
private void markupHeaders() throws Exception {
monitor.setMessage("Marking up DYLD headers...");
monitor.initialize(1);
dyldCacheHeader.parseFromMemory(program, space, log, monitor);
dyldCacheHeader.markup(program, space, monitor, log);
monitor.incrementProgress(1);
}
/**
* Marks up the DYLD Cache branch islands.
*
* @throws Exception if there was a problem marking up the branch islands.
*/
private void markupBranchIslands() throws Exception {
monitor.setMessage("Marking up DYLD branch islands...");
monitor.initialize(dyldCacheHeader.getBranchPoolAddresses().size());
for (Long addr : dyldCacheHeader.getBranchPoolAddresses()) {
try {
MachHeader header =
MachHeader.createMachHeader(MessageLogContinuesFactory.create(log), provider,
addr - dyldCacheHeader.getBaseAddress());
header.parse();
super.markupHeaders(header, space.getAddress(addr));
}
catch (MachException | IOException e) {
// Not a show-stopper...carry on.
}
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
/**
* Creates the DYLD Cache symbols.
*
* @throws Exception if there was a problem creating the symbols
*/
private void createSymbols() throws Exception {
DyldCacheLocalSymbolsInfo localSymbolsInfo = dyldCacheHeader.getLocalSymbolsInfo();
if (localSymbolsInfo != null) {
monitor.setMessage("Processing DYLD symbols...");
monitor.initialize(localSymbolsInfo.getNList().size());
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);
}
catch (Exception e) {
log.appendMsg(e.getMessage() + " " + nlist.getString());
}
}
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
}
/**
* Processes the DYLD Cache's DYLIB files. This will mark up the DYLIB files, added them to the
* program tree, and make memory blocks for them.
*
* @throws Exception if there was a problem processing the DYLIB files
*/
private void processDylibs() throws Exception {
// Create an "info" object for each DyldCache DYLIB, which will make processing them
// easier
monitor.setMessage("Parsing DYLIB's...");
monitor.initialize(dyldCacheHeader.getImageInfos().size());
TreeSet<DyldCacheMachoInfo> dyldCacheMachoInfoSet =
new TreeSet<>((a, b) -> a.headerAddr.compareTo(b.headerAddr));
for (DyldCacheImageInfo dyldCacheImageInfo : dyldCacheHeader.getImageInfos()) {
dyldCacheMachoInfoSet.add(new DyldCacheMachoInfo(provider,
dyldCacheImageInfo.getAddress() - dyldCacheHeader.getBaseAddress(),
space.getAddress(dyldCacheImageInfo.getAddress()), dyldCacheImageInfo.getPath()));
monitor.checkCanceled();
monitor.incrementProgress(1);
}
// Markup DyldCache Mach-O headers
monitor.setMessage("Marking up DYLIB headers...");
monitor.initialize(dyldCacheMachoInfoSet.size());
for (DyldCacheMachoInfo info : dyldCacheMachoInfoSet) {
info.markupHeaders();
monitor.checkCanceled();
monitor.incrementProgress(1);
}
// Add DyldCache Mach-O's to program tree
monitor.setMessage("Adding DYLIB's to program tree...");
monitor.initialize(dyldCacheMachoInfoSet.size());
Iterator<DyldCacheMachoInfo> iter = dyldCacheMachoInfoSet.iterator();
if (iter.hasNext()) {
DyldCacheMachoInfo curr = iter.next();
do {
DyldCacheMachoInfo next = iter.hasNext() ? iter.next() : null;
curr.addToProgramTree(next);
curr = next;
monitor.checkCanceled();
monitor.incrementProgress(1);
}
while (iter.hasNext());
}
// Process DyldCache DYLIB memory blocks. Need to do it in descending (reverse) order or
// the memory block splitting will be way too slow.
monitor.setMessage("Processing DYLIB memory blocks...");
monitor.initialize(dyldCacheMachoInfoSet.size());
Iterator<DyldCacheMachoInfo> descendingIter = dyldCacheMachoInfoSet.descendingIterator();
while (descendingIter.hasNext()) {
descendingIter.next().processMemoryBlocks();
monitor.checkCanceled();
monitor.incrementProgress(1);
}
}
/**
* Convenience class to store information we need about an individual Mach-O.
*/
private class DyldCacheMachoInfo {
private Address headerAddr;
private MachHeader header;
private String path;
private String name;
/**
* Creates a new {@link DyldCacheMachoInfo} object with the given parameters.
*
* @param provider The {@link ByteProvider} that contains the Mach-O's bytes
* @param offset The offset in the provider to the start of the Mach-O
* @param headerAddr The Mach-O's header address
* @param path The path of the Mach-O
* @throws Exception If there was a problem handling the Mach-O info
*/
public DyldCacheMachoInfo(ByteProvider provider, long offset, Address headerAddr,
String path) throws Exception {
this.headerAddr = headerAddr;
this.header = MachHeader.createMachHeader(MessageLogContinuesFactory.create(log),
provider, offset);
this.header.parse();
this.path = path;
this.name = new File(path).getName();
}
/**
* Processes memory blocks for this Mach-O.
*
* @throws Exception If there was a problem processing memory blocks for this Mach-O
* @see DyldCacheProgramBuilder#processMemoryBlocks(MachHeader, String, boolean, boolean)
*/
public void processMemoryBlocks() throws Exception {
DyldCacheProgramBuilder.this.processMemoryBlocks(header, name,
shouldCreateDylibSections, false);
}
/**
* Marks up the Mach-O headers.
*
* @throws Exception If there was a problem marking up the Mach-O's headers
* @see DyldCacheProgramBuilder#markupHeaders(MachHeader, Address)
*/
public void markupHeaders() throws Exception {
DyldCacheProgramBuilder.this.markupHeaders(header, headerAddr);
if (!name.isEmpty()) {
listing.setComment(headerAddr, CodeUnit.PLATE_COMMENT, path);
}
}
/**
* Adds an entry to the program tree for this Mach-O
*
* @param next The Mach-O that comes directly after this one. Could be null if this
* is the last one.
* @throws Exception If there was a problem adding this Mach-O to the program tree
*/
public void addToProgramTree(DyldCacheMachoInfo next) throws Exception {
ProgramFragment fragment = listing.getDefaultRootModule().createFragment(path);
if (next != null) {
fragment.move(headerAddr, next.headerAddr.subtract(1));
}
else {
// This is the last Mach-O, so we'll assume it ends where the mapping that contains
// it ends.
for (DyldCacheMappingInfo mappingInfo : dyldCacheHeader.getMappingInfos()) {
Address mappingAddr = space.getAddress(mappingInfo.getAddress());
if (headerAddr.compareTo(mappingAddr) >= 0 &&
headerAddr.compareTo(mappingAddr.add(mappingInfo.getSize() - 1)) <= 0) {
fragment.move(headerAddr, mappingAddr.add(mappingInfo.getSize() - 1));
}
}
}
}
}
}

View File

@ -13,16 +13,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.file.formats.ios.dyldcache;
package ghidra.app.util.opinion;
import java.io.IOException;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.macho.dyld.DyldArchitecture;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.exception.NotYetImplementedException;
public final class DyldCacheUtil {
/**
* Utilities methods for working with Mach-O DYLD shared cache binaries.
*/
public class DyldCacheUtils {
/**
* Determines if the given {@link Program} is a DYLD cache.
*
* @param program The {@link Program}
* @return True if the given {@link Program} is a DYLD cache; otherwise, false
*/
public final static boolean isDyldCache(Program program) {
if (program == null) {
return false;
@ -30,20 +41,45 @@ public final class DyldCacheUtil {
if (program.getMemory().getSize() < DyldArchitecture.DYLD_V1_SIGNATURE_LEN) {
return false;
}
byte [] bytes = new byte[ DyldArchitecture.DYLD_V1_SIGNATURE_LEN ];
byte[] bytes = new byte[DyldArchitecture.DYLD_V1_SIGNATURE_LEN];
try {
Address address = program.getMinAddress();
program.getMemory().getBytes(address, bytes);
}
catch (MemoryAccessException e) {
return false;
}
return isDyldCache(new String(bytes).trim());
}
/**
* Determines if the given {@link ByteProvider} is a DYLD cache.
*
* @param provider The {@link ByteProvider}
* @return True if the given {@link ByteProvider} is a DYLD cache; otherwise, false
*/
public final static boolean isDyldCache(ByteProvider provider) {
throw new NotYetImplementedException();
if (provider == null) {
return false;
}
byte[] bytes = new byte[DyldArchitecture.DYLD_V1_SIGNATURE_LEN];
try {
bytes = provider.readBytes(0, DyldArchitecture.DYLD_V1_SIGNATURE_LEN);
}
catch (IOException e) {
return false;
}
return isDyldCache(new String(bytes).trim());
}
/**
* Determines if the given signature represents a DYLD cache signature with an architecture we
* support.
*
* @param signature The DYLD cache signature
* @return True if the given signature represents a DYLD cache signature with an architecture we
* support; otherwise, false
*/
public final static boolean isDyldCache(String signature) {
for (DyldArchitecture architecture : DyldArchitecture.ARCHITECTURES) {
if (architecture.getSignature().equals(signature)) {

View File

@ -305,10 +305,10 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
*
* @throws Exception If there was a problem processing memory blocks for this PRELINK
* Mach-O.
* @see MachoPrelinkProgramBuilder#processMemoryBlocks(MachHeader, String, boolean)
* @see MachoPrelinkProgramBuilder#processMemoryBlocks(MachHeader, String, boolean, boolean)
*/
public void processMemoryBlocks() throws Exception {
MachoPrelinkProgramBuilder.this.processMemoryBlocks(header, name, false);
MachoPrelinkProgramBuilder.this.processMemoryBlocks(header, name, true, false);
}
/**

View File

@ -118,7 +118,7 @@ public class MachoProgramBuilder {
try {
setImageBase();
processEntryPoint();
processMemoryBlocks(machoHeader, provider.getName(), true);
processMemoryBlocks(machoHeader, provider.getName(), true, true);
processUnsupportedLoadCommands();
processSymbolTables();
processIndirectSymbols();
@ -206,20 +206,25 @@ public class MachoProgramBuilder {
*
* @param header The Mach-O header to process for memory block creation.
* @param source A name that represents where the memory blocks came from.
* @param processSections True to split segments into their sections.
* @param allowZeroAddr True if memory blocks at address 0 should be processed; otherwise,
* false.
* @throws Exception If there was a problem processing the memory blocks.
*/
protected void processMemoryBlocks(MachHeader header, String source, boolean allowZeroAddr)
throws Exception {
protected void processMemoryBlocks(MachHeader header, String source, boolean processSections,
boolean allowZeroAddr) throws Exception {
monitor.setMessage("Processing memory blocks for " + source + "...");
if (header.getFileType() == MachHeaderFileTypes.MH_DYLIB_STUB) {
return;
}
// Create memory blocks for segments
for (SegmentCommand segment : header.getAllSegments()) {
// Create memory blocks for segments. Create them in reverse order so the splitting
// is more efficient.
List<SegmentCommand> segments = header.getAllSegments();
segments.sort((SegmentCommand a, SegmentCommand b) -> Long.compare(b.getVMaddress(),
a.getVMaddress()));
for (SegmentCommand segment : segments) {
if (monitor.isCancelled()) {
break;
}
@ -251,25 +256,31 @@ public class MachoProgramBuilder {
}
// Create memory blocks for sections. They will be in the segments we just created, so the
// segment blocks will be split and possible replaced.
for (Section section : header.getAllSections()) {
if (monitor.isCancelled()) {
break;
}
if (section.getSize() > 0 && (allowZeroAddr || section.getAddress() != 0)) {
if (createMemoryBlock(section.getSectionName(),
space.getAddress(section.getAddress()), section.getOffset(), section.getSize(),
section.getSegmentName(), source, section.isRead(), section.isWrite(),
section.isExecute(), section.getType() == SectionTypes.S_ZEROFILL) == null) {
log.appendMsg(String.format("Failed to create block: %s.%s 0x%x 0x%x %s",
section.getSegmentName(), section.getSectionName(), section.getAddress(),
section.getSize(), source));
// segment blocks will be split and possibly replaced. Create them in reverse order so
// the splitting is more efficient.
if (processSections) {
List<Section> sections = header.getAllSections();
sections.sort((Section a, Section b) -> Long.compare(b.getAddress(), a.getAddress()));
for (Section section : sections) {
if (monitor.isCancelled()) {
break;
}
if (section.getSize() > 0 && (allowZeroAddr || section.getAddress() != 0)) {
if (createMemoryBlock(section.getSectionName(),
space.getAddress(section.getAddress()), section.getOffset(),
section.getSize(), section.getSegmentName(), source, section.isRead(),
section.isWrite(), section.isExecute(),
section.getType() == SectionTypes.S_ZEROFILL) == null) {
log.appendMsg(String.format("Failed to create block: %s.%s 0x%x 0x%x %s",
section.getSegmentName(), section.getSectionName(),
section.getAddress(), section.getSize(), source));
}
}
else {
log.appendMsg("Skipping section: " + section.getSegmentName() + "." +
section.getSectionName() + " (" + source + ")");
}
}
else {
log.appendMsg("Skipping section: " + section.getSegmentName() + "." +
section.getSectionName() + " (" + source + ")");
}
}
}
@ -527,7 +538,7 @@ public class MachoProgramBuilder {
}
else if (command instanceof SubLibraryCommand) {
SubLibraryCommand sublibCommand = (SubLibraryCommand) command;
addLibrary(sublibCommand.getSubLibraryName());
addLibrary(sublibCommand.getSubLibraryName().getString());
}
else if (command instanceof PreboundDynamicLibraryCommand) {
PreboundDynamicLibraryCommand pbdlCommand = (PreboundDynamicLibraryCommand) command;
@ -550,14 +561,14 @@ public class MachoProgramBuilder {
List<SubUmbrellaCommand> umbrellas = machoHeader.getLoadCommands(SubUmbrellaCommand.class);
for (int i = 0; i < umbrellas.size(); ++i) {
props.setString("Mach-O Sub-umbrella " + i,
umbrellas.get(i).getSubUmbrellaFrameworkName());
umbrellas.get(i).getSubUmbrellaFrameworkName().getString());
}
List<SubFrameworkCommand> frameworks =
machoHeader.getLoadCommands(SubFrameworkCommand.class);
for (int i = 0; i < frameworks.size(); ++i) {
props.setString("Mach-O Sub-framework " + i,
frameworks.get(i).getUmbrellaFrameworkName());
frameworks.get(i).getUmbrellaFrameworkName().getString());
}
}
@ -793,6 +804,34 @@ public class MachoProgramBuilder {
StructConverter.STRING, loadCommand.getCommandSize() - path.getOffset(),
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
}
else if (loadCommand instanceof SubFrameworkCommand) {
SubFrameworkCommand subFrameworkCommand = (SubFrameworkCommand) loadCommand;
LoadCommandString name = subFrameworkCommand.getUmbrellaFrameworkName();
DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()),
StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(),
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
}
else if (loadCommand instanceof SubClientCommand) {
SubClientCommand subClientCommand = (SubClientCommand) loadCommand;
LoadCommandString name = subClientCommand.getClientName();
DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()),
StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(),
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
}
else if (loadCommand instanceof SubLibraryCommand) {
SubLibraryCommand subLibraryCommand = (SubLibraryCommand) loadCommand;
LoadCommandString name = subLibraryCommand.getSubLibraryName();
DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()),
StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(),
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
}
else if (loadCommand instanceof SubUmbrellaCommand) {
SubUmbrellaCommand subUmbrellaCommand = (SubUmbrellaCommand) loadCommand;
LoadCommandString name = subUmbrellaCommand.getSubUmbrellaFrameworkName();
DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()),
StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(),
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
}
}
}
catch (CodeUnitInsertionException e) {

View File

@ -17,8 +17,12 @@ package ghidra.file.formats.ios.dyldcache;
import ghidra.app.cmd.formats.MachoBinaryAnalysisCommand;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.macho.dyld.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.BinaryLoader;
import ghidra.app.util.opinion.DyldCacheUtils;
import ghidra.file.analyzers.FileFormatAnalyzer;
import ghidra.framework.options.Options;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.DataType;
@ -53,22 +57,22 @@ public class DyldCacheAnalyzer extends FileFormatAnalyzer {
createFragment(program, headerDataType.getName(), headerData.getMinAddress(),
headerData.getMaxAddress().add(1));
reader.setPointerIndex(header.getStartAddress());
Address address = toAddr(program, header.getStartAddress());
reader.setPointerIndex(header.getImagesOffset());
Address address = toAddr(program, header.getImagesOffset());
for (int i = 0; i < header.getLibraryCount(); ++i) {
for (int i = 0; i < header.getImagesCount(); ++i) {
if (monitor.isCancelled()) {
break;
}
DyldCacheData data = new DyldCacheData(reader);
DyldCacheImageInfo data = new DyldCacheImageInfo(reader);
DataType dataDataType = data.toDataType();
Data dataData = createData(program, address, dataDataType);
createFragment(program, dataDataType.getName(), dataData.getMinAddress(),
dataData.getMaxAddress().add(1));
Address fileOffset = toAddr(program, data.getFileOffset());
Address fileOffset = toAddr(program, data.getAddress());
Data fileData = createData(program, fileOffset, new StringDataType());
createFragment(program, "LibraryNames", fileData.getMinAddress(),
fileData.getMaxAddress().add(1));
@ -76,7 +80,7 @@ public class DyldCacheAnalyzer extends FileFormatAnalyzer {
String filePath = (String) fileData.getValue();
Address libraryOffsetAddress =
toAddr(program, data.getLibraryOffset() - header.getBaseAddress());
toAddr(program, data.getAddress() - header.getBaseAddress());
MachoBinaryAnalysisCommand command = new MachoBinaryAnalysisCommand(
libraryOffsetAddress, false, program.getListing().getDefaultRootModule());
@ -102,12 +106,17 @@ public class DyldCacheAnalyzer extends FileFormatAnalyzer {
@Override
public boolean canAnalyze(Program program) {
return DyldCacheUtil.isDyldCache(program);
Options options = program.getOptions("Program Information");
String format = options.getString("Executable Format", null);
if (!BinaryLoader.BINARY_NAME.equals(format)) {
return false;
}
return DyldCacheUtils.isDyldCache(program);
}
@Override
public boolean getDefaultEnablement(Program program) {
return DyldCacheUtil.isDyldCache(program);
return DyldCacheUtils.isDyldCache(program);
}
@Override

View File

@ -1,65 +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.file.formats.ios.dyldcache;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
public class DyldCacheData implements StructConverter{
private long libraryOffset;
private long unknown0;
private long unknown1;
private long fileOffset;
private String _path;
public DyldCacheData(BinaryReader reader) throws IOException {
libraryOffset = reader.readNextLong();
unknown0 = reader.readNextLong();
unknown1 = reader.readNextLong();
fileOffset = reader.readNextLong();
_path = reader.readAsciiString( fileOffset );
}
public long getLibraryOffset() {
return libraryOffset;
}
public long getFileOffset() {
return fileOffset;
}
public long getUnknown(int index) {
switch (index) {
case 0: return unknown0;
case 1: return unknown1;
}
return -1;
}
public String getPath() {
return _path;
}
public DataType toDataType() throws DuplicateNameException, IOException {
return StructConverterUtil.toDataType(this);
}
}

View File

@ -22,6 +22,10 @@ import java.util.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.macho.MachException;
import ghidra.app.util.bin.format.macho.dyld.DyldCacheHeader;
import ghidra.app.util.bin.format.macho.dyld.DyldCacheImageInfo;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.DyldCacheUtils;
import ghidra.formats.gfilesystem.*;
import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
import ghidra.formats.gfilesystem.factory.GFileSystemBaseFactory;
@ -33,7 +37,7 @@ import ghidra.util.task.TaskMonitor;
public class DyldCacheFileSystem extends GFileSystemBase {
private DyldCacheHeader header;
private Map<GFile, DyldCacheData> map = new HashMap<>();
private Map<GFile, DyldCacheImageInfo> map = new HashMap<>();
public DyldCacheFileSystem(String fileSystemName, ByteProvider provider) {
super(fileSystemName, provider);
@ -47,11 +51,11 @@ public class DyldCacheFileSystem extends GFileSystemBase {
@Override
protected InputStream getData(GFile file, TaskMonitor monitor) throws IOException {
DyldCacheData data = map.get(file);
DyldCacheImageInfo data = map.get(file);
if (data == null) {
return null;
}
long machHeaderStartIndexInProvider = data.getLibraryOffset() - header.getBaseAddress();
long machHeaderStartIndexInProvider = data.getAddress() - header.getBaseAddress();
try {
/*
* //check to make sure mach-o header is valid MachHeader header =
@ -136,11 +140,7 @@ public class DyldCacheFileSystem extends GFileSystemBase {
@Override
public boolean isValid(TaskMonitor monitor) throws IOException {
BinaryReader reader = new BinaryReader(provider, true);
DyldCacheHeader header = new DyldCacheHeader(reader);
return DyldCacheUtil.isDyldCache(new String(header.getVersion()).trim());
return DyldCacheUtils.isDyldCache(provider);
}
@Override
@ -150,13 +150,13 @@ public class DyldCacheFileSystem extends GFileSystemBase {
BinaryReader reader = new BinaryReader(provider, true);
header = new DyldCacheHeader(reader);
header.parse(monitor);
header.parseFromFile(false, new MessageLog(), monitor);
List<DyldCacheData> dataList = header.getDataList();
List<DyldCacheImageInfo> dataList = header.getImageInfos();
monitor.initialize(dataList.size());
for (DyldCacheData data : dataList) {
for (DyldCacheImageInfo data : dataList) {
if (monitor.isCancelled()) {
break;
@ -168,11 +168,11 @@ public class DyldCacheFileSystem extends GFileSystemBase {
0/*TODO compute length?*/ );
storeFile(file, data);
file.setLength(provider.length() - (data.getLibraryOffset() - header.getBaseAddress()));
file.setLength(provider.length() - (data.getAddress() - header.getBaseAddress()));
}
}
private void storeFile(GFile file, DyldCacheData data) {
private void storeFile(GFile file, DyldCacheImageInfo data) {
if (file == null) {
return;
}

View File

@ -1,101 +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.file.formats.ios.dyldcache;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class DyldCacheHeader implements StructConverter {
private byte [] version;
private int baseAddressOffset;
private int unknown;
private int startAddress;
private int libraryCount;
private long dyldAddress;
private BinaryReader _reader;
private long _baseAddress;
private List<DyldCacheData> _list = new ArrayList<DyldCacheData>();
private DyldArchitecture _architecture;
public DyldCacheHeader(BinaryReader reader) throws IOException {
_reader = reader;
version = reader.readNextByteArray( 16 );
baseAddressOffset = reader.readNextInt();
unknown = reader.readNextInt();
startAddress = reader.readNextInt();
libraryCount = reader.readNextInt();
dyldAddress = reader.readNextLong();
_baseAddress = reader.readLong( baseAddressOffset & 0xffffffffL );
_architecture = DyldArchitecture.getArchitecture( new String( version ).trim() );
}
public void parse(TaskMonitor monitor) throws IOException {
_reader.setPointerIndex( startAddress );
for (int i = 0 ; i < libraryCount ; ++i) {
if (monitor.isCancelled()) {
break;
}
DyldCacheData data = new DyldCacheData( _reader );
_list.add( data );
}
}
public byte [] getVersion() {
return version;
}
public int getBaseAddressOffset() {
return baseAddressOffset;
}
public long getBaseAddress() {
return _baseAddress;
}
public int getUnknown() {
return unknown;
}
public int getStartAddress() {
return startAddress;
}
public int getLibraryCount() {
return libraryCount;
}
public long getDyldAddress() {
return dyldAddress;
}
public List<DyldCacheData> getDataList() {
return _list;
}
public DyldArchitecture getArchitecture() {
return _architecture;
}
public DataType toDataType() throws DuplicateNameException, IOException {
return StructConverterUtil.toDataType(this);
}
}

View File

@ -1,21 +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.file.formats.ios.dyldcache;
public class DyldCacheLibrary {
}

View File

@ -5,6 +5,9 @@
<constraint loader="Mac OS X Mach-O" compilerSpecID="default">
<constraint primary="16777228" processor="AARCH64" endian="little" size="64" />
</constraint>
<constraint loader="DYLD Cache" compilerSpecID="default">
<constraint primary="AARCH64" processor="AARCH64" endian="little" size="64" />
</constraint>
<constraint loader="Portable Executable (PE)" compilerSpecID="windows">
<constraint primary="43620" processor="AARCH64" endian="little" size="64" variant="v8A" />
</constraint>

View File

@ -26,6 +26,10 @@
<constraint primary="12.11" processor="ARM" endian="little" size="32" variant="v8" /><!-- ARM v8s -->
<constraint primary="12.12" processor="ARM" endian="little" size="32" variant="v8" /><!-- ARM v8k -->
</constraint>
<constraint loader="DYLD Cache" compilerSpecID="default">
<constraint primary="armv6" processor="ARM" endian="little" size="32" variant="v6" />
<constraint primary="arm7" processor="ARM" endian="little" size="32" variant="v7" />
</constraint>
<constraint loader="MS Common Object File Format (COFF)" compilerSpecID="windows">
<constraint primary="448" processor="ARM" endian="little" size="32" variant="v8" />
<constraint primary="450" processor="ARM" endian="little" size="32" variant="v8" />