GP-2113: Objective-C support in dyld_shared_cache

This commit is contained in:
Ryan Kurtz 2022-06-03 19:27:35 -04:00
parent d2883bbb8c
commit 1430d18f65
9 changed files with 564 additions and 242 deletions

View File

@ -15,8 +15,12 @@
*/
package ghidra.app.plugin.core.analysis;
import java.io.IOException;
import java.util.*;
import ghidra.app.services.*;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.macho.dyld.LibObjcOptimization;
import ghidra.app.util.bin.format.objc2.*;
import ghidra.app.util.bin.format.objectiveC.ObjectiveC1_Constants;
import ghidra.app.util.bin.format.objectiveC.ObjectiveC1_Utilities;
@ -32,8 +36,6 @@ import ghidra.program.model.symbol.Namespace;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import java.io.IOException;
public class ObjectiveC2_ClassAnalyzer extends AbstractAnalyzer {
private static final String NAME = "Objective-C 2 Class";
private static final String DESCRIPTION =
@ -47,11 +49,13 @@ public class ObjectiveC2_ClassAnalyzer extends AbstractAnalyzer {
setPriority(AnalysisPriority.FORMAT_ANALYSIS);
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
return processObjectiveC2(program, monitor, log);
}
@Override
public boolean canAnalyze(Program program) {
return ObjectiveC2_Constants.isObjectiveC2(program);
}
@ -60,27 +64,52 @@ public class ObjectiveC2_ClassAnalyzer extends AbstractAnalyzer {
/* ************************************************************************** */
private boolean processObjectiveC2(Program program, TaskMonitor monitor, MessageLog log) {
ByteProvider provider =
new MemoryByteProvider(program.getMemory(),
program.getAddressFactory().getDefaultAddressSpace());
BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian());
ObjectiveC2_State state =
new ObjectiveC2_State(program, monitor, ObjectiveC2_Constants.CATEGORY_PATH);
try {
processImageInfo(state, reader);
try (ByteProvider provider = new MemoryByteProvider(program.getMemory(),
program.getAddressFactory().getDefaultAddressSpace())) {
BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian());
processClassList(state, reader);
processCategoryList(state, reader);
processProtocolList(state, reader);
// Create a map of Objective-C specific memory blocks. If this is a dyld_shared_cache
// file, there will be many of each type.
Map<String, List<MemoryBlock>> objcBlockMap = new HashMap<>();
for (MemoryBlock block : program.getMemory().getBlocks()) {
String name = block.getName();
if (name.startsWith(ObjectiveC2_Constants.OBJC2_PREFIX)) {
List<MemoryBlock> list = objcBlockMap.get(name);
if (list == null) {
list = new ArrayList<>();
objcBlockMap.put(name, list);
}
list.add(block);
}
if (name.equals(LibObjcOptimization.SECTION_NAME)) {
// If this is a dyld_shared_cache, there should one of these. We'll need to
// save it so we can later extract selector/method names.
try {
state.libObjcOptimization =
new LibObjcOptimization(program, block.getStart());
}
catch (IOException e) {
log.appendMsg(
"Failed to parse libobjc. Method names may not be recoverable.");
}
}
}
processClassReferences(state);
processSuperReferences(state);
processProtocolReferences(state);
processNonLazyClassReferences(state);
processSelectorReferences(state);
processMessageReferences(state, reader);
processImageInfo(state, reader, objcBlockMap);
processClassList(state, reader, objcBlockMap);
processCategoryList(state, reader, objcBlockMap);
processProtocolList(state, reader, objcBlockMap);
processClassReferences(state, objcBlockMap);
processSuperReferences(state, objcBlockMap);
processProtocolReferences(state, objcBlockMap);
processNonLazyClassReferences(state, objcBlockMap);
processSelectorReferences(state, objcBlockMap);
processMessageReferences(state, reader, objcBlockMap);
ObjectiveC1_Utilities.createMethods(state);
ObjectiveC1_Utilities.createInstanceVariablesC2_OBJC2(state);
@ -96,12 +125,6 @@ public class ObjectiveC2_ClassAnalyzer extends AbstractAnalyzer {
}
finally {
state.dispose();
try {
provider.close();
}
catch (IOException e) {
}
}
return true;
}
@ -139,289 +162,313 @@ public class ObjectiveC2_ClassAnalyzer extends AbstractAnalyzer {
}
}
private void processProtocolReferences(ObjectiveC2_State state) throws Exception {
private void processProtocolReferences(ObjectiveC2_State state,
Map<String, List<MemoryBlock>> objcBlockMap) throws Exception {
state.monitor.setMessage("Objective-C 2.0 Protocol References...");
MemoryBlock block =
state.program.getMemory().getBlock(ObjectiveC2_Constants.OBJC2_PROTOCOL_REFS);
if (block == null) {
List<MemoryBlock> blocks = objcBlockMap.get(ObjectiveC2_Constants.OBJC2_PROTOCOL_REFS);
if (blocks == null) {
return;
}
ObjectiveC1_Utilities.clear(state, block);
long count = block.getSize() / state.pointerSize;
for (MemoryBlock block : blocks) {
ObjectiveC1_Utilities.clear(state, block);
state.monitor.initialize((int) count);
long count = block.getSize() / state.pointerSize;
Address address = block.getStart();
state.monitor.initialize((int) count);
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
Address address = block.getStart();
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
}
state.monitor.setProgress(i);
ObjectiveC1_Utilities.createPointerAndReturnAddressBeingReferenced(state.program,
address);
address = address.add(state.pointerSize);
}
state.monitor.setProgress(i);
ObjectiveC1_Utilities.createPointerAndReturnAddressBeingReferenced(state.program,
address);
address = address.add(state.pointerSize);
}
}
private void processClassReferences(ObjectiveC2_State state) throws Exception {
private void processClassReferences(ObjectiveC2_State state,
Map<String, List<MemoryBlock>> objcBlockMap) throws Exception {
state.monitor.setMessage("Objective-C 2.0 Class References...");
MemoryBlock block =
state.program.getMemory().getBlock(ObjectiveC2_Constants.OBJC2_CLASS_REFS);
if (block == null) {
List<MemoryBlock> blocks = objcBlockMap.get(ObjectiveC2_Constants.OBJC2_CLASS_REFS);
if (blocks == null) {
return;
}
ObjectiveC1_Utilities.clear(state, block);
long count = block.getSize() / state.pointerSize;
for (MemoryBlock block : blocks) {
ObjectiveC1_Utilities.clear(state, block);
state.monitor.initialize((int) count);
long count = block.getSize() / state.pointerSize;
Address address = block.getStart();
state.monitor.initialize((int) count);
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
Address address = block.getStart();
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
}
state.monitor.setProgress(i);
ObjectiveC1_Utilities.createPointerAndReturnAddressBeingReferenced(state.program,
address);
address = address.add(state.pointerSize);
}
state.monitor.setProgress(i);
ObjectiveC1_Utilities.createPointerAndReturnAddressBeingReferenced(state.program,
address);
address = address.add(state.pointerSize);
}
}
private void processNonLazyClassReferences(ObjectiveC2_State state) throws Exception {
private void processNonLazyClassReferences(ObjectiveC2_State state,
Map<String, List<MemoryBlock>> objcBlockMap) throws Exception {
state.monitor.setMessage("Objective-C 2.0 Non-lazy Class Lists...");
MemoryBlock block =
state.program.getMemory().getBlock(ObjectiveC2_Constants.OBJC2_NON_LAZY_CLASS_LIST);
if (block == null) {
List<MemoryBlock> blocks =
objcBlockMap.get(ObjectiveC2_Constants.OBJC2_NON_LAZY_CLASS_LIST);
if (blocks == null) {
return;
}
ObjectiveC1_Utilities.clear(state, block);
long count = block.getSize() / state.pointerSize;
for (MemoryBlock block : blocks) {
ObjectiveC1_Utilities.clear(state, block);
state.monitor.initialize((int) count);
long count = block.getSize() / state.pointerSize;
Address address = block.getStart();
state.monitor.initialize((int) count);
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
Address address = block.getStart();
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
}
state.monitor.setProgress(i);
ObjectiveC1_Utilities.createPointerAndReturnAddressBeingReferenced(state.program,
address);
address = address.add(state.pointerSize);
}
state.monitor.setProgress(i);
ObjectiveC1_Utilities.createPointerAndReturnAddressBeingReferenced(state.program,
address);
address = address.add(state.pointerSize);
}
}
private void processSuperReferences(ObjectiveC2_State state) throws Exception {
private void processSuperReferences(ObjectiveC2_State state,
Map<String, List<MemoryBlock>> objcBlockMap) throws Exception {
state.monitor.setMessage("Objective-C 2.0 Super References...");
MemoryBlock block =
state.program.getMemory().getBlock(ObjectiveC2_Constants.OBJC2_SUPER_REFS);
if (block == null) {
List<MemoryBlock> blocks = objcBlockMap.get(ObjectiveC2_Constants.OBJC2_SUPER_REFS);
if (blocks == null) {
return;
}
ObjectiveC1_Utilities.clear(state, block);
long count = block.getSize() / state.pointerSize;
for (MemoryBlock block : blocks) {
ObjectiveC1_Utilities.clear(state, block);
state.monitor.initialize((int) count);
long count = block.getSize() / state.pointerSize;
Address address = block.getStart();
state.monitor.initialize((int) count);
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
Address address = block.getStart();
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
}
state.monitor.setProgress(i);
ObjectiveC1_Utilities.createPointerAndReturnAddressBeingReferenced(state.program,
address);
address = address.add(state.pointerSize);
}
state.monitor.setProgress(i);
ObjectiveC1_Utilities.createPointerAndReturnAddressBeingReferenced(state.program,
address);
address = address.add(state.pointerSize);
}
}
private void processCategoryList(ObjectiveC2_State state, BinaryReader reader) throws Exception {
private void processCategoryList(ObjectiveC2_State state, BinaryReader reader,
Map<String, List<MemoryBlock>> objcBlockMap) throws Exception {
state.monitor.setMessage("Objective-C 2.0 Category Information...");
MemoryBlock block =
state.program.getMemory().getBlock(ObjectiveC2_Constants.OBJC2_CATEGORY_LIST);
if (block == null) {
List<MemoryBlock> blocks = objcBlockMap.get(ObjectiveC2_Constants.OBJC2_CATEGORY_LIST);
if (blocks == null) {
return;
}
ObjectiveC1_Utilities.clear(state, block);
for (MemoryBlock block : blocks) {
ObjectiveC1_Utilities.clear(state, block);
long count = block.getSize() / state.pointerSize;
long count = block.getSize() / state.pointerSize;
state.monitor.initialize((int) count);
state.monitor.initialize((int) count);
Address address = block.getStart();
Address address = block.getStart();
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
}
state.monitor.setProgress(i);
Address categoryAddress = ObjectiveC1_Utilities
.createPointerAndReturnAddressBeingReferenced(state.program, address);
reader.setPointerIndex(categoryAddress.getOffset());
ObjectiveC2_Category category = new ObjectiveC2_Category(state, reader);
category.applyTo();
address = address.add(state.pointerSize);
}
state.monitor.setProgress(i);
Address categoryAddress =
ObjectiveC1_Utilities.createPointerAndReturnAddressBeingReferenced(state.program,
address);
reader.setPointerIndex(categoryAddress.getOffset());
ObjectiveC2_Category category = new ObjectiveC2_Category(state, reader);
category.applyTo();
address = address.add(state.pointerSize);
}
}
private void processImageInfo(ObjectiveC2_State state, BinaryReader reader) throws Exception {
private void processImageInfo(ObjectiveC2_State state, BinaryReader reader,
Map<String, List<MemoryBlock>> objcBlockMap) throws Exception {
state.monitor.setMessage("Objective-C 2.0 Image Information...");
MemoryBlock block =
state.program.getMemory().getBlock(ObjectiveC2_Constants.OBJC2_IMAGE_INFO);
if (block == null) {
List<MemoryBlock> blocks = objcBlockMap.get(ObjectiveC2_Constants.OBJC2_IMAGE_INFO);
if (blocks == null) {
return;
}
Address address = block.getStart();
reader.setPointerIndex(address.getOffset());
ObjectiveC2_ImageInfo imageInfo = new ObjectiveC2_ImageInfo(state, reader);
imageInfo.applyTo();
for (MemoryBlock block : blocks) {
Address address = block.getStart();
reader.setPointerIndex(address.getOffset());
ObjectiveC2_ImageInfo imageInfo = new ObjectiveC2_ImageInfo(state, reader);
imageInfo.applyTo();
}
}
private void processProtocolList(ObjectiveC2_State state, BinaryReader reader) throws Exception {
private void processProtocolList(ObjectiveC2_State state, BinaryReader reader,
Map<String, List<MemoryBlock>> objcBlockMap) throws Exception {
state.monitor.setMessage("Objective-C 2.0 Protocol Information...");
MemoryBlock block =
state.program.getMemory().getBlock(ObjectiveC2_Constants.OBJC2_PROTOCOL_LIST);
if (block == null) {
List<MemoryBlock> blocks = objcBlockMap.get(ObjectiveC2_Constants.OBJC2_PROTOCOL_LIST);
if (blocks == null) {
return;
}
ObjectiveC1_Utilities.clear(state, block);
for (MemoryBlock block : blocks) {
ObjectiveC1_Utilities.clear(state, block);
long count = block.getSize() / state.pointerSize;
long count = block.getSize() / state.pointerSize;
state.monitor.initialize((int) count);
state.monitor.initialize((int) count);
Address address = block.getStart();
Address address = block.getStart();
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
}
state.monitor.setProgress(i);
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
}
state.monitor.setProgress(i);
Address protocolAddress =
ObjectiveC1_Utilities.createPointerAndReturnAddressBeingReferenced(state.program,
address);
reader.setPointerIndex(protocolAddress.getOffset());
Address protocolAddress = ObjectiveC1_Utilities
.createPointerAndReturnAddressBeingReferenced(state.program, address);
reader.setPointerIndex(protocolAddress.getOffset());
ObjectiveC2_Protocol protocol = new ObjectiveC2_Protocol(state, reader);
Namespace namespace =
ObjectiveC1_Utilities.createNamespace(state.program,
ObjectiveC2_Protocol protocol = new ObjectiveC2_Protocol(state, reader);
Namespace namespace = ObjectiveC1_Utilities.createNamespace(state.program,
ObjectiveC1_Constants.NAMESPACE, "Protocols", protocol.getName());
protocol.applyTo(namespace);
address = address.add(state.pointerSize);
protocol.applyTo(namespace);
address = address.add(state.pointerSize);
}
}
}
private void processClassList(ObjectiveC2_State state, BinaryReader reader) throws Exception {
private void processClassList(ObjectiveC2_State state, BinaryReader reader,
Map<String, List<MemoryBlock>> objcBlockMap) throws Exception {
state.monitor.setMessage("Objective-C 2.0 Class Information...");
MemoryBlock block =
state.program.getMemory().getBlock(ObjectiveC2_Constants.OBJC2_CLASS_LIST);
if (block == null) {
List<MemoryBlock> blocks = objcBlockMap.get(ObjectiveC2_Constants.OBJC2_CLASS_LIST);
if (blocks == null) {
return;
}
ObjectiveC1_Utilities.clear(state, block);
for (MemoryBlock block : blocks) {
ObjectiveC1_Utilities.clear(state, block);
long count = block.getSize() / state.pointerSize;
long count = block.getSize() / state.pointerSize;
state.monitor.initialize((int) count);
state.monitor.initialize((int) count);
Address address = block.getStart();
Address address = block.getStart();
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
}
state.monitor.setProgress(i);
Address classAddress = ObjectiveC1_Utilities
.createPointerAndReturnAddressBeingReferenced(state.program, address);
reader.setPointerIndex(classAddress.getOffset() & 0xfffffffffffL);
ObjectiveC2_Class clazz = new ObjectiveC2_Class(state, reader);
clazz.applyTo();
address = address.add(state.pointerSize);
}
state.monitor.setProgress(i);
Address classAddress =
ObjectiveC1_Utilities.createPointerAndReturnAddressBeingReferenced(state.program,
address);
reader.setPointerIndex(classAddress.getOffset());
ObjectiveC2_Class clazz = new ObjectiveC2_Class(state, reader);
clazz.applyTo();
address = address.add(state.pointerSize);
}
}
private void processMessageReferences(ObjectiveC2_State state, BinaryReader reader)
private void processMessageReferences(ObjectiveC2_State state, BinaryReader reader,
Map<String, List<MemoryBlock>> objcBlockMap)
throws Exception {
state.monitor.setMessage("Objective-C 2.0 Message References...");
MemoryBlock block =
state.program.getMemory().getBlock(ObjectiveC2_Constants.OBJC2_MESSAGE_REFS);
if (block == null) {
List<MemoryBlock> blocks = objcBlockMap.get(ObjectiveC2_Constants.OBJC2_MESSAGE_REFS);
if (blocks == null) {
return;
}
ObjectiveC1_Utilities.clear(state, block);
long count = block.getSize() / ObjectiveC2_MessageReference.SIZEOF(state);
for (MemoryBlock block : blocks) {
ObjectiveC1_Utilities.clear(state, block);
state.monitor.initialize((int) count);
long count = block.getSize() / ObjectiveC2_MessageReference.SIZEOF(state);
Address address = block.getStart();
state.monitor.initialize((int) count);
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
Address address = block.getStart();
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
}
state.monitor.setProgress(i);
reader.setPointerIndex(address.getOffset());
ObjectiveC2_MessageReference messageRef =
new ObjectiveC2_MessageReference(state, reader);
DataType dt = messageRef.toDataType();
Data messageRefData = state.program.getListing().createData(address, dt);
Data selData = messageRefData.getComponent(1);
Object selAddress = selData.getValue();
Data selStringData = state.program.getListing().getDataAt((Address) selAddress);
Object selString = selStringData.getValue();
ObjectiveC1_Utilities.createSymbol(state.program, null,
selString + "_" + ObjectiveC2_MessageReference.NAME, address);
address = address.add(dt.getLength());
}
state.monitor.setProgress(i);
reader.setPointerIndex(address.getOffset());
ObjectiveC2_MessageReference messageRef =
new ObjectiveC2_MessageReference(state, reader);
DataType dt = messageRef.toDataType();
Data messageRefData = state.program.getListing().createData(address, dt);
Data selData = messageRefData.getComponent(1);
Object selAddress = selData.getValue();
Data selStringData = state.program.getListing().getDataAt((Address) selAddress);
Object selString = selStringData.getValue();
ObjectiveC1_Utilities.createSymbol(state.program, null, selString + "_" +
ObjectiveC2_MessageReference.NAME, address);
address = address.add(dt.getLength());
}
}
private void processSelectorReferences(ObjectiveC2_State state) throws Exception {
private void processSelectorReferences(ObjectiveC2_State state,
Map<String, List<MemoryBlock>> objcBlockMap) throws Exception {
state.monitor.setMessage("Objective-C 2.0 Selector References...");
MemoryBlock block =
state.program.getMemory().getBlock(ObjectiveC2_Constants.OBJC2_SELECTOR_REFS);
if (block == null) {
List<MemoryBlock> blocks = objcBlockMap.get(ObjectiveC2_Constants.OBJC2_SELECTOR_REFS);
if (blocks == null) {
return;
}
ObjectiveC1_Utilities.clear(state, block);
long count = block.getSize() / state.pointerSize;
for (MemoryBlock block : blocks) {
ObjectiveC1_Utilities.clear(state, block);
state.monitor.initialize((int) count);
long count = block.getSize() / state.pointerSize;
Address address = block.getStart();
state.monitor.initialize((int) count);
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
Address address = block.getStart();
for (int i = 0; i < count; ++i) {
if (state.monitor.isCancelled()) {
break;
}
state.monitor.setProgress(i);
ObjectiveC1_Utilities.createPointerAndReturnAddressBeingReferenced(state.program,
address);
address = address.add(state.pointerSize);
}
state.monitor.setProgress(i);
ObjectiveC1_Utilities.createPointerAndReturnAddressBeingReferenced(state.program,
address);
address = address.add(state.pointerSize);
}
}

View File

@ -696,9 +696,8 @@ public class DyldCacheHeader implements StructConverter {
}
monitor.setMessage("Parsing DYLD accelerateor info...");
monitor.initialize(imagesTextCount);
try {
Address addr = space.getAddress(accelerateInfoAddr);
ByteProvider bytes = new MemoryByteProvider(program.getMemory(), addr);
Address addr = space.getAddress(accelerateInfoAddr);
try (ByteProvider bytes = new MemoryByteProvider(program.getMemory(), addr)) {
BinaryReader memoryReader =
new BinaryReader(bytes, !program.getLanguage().isBigEndian());
accelerateInfo = new DyldCacheAccelerateInfo(memoryReader);

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.format.macho.MachHeader;
import ghidra.app.util.bin.format.macho.Section;
import ghidra.app.util.bin.format.macho.commands.SegmentNames;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Program;
import ghidra.util.task.TaskMonitor;
/**
* A class to represent the libobjc DYLIB Mach-O that resides within a DYLD cache
*/
public class LibObjcDylib {
private MachHeader libObjcHeader;
private Program program;
private AddressSpace space;
private MessageLog log;
private TaskMonitor monitor;
private LibObjcOptimization libObjcOptimization;
/**
* Creates a new {@link LibObjcDylib}
*
* @param libObjcHeader The libobjc DYLIB header
* @param program The {@link Program}
* @param space The {@link AddressSpace}
* @param log The log
* @param monitor A cancelable task monitor
* @throws IOException if an IO-related error occurred while parsing
*/
public LibObjcDylib(MachHeader libObjcHeader, Program program, AddressSpace space,
MessageLog log, TaskMonitor monitor) throws IOException {
this.libObjcHeader = libObjcHeader;
this.program = program;
this.space = space;
this.log = log;
this.monitor = monitor;
libObjcOptimization = parseLibObjcOptimization();
}
/**
* Marks up the libobjc DYLIB
*/
public void markup() {
if (libObjcOptimization != null) {
libObjcOptimization.markup(program, space, log, monitor);
}
}
/**
* Parses the objc_opt_t structure which lives at the start of the __objc_opt_ro section in the
* libobjc DYLIB
*
* @return The parsed {@link LibObjcOptimization objc_opt_t} structure, or null if it doesn't
* exist
* @throws IOException if an IO-related error occurred while parsing
*/
private LibObjcOptimization parseLibObjcOptimization() throws IOException {
Section section =
libObjcHeader.getSection(SegmentNames.SEG_TEXT, LibObjcOptimization.SECTION_NAME);
if (section == null) {
return null;
}
return new LibObjcOptimization(program, space.getAddress(section.getAddress()));
}
}

View File

@ -0,0 +1,177 @@
/* ###
* 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.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a objc_opt_t structure, which resides in the libobjc DYLIB within a DYLD cache
*
* @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/objc-shared-cache.h">dyld/include/objc-shared-cache.h</a>
*/
@SuppressWarnings("unused")
public class LibObjcOptimization implements StructConverter {
/**
* The name of the section that contains the objc_opt_t_structure
*/
public final static String SECTION_NAME = "__objc_opt_ro";
private int version;
private int flags;
private int selopt_offset;
private int headeropt_ro_offset;
private int clsopt_offset;
private int protocolopt1_offset;
private int headeropt_rw_offset;
private int protocolopt2_offset;
private int largeSharedCachesClassOffset;
private int largeSharedCachesProtocolOffset;
private long relativeMethodSelectorBaseAddressOffset;
private long objcOptAddr;
/**
* Create a new {@link LibObjcOptimization}.
*
* @param program The {@link Program}
* @param objcOptRoSectionAddr The start address of the __objc_opt_ro section
* @throws IOException if there was an IO-related problem parsing the structure
*/
public LibObjcOptimization(Program program, Address objcOptRoSectionAddr) throws IOException {
try (ByteProvider provider =
new MemoryByteProvider(program.getMemory(), objcOptRoSectionAddr)) {
BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian());
version = reader.readNextInt();
if (version <= 14) {
selopt_offset = reader.readNextInt();
headeropt_ro_offset = reader.readNextInt();
clsopt_offset = reader.readNextInt();
if (version >= 13) {
protocolopt1_offset = reader.readNextInt();
}
}
else {
flags = reader.readNextInt();
selopt_offset = reader.readNextInt();
headeropt_ro_offset = reader.readNextInt();
clsopt_offset = reader.readNextInt();
protocolopt1_offset = reader.readNextInt();
headeropt_rw_offset = reader.readNextInt();
protocolopt2_offset = reader.readNextInt();
if (version >= 16) {
largeSharedCachesClassOffset = reader.readNextInt();
largeSharedCachesProtocolOffset = reader.readNextInt();
relativeMethodSelectorBaseAddressOffset = reader.readNextLong();
}
}
}
objcOptAddr = objcOptRoSectionAddr.getOffset();
}
/**
* Gets the address of the objc_opt_t structure
*
* @return The address of the objc_opt_t structure
*/
public long getAddr() {
return objcOptAddr;
}
/**
* Gets the relative method selector base address offset. This will be 0 if the version < 16.
*
* @return The relative method selector base address offset
*/
public long getRelativeSelectorBaseAddressOffset() {
return relativeMethodSelectorBaseAddressOffset;
}
/**
* Marks up up this structure in memory
*
* @param program The {@link Program}
* @param space The {@link AddressSpace}
* @param log The log
* @param monitor A cancelable task monitor
*/
public void markup(Program program, AddressSpace space, MessageLog log, TaskMonitor monitor) {
Address addr = space.getAddress(getAddr());
try {
DataUtilities.createData(program, addr, toDataType(), -1, false,
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
}
catch (CodeUnitInsertionException | DuplicateNameException | IOException e) {
log.appendMsg(LibObjcOptimization.class.getSimpleName(),
"Failed to markup objc_opt_t.");
}
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("objc_opt_t", 0);
if (version <= 12) {
struct.add(DWORD, "version", "");
struct.add(DWORD, "selopt_offset", "");
struct.add(DWORD, "headeropt_offset", "");
struct.add(DWORD, "clsopt_offset", "");
}
else if (version >= 13 && version <= 14) {
struct.add(DWORD, "version", "");
struct.add(DWORD, "selopt_offset", "");
struct.add(DWORD, "headeropt_offset", "");
struct.add(DWORD, "clsopt_offset", "");
struct.add(DWORD, "protocolopt_offset", "");
}
else if (version == 15) {
struct.add(DWORD, "version", "");
struct.add(DWORD, "flags", "");
struct.add(DWORD, "selopt_offset", "");
struct.add(DWORD, "headeropt_ro_offset", "");
struct.add(DWORD, "clsopt_offset", "");
struct.add(DWORD, "unused_protocolopt_offset", "");
struct.add(DWORD, "headeropt_rw_offset", "");
struct.add(DWORD, "protocolopt_offset", "");
}
else { // version >= 16
struct.add(DWORD, "version", "");
struct.add(DWORD, "flags", "");
struct.add(DWORD, "selopt_offset", "");
struct.add(DWORD, "headeropt_ro_offset", "");
struct.add(DWORD, "unused_clsopt_offset", "");
struct.add(DWORD, "unused_protocolopt_offset", "");
struct.add(DWORD, "headeropt_rw_offset", "");
struct.add(DWORD, "unused_protocolopt2_offset", "");
struct.add(DWORD, "largeSharedCachesClassOffset", "");
struct.add(DWORD, "largeSharedCachesProtocolOffset", "");
struct.add(QWORD, "relativeMethodSelectorBaseAddressOffset", "");
}
return struct;
}
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,21 +15,22 @@
*/
package ghidra.app.util.bin.format.objc2;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.opinion.DyldCacheLoader;
import ghidra.app.util.opinion.MachoLoader;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public final class ObjectiveC2_Constants {
/**
* The name prefix of all Objective-C 2 sections.
*/
private final static String OBJC2_PREFIX = "__objc_";
public final static String OBJC2_PREFIX = "__objc_";
/** Objective-C 2 category list. */
public final static String OBJC2_CATEGORY_LIST = "__objc_catlist";
@ -84,7 +84,8 @@ public final class ObjectiveC2_Constants {
*/
public final static boolean isObjectiveC2(Program program) {
String format = program.getExecutableFormat();
if (MachoLoader.MACH_O_NAME.equals(format)) {
if (MachoLoader.MACH_O_NAME.equals(format) ||
DyldCacheLoader.DYLD_CACHE_NAME.equals(format)) {
MemoryBlock [] blocks = program.getMemory().getBlocks();
for (MemoryBlock memoryBlock : blocks) {
if (memoryBlock.getName().startsWith(OBJC2_PREFIX)) {

View File

@ -42,7 +42,20 @@ public class ObjectiveC2_Method extends ObjectiveC_Method {
namePtr = reader.readInt(_index + nameOffset);
}
else {
namePtr = reader.readLong(_index + nameOffset);
if (state.libObjcOptimization != null) {
// We are in a DYLD Cache
if (state.libObjcOptimization.getRelativeSelectorBaseAddressOffset() > 0) {
namePtr = state.libObjcOptimization.getAddr() +
state.libObjcOptimization.getRelativeSelectorBaseAddressOffset() +
nameOffset;
}
else {
namePtr = _index + nameOffset;
}
}
else {
namePtr = reader.readLong(_index + nameOffset);
}
}
name = reader.readAsciiString(namePtr);

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,14 +15,15 @@
*/
package ghidra.app.util.bin.format.objc2;
import java.util.*;
import ghidra.app.util.bin.format.macho.dyld.LibObjcOptimization;
import ghidra.app.util.bin.format.objectiveC.ObjectiveC1_State;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.listing.Program;
import ghidra.util.task.TaskMonitor;
import java.util.*;
public class ObjectiveC2_State extends ObjectiveC1_State {
/**
@ -36,6 +36,11 @@ public class ObjectiveC2_State extends ObjectiveC1_State {
*/
public final Map<Address, ObjectiveC2_InstanceVariable> variableMap = new HashMap<Address, ObjectiveC2_InstanceVariable>();
/**
* The dyld_shared_cache libobjc objc_opt_t structure, if it exists
*/
public LibObjcOptimization libObjcOptimization = null;
public ObjectiveC2_State(Program program, TaskMonitor monitor, CategoryPath categoryPath) {
super(program, monitor, categoryPath);
}

View File

@ -45,9 +45,6 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader {
/** Loader option to create memory blocks for DYLIB sections */
static final String CREATE_DYLIB_SECTIONS_OPTION_NAME = "Create DYLIB section memory blocks";
/** Default value for loader option to create memory blocks for DYLIB sections */
static final boolean CREATE_DYLIB_SECTIONS_OPTION_DEFAULT = false;
/** Loader option to add relocation entries for chained fixups */
static final String ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_NAME =
"Add relocation entries for chained fixups";
@ -97,9 +94,8 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader {
try {
DyldCacheProgramBuilder.buildProgram(program, provider,
MemoryBlockUtils.createFileBytes(program, provider, monitor),
shouldProcessSymbols(options), shouldCreateDylibSections(options),
shouldAddChainedFixupsRelocations(options), shouldCombineSplitFiles(options), log,
monitor);
shouldProcessSymbols(options), shouldAddChainedFixupsRelocations(options),
shouldCombineSplitFiles(options), log, monitor);
}
catch (CancelledException e) {
return;
@ -117,9 +113,6 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader {
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"));
list.add(new Option(ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_NAME,
ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_DEFAULT, Boolean.class,
Loader.COMMAND_LINE_ARG_PREFIX + "-addChainedFixupsRelocations"));
@ -134,11 +127,6 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader {
PROCESS_SYMBOLS_OPTION_DEFAULT);
}
private boolean shouldCreateDylibSections(List<Option> options) {
return OptionUtils.getOption(CREATE_DYLIB_SECTIONS_OPTION_NAME, options,
CREATE_DYLIB_SECTIONS_OPTION_DEFAULT);
}
private boolean shouldAddChainedFixupsRelocations(List<Option> options) {
return OptionUtils.getOption(ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_NAME, options,
ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_DEFAULT);

View File

@ -45,7 +45,6 @@ import ghidra.util.task.TaskMonitor;
public class DyldCacheProgramBuilder extends MachoProgramBuilder {
private boolean shouldProcessSymbols;
private boolean shouldCreateDylibSections;
private boolean shouldCombineSplitFiles;
/**
@ -55,8 +54,6 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
* @param provider The {@link ByteProvider} that contains the DYLD Cache bytes
* @param fileBytes Where the DYLD Cache's bytes came from
* @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 shouldAddChainedFixupsRelocations True if relocations should be added for chained
* fixups; otherwise, false
* @param shouldCombineSplitFiles True if split DYLD Cache files should be automatically
@ -65,12 +62,10 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
* @param monitor A cancelable task monitor
*/
protected DyldCacheProgramBuilder(Program program, ByteProvider provider, FileBytes fileBytes,
boolean shouldProcessSymbols, boolean shouldCreateDylibSections,
boolean shouldAddChainedFixupsRelocations, boolean shouldCombineSplitFiles,
MessageLog log, TaskMonitor monitor) {
boolean shouldProcessSymbols, boolean shouldAddChainedFixupsRelocations,
boolean shouldCombineSplitFiles, MessageLog log, TaskMonitor monitor) {
super(program, provider, fileBytes, shouldAddChainedFixupsRelocations, log, monitor);
this.shouldProcessSymbols = shouldProcessSymbols;
this.shouldCreateDylibSections = shouldCreateDylibSections;
this.shouldCombineSplitFiles = shouldCombineSplitFiles;
}
@ -81,8 +76,6 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
* @param provider The {@link ByteProvider} that contains the DYLD Cache's bytes
* @param fileBytes Where the Mach-O's bytes came from
* @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 shouldAddChainedFixupsRelocations True if relocations should be added for chained
* fixups; otherwise, false
* @param shouldCombineSplitFiles True if split DYLD Cache files should be automatically
@ -92,12 +85,11 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
* @throws Exception if a problem occurs
*/
public static void buildProgram(Program program, ByteProvider provider, FileBytes fileBytes,
boolean shouldProcessSymbols, boolean shouldCreateDylibSections,
boolean shouldAddChainedFixupsRelocations, boolean shouldCombineSplitFiles,
MessageLog log, TaskMonitor monitor) throws Exception {
boolean shouldProcessSymbols, boolean shouldAddChainedFixupsRelocations,
boolean shouldCombineSplitFiles, MessageLog log, TaskMonitor monitor) throws Exception {
DyldCacheProgramBuilder dyldCacheProgramBuilder = new DyldCacheProgramBuilder(program,
provider, fileBytes, shouldProcessSymbols, shouldCreateDylibSections,
shouldAddChainedFixupsRelocations, shouldCombineSplitFiles, log, monitor);
provider, fileBytes, shouldProcessSymbols, shouldAddChainedFixupsRelocations,
shouldCombineSplitFiles, log, monitor);
dyldCacheProgramBuilder.build();
}
@ -294,16 +286,21 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
private void processDylibs(SplitDyldCache splitDyldCache, DyldCacheHeader dyldCacheHeader,
ByteProvider bp, boolean localSymbolsPresent) throws Exception {
// Create an "info" object for each DyldCache DYLIB, which will make processing them
// easier
// easier. Save off the "libobjc" DYLIB for additional processing later.
monitor.setMessage("Parsing DYLIB's...");
DyldCacheMachoInfo libobjcInfo = null;
TreeSet<DyldCacheMachoInfo> infoSet =
new TreeSet<>((a, b) -> a.headerAddr.compareTo(b.headerAddr));
List<DyldCacheImage> mappedImages = dyldCacheHeader.getMappedImages();
monitor.initialize(mappedImages.size());
for (DyldCacheImage mappedImage : mappedImages) {
infoSet.add(new DyldCacheMachoInfo(splitDyldCache, bp,
DyldCacheMachoInfo info = new DyldCacheMachoInfo(splitDyldCache, bp,
mappedImage.getAddress() - dyldCacheHeader.getBaseAddress(),
space.getAddress(mappedImage.getAddress()), mappedImage.getPath()));
space.getAddress(mappedImage.getAddress()), mappedImage.getPath());
infoSet.add(info);
if (libobjcInfo == null && info.name.contains("libobjc.")) {
libobjcInfo = info;
}
monitor.checkCanceled();
monitor.incrementProgress(1);
}
@ -328,7 +325,6 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
monitor.incrementProgress(1);
}
}
// Markup DyldCache Mach-O headers
monitor.setMessage("Marking up DYLIB headers...");
@ -348,7 +344,7 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
monitor.incrementProgress(1);
}
// Process DyldCache DYLIB memory blocks.
// Process DyldCache DYLIB memory blocks
monitor.setMessage("Processing DYLIB memory blocks...");
monitor.initialize(infoSet.size());
for (DyldCacheMachoInfo info : infoSet) {
@ -356,6 +352,16 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
monitor.checkCanceled();
monitor.incrementProgress(1);
}
// Process and markup the libobjc DYLIB
monitor.setMessage("Processing libobjc...");
DyldCacheMachoInfo libObjcInfo =
infoSet.stream().filter(e -> e.name.contains("libobjc.")).findAny().orElse(null);
if (libObjcInfo != null) {
LibObjcDylib libObjcDylib =
new LibObjcDylib(libObjcInfo.header, program, space, log, monitor);
libObjcDylib.markup();
}
}
/**
@ -393,8 +399,7 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
* @see DyldCacheProgramBuilder#processMemoryBlocks(MachHeader, String, boolean, boolean)
*/
public void processMemoryBlocks() throws Exception {
DyldCacheProgramBuilder.this.processMemoryBlocks(header, name,
shouldCreateDylibSections, false);
DyldCacheProgramBuilder.this.processMemoryBlocks(header, name, true, false);
}
/**