mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-21 10:21:55 +08:00
Merge remote-tracking branch
'origin/GP-6347_ryanmkurtz_sourcelanguages--SQUASHED' (Closes #8740)
This commit is contained in:
@@ -236,7 +236,6 @@ data/typeinfo/golang/patchverdiffs/go1.24.4.json.diff||GHIDRA||||END|
|
||||
data/typeinfo/golang/patchverdiffs/go1.24.5.json.diff||GHIDRA||||END|
|
||||
data/typeinfo/golang/patchverdiffs/go1.24.6.json.diff||GHIDRA||||END|
|
||||
data/typeinfo/mac_10.9/mac_osx.gdt||GHIDRA||||END|
|
||||
data/typeinfo/rust/rust-common.gdt||GHIDRA||||END|
|
||||
data/typeinfo/win32/msvcrt/clsids.txt||GHIDRA||reviewed||END|
|
||||
data/typeinfo/win32/msvcrt/guids.txt||GHIDRA||reviewed||END|
|
||||
data/typeinfo/win32/msvcrt/iids.txt||GHIDRA||||END|
|
||||
|
||||
@@ -5,6 +5,9 @@ FieldFactory
|
||||
FieldMouseHandler
|
||||
StringHandler
|
||||
Loader
|
||||
SourceLanguage
|
||||
SourceLanguageDataArchive
|
||||
SourceLanguageSpecExtension
|
||||
TableRowMapper
|
||||
ScriptProvider
|
||||
Recognizer
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
//Removes a source language from the program
|
||||
//@category Program
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.app.util.sourcelanguage.SourceLanguageID;
|
||||
|
||||
public class RemoveSourceLanguage extends GhidraScript {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
if (currentProgram == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<SourceLanguageID> idSet = currentProgram.getSourceLanguageIDs();
|
||||
if (idSet.isEmpty()) {
|
||||
println("No source languages found in " + currentProgram);
|
||||
}
|
||||
|
||||
Set<SourceLanguageID> selected = new HashSet<>(askChoices("Remove Source Language(s)",
|
||||
"Select source languages to remove from the program", new ArrayList<>(idSet)));
|
||||
|
||||
// Remove from the "Source Language" program property
|
||||
idSet.removeIf(selected::contains);
|
||||
currentProgram.setSourceLanguageIDs(idSet);
|
||||
|
||||
// Future: Remove associated spec extensions if possible
|
||||
|
||||
// Future: Remove associated data archives if possible
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -27,6 +27,7 @@ import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.sourcelanguage.SourceLanguageService;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.options.OptionType;
|
||||
@@ -194,6 +195,38 @@ public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add source language data archives
|
||||
result.addAll(getSourceLanguageDTMs(program, log, monitor));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<DataTypeManager> getSourceLanguageDTMs(Program program, MessageLog log,
|
||||
TaskMonitor monitor) {
|
||||
List<DataTypeManager> result = new ArrayList<>();
|
||||
for (ResourceFile file : SourceLanguageService.getDataArchives(program,
|
||||
program.getSourceLanguageIDs(), log, monitor)) {
|
||||
if (monitor.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
DataTypeManager dtm = dtmService.openArchive(file, false);
|
||||
result.add(dtm);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof VersionException) {
|
||||
log.appendMsg("Apply Data Archives",
|
||||
"Unable to open archive %s: %s".formatted(file, cause.toString()));
|
||||
}
|
||||
else {
|
||||
String msg = Objects.requireNonNullElse(e.getMessage(), e.toString());
|
||||
log.appendMsg("Apply Data Archives",
|
||||
"Unexpected Error opening archive %s: %s".formatted(file, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/* ###
|
||||
* 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.plugin.core.analysis;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.sourcelanguage.SourceLanguageID;
|
||||
import ghidra.app.util.sourcelanguage.SourceLanguageService;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Adds/updates source language-specific support to the program
|
||||
*/
|
||||
public class SourceLanguageAnalyzer extends AbstractAnalyzer {
|
||||
|
||||
private final static String NAME = "Source Language Support";
|
||||
private final static String DESCRIPTION =
|
||||
"Adds/updates source language-specific support to the program";
|
||||
|
||||
private static String OPTION_NAME_SPEC_EXTENSIONS = "Add specification extensions";
|
||||
private static String OPTION_DESC_SPEC_EXTENSIONS =
|
||||
"Add any source language-specific specification extensions to the program.";
|
||||
private static boolean OPTION_DEFAULT_SPEC_EXTENSIONS = true;
|
||||
|
||||
private AtomicBoolean analysisStarted = new AtomicBoolean(false);
|
||||
private boolean addSpecExtensions = OPTION_DEFAULT_SPEC_EXTENSIONS;
|
||||
|
||||
/**
|
||||
* Creates a new {@link SourceLanguageAnalyzer}
|
||||
*/
|
||||
public SourceLanguageAnalyzer() {
|
||||
super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
|
||||
setPriority(AnalysisPriority.FORMAT_ANALYSIS.before().before().before().before().before());
|
||||
setDefaultEnablement(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
||||
throws CancelledException {
|
||||
|
||||
// Enforce that the below code only executes once per analysis session
|
||||
if (analysisStarted.getAndSet(true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Look for new source languages in the program
|
||||
Set<SourceLanguageID> ids = SourceLanguageService.find(program, log, monitor);
|
||||
|
||||
// Update the "Source Languages" program property
|
||||
program.setSourceLanguageIDs(ids);
|
||||
|
||||
// Optionally add source language spec extensions
|
||||
if (addSpecExtensions) {
|
||||
if (program.hasExclusiveAccess()) {
|
||||
SourceLanguageService.addSpecExtensions(program, ids, log, monitor);
|
||||
}
|
||||
else {
|
||||
log.appendMsg(
|
||||
NAME + ": Cannot add spec extensions without exclusive access to program.");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerOptions(Options options, Program program) {
|
||||
options.registerOption(OPTION_NAME_SPEC_EXTENSIONS, OPTION_DEFAULT_SPEC_EXTENSIONS, null,
|
||||
OPTION_DESC_SPEC_EXTENSIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(Options options, Program program) {
|
||||
addSpecExtensions =
|
||||
options.getBoolean(OPTION_NAME_SPEC_EXTENSIONS, OPTION_DEFAULT_SPEC_EXTENSIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void analysisEnded(Program program) {
|
||||
analysisStarted.set(false);
|
||||
}
|
||||
}
|
||||
-150
@@ -1,150 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.analysis.rust;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighException;
|
||||
import ghidra.app.util.bin.format.elf.info.ElfComment;
|
||||
import ghidra.app.util.opinion.ElfLoader;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.database.SpecExtension;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Processor;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.bytesearch.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.xml.XmlParseException;
|
||||
|
||||
/**
|
||||
* Rust utility functions
|
||||
*/
|
||||
public class RustUtilities {
|
||||
|
||||
private static final java.util.regex.Pattern ELF_COMMENT_REGEX =
|
||||
java.util.regex.Pattern.compile("^rustc version .*$");
|
||||
|
||||
/**
|
||||
* Checks if the specified program contains Rust stuff, either by metadata or by searching
|
||||
* the specified {@link MemoryBlock} for byte pattern signatures.
|
||||
* <p>
|
||||
* This may be used by loaders to determine if a program was compiled with rust.
|
||||
* If the program is determined to be rust, then the compiler property is set to
|
||||
* {@link RustConstants#RUST_COMPILER}.
|
||||
*
|
||||
* @param program The {@link Program}
|
||||
* @param block The {@link MemoryBlock} to scan for Rust signatures
|
||||
* @param monitor The monitor
|
||||
* @return True if the given {@link MemoryBlock} is not null and contains a Rust signature;
|
||||
* otherwise, false
|
||||
* @throws IOException if there was an IO-related error
|
||||
* @throws CancelledException if the user cancelled the operation
|
||||
*/
|
||||
public static boolean isRust(Program program, MemoryBlock block, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
if ( ElfLoader.isElf(program)) {
|
||||
// ELF binaries can contain a ".comment" section that records the toolchains that
|
||||
// produced the binary. Search this first as its quick and easy.
|
||||
ElfComment elfComments = ElfComment.fromProgram(program);
|
||||
if ( elfComments != null ) {
|
||||
for(String s : elfComments.getCommentStrings() ) {
|
||||
if (ELF_COMMENT_REGEX.matcher(s).matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (block == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use a MemoryBytePatternSearch for more efficient byte searching over a list of potential
|
||||
// byte signatures. The below action sets our supplied boolean to true on a match, which we
|
||||
// can later query and use as a return value for this method.
|
||||
GenericMatchAction<AtomicBoolean> action =
|
||||
new GenericMatchAction<AtomicBoolean>(new AtomicBoolean()) {
|
||||
@Override
|
||||
public void apply(Program prog, Address addr, Match match) {
|
||||
getMatchValue().set(true);
|
||||
}
|
||||
};
|
||||
MemoryBytePatternSearcher searcher = new MemoryBytePatternSearcher("Rust signatures");
|
||||
for (byte[] sig : RustConstants.RUST_SIGNATURES) {
|
||||
searcher.addPattern(new GenericByteSequencePattern<AtomicBoolean>(sig, action));
|
||||
}
|
||||
|
||||
searcher.search(program, new AddressSet(block.getAddressRange()), monitor);
|
||||
|
||||
return action.getMatchValue().get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given program has earlier been tagged as having a Rust compiler by
|
||||
* the loader.
|
||||
*
|
||||
* @param program {@link Program}
|
||||
* @return boolean true if program's compiler property includes rust
|
||||
*/
|
||||
public static boolean isRustProgram(Program program) {
|
||||
String name = program.getCompiler();
|
||||
return name != null && name.contains(RustConstants.RUST_COMPILER);
|
||||
}
|
||||
|
||||
public static int addExtensions(Program program, TaskMonitor monitor, String subPath)
|
||||
throws IOException {
|
||||
Language language = program.getLanguageCompilerSpecPair().getLanguage();
|
||||
Processor processor = language.getProcessor();
|
||||
int bitSize = language.getLanguageDescription().getSize();
|
||||
String spath = RustConstants.RUST_EXTENSIONS_PATH + subPath + bitSize;
|
||||
ResourceFile module = Application.getModuleDataSubDirectory(processor.toString(), spath);
|
||||
|
||||
int extensionCount = 0;
|
||||
|
||||
ResourceFile[] files = module.listFiles();
|
||||
if (files != null) {
|
||||
for (ResourceFile file : files) {
|
||||
InputStream stream = file.getInputStream();
|
||||
byte[] bytes = stream.readAllBytes();
|
||||
String xml = new String(bytes);
|
||||
|
||||
try {
|
||||
SpecExtension extension = new SpecExtension(program);
|
||||
extension.addReplaceCompilerSpecExtension(xml, monitor);
|
||||
extensionCount++;
|
||||
}
|
||||
catch (SleighException | SAXException | XmlParseException | LockException e) {
|
||||
Msg.error(RustUtilities.class,
|
||||
"Failed to load Rust cspec extension: " + file.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extensionCount;
|
||||
}
|
||||
}
|
||||
-5
@@ -18,7 +18,6 @@ package ghidra.app.plugin.core.datamgr.util;
|
||||
import java.util.*;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.analysis.rust.RustConstants;
|
||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||
import ghidra.app.util.opinion.*;
|
||||
import ghidra.framework.Application;
|
||||
@@ -143,10 +142,6 @@ public class DataTypeArchiveUtility {
|
||||
list.add("generic_clib");
|
||||
}
|
||||
|
||||
if (program.getCompiler().contains(RustConstants.RUST_COMPILER)) {
|
||||
list.add("rust-common");
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.format.elf.*;
|
||||
import ghidra.app.util.bin.format.golang.GoConstants;
|
||||
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
|
||||
import ghidra.app.util.bin.format.swift.SwiftUtils;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.ProjectData;
|
||||
import ghidra.framework.options.Options;
|
||||
@@ -206,9 +205,6 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
|
||||
List<String> sectionNames = Arrays.stream(elf.getSections())
|
||||
.map(ElfSectionHeader::getNameAsString)
|
||||
.toList();
|
||||
if (SwiftUtils.isSwift(sectionNames)) {
|
||||
return SwiftUtils.SWIFT_COMPILER;
|
||||
}
|
||||
if (GoRttiMapper.hasGolangSections(sectionNames)) {
|
||||
return GoConstants.GOLANG_CSPEC_NAME;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,6 @@ import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ghidra.app.cmd.label.SetLabelPrimaryCmd;
|
||||
import ghidra.app.plugin.core.analysis.rust.RustConstants;
|
||||
import ghidra.app.plugin.core.analysis.rust.RustUtilities;
|
||||
import ghidra.app.util.*;
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.format.MemoryLoadable;
|
||||
@@ -188,8 +186,6 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
||||
|
||||
markupElfInfoProducers(monitor);
|
||||
|
||||
setCompiler(monitor);
|
||||
|
||||
success = true;
|
||||
}
|
||||
finally {
|
||||
@@ -2464,22 +2460,6 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private void setCompiler(TaskMonitor monitor) throws CancelledException {
|
||||
// Check for Rust
|
||||
try {
|
||||
if (RustUtilities.isRust(program, memory.getBlock(ElfSectionHeaderConstants.dot_rodata),
|
||||
monitor)) {
|
||||
program.setCompiler(RustConstants.RUST_COMPILER);
|
||||
int extensionCount = RustUtilities.addExtensions(program, monitor,
|
||||
RustConstants.RUST_EXTENSIONS_UNIX);
|
||||
log.appendMsg("Installed " + extensionCount + " Rust cspec extensions");
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.appendMsg("Rust error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void markupHashTable(TaskMonitor monitor) {
|
||||
|
||||
ElfDynamicTable dynamicTable = elf.getDynamicTable();
|
||||
|
||||
@@ -28,7 +28,6 @@ import ghidra.app.util.bin.format.macho.*;
|
||||
import ghidra.app.util.bin.format.macho.commands.*;
|
||||
import ghidra.app.util.bin.format.macho.dyld.DyldArchitecture;
|
||||
import ghidra.app.util.bin.format.macho.dyld.DyldCacheHeader;
|
||||
import ghidra.app.util.bin.format.swift.SwiftUtils;
|
||||
import ghidra.app.util.bin.format.ubi.*;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
@@ -543,9 +542,6 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
||||
.flatMap(seg -> seg.getSections().stream())
|
||||
.map(section -> section.getSectionName())
|
||||
.toList();
|
||||
if (SwiftUtils.isSwift(sectionNames)) {
|
||||
return SwiftUtils.SWIFT_COMPILER;
|
||||
}
|
||||
if (GoRttiMapper.hasGolangSections(sectionNames)) {
|
||||
return GoConstants.GOLANG_CSPEC_NAME;
|
||||
}
|
||||
|
||||
+2
-7
@@ -30,7 +30,8 @@ import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
@@ -173,12 +174,6 @@ public class MachoPrelinkProgramBuilder extends MachoProgramBuilder {
|
||||
return machoInfoList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renameObjMsgSendRtpSymbol()
|
||||
throws DuplicateNameException, InvalidInputException {
|
||||
// Do nothing. This is not applicable for a PRELINK Mach-O.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void markupChainedFixups(MachHeader header, List<Address> fixups)
|
||||
throws CancelledException {
|
||||
|
||||
@@ -15,14 +15,11 @@
|
||||
*/
|
||||
package ghidra.app.util.opinion;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.collections4.map.LazySortedMap;
|
||||
|
||||
import ghidra.app.plugin.core.analysis.rust.RustConstants;
|
||||
import ghidra.app.plugin.core.analysis.rust.RustUtilities;
|
||||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.format.RelocationException;
|
||||
@@ -40,8 +37,6 @@ import ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr.DyldChainType;
|
||||
import ghidra.app.util.bin.format.macho.dyld.DyldFixup;
|
||||
import ghidra.app.util.bin.format.macho.relocation.*;
|
||||
import ghidra.app.util.bin.format.macho.threadcommand.ThreadCommand;
|
||||
import ghidra.app.util.bin.format.objc.ObjcUtils;
|
||||
import ghidra.app.util.bin.format.objc.objc1.Objc1Constants;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.database.function.OverlappingFunctionException;
|
||||
@@ -173,9 +168,7 @@ public class MachoProgramBuilder {
|
||||
}
|
||||
|
||||
// Perform additional actions
|
||||
renameObjMsgSendRtpSymbol();
|
||||
fixupProgramTree(null); // should be done last to account for new memory blocks
|
||||
setCompiler(provider.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1808,53 +1801,6 @@ public class MachoProgramBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
protected void setCompiler(String source) throws CancelledException {
|
||||
if (ObjcUtils.isObjc(program)) {
|
||||
try {
|
||||
program.setCompiler(ObjcUtils.OBJC_COMPILER);
|
||||
int count = ObjcUtils.addExtensions(program, monitor);
|
||||
if (count > 0) {
|
||||
log.appendMsg("%s: installed %d objc SpecExtensions".formatted(source, count));
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.appendMsg("%s: objc error - %s".formatted(source, e.getMessage()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Section section =
|
||||
machoHeader.getSection(SegmentNames.SEG_TEXT, SectionNames.TEXT_CONST);
|
||||
if (section != null && RustUtilities.isRust(program,
|
||||
memory.getBlock(space.getAddress(section.getAddress())), monitor)) {
|
||||
program.setCompiler(RustConstants.RUST_COMPILER);
|
||||
int count = RustUtilities.addExtensions(program, monitor,
|
||||
RustConstants.RUST_EXTENSIONS_UNIX);
|
||||
if (count > 0) {
|
||||
log.appendMsg("%s: installed %d rust SpecExtensions".formatted(source, count));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.appendMsg("%s: Rust error - %s".formatted(source, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
protected void renameObjMsgSendRtpSymbol()
|
||||
throws DuplicateNameException, InvalidInputException {
|
||||
Address address = space.getAddress(Objc1Constants.OBJ_MSGSEND_RTP);
|
||||
Symbol symbol = program.getSymbolTable().getPrimarySymbol(address);
|
||||
if (symbol != null && symbol.isDynamic()) {
|
||||
symbol.setName(Objc1Constants.OBJC_MSG_SEND_RTP_NAME, SourceType.IMPORTED);
|
||||
}
|
||||
else {
|
||||
program.getSymbolTable()
|
||||
.createLabel(address, Objc1Constants.OBJC_MSG_SEND_RTP_NAME,
|
||||
SourceType.IMPORTED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates the given {@link Symbol} with the correct external {@link Library} (fixing
|
||||
* the {@code <EXTERNAL>} association)
|
||||
|
||||
@@ -23,8 +23,6 @@ import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
import ghidra.app.plugin.core.analysis.rust.RustConstants;
|
||||
import ghidra.app.plugin.core.analysis.rust.RustUtilities;
|
||||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
@@ -39,7 +37,6 @@ import ghidra.app.util.bin.format.pe.ImageCor20Header.ImageCor20Flags;
|
||||
import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout;
|
||||
import ghidra.app.util.bin.format.pe.debug.DebugCOFFSymbol;
|
||||
import ghidra.app.util.bin.format.pe.debug.DebugDirectoryParser;
|
||||
import ghidra.app.util.bin.format.swift.SwiftUtils;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.options.Options;
|
||||
@@ -925,9 +922,7 @@ public class PeLoader extends AbstractPeDebugLoader {
|
||||
BorlandCpp("borland:c++", "borlandcpp"),
|
||||
BorlandUnk("borland:unknown", "borlandcpp"),
|
||||
CLI("cli", "cli"),
|
||||
Rustc(RustConstants.RUST_COMPILER, RustConstants.RUST_COMPILER),
|
||||
GOLANG("golang", "golang"),
|
||||
Swift(SwiftUtils.SWIFT_COMPILER, SwiftUtils.SWIFT_COMPILER),
|
||||
Unknown("unknown", "unknown"),
|
||||
|
||||
// The following values represent the presence of ambiguous indicators
|
||||
@@ -981,34 +976,6 @@ public class PeLoader extends AbstractPeDebugLoader {
|
||||
|
||||
DOSHeader dh = pe.getDOSHeader();
|
||||
|
||||
// Check for Rust. Program object is required, which may be null.
|
||||
try {
|
||||
if (program != null && RustUtilities.isRust(program,
|
||||
program.getMemory().getBlock(".rdata"), monitor)) {
|
||||
try {
|
||||
int extensionCount = RustUtilities.addExtensions(program, monitor,
|
||||
RustConstants.RUST_EXTENSIONS_WINDOWS);
|
||||
log.appendMsg("Installed " + extensionCount + " Rust cspec extensions");
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.appendMsg("Rust error: " + e.getMessage());
|
||||
}
|
||||
return CompilerEnum.Rustc;
|
||||
}
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// Move on
|
||||
}
|
||||
|
||||
// Check for Swift
|
||||
List<String> sectionNames =
|
||||
Arrays.stream(pe.getNTHeader().getFileHeader().getSectionHeaders())
|
||||
.map(section -> section.getName())
|
||||
.toList();
|
||||
if (SwiftUtils.isSwift(sectionNames)) {
|
||||
return CompilerEnum.Swift;
|
||||
}
|
||||
|
||||
// Check for managed code (.NET)
|
||||
if (pe.getNTHeader().getOptionalHeader().isCLI()) {
|
||||
return CompilerEnum.CLI;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Objective-C
|
||||
|
||||
The module provides improved support for binaries written in the Objective-C programming language.
|
||||
@@ -0,0 +1,25 @@
|
||||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
|
||||
apply plugin: 'eclipse'
|
||||
|
||||
eclipse.project.name = 'Features Objective-C'
|
||||
|
||||
dependencies {
|
||||
api project(":Base")
|
||||
api project(":Decompiler")
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
##VERSION: 2.0
|
||||
Module.manifest||GHIDRA||||END|
|
||||
README.md||GHIDRA||||END|
|
||||
data/extensions.json||GHIDRA||||END|
|
||||
data/extensions/aarch64/chkstk_darwin_fixup.xml||GHIDRA||||END|
|
||||
data/extensions/aarch64/objc_getProperty_fixup.xml||GHIDRA||||END|
|
||||
data/extensions/aarch64/objc_load_fixup.xml||GHIDRA||||END|
|
||||
data/extensions/aarch64/objc_msgSend_stub.xml||GHIDRA||||END|
|
||||
data/extensions/aarch64/objc_release_fixup.xml||GHIDRA||||END|
|
||||
data/extensions/aarch64/objc_retain_fixup.xml||GHIDRA||||END|
|
||||
data/extensions/aarch64/objc_setProperty_fixup.xml||GHIDRA||||END|
|
||||
data/extensions/aarch64/objc_store_fixup.xml||GHIDRA||||END|
|
||||
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"processor": "AARCH64",
|
||||
"endian": "little",
|
||||
"size": "64",
|
||||
"directory": "extensions/aarch64"
|
||||
}
|
||||
]
|
||||
+6
-3
@@ -32,6 +32,7 @@ import ghidra.app.util.bin.format.objc.objc2.*;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.MachoLoader;
|
||||
import ghidra.app.util.opinion.MachoProgramBuilder;
|
||||
import ghidra.app.util.sourcelanguage.ObjcSourceLanguageSpecExtension;
|
||||
import ghidra.framework.options.OptionType;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.address.*;
|
||||
@@ -275,8 +276,9 @@ public class ObjcMessageAnalyzer extends AbstractAnalyzer {
|
||||
String cc = CompilerSpec.CALLING_CONVENTION_unknown;
|
||||
if (isStub) {
|
||||
if (program.getDataTypeManager()
|
||||
.getCallingConvention(ObjcUtils.OBJC_MSGSEND_STUBS_CC) != null) {
|
||||
cc = ObjcUtils.OBJC_MSGSEND_STUBS_CC;
|
||||
.getCallingConvention(
|
||||
ObjcSourceLanguageSpecExtension.OBJC_MSGSEND_STUBS) != null) {
|
||||
cc = ObjcSourceLanguageSpecExtension.OBJC_MSGSEND_STUBS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -998,7 +1000,8 @@ public class ObjcMessageAnalyzer extends AbstractAnalyzer {
|
||||
if (args.length >= 2 && args[1].getDataType().equals(dataTypes.sel)) {
|
||||
signature.setArguments(ArrayUtils.remove(args, 1));
|
||||
}
|
||||
signature.setCallingConvention(ObjcUtils.OBJC_MSGSEND_STUBS_CC);
|
||||
signature.setCallingConvention(
|
||||
ObjcSourceLanguageSpecExtension.OBJC_MSGSEND_STUBS);
|
||||
HighFunctionDBUtil.writeOverride(funcMgr.getFunctionContaining(fromAddress),
|
||||
fromAddress, signature);
|
||||
}
|
||||
+3
-74
@@ -15,32 +15,25 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.objc;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.cmd.data.CreateDataCmd;
|
||||
import ghidra.app.cmd.disassemble.DisassembleCommand;
|
||||
import ghidra.app.cmd.function.CreateFunctionCmd;
|
||||
import ghidra.app.cmd.register.SetRegisterCmd;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighException;
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.objc.objc2.Objc2Constants;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.cmd.BackgroundCommand;
|
||||
import ghidra.framework.cmd.Command;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.database.SpecExtension;
|
||||
import ghidra.program.database.SpecExtension.DocInfo;
|
||||
import ghidra.program.database.symbol.ClassSymbol;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.DataUtilities.ClearDataMode;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.lang.Processor;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
@@ -51,24 +44,12 @@ import ghidra.util.Msg;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.xml.XmlParseException;
|
||||
|
||||
/**
|
||||
* Objective-C utilities
|
||||
*/
|
||||
public final class ObjcUtils {
|
||||
|
||||
/**
|
||||
* The Objective-C compiler name, used by {@link Program#setCompiler(String)}
|
||||
*/
|
||||
public static final String OBJC_COMPILER = "objc";
|
||||
|
||||
/**
|
||||
* The Objective-C {@code _objc_msgSend} stub calling convention name (added with processor
|
||||
* extension)
|
||||
*/
|
||||
public static final String OBJC_MSGSEND_STUBS_CC = "__objc_msgSend_stub";
|
||||
|
||||
/**
|
||||
* String that prefixes Objective-C class symbols
|
||||
*/
|
||||
@@ -446,56 +427,4 @@ public final class ObjcUtils {
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Objective-C processor extensions to the {@link Program}, which include:
|
||||
* <ul>
|
||||
* <li>A special calling convention used by objc_msgSend stubs</li>
|
||||
* <li>Call fixups to clear out a lot of Objective-C Automatic Reference Counting (ARC) clutter</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param program The {@link Program} to add the extensions to
|
||||
* @param monitor A cancelable task monitor
|
||||
* @return The number of extensions successfully added
|
||||
* @throws IOException if an IO-related error occurred
|
||||
* @see <a href="https://doi.org/10.1109/STATIC66697.2025.00005">Heros in Action: Analyzing Objective-C Binaries through Decompilation and IFDS</a>
|
||||
* @see <a href="https://youtu.be/ojXI7Gio8Pg?si=zcAaZ2KGeBFcAabn">RE//verse 2025: Langs Beyond The C</a>
|
||||
*/
|
||||
public static int addExtensions(Program program, TaskMonitor monitor) throws IOException {
|
||||
Language language = program.getLanguageCompilerSpecPair().getLanguage();
|
||||
Processor processor = language.getProcessor();
|
||||
String spath = "extensions/" + OBJC_COMPILER;
|
||||
|
||||
int extensionCount = 0;
|
||||
|
||||
try {
|
||||
ResourceFile module =
|
||||
Application.getModuleDataSubDirectory(processor.toString(), spath);
|
||||
ResourceFile[] files = module.listFiles();
|
||||
if (files != null) {
|
||||
for (ResourceFile file : files) {
|
||||
InputStream stream = file.getInputStream();
|
||||
byte[] bytes = stream.readAllBytes();
|
||||
String xml = new String(bytes);
|
||||
try {
|
||||
SpecExtension extension = new SpecExtension(program);
|
||||
DocInfo docInfo = extension.testExtensionDocument(xml);
|
||||
if (SpecExtension.getCompilerSpecExtension(program, docInfo) == null) {
|
||||
extension.addReplaceCompilerSpecExtension(xml, monitor);
|
||||
extensionCount++;
|
||||
}
|
||||
}
|
||||
catch (SleighException | SAXException | XmlParseException | LockException e) {
|
||||
Msg.error(ObjcUtils.class,
|
||||
"Failed to load Objective-C cspec extension: " + file, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
// fall thru
|
||||
}
|
||||
|
||||
return extensionCount;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user