diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC2_ClassAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC2_ClassAnalyzer.java index 87c25fdcd0..b5058dbf59 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC2_ClassAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC2_ClassAnalyzer.java @@ -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> objcBlockMap = new HashMap<>(); + for (MemoryBlock block : program.getMemory().getBlocks()) { + String name = block.getName(); + if (name.startsWith(ObjectiveC2_Constants.OBJC2_PREFIX)) { + List 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> 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 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> 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 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> 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 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> 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 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> 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 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> 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 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> 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 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> 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 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> 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 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> 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 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); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java index 83cc230cd1..07738020b1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java @@ -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); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/LibObjcDylib.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/LibObjcDylib.java new file mode 100644 index 0000000000..eca25ac2d0 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/LibObjcDylib.java @@ -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())); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/LibObjcOptimization.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/LibObjcOptimization.java new file mode 100644 index 0000000000..5bb0105a7c --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/LibObjcOptimization.java @@ -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 dyld/include/objc-shared-cache.h + */ +@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; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc2/ObjectiveC2_Constants.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc2/ObjectiveC2_Constants.java index e722d9a7c4..03e7b175c4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc2/ObjectiveC2_Constants.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc2/ObjectiveC2_Constants.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -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)) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc2/ObjectiveC2_Method.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc2/ObjectiveC2_Method.java index dfa60e6516..052b4fdcf5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc2/ObjectiveC2_Method.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc2/ObjectiveC2_Method.java @@ -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); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc2/ObjectiveC2_State.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc2/ObjectiveC2_State.java index 5b586cec1f..5e98c3fb74 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc2/ObjectiveC2_State.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc2/ObjectiveC2_State.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -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 variableMap = new HashMap(); + /** + * 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); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheLoader.java index e1f5cc5fbf..31bb607087 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheLoader.java @@ -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