Merge remote-tracking branch 'origin/Ghidra_12.2'

This commit is contained in:
Ryan Kurtz
2026-09-17 13:40:34 -04:00
17 changed files with 183 additions and 70 deletions

View File

@@ -31,12 +31,14 @@ import org.jgrapht.traverse.TopologicalOrderIterator;
import org.osgi.framework.*;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.wiring.*;
import org.osgi.service.url.URLStreamHandlerService;
import generic.io.NullPrintWriter;
import generic.jar.ResourceFile;
import ghidra.framework.Application;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.protocol.ghidra.*;
import ghidra.util.Msg;
import ghidra.util.task.TaskLauncher;
import ghidra.util.task.TaskMonitor;
@@ -417,10 +419,6 @@ public class BundleHost {
// setup the cache path
config.setProperty(Constants.FRAMEWORK_STORAGE, makeCacheDir());
// prevent the use of Felix URL handlers which can interfere with URL.openConnection
// exception handling
config.put(FelixConstants.SERVICE_URLHANDLERS_PROP, "false");
config.put(FelixConstants.LOG_LEVEL_PROP, "1");
if (STDERR_DEBUGGING) {
config.put(FelixConstants.LOG_LEVEL_PROP, "999");
@@ -481,6 +479,8 @@ public class BundleHost {
throw new OSGiException("Felix OSGi framework has no bundle context");
}
registerGhidraProtocolHandler(frameworkBundleContext);
addDebuggingListeners();
Bundle bundle = frameworkBundleContext.getBundle();
@@ -521,6 +521,23 @@ public class BundleHost {
}
}
/**
* Install Ghidra URL stream handler service to force standard use of {@code ghidra} protocol
* {@link Handler}. This bypasses the improper IOException propagation caused by
* {@code URLHandlersStreamHandlerProxy.openConnection(URL)} which forces itself to
* act as a proxy for all normal protocol handlers.
*
* @param context OSGI Bundle context
*/
private void registerGhidraProtocolHandler(BundleContext context) {
Hashtable<String, Object> properties = new Hashtable<>();
properties.put("url.handler.protocol", new String[] { GhidraURL.PROTOCOL });
context.registerService(
URLStreamHandlerService.class.getName(),
new GhidraOSGIStreamHandler(),
properties);
}
/**
* Gets the host framework.
* @return the OSGi framework

View File

@@ -3214,8 +3214,6 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
long loadSizeBytes = elfProgramHeader.getAdjustedLoadSize();
long fullSizeBytes = elfProgramHeader.getAdjustedMemorySize();
boolean maintainExecuteBit = elf.getSectionHeaderCount() == 0;
if (fullSizeBytes <= 0) {
if (!space.isLoadedMemorySpace() && loadSizeBytes > 0) {
fullSizeBytes = loadSizeBytes;
@@ -3239,16 +3237,12 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
String comment = getSectionComment(addr, fullSizeBytes, space.getAddressableUnitSize(),
elfProgramHeader.getDescription(), address.isLoadedMemoryAddress());
if (!maintainExecuteBit && elfProgramHeader.isExecute()) {
comment += " (disabled execute bit)";
}
String blockName = getSegmentName(elfProgramHeader, segmentNumber);
if (loadSizeBytes != 0) {
addInitializedMemorySection(elfProgramHeader, elfProgramHeader.getOffset(),
loadSizeBytes, address, blockName, elfProgramHeader.isRead(),
elfProgramHeader.isWrite(),
maintainExecuteBit ? elfProgramHeader.isExecute() : false, comment,
elfProgramHeader.isWrite(), elfProgramHeader.isExecute(), comment,
isFragmentationOK,
elfProgramHeader.getType() == ElfProgramHeaderConstants.PT_LOAD);
}
@@ -3435,11 +3429,11 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
}
Address address = null;
ElfProgramHeader loadHeader = elf.getProgramLoadHeaderContaining(addr);
if (sectionByteLength == 0 &&
elfSectionToLoad.getType() == ElfSectionHeaderConstants.SHT_PROGBITS) {
// Check for and consume uninitialized portion of PT_LOAD segment if possible
ElfProgramHeader loadHeader = elf.getProgramLoadHeaderContaining(addr);
if (loadHeader != null) {
// NOTE: should never apply to relocatable ELF
Address segmentStart = getSegmentLoadAddress(loadHeader);
@@ -3478,6 +3472,19 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
final String blockName = elfSectionToLoad.getNameAsString();
boolean isExecute = elfSectionToLoad.isExecutable();
boolean isWrite = elfSectionToLoad.isWritable();
boolean isRead = true;
if (loadHeader != null) {
// If PT_LOAD exists, defer to it for permissions
// NOTE: This does not handle a section not fully contained within a program
// header loaded region
isExecute = loadHeader.isExecute();
isWrite = loadHeader.isWrite();
isRead = loadHeader.isRead();
}
try {
if (loadOffset == -1 ||
elfSectionToLoad.getType() == ElfSectionHeaderConstants.SHT_NOBITS) {
@@ -3489,7 +3496,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
getSectionComment(addr, sectionByteLength, space.getAddressableUnitSize(),
elfSectionToLoad.getTypeAsString(), address.isLoadedMemoryAddress());
addUninitializedMemorySection(elfSectionToLoad, sectionByteLength, address,
blockName, true, elfSectionToLoad.isWritable(), elfSectionToLoad.isExecutable(),
blockName, isRead, isWrite, isExecute,
comment, false);
}
else {
@@ -3497,8 +3504,8 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
getSectionComment(addr, sectionByteLength, space.getAddressableUnitSize(),
elfSectionToLoad.getTypeAsString(), address.isLoadedMemoryAddress());
addInitializedMemorySection(elfSectionToLoad, loadOffset, sectionByteLength,
address, blockName, elfSectionToLoad.isAlloc(), elfSectionToLoad.isWritable(),
elfSectionToLoad.isExecutable(), comment, false, elfSectionToLoad.isAlloc());
address, blockName, isRead, isWrite,
isExecute, comment, false, elfSectionToLoad.isAlloc());
}
}
catch (AddressOverflowException e) {

View File

@@ -111,41 +111,55 @@ public class URLAnnotatedStringHandler implements AnnotatedStringHandler {
}
}
private boolean isUnsupportedGhidraURL(URL url) {
try {
return GhidraURL.isGhidraURL(url) && GhidraURL.getProjectPathname(url) == null;
}
catch (Exception e) {
return true;
}
}
@Override
public boolean handleMouseClick(String[] annotationParts, Navigatable navigatable,
ServiceProvider serviceProvider) {
String urlString = annotationParts[1];
URL url = getURLForString(urlString);
if (url != null) {
String protocol = url.getProtocol();
if (!allowedProtocols.contains(protocol)) {
Msg.showError(this, null, "URL Access Not Allowed",
"Unsupported URL annotation protocol - " + allowedProtocolsStr +
" required:\n\n" +
urlString);
return false;
}
if (!ClientUtil.getAllowListProvider().isAllowed(url)) {
Msg.showError(this, null, "URL Access Not Allowed",
"Access denied by Server Allow List");
return false;
}
if (GhidraURL.PROTOCOL.equals(url.getProtocol())) {
ProgramManager programManager = serviceProvider.getService(ProgramManager.class);
return programManager.openProgram(url, ProgramManager.OPEN_CURRENT) != null;
}
BrowserLoader.display(url, null, serviceProvider);
return true;
if (url == null) {
Msg.showError(this, null, "Invalid URL",
"Invalid URL annotation: " + urlString);
return false;
}
Msg.showError(this, null, "Invalid URL",
"Invalid URL annotation - not a valid URL: " + urlString);
String protocol = url.getProtocol();
if (!allowedProtocols.contains(protocol)) {
Msg.showError(this, null, "URL Access Not Allowed",
"Unsupported URL annotation protocol - " + allowedProtocolsStr +
" required:\n" + urlString);
return false;
}
return false;
if (isUnsupportedGhidraURL(url)) {
Msg.showError(this, null, "Invalid Ghidra URL",
"Unsupported Ghidra URL annotation:\n" + urlString);
return false;
}
if (!GhidraURL.isLocalURL(url) && !ClientUtil.getAllowListProvider().isAllowed(url)) {
Msg.showError(this, null, "URL Access Not Allowed",
"Access denied by Server Allow List");
return false;
}
if (GhidraURL.isGhidraURL(url)) {
ProgramManager programManager = serviceProvider.getService(ProgramManager.class);
return programManager.openProgram(url, ProgramManager.OPEN_CURRENT) != null;
}
BrowserLoader.display(url, null, serviceProvider);
return true;
}
@Override

View File

@@ -0,0 +1,38 @@
/* ###
* 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.framework.protocol.ghidra;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import org.osgi.service.url.AbstractURLStreamHandlerService;
/**
* {@link GhidraOSGIStreamHandler} provides a Ghidra URL stream handler service for
* Felix OSGI. This allows direct use of the standard {@code ghidra} protocol
* {@link Handler} and bypasses the improper IOException propagation caused by
* {@code URLHandlersStreamHandlerProxy.openConnection(URL)}.
*/
public class GhidraOSGIStreamHandler extends AbstractURLStreamHandlerService {
private static final Handler ghidraProtocolHandler = new Handler();
@Override
public URLConnection openConnection(URL url) throws IOException {
return ghidraProtocolHandler.openConnection(url);
}
}

View File

@@ -212,6 +212,9 @@ public class ExternalSymbolResolver implements Closeable {
logger.accept("\t%d external symbols resolved, %d remain unresolved"
.formatted(getResolvedSymbolCount(), unresolvedExternalFunctionIds.size()));
for (ExtLibInfo extLib : extLibs) {
if (Library.UNKNOWN.equals(extLib.getName())) {
continue;
}
String libPath = extLib.getAssociatedProgramPath();
String loggedLibPath = libPath != null ? libPath : "missing";
if (extLib.problem != null) {
@@ -269,7 +272,9 @@ public class ExternalSymbolResolver implements Closeable {
try (Transaction tx = program.openTransaction("Resolve External Symbols")) {
for (ExtLibInfo extLib : extLibs) {
monitor.checkCancelled();
resolveSymbolsToLibrary(extLib);
if (extLib.libProgram != null) {
resolveSymbolsToLibrary(extLib);
}
}
}
}
@@ -292,7 +297,7 @@ public class ExternalSymbolResolver implements Closeable {
Program libProg = libPath != null ? getLibraryProgram(libPath) : null;
Throwable problem =
libProg == null && libPath != null ? problemLibraries.get(libPath) : null;
result.add(new ExtLibInfo(lib, problem));
result.add(new ExtLibInfo(lib, libProg, problem));
}
return result;
}
@@ -322,7 +327,7 @@ public class ExternalSymbolResolver implements Closeable {
ExternalLocation extLoc = externalManager.getExternalLocation(s);
String extLocName =
Objects.requireNonNullElse(extLoc.getOriginalImportedName(), extLoc.getLabel());
if (isExportedSymbol(program, extLocName)) {
if (isExportedSymbol(extLib.libProgram, extLocName)) {
try {
s.setNamespace(extLib.lib);
idIterator.remove();
@@ -360,6 +365,7 @@ public class ExternalSymbolResolver implements Closeable {
private class ExtLibInfo {
final Library lib;
final Program libProgram; // may be null
final List<String> resolvedSymbols = new ArrayList<>();
final Throwable problem;
@@ -367,13 +373,15 @@ public class ExternalSymbolResolver implements Closeable {
* Define external Library dependency associated with {@link ProgramSymbolResolver}
* instance.
* @param lib external library dependency
* @param problem exception which occured while accessing Library
* @param libProgram imported or discovered program which corresponds to lib, or null if not found
* @param problem exception which occurred while accessing Library
*/
ExtLibInfo(Library lib, Throwable problem) {
ExtLibInfo(Library lib, Program libProgram, Throwable problem) {
if (program != lib.getSymbol().getProgram()) {
throw new AssertionError("Program mismatch");
}
this.lib = lib;
this.libProgram = libProgram;
this.problem = problem;
}
@@ -416,7 +424,9 @@ public class ExternalSymbolResolver implements Closeable {
* @return true if program publishes a symbol the specified name
*/
private static boolean isExportedSymbol(Program program, String name) {
if (program == null) {
return false;
}
for (Symbol s : program.getSymbolTable().getLabelOrFunctionSymbols(name, null)) {
if (s.isExternalEntryPoint()) {
return true;

View File

@@ -59,18 +59,27 @@ public class GhidraGoPlugin extends Plugin implements ApplicationLevelOnlyPlugin
private void processUrl(URL url) {
URL projectUrl = GhidraURL.getProjectURL(url);
Msg.info(this, "GhidraGo accepting the resource at " + projectUrl);
FrontEndTool frontEndTool = AppInfo.getFrontEndTool();
// Check for case where server access has already been blocked to
// launching tool and then failing to access program.
if (!ClientUtil.getAllowListProvider().isAllowed(url)) {
Msg.showError(this, frontEndTool.getActiveWindow(), "URL Access Not Allowed",
"Access denied by Server Allow List:\n" + projectUrl);
try {
URL projectUrl = GhidraURL.getProjectURL(url);
// Check for case where remote server access has already been blocked to
// launching tool and then failing to access program.
if (!GhidraURL.isLocalURL(url) && !ClientUtil.getAllowListProvider().isAllowed(url)) {
Msg.showError(this, frontEndTool.getActiveWindow(), "URL Access Not Allowed",
"Access denied by Server Allow List:\n" + projectUrl);
return;
}
Msg.info(this, "GhidraGo accepting the resource at " + projectUrl);
}
catch (Exception e) {
Msg.showError(this, frontEndTool.getActiveWindow(), "GhidraGo Failed",
"GhidraGo rejected invalid URL: " + url);
return;
}
Swing.runLater(() -> {
frontEndTool.toFront();
frontEndTool.accept(url);