GT-3481 - Gnu Demangler - Checkpoint 1 - Separation of demangler

analyzers; gnu options in analyzer; still require javadocs, help and
tests
This commit is contained in:
dragonmacher 2020-02-11 15:46:43 -05:00
parent b6fb46f5df
commit b774ecb2d6
22 changed files with 1231 additions and 267 deletions

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.plugin.core.analysis;
import ghidra.app.services.*;
import ghidra.app.util.demangler.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
// TODO docme
public abstract class AbstractDemanglerAnalyzer extends AbstractAnalyzer {
public AbstractDemanglerAnalyzer(String name, String description) {
super(name, description, AnalyzerType.BYTE_ANALYZER);
setPriority(AnalysisPriority.DATA_TYPE_PROPOGATION.before().before().before());
}
@Override
public boolean canAnalyze(Program program) {
// override this to be enable for a binary containing symbols you wisht to process
return true;
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
DemanglerOptions options = getOptions();
if (!validateOptions(options, log)) {
log.error(getName(), "Invalid demangler options--cannot demangle");
return false;
}
monitor.initialize(100);
SymbolTable symbolTable = program.getSymbolTable();
SymbolIterator it = symbolTable.getPrimarySymbolIterator(set, true);
while (it.hasNext()) {
monitor.checkCanceled();
Symbol symbol = it.next();
if (skipSymbol(symbol)) {
continue;
}
Address address = symbol.getAddress();
String mangled = cleanSymbol(address, symbol.getName());
DemangledObject demangled = demangle(mangled, options, log);
if (demangled != null) {
apply(program, address, demangled, options, log, monitor);
}
Address min = set.getMinAddress();
Address max = set.getMaxAddress();
int distance = (int) (address.getOffset() - min.getOffset());
int percent = (int) ((distance / max.getOffset()) * 100);
monitor.setProgress(percent);
}
return true;
}
// TODO callback before demangling begins...
protected boolean validateOptions(DemanglerOptions options, MessageLog log) {
// override to validate custom options for a particular demangler
return true;
}
private boolean skipSymbol(Symbol symbol) {
if (symbol.getSource() == SourceType.DEFAULT) {
return true;
}
// Only demangle global or external symbols when directly parented to a Library namespace
Namespace parentNamespace = symbol.getParentNamespace();
if (symbol.isExternal()) {
if (!(parentNamespace instanceof Library)) {
return true;
}
}
else if (!parentNamespace.isGlobal()) {
return true;
}
// Someone has already added arguments or return to the function signature
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
Function function = (Function) symbol.getObject();
if (function.getSignatureSource() != SourceType.DEFAULT) {
return true;
}
}
return false;
}
protected abstract DemangledObject doDemangle(String mangled, DemanglerOptions options,
MessageLog log) throws DemangledException;
protected DemanglerOptions getOptions() {
// note: these can be stored in the analyzer subclass and updated when the
// analysis options change
DemanglerOptions options = new DemanglerOptions();
options.setApplySignature(true);
options.setDoDisassembly(true);
options.setDemangleOnlyKnownPatterns(false);
return options;
}
protected DemangledObject demangle(String mangled, DemanglerOptions options,
MessageLog log) {
DemangledObject demangled = null;
try {
demangled = doDemangle(mangled, options, log);
}
catch (Throwable e) {
if (e instanceof DemangledException) {
if (((DemangledException) e).isInvalidMangledName()) {
//ignore invalid names, consider as not an error
return null;
}
}
log.appendMsg(getName(),
"Unable to demangle symbol: " + mangled + ". Message: " + e.getMessage());
return null;
}
return demangled;
}
protected void apply(Program program, Address address, DemangledObject demangled,
DemanglerOptions options, MessageLog log, TaskMonitor monitor) {
try {
if (demangled.applyTo(program, address, options, monitor)) {
return;
}
}
catch (Exception e) {
String message = e.getMessage();
message = message == null ? "" : ". Message: " + message;
log.appendMsg(getName(),
"Unable to demangle symbol at " + address + "; name: " +
demangled.getMangledName() + message);
}
log.appendMsg(getName(),
"Failed to apply mangled symbol at " + address + "; name: " +
demangled.getMangledName() + " (" +
getName() + "/" + demangled.getClass().getName() + ")");
}
protected String cleanSymbol(Address address, String name) {
return SymbolUtilities.getCleanSymbolName(name, address);
}
}

View file

@ -1,138 +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;
import ghidra.app.cmd.label.DemanglerCmd;
import ghidra.app.services.*;
import ghidra.app.util.demangler.DemanglerOptions;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class DemanglerAnalyzer extends AbstractAnalyzer {
private static final String NAME = "Demangler";
private static final String DESCRIPTION =
"After a function is created, this analyzer will attempt to demangle " +
"the name and apply datatypes to parameters.";
private static final String OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS =
"Only Demangle Known Mangled Symbols";
private static final String OPTION_DESCRIPTION_USE_KNOWN_PATTERNS =
"Only demangle " + "symbols that follow known compiler mangling patterns. " +
"Leaving this option off may cause non-mangled symbols to get demangled.";
private final static String OPTION_NAME_COMMIT_SIGNATURE = "Commit Function Signatures";
private static final String OPTION_DESCRIPTION_COMMIT_SIGNATURE =
"Apply any recovered function signature, in addition to the function name";
private boolean doSignatureEnabled = true;
private boolean demangleAllSymbols = false;
public DemanglerAnalyzer() {
super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
setPriority(AnalysisPriority.DATA_TYPE_PROPOGATION.before().before().before());
setDefaultEnablement(true);
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
SymbolTable symbolTable = program.getSymbolTable();
int progress = 0;
monitor.initialize(symbolTable.getNumSymbols());
monitor.setShowProgressValue(false);
SymbolIterator symiter = symbolTable.getPrimarySymbolIterator(set, true);
while (symiter.hasNext()) {
monitor.checkCanceled();
Symbol symbol = symiter.next();
Address address = symbol.getAddress();
if (address.compareTo(set.getMaxAddress()) > 0) {
break;
}
if (symbol.getSource() == SourceType.DEFAULT) {
continue;
}
// Only demangle global memory symbols or external
// symbols directly parented to a Library namespace
Namespace parentNamespace = symbol.getParentNamespace();
if (symbol.isExternal()) {
if (!(parentNamespace instanceof Library)) {
continue;
}
}
else if (!parentNamespace.isGlobal()) {
continue;
}
// Someone has already added arguments or return to the function
// signature.
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
Function function = (Function) symbol.getObject();
if (function.getSignatureSource() != SourceType.DEFAULT) {
continue;
}
}
// retrieve symbol count each time, as the number of symbols changes while we are working
int count = symbolTable.getNumSymbols();
monitor.setMaximum(count);
monitor.setProgress((int) ((progress++ / (double) count) * count));
DemanglerOptions options = new DemanglerOptions();
options.setDoDisassembly(true);
options.setApplySignature(doSignatureEnabled);
options.setDemangleOnlyKnownPatterns(demangleAllSymbols);
DemanglerCmd cmd = new DemanglerCmd(address, symbol.getName(), options);
if (!cmd.applyTo(program)) {
String message = cmd.getStatusMsg();
if (message != null) {
log.appendMsg(cmd.getName(), message);
log.setStatus(message);
}
}
}
monitor.setShowProgressValue(true);
return true;
}
@Override
public void registerOptions(Options options, Program program) {
options.registerOption(OPTION_NAME_COMMIT_SIGNATURE, doSignatureEnabled, null,
OPTION_DESCRIPTION_COMMIT_SIGNATURE);
options.registerOption(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, false, null,
OPTION_DESCRIPTION_USE_KNOWN_PATTERNS);
}
@Override
public void optionsChanged(Options options, Program program) {
doSignatureEnabled = options.getBoolean(OPTION_NAME_COMMIT_SIGNATURE, doSignatureEnabled);
demangleAllSymbols =
options.getBoolean(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleAllSymbols);
}
}

View file

@ -115,6 +115,14 @@ public abstract class DemangledObject {
return demangledName;
}
/**
* Returns the original mangled name
* @return the name
*/
public String getMangledName() {
return originalMangled;
}
/**
* Returns the demangled name of this object.
* NOTE: unsupported symbol characters, like whitespace, will be
@ -495,8 +503,11 @@ public abstract class DemangledObject {
List<Symbol> symbols = symbolTable.getSymbols(namespaceName, namespace);
Symbol namespaceSymbol =
symbols.stream().filter(s -> (s.getSymbolType() == SymbolType.NAMESPACE ||
s.getSymbolType() == SymbolType.CLASS)).findFirst().orElse(null);
symbols.stream()
.filter(s -> (s.getSymbolType() == SymbolType.NAMESPACE ||
s.getSymbolType() == SymbolType.CLASS))
.findFirst()
.orElse(null);
if (namespaceSymbol == null) {
try {
namespace =

View file

@ -23,8 +23,19 @@ import ghidra.util.classfinder.ExtensionPoint;
* the ClassSearcher will not find them.
*/
public interface Demangler extends ExtensionPoint {
public boolean canDemangle(Program program);
// TODO deprecate
@Deprecated
public DemangledObject demangle(String mangled, boolean demangleOnlyKnownPatterns)
throws DemangledException;
// TODO docme
public DemangledObject demangle(String mangled, DemanglerOptions options)
throws DemangledException;
public default DemanglerOptions createDefaultOptions() {
return new DemanglerOptions();
}
}

View file

@ -92,4 +92,14 @@ public class DemanglerOptions {
this.demangleOnlyKnownPatterns = demangleOnlyKnownPatterns;
}
@Override
public String toString() {
//@formatter:off
return "{\n" +
"\tdoDisassembly: " + doDisassembly + ",\n" +
"\tapplySignature: " + applySignature + ",\n" +
"\tdemangleOnlyKnownPatterns: " + demangleOnlyKnownPatterns + ",\n" +
"}";
//@formatter:on
}
}

View file

@ -1,30 +0,0 @@
/* ###
* 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.
* 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.demangler;
public interface DemanglerParser {
/**
* Parses the output of the demangler process and converts
* it into a demangled object.
* @param mangled the original mangled string - e.g., "_ZdaPv"
* @param demangled the demangled string - e.g., "operator_delete[](void*)"
* @return a demangled object
*/
public DemangledObject parse(String mangled, String demangled);
}

View file

@ -32,7 +32,13 @@ public class DemanglerUtil {
private static final Pattern TRAILING_PARAMETER_SPACE_PATTERN = Pattern.compile("([\\(\\,]) ");
/**
* Locates all available demanglers, then it attempts to demangle.
* Locates all available demanglers, then it attempts to demangle. This method will
* query all demanglers regardless of architecture.
*
* <p>This method will use only the default options for demangling. If you need to
* specify options, then you will have to call each specific demangler directly, creating
* the options specifically needed for each demangler. See
* {@link Demangler#createDefaultOptions()}.
*
* @param mangled the mangled name
* @return the demangled object or null
@ -59,6 +65,11 @@ public class DemanglerUtil {
* Locates all available demanglers and checks to see if the supplied program is
* supported, then it attempts to demangle.
*
* <p>This method will use only the default options for demangling. If you need to
* specify options, then you will have to call each specific demangler directly, creating
* the options specifically needed for each demangler. See
* {@link Demangler#createDefaultOptions()}.
*
* @param program the program containing the mangled name
* @param mangled the mangled name
* @return the demangled object or null
@ -95,12 +106,14 @@ public class DemanglerUtil {
}
/**
* Converts the list of names into a namespace linked list.
* Converts the list of names into a namespace demangled type.
* Given names = { "A", "B", "C" }, which represents "A::B::C".
* The following will be created {@literal "Namespace{A}->Namespace{B}->Namespace{C}"}
* and Namespace{C} will be returned.
*
* NOTE: the list will be empty after the call.
* @param names the names to convert
* @return the newly created type
*/
public static DemangledType convertToNamespaces(List<String> names) {
if (names.size() == 0) {
@ -129,13 +142,13 @@ public class DemanglerUtil {
private static String replace(String str, Pattern spaceCleanerPattern) {
Matcher matcher = spaceCleanerPattern.matcher(str);
StringBuffer buf = new StringBuffer();
StringBuilder buffy = new StringBuilder();
while (matcher.find()) {
String captureGroup = matcher.group(1);
matcher.appendReplacement(buf, captureGroup);
matcher.appendReplacement(buffy, captureGroup);
}
matcher.appendTail(buf);
return buf.toString();
matcher.appendTail(buffy);
return buffy.toString();
}
public static void setNamespace(DemangledType dt, DemangledType namespace) {

View file

@ -83,11 +83,11 @@ public class NamespacePropertyEditor extends PropertyEditorSupport implements Cu
showLibraryInNamespaceCheckBox = new GCheckBox(DISPLAY_LIBRARY_IN_NAMESPACE_LABEL);
showLibraryInNamespaceCheckBox.setSelected(true);
showLocalCheckBox.setToolTipText(SHOW_LIBRARY_IN_NAMESPACE_TOOLTIP);
showLibraryInNamespaceCheckBox.setToolTipText(SHOW_LIBRARY_IN_NAMESPACE_TOOLTIP);
panel.add(showNonLocalCheckBox);
panel.add(showLocalCheckBox);
panel.add(showLibraryInNamespaceCheckBox);
panel.add(showLocalCheckBox);
localPrefixField =
createLocalPrefixTextField(LOCAL_NAMESPACE_PREFIX_LABEL, LOCAL_PREFIX_TOOLTIP, panel);
@ -169,7 +169,8 @@ public class NamespacePropertyEditor extends PropertyEditorSupport implements Cu
if (namespaceOption.isShowLocalNamespace() != showLocalCheckBox.isSelected()) {
showLocalCheckBox.setSelected(namespaceOption.isShowLocalNamespace());
}
if (namespaceOption.isShowLibraryInNamespace() != showLibraryInNamespaceCheckBox.isSelected()) {
if (namespaceOption.isShowLibraryInNamespace() != showLibraryInNamespaceCheckBox
.isSelected()) {
showLibraryInNamespaceCheckBox.setSelected(namespaceOption.isShowLibraryInNamespace());
}
if (namespaceOption.isUseLocalPrefixOverride() != useLocalPrefixCheckBox.isSelected()) {

View file

@ -75,6 +75,7 @@ public class DemangleElfWithOptionScript extends GhidraScript {
return;
}
// TODO change to GnuDemanglerOptions
DemanglerOptions options = new DemanglerOptions();
options.setDoDisassembly(false);
options.setApplySignature(true);
@ -113,6 +114,7 @@ public class DemangleElfWithOptionScript extends GhidraScript {
return executableFormat != null && executableFormat.indexOf(MachoLoader.MACH_O_NAME) != -1;
}
/// TODO this is here because we did not support program arguments. replace this code
private Process createProcess(String executableName) throws Exception {
String demanglerName = GnuDemanglerNativeProcess.DEMANGLER_GNU;

View file

@ -0,0 +1,195 @@
/* ###
* 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.io.IOException;
import org.apache.commons.lang3.StringUtils;
import ghidra.app.util.demangler.*;
import ghidra.app.util.demangler.gnu.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.*;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
private static final String NAME = "Demangler GNU";
private static final String DESCRIPTION =
"After a function is created, this analyzer will attempt to demangle " +
"the name and apply datatypes to parameters.";
private static final String OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS =
"Only Demangle Known Mangled Symbols";
private static final String OPTION_DESCRIPTION_USE_KNOWN_PATTERNS =
"Only demangle symbols that follow known compiler mangling patterns. " +
"Leaving this option off may cause non-mangled symbols to get demangled.";
private static final String OPTION_NAME_APPLY_SIGNATURE = "Apply Function Signatures";
private static final String OPTION_DESCRIPTION_APPLY_SIGNATURE =
"Apply any recovered function signature, in addition to the function name";
// note: we use 'Z' as a trick to be below the other options
private static final String OPTION_NAME_GNU_DEMANGLER = "Z GNU Demangler";
private boolean doSignatureEnabled = true;
private boolean demangleOnlyKnownPatterns = false;
private GnuDemanglerOptionsPropertyEditor gnuOptionsEditor =
new GnuDemanglerOptionsPropertyEditor();
private GnuDemanglerWrappedOption gnuWrappedOptions;
private GnuDemangler demangler = new GnuDemangler();
public GnuDemanglerAnalyzer() {
super(NAME, DESCRIPTION);
setDefaultEnablement(true);
}
@Override
public boolean canAnalyze(Program program) {
return demangler.canDemangle(program);
}
@Override
public void registerOptions(Options options, Program program) {
options.registerOption(OPTION_NAME_APPLY_SIGNATURE, doSignatureEnabled, null,
OPTION_DESCRIPTION_APPLY_SIGNATURE);
options.registerOption(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, false, null,
OPTION_DESCRIPTION_USE_KNOWN_PATTERNS);
options.registerOptionsEditor(null);
HelpLocation help = new HelpLocation("AutoAnalysisPlugin", "Demangler_Analyzer");
options.registerOption(OPTION_NAME_GNU_DEMANGLER, OptionType.CUSTOM_TYPE,
new GnuDemanglerWrappedOption(), help, "Advanced GNU demangler options",
gnuOptionsEditor);
CustomOption customOption = options.getCustomOption(OPTION_NAME_GNU_DEMANGLER,
new GnuDemanglerWrappedOption());
if (!(customOption instanceof GnuDemanglerWrappedOption)) {
customOption = new GnuDemanglerWrappedOption();
Msg.debug(this, "Unexpected custom option type for GNU Demangler: " +
customOption.getClass());
}
gnuWrappedOptions = (GnuDemanglerWrappedOption) customOption;
}
@Override
public void optionsChanged(Options options, Program program) {
doSignatureEnabled = options.getBoolean(OPTION_NAME_APPLY_SIGNATURE, doSignatureEnabled);
demangleOnlyKnownPatterns =
options.getBoolean(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns);
gnuWrappedOptions =
(GnuDemanglerWrappedOption) options.getCustomOption(OPTION_NAME_GNU_DEMANGLER,
new GnuDemanglerWrappedOption());
}
@Override
protected DemanglerOptions getOptions() {
GnuDemanglerOptions options = new GnuDemanglerOptions();
options.setDoDisassembly(true);
options.setApplySignature(doSignatureEnabled);
options.setDemangleOnlyKnownPatterns(demangleOnlyKnownPatterns);
options.setUseDeprecatedDemangler(gnuWrappedOptions.useDeprecatedDemangler());
String text = null;
if (gnuWrappedOptions.useDemanglerParameters()) {
text = gnuWrappedOptions.getDemanglerParametersText();
}
options.setDemanglerApplicationArguments(text);
return options;
}
@Override
protected boolean validateOptions(DemanglerOptions demanglerOtions, MessageLog log) {
GnuDemanglerOptions options = (GnuDemanglerOptions) demanglerOtions;
String applicationArguments = options.getDemanglerApplicationArguments();
if (StringUtils.isBlank(applicationArguments)) {
return true;
}
// Check that the supplied arguments will work with at least one of the requested
// demanglers. (Different versions of the GNU demangler support different arguments.)
String demanglerName = options.getDemanglerName();
try {
GnuDemanglerNativeProcess.getDemanglerNativeProcess(demanglerName,
applicationArguments);
return true;
}
catch (IOException e) {
log.error(getName(), "Invalid options for GNU dangler '" + demanglerName +
"': " + applicationArguments);
log.appendException(e);
}
if (options.useDeprecatedDemangler()) {
// see if the options work in the deprecated demangler
GnuDemanglerOptions deprecatedOptions = options.withDeprecatedDemangler();
String deprecatedName = deprecatedOptions.getDemanglerName();
try {
GnuDemanglerNativeProcess.getDemanglerNativeProcess(deprecatedName,
applicationArguments);
return true;
}
catch (IOException e) {
log.error(getName(),
"Invalid options for GNU dangler '" + deprecatedName + "': " +
applicationArguments);
log.appendException(e);
}
}
return false;
}
@Override
protected DemangledObject doDemangle(String mangled, DemanglerOptions demanglerOtions,
MessageLog log)
throws DemangledException {
GnuDemanglerOptions options = (GnuDemanglerOptions) demanglerOtions;
DemangledObject demangled = null;
try {
demangled = demangler.demangle(mangled, options);
}
catch (DemangledException e) {
if (!options.useDeprecatedDemangler()) {
throw e; // let our parent handle this
}
}
if (demangled != null) {
return demangled;
}
if (options.useDeprecatedDemangler()) {
GnuDemanglerOptions newOptions = options.withDeprecatedDemangler();
demangled = demangler.demangle(mangled, newOptions);
}
return demangled;
}
}

View file

@ -0,0 +1,179 @@
/* ###
* 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.awt.Component;
import java.awt.Container;
import java.beans.PropertyEditorSupport;
import java.util.Objects;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import docking.widgets.checkbox.GCheckBox;
import ghidra.framework.options.CustomOptionsEditor;
import ghidra.util.layout.HorizontalLayout;
import ghidra.util.layout.VerticalLayout;
public class GnuDemanglerOptionsPropertyEditor extends PropertyEditorSupport
implements CustomOptionsEditor {
private static final String USE_DEPRECATED_DEMANGLER = "Use Deprecated Demangler";
private static final String USE_DEMANGLER_PARAMETERS = "Use Demangler Program Parameters";
private static final String USE_DEPRECATED_DEMANGLER_TOOLTIP =
"Signals to use the deprecated demangler when the modern demangler cannot demangle a " +
"given string";
private static final String USE_DEMANGLER_PARAMETERS_TOOLTIP =
"Signals to use pass the given parameters to the demangler program";
private static final String[] NAMES =
{ USE_DEPRECATED_DEMANGLER, USE_DEMANGLER_PARAMETERS };
private static final String[] DESCRIPTIONS = { USE_DEPRECATED_DEMANGLER_TOOLTIP,
USE_DEMANGLER_PARAMETERS_TOOLTIP };
private GnuDemanglerWrappedOption wrappedOption;
private Component editorComponent;
private GCheckBox useDeprecatedDemanglerBox;
private GCheckBox useDemanglerParametersBox;
private JTextField demanglerParametersTextField;
public GnuDemanglerOptionsPropertyEditor() {
editorComponent = buildEditor();
}
private Component buildEditor() {
// we want to have a panel with our options so that we may group them together
JPanel panel = new JPanel(new VerticalLayout(3));
useDeprecatedDemanglerBox = new GCheckBox(USE_DEPRECATED_DEMANGLER);
useDeprecatedDemanglerBox.setSelected(false);
useDeprecatedDemanglerBox.setToolTipText(USE_DEPRECATED_DEMANGLER_TOOLTIP);
useDeprecatedDemanglerBox.addItemListener(e -> firePropertyChange());
panel.add(useDeprecatedDemanglerBox);
createParameterComponent(panel);
return panel;
}
private void createParameterComponent(Container parent) {
JPanel textFieldPanel = new JPanel(new HorizontalLayout(0));
JTextField textField = new JTextField(15);
useDemanglerParametersBox = new GCheckBox(USE_DEMANGLER_PARAMETERS);
useDemanglerParametersBox.setToolTipText(USE_DEMANGLER_PARAMETERS_TOOLTIP);
useDemanglerParametersBox.addItemListener(e -> {
textField.setEnabled(useDemanglerParametersBox.isSelected());
firePropertyChange();
});
textField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
firePropertyChange();
}
@Override
public void insertUpdate(DocumentEvent e) {
firePropertyChange();
}
@Override
public void removeUpdate(DocumentEvent e) {
firePropertyChange();
}
});
textField.setEnabled(false);
textFieldPanel.add(useDemanglerParametersBox);
textFieldPanel.add(Box.createHorizontalStrut(10));
textFieldPanel.add(textField);
parent.add(textFieldPanel);
demanglerParametersTextField = textField;
}
@Override
public void setValue(Object value) {
if (!(value instanceof GnuDemanglerWrappedOption)) {
return;
}
wrappedOption = (GnuDemanglerWrappedOption) value;
setLocalValues(wrappedOption);
firePropertyChange();
}
private void setLocalValues(GnuDemanglerWrappedOption newOption) {
if (newOption.useDeprecatedDemangler() != useDeprecatedDemanglerBox.isSelected()) {
useDeprecatedDemanglerBox.setSelected(newOption.useDeprecatedDemangler());
}
if (newOption.useDemanglerParameters() != useDemanglerParametersBox.isSelected()) {
useDemanglerParametersBox.setSelected(newOption.useDemanglerParameters());
}
String newText = newOption.getDemanglerParametersText();
String currentText = demanglerParametersTextField.getText();
if (!Objects.equals(newText, currentText)) {
demanglerParametersTextField.setText(newText);
}
}
@Override
public Object getValue() {
return cloneNamespaceValues();
}
private GnuDemanglerWrappedOption cloneNamespaceValues() {
GnuDemanglerWrappedOption newOption = new GnuDemanglerWrappedOption();
newOption.setUseDeprecatedDemangler(useDeprecatedDemanglerBox.isSelected());
newOption.setUseDemanglerParameters(useDemanglerParametersBox.isSelected());
newOption.setDemanglerParametersText(demanglerParametersTextField.getText());
return newOption;
}
@Override
public String[] getOptionNames() {
return NAMES;
}
@Override
public String[] getOptionDescriptions() {
return DESCRIPTIONS;
}
@Override
public Component getCustomEditor() {
return editorComponent;
}
@Override
public boolean supportsCustomEditor() {
return true;
}
}

View file

@ -0,0 +1,125 @@
/* ###
* 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.Objects;
import ghidra.framework.options.CustomOption;
import ghidra.framework.options.SaveState;
/**
* A simple java bean adapted to the {@link CustomOption} interface. The public
* getters and setters are self-documenting.
*/
public class GnuDemanglerWrappedOption implements CustomOption {
private static final String USE_DEPRECATED_DEMANGLER = "USE_DEPRECATED_DEMANGLER";
private static final String USE_DEMANGLER_PARAMETERS = "USE_DEMANGLER_PARAMETERS";
private static final String DEMANGLER_PARAMETERS = "DEMANGLER_PARAMETERS";
private boolean useDeprecatedDemangler = false;
private boolean useDemanglerParameters = false;
private String demanglerParametersText = null;
public void setUseDeprecatedDemangler(boolean doUse) {
this.useDeprecatedDemangler = doUse;
}
public boolean useDeprecatedDemangler() {
return useDeprecatedDemangler;
}
public void setDemanglerParametersText(String text) {
this.demanglerParametersText = text;
}
public String getDemanglerParametersText() {
return demanglerParametersText;
}
public void setUseDemanglerParameters(boolean doUse) {
this.useDemanglerParameters = doUse;
}
public boolean useDemanglerParameters() {
return useDemanglerParameters;
}
@Override
public void readState(SaveState state) {
useDeprecatedDemangler =
state.getBoolean(USE_DEPRECATED_DEMANGLER, useDemanglerParameters);
useDemanglerParameters =
state.getBoolean(USE_DEPRECATED_DEMANGLER, useDemanglerParameters);
demanglerParametersText =
state.getString(DEMANGLER_PARAMETERS, demanglerParametersText);
}
@Override
public void writeState(SaveState state) {
state.putBoolean(USE_DEPRECATED_DEMANGLER, useDeprecatedDemangler);
state.putBoolean(USE_DEMANGLER_PARAMETERS, useDemanglerParameters);
state.putString(USE_DEMANGLER_PARAMETERS, demanglerParametersText);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result +
((demanglerParametersText == null) ? 0 : demanglerParametersText.hashCode());
result = prime * result + (useDemanglerParameters ? 1231 : 1237);
result = prime * result + (useDeprecatedDemangler ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
GnuDemanglerWrappedOption other = (GnuDemanglerWrappedOption) obj;
if (!Objects.equals(demanglerParametersText, other.demanglerParametersText)) {
return false;
}
if (useDemanglerParameters != other.useDemanglerParameters) {
return false;
}
if (useDeprecatedDemangler != other.useDeprecatedDemangler) {
return false;
}
return true;
}
@Override
public String toString() {
//@formatter:off
return "{\n" +
"\tuseDeprecatedDemangler: " + useDeprecatedDemangler + ",\n" +
"\tuseDemanglerParameters: " + useDemanglerParameters + ",\n" +
"\tdemanglerParametersText: " + demanglerParametersText + ",\n" +
"}";
//@formatter:on
}
}

View file

@ -38,17 +38,22 @@ public class GnuDemangler implements Demangler {
// needed to instantiate dynamically
}
@Override
public DemanglerOptions createDefaultOptions() {
return new GnuDemanglerOptions();
}
@Override
public boolean canDemangle(Program program) {
String executableFormat = program.getExecutableFormat();
if (isELF(executableFormat) || isMacho(executableFormat)) {
return true;
}
//check if language is GCC
CompilerSpec compilerSpec = program.getCompilerSpec();
if (compilerSpec.getCompilerSpecID().getIdAsString().toLowerCase().indexOf(
"windows") == -1) {
CompilerSpec spec = program.getCompilerSpec();
String specId = spec.getCompilerSpecID().getIdAsString();
if (!specId.toLowerCase().contains("windows")) {
return true;
}
return false;
@ -57,13 +62,21 @@ public class GnuDemangler implements Demangler {
@Override
public DemangledObject demangle(String mangled, boolean demangleOnlyKnownPatterns)
throws DemangledException {
GnuDemanglerOptions options = new GnuDemanglerOptions();
options.setDemangleOnlyKnownPatterns(demangleOnlyKnownPatterns);
return demangle(mangled, options);
}
if (skip(mangled, demangleOnlyKnownPatterns)) {
@Override
public DemangledObject demangle(String mangled, DemanglerOptions demanglerOtions)
throws DemangledException {
if (skip(mangled, demanglerOtions)) {
return null;
}
GnuDemanglerOptions options = getGnuOptions(demanglerOtions);
String originalMangled = mangled;
String globalPrefix = null;
if (mangled.startsWith(GLOBAL_PREFIX)) {
int index = mangled.indexOf("_Z");
@ -84,15 +97,16 @@ public class GnuDemangler implements Demangler {
}
try {
GnuDemanglerNativeProcess process = GnuDemanglerNativeProcess.getDemanglerNativeProcess();
String demangled = process.demangle(mangled).trim();
GnuDemanglerNativeProcess process = getNativeProcess(options);
String demangled = process.demangle(mangled).trim();
if (mangled.equals(demangled) || demangled.length() == 0) {
throw new DemangledException(true);
}
boolean onlyKnownPatterns = options.demangleOnlyKnownPatterns();
DemangledObject demangledObject =
parse(mangled, process, demangled, demangleOnlyKnownPatterns);
parse(mangled, process, demangled, onlyKnownPatterns);
if (demangledObject == null) {
return demangledObject;
}
@ -107,6 +121,7 @@ public class GnuDemangler implements Demangler {
else {
demangledObject.setSignature(demangled);
}
demangledObject.setOriginalMangled(originalMangled);
if (isDwarf) {
@ -121,7 +136,7 @@ public class GnuDemangler implements Demangler {
return demangledObject;
}
catch (IOException e) {
if (e.getMessage().endsWith("14001")) {//missing runtime dlls, prolly
if (e.getMessage().endsWith("14001")) {
ResourceFile installationDir = Application.getInstallationDirectory();
throw new DemangledException("Missing runtime libraries. " + "Please install " +
installationDir + File.separatorChar + "support" + File.separatorChar +
@ -131,7 +146,25 @@ public class GnuDemangler implements Demangler {
}
}
private boolean skip(String mangled, boolean demangleOnlyKnownPatterns) {
private GnuDemanglerOptions getGnuOptions(DemanglerOptions options) {
if (options instanceof GnuDemanglerOptions) {
return (GnuDemanglerOptions) options;
}
return new GnuDemanglerOptions(options);
}
private GnuDemanglerNativeProcess getNativeProcess(GnuDemanglerOptions options)
throws IOException {
String demanglerName = options.getDemanglerName();
String applicationOptions = options.getDemanglerApplicationArguments();
return GnuDemanglerNativeProcess.getDemanglerNativeProcess(demanglerName,
applicationOptions);
}
private boolean skip(String mangled, DemanglerOptions options) {
// Ignore versioned symbols which are generally duplicated at the same address
if (mangled.indexOf("@") > 0) { // do not demangle versioned symbols
@ -143,7 +176,7 @@ public class GnuDemangler implements Demangler {
return true;
}
if (!demangleOnlyKnownPatterns) {
if (!options.demangleOnlyKnownPatterns()) {
return false; // let it go through
}
@ -167,24 +200,20 @@ public class GnuDemangler implements Demangler {
return true;
}
private DemangledObject parse(String mangled, GnuDemanglerNativeProcess process, String demangled,
private DemangledObject parse(String mangled, GnuDemanglerNativeProcess process,
String demangled,
boolean demangleOnlyKnownPatterns) {
if (demangleOnlyKnownPatterns && !isMangledString(mangled, demangled)) {
if (demangleOnlyKnownPatterns && !isKnownMangledString(mangled, demangled)) {
return null;
}
try {
GnuDemanglerParser parser = new GnuDemanglerParser(process);
DemangledObject demangledObject = parser.parse(mangled, demangled);
return demangledObject;
}
catch (Exception e) {
throw e;
}
}
private boolean isMangledString(String mangled, String demangled) {
private boolean isKnownMangledString(String mangled, String demangled) {
//
// We get requests to demangle strings that are not mangled. For newer mangled strings
// we know how to avoid that. However, older mangled strings can be of many forms. To

View file

@ -16,29 +16,68 @@
package ghidra.app.util.demangler.gnu;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import ghidra.framework.Application;
import ghidra.framework.Platform;
public class GnuDemanglerNativeProcess {
public static final String DEMANGLER_GNU = "demangler_gnu_v2.33.1";
public static final String DEMANGLER_GNU = GnuDemanglerOptions.GNU_DEMANGLER_DEFAULT;
private static GnuDemanglerNativeProcess demanglerNativeProcess;
private static final String DEFAULT_NATIVE_OPTIONS = "";
private static final Map<String, GnuDemanglerNativeProcess> processesByName =
new HashMap<>();
private String applicationName;
private String options;
private boolean isDisposed;
private Process process;
private BufferedReader reader;
private PrintWriter writer;
// TODO docme
public static synchronized GnuDemanglerNativeProcess getDemanglerNativeProcess()
throws IOException {
if (demanglerNativeProcess == null) {
demanglerNativeProcess = new GnuDemanglerNativeProcess();
}
return demanglerNativeProcess;
return getDemanglerNativeProcess(DEMANGLER_GNU);
}
private GnuDemanglerNativeProcess() throws IOException {
// TODO docme
public static synchronized GnuDemanglerNativeProcess getDemanglerNativeProcess(String name)
throws IOException {
return getDemanglerNativeProcess(name, DEFAULT_NATIVE_OPTIONS);
}
// TODO docme
// TODO we should probably age-off all demanglers by access time
public static synchronized GnuDemanglerNativeProcess getDemanglerNativeProcess(String name,
String nativeOptions)
throws IOException {
String options = nativeOptions;
if (StringUtils.isBlank(options)) {
options = DEFAULT_NATIVE_OPTIONS;
}
String key = name + nativeOptions;
GnuDemanglerNativeProcess nativeProcess = processesByName.get(key);
if (nativeProcess == null) {
nativeProcess = new GnuDemanglerNativeProcess(name, options);
processesByName.put(key, nativeProcess);
}
return nativeProcess;
}
private GnuDemanglerNativeProcess(String applicationName, String options) throws IOException {
this.applicationName = applicationName;
this.options = options;
createProcess();
}
@ -51,22 +90,25 @@ public class GnuDemanglerNativeProcess {
private String demangle(String mangled, boolean restart) throws IOException {
try {
writer.println(mangled);
writer.flush();
return reader.readLine();
return doDemangle(mangled);
}
catch (IOException e) {
dispose();
if (!restart) {
demanglerNativeProcess = null;
throw new IOException("Demangler process is not running.");
processesByName.remove(applicationName);
throw new IOException("Demangler process is not running.", e);
}
createProcess();
return demangle(mangled, false);
}
}
private String doDemangle(String mangled) throws IOException {
writer.println(mangled);
writer.flush();
return reader.readLine();
}
private void dispose() {
try {
if (process != null) {
@ -86,19 +128,66 @@ public class GnuDemanglerNativeProcess {
private void createProcess() throws IOException {
String executableName = DEMANGLER_GNU + Platform.CURRENT_PLATFORM.getExecutableExtension();
File commandPath = Application.getOSFile(executableName);
String[] command = new String[] { commandPath.getAbsolutePath() };
String[] command = buildCommand();
process = Runtime.getRuntime().exec(command);
InputStream in = process.getInputStream();
OutputStream out = process.getOutputStream();
reader = new BufferedReader(new InputStreamReader(in));
writer = new PrintWriter(out);
checkForError(command);
isDisposed = false;
}
private String[] buildCommand() throws FileNotFoundException {
String executableName =
applicationName + Platform.CURRENT_PLATFORM.getExecutableExtension();
File commandPath = Application.getOSFile(executableName);
String[] command = new String[] { commandPath.getAbsolutePath() };
if (!StringUtils.isBlank(options)) {
String[] optionsArray = options.split("\\s");
command = ArrayUtils.addAll(command, optionsArray);
}
return command;
}
private void checkForError(String[] command) throws IOException {
//
// We do not want to read the error stream in the happy path case, as that will block.
// Send a test string over and read the result. If the test string is blank, then
// there was an error.
//
String testResult = doDemangle("test");
if (!StringUtils.isBlank(testResult)) {
return;
}
InputStream err = process.getErrorStream();
String error = null;
try {
List<String> errorLines = IOUtils.readLines(err, Charset.defaultCharset());
error = StringUtils.join(errorLines, '\n');
}
catch (IOException e) {
throw new IOException("Unable to read process error stream: ", e);
}
if (StringUtils.isBlank(error)) {
return;
}
String executable = command[0];
String baseName = FilenameUtils.getBaseName(executable);
command[0] = baseName;
// cleanup full path, as it is ugly in the error message
error = error.replace(executable, "");
throw new IOException("Error starting demangler with command: '" +
Arrays.toString(command) + "' " + error);
}
}

View file

@ -0,0 +1,115 @@
/* ###
* 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.demangler.gnu;
import org.apache.commons.lang3.StringUtils;
import ghidra.app.util.demangler.DemanglerOptions;
public class GnuDemanglerOptions extends DemanglerOptions {
/**
* Version 2.24 of the GNU demangler. This version supports older formats and older bugs.
*/
public static final String GNU_DEMANGLER_V2_24 = "demangler_gnu_v2_24";
/**
* Version 2.33.1 of the GNU demangler. This version supports less formats than older versions.
*/
public static final String GNU_DEMANGLER_V2_33_1 = "demangler_gnu_v2_33_1";
/**
* The default version to use of the GNU demangler
*/
public static final String GNU_DEMANGLER_DEFAULT = GNU_DEMANGLER_V2_33_1;
private String demanglerName = GNU_DEMANGLER_DEFAULT;
private String demanglerApplicationArguments;
private boolean useDeprecatedDemangler;
public GnuDemanglerOptions() {
// use default values
}
public GnuDemanglerOptions(DemanglerOptions copy) {
super(copy);
if (copy instanceof GnuDemanglerOptions) {
GnuDemanglerOptions gCopy = (GnuDemanglerOptions) copy;
demanglerName = gCopy.demanglerName;
demanglerApplicationArguments = gCopy.demanglerApplicationArguments;
useDeprecatedDemangler = gCopy.useDeprecatedDemangler;
}
}
// TODO docme
public String getDemanglerName() {
return demanglerName;
}
// TODO docme
// TODO should we validate and or log a message it the name is unknown?
public void setDemanglerName(String name) {
this.demanglerName = name;
}
// TODO docme
public String getDemanglerApplicationArguments() {
return demanglerApplicationArguments;
}
// TODO docme
public void setDemanglerApplicationArguments(String args) {
this.demanglerApplicationArguments = args;
}
// TODO docme
// TODO mabye rename to hasNativeApplicationOptions()
public boolean hasDemanglerApplicationArguments() {
return !StringUtils.isBlank(demanglerApplicationArguments);
}
// TODO docme
public void setUseDeprecatedDemangler(boolean doUse) {
this.useDeprecatedDemangler = doUse;
}
// TODO docme
public boolean useDeprecatedDemangler() {
return useDeprecatedDemangler;
}
// TODO docme
public GnuDemanglerOptions withDeprecatedDemangler() {
GnuDemanglerOptions newOptions = new GnuDemanglerOptions(this);
newOptions.setDemanglerName(GNU_DEMANGLER_V2_24);
return newOptions;
}
@Override
public String toString() {
//@formatter:off
return "{\n" +
"\tdoDisassembly: " + doDisassembly() + ",\n" +
"\tapplySignature: " + applySignature() + ",\n" +
"\tdemangleOnlyKnownPatterns: " + demangleOnlyKnownPatterns() + ",\n" +
"\tdemanglerName: " + demanglerName + ",\n" +
"\tuseDeprecatedDemangler: " + useDeprecatedDemangler + ",\n" +
"\tdemanglerApplicationArguments: " + demanglerApplicationArguments + ",\n" +
"}";
//@formatter:on
}
}

View file

@ -27,7 +27,7 @@ import ghidra.app.util.demangler.*;
import ghidra.program.model.lang.CompilerSpec;
import ghidra.util.StringUtilities;
public class GnuDemanglerParser implements DemanglerParser {
public class GnuDemanglerParser {
private static final String CONSTRUCTION_VTABLE_FOR = "construction vtable for ";
private static final String VTT_FOR = "VTT for ";
@ -154,7 +154,6 @@ public class GnuDemanglerParser implements DemanglerParser {
this.process = process;
}
@Override
public DemangledObject parse(String mangled, String demangled) {
try {
return doParse(mangled, demangled);

View file

@ -33,7 +33,8 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
@Before
public void setUp() throws Exception {
process = GnuDemanglerNativeProcess.getDemanglerNativeProcess();
process = GnuDemanglerNativeProcess
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_33_1);
parser = new GnuDemanglerParser(process);
}
@ -41,38 +42,37 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
public void test() throws Exception {
long start = System.currentTimeMillis();
demangle(process, parser, "_ZTVN6Magick21DrawableTextAntialiasE");
demangle(process, parser, "_ZGVZN10KDirLister11emitChangesEvE3dot");//guard variables
demangle("_ZTVN6Magick21DrawableTextAntialiasE");
demangle("_ZGVZN10KDirLister11emitChangesEvE3dot");//guard variables
demangle(process, parser, "_ZZ18__gthread_active_pvE20__gthread_active_ptr");
demangle("_ZZ18__gthread_active_pvE20__gthread_active_ptr");
demangle(process, parser, "_ZNSt10_List_baseIN6Magick5VPathESaIS1_EE5clearEv");
demangle(process, parser, "_ZTISt14unary_functionIPN9MagickLib12_DrawContextEvE");
demangle(process, parser, "_ZTSSt14unary_functionIPN9MagickLib12_DrawContextEvE");
demangle(process, parser, "_ZTCN4Arts17StdoutWriter_implE68_NS_11Object_skelE");
demangle(process, parser, "_ZN6Magick5ImageD1Ev");
demangle(process, parser,
demangle("_ZNSt10_List_baseIN6Magick5VPathESaIS1_EE5clearEv");
demangle("_ZTISt14unary_functionIPN9MagickLib12_DrawContextEvE");
demangle("_ZTSSt14unary_functionIPN9MagickLib12_DrawContextEvE");
demangle("_ZTCN4Arts17StdoutWriter_implE68_NS_11Object_skelE");
demangle("_ZN6Magick5ImageD1Ev");
demangle(
"_ZN6Magick19matteFloodfillImageC2ERKNS_5ColorEjiiN9MagickLib11PaintMethodE");
demangle(process, parser, "_ZThn8_N14nsPrintSession6AddRefEv");// non-virtual thunk
demangle(process, parser,
demangle("_ZThn8_N14nsPrintSession6AddRefEv");// non-virtual thunk
demangle(
"_ZTv0_n24_NSt19basic_ostringstreamIcSt11char_traitsIcE14pool_allocatorIcEED0Ev");// virtual thunk
demangle(process, parser, "_ZTch0_h16_NK8KHotKeys13WindowTrigger4copyEPNS_10ActionDataE");// covariant return thunk
demangle("_ZTch0_h16_NK8KHotKeys13WindowTrigger4copyEPNS_10ActionDataE");// covariant return thunk
demangle(process, parser, "_ZNK2cc14ScrollSnapTypeneERKS0_");
demangle("_ZNK2cc14ScrollSnapTypeneERKS0_");
List<String> list = loadTextResource(GnuDemanglerParserTest.class, "libMagick.symbols.txt");
for (String mangled : list) {
if (mangled == null) {
break;
}
demangle(process, parser, mangled);
demangle(mangled);
}
System.out.println("Elapsed Time: " + (System.currentTimeMillis() - start));
}
private void demangle(GnuDemanglerNativeProcess process, GnuDemanglerParser parser,
String mangled) throws IOException {
private void demangle(String mangled) throws IOException {
String demangled = process.demangle(mangled);
assertNotNull(demangled);
assertNotEquals(mangled, demangled);
@ -81,14 +81,12 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
}
@Test
public void testParsingBug() {
// std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char> >(std::basic_istream<char, std::char_traits<char> >&, char&)
// _ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_RS3_
// _ZStrsIcSt11char_traitsIcESaIcEERSt13basic_istreamIT_T0_ES7_RSbIS4_S5_T1_E
GnuDemanglerParser parser = new GnuDemanglerParser(null);
public void testOverloadedShiftOperatorParsingBug() {
parser = new GnuDemanglerParser(null);
DemangledObject object = parser.parse(null,
"std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char> >(std::basic_istream<char, std::char_traits<char> >&, char&)");
"std::basic_istream<char, std::char_traits<char> >& " +
"std::operator>><char, std::char_traits<char> >" +
"(std::basic_istream<char, std::char_traits<char> >&, char&)");
String name = object.getName();
assertEquals("operator>><char,std--char_traits<char>>", name);
}
@ -210,6 +208,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
public void testFunctionPointers() throws Exception {
String mangled = "__t6XpsMap2ZlZP14CORBA_TypeCodePFRCl_UlUlUlf";
process = GnuDemanglerNativeProcess
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
@ -431,6 +432,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
//
String mangled = "CalcPortExposedRect__13LScrollerViewCFR4Rectb";
process = GnuDemanglerNativeProcess
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
@ -452,6 +456,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
//
String mangled = "__dt__Q26MsoDAL9VertFrameFv";
process = GnuDemanglerNativeProcess
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
@ -491,6 +498,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
//
String mangled = "GetColWidths__13CDataRendererCFRA7_s";
process = GnuDemanglerNativeProcess
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
@ -512,6 +522,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
//
String mangled = "GetColWidths__13CDataRendererCFPA7_s";
process = GnuDemanglerNativeProcess
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
@ -567,6 +580,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
//
String mangled = "_gmStage2__FP12SECTION_INFOPiPA12_iiPCs";
process = GnuDemanglerNativeProcess
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
@ -598,6 +614,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
//
String mangled = "__ct__Q24CStr6BufferFR4CStrUl";
process = GnuDemanglerNativeProcess
.getDemanglerNativeProcess(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
@ -1085,6 +1104,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
// Mangled: _ZN12uavcan_stm329CanDriverC1ILj64EEERA2_AT__NS_9CanRxItemE
//
// Demangled: uavcan_stm32::CanDriver::CanDriver<64u>(uavcan_stm32::CanRxItem (&) [2][64u])
//
String mangled = "_ZN12uavcan_stm329CanDriverC1ILj64EEERA2_AT__NS_9CanRxItemE";
@ -1113,4 +1133,25 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
DemangledObject res = demangler.demangle(mangled, true);
assertNull(res);
}
@Test
public void testFunctionWithLambda_WrappingAnotherFunctionCall() throws Exception {
//
// Mangled: _Z11wrap_360_cdIiEDTcl8wrap_360fp_Lf42c80000EEET_
//
// Demangled: (wrap_360({parm#1}, (float)[42c80000])) wrap_360_cd<int>(int)
//
String mangled = "_Z11wrap_360_cdIiEDTcl8wrap_360fp_Lf42c80000EEET_";
String demangled = process.demangle(mangled);
DemangledObject object = parser.parse(mangled, demangled);
assertNotNull(object);
assertTrue(object instanceof DemangledFunction);
// TODO maybe put full output in setUtilDemangled()
String signature = object.getSignature(false);
assertEquals("undefined wrap_360_cd<int>(int)", signature);
}
}

View file

@ -17,11 +17,12 @@ package ghidra.app.util.demangler.gnu;
import static org.junit.Assert.*;
import java.io.IOException;
import org.junit.Before;
import org.junit.Test;
import generic.test.AbstractGenericTest;
import ghidra.app.cmd.label.DemanglerCmd;
import ghidra.app.util.demangler.*;
import ghidra.program.database.ProgramDB;
import ghidra.program.model.address.Address;
@ -30,6 +31,7 @@ import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Data;
import ghidra.program.model.symbol.*;
import ghidra.test.ToyProgramBuilder;
import ghidra.util.Msg;
import ghidra.util.task.TaskMonitor;
public class GnuDemanglerTest extends AbstractGenericTest {
@ -161,7 +163,10 @@ public class GnuDemanglerTest extends AbstractGenericTest {
GnuDemangler demangler = new GnuDemangler();
demangler.canDemangle(program);// this perform initialization
DemangledObject result = demangler.demangle(mangled, false);
GnuDemanglerOptions options = new GnuDemanglerOptions();
options.setDemangleOnlyKnownPatterns(false);
options.setDemanglerName(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
DemangledObject result = demangler.demangle(mangled, options);
assertNotNull(result);
assertEquals("undefined MyFunction::~MyFunction(void)", result.getSignature(false));
}
@ -184,10 +189,63 @@ public class GnuDemanglerTest extends AbstractGenericTest {
GnuDemangler demangler = new GnuDemangler();
demangler.canDemangle(program);// this perform initialization
DemangledObject result = demangler.demangle(mangled, true);
GnuDemanglerOptions options = new GnuDemanglerOptions();
options.setDemangleOnlyKnownPatterns(true); // do not try
options.setDemanglerName(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
DemangledObject result = demangler.demangle(mangled, options);
assertNull(result);
}
@Test
public void testDemangler_Format_CodeWarrior_MacOS8or9() throws DemangledException {
// .scroll__10TTextPanelFUcsi
String mangled = ".scroll__10TTextPanelFUcsi";
GnuDemangler demangler = new GnuDemangler();
demangler.canDemangle(program);// this perform initialization
GnuDemanglerOptions options = new GnuDemanglerOptions();
options.setDemangleOnlyKnownPatterns(false);
options.setDemanglerName(GnuDemanglerOptions.GNU_DEMANGLER_V2_24);
DemangledObject result = demangler.demangle(mangled, options);
assertNotNull(result);
assertEquals("undefined TTextPanel::scroll(unsigned char,short,int)",
result.getSignature(false));
}
@Test
public void testGnuNativeProcessWithValidArguments() {
String demanglerName = GnuDemanglerOptions.GNU_DEMANGLER_DEFAULT;
String applicationArguments = "-s auto";
try {
GnuDemanglerNativeProcess.getDemanglerNativeProcess(demanglerName,
applicationArguments);
}
catch (IOException e) {
fail("Expected an exception when passing unknown arguments to the native demangler");
}
}
@Test
public void testGnuNativeProcessWithUnknownArguments() {
String demanglerName = GnuDemanglerOptions.GNU_DEMANGLER_DEFAULT;
String applicationArguments = "-s MrBob";
try {
GnuDemanglerNativeProcess.getDemanglerNativeProcess(demanglerName,
applicationArguments);
fail("Expected an exception when passing unknown arguments to the native demangler");
}
catch (IOException e) {
// expected
Msg.error(this, "Test error", e);
}
}
private Address addr(String address) {
return program.getAddressFactory().getAddress(address);
}

View file

@ -0,0 +1,65 @@
/* ###
* 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 ghidra.app.util.demangler.*;
import ghidra.app.util.demangler.microsoft.MicrosoftDemangler;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.program.model.listing.Program;
public class MicrosoftDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
private static final String NAME = "Demangler Microsoft";
private static final String DESCRIPTION =
"After a function is created, this analyzer will attempt to demangle " +
"the name and apply datatypes to parameters.";
private final static String OPTION_NAME_APPLY_SIGNATURE = "Apply Function Signatures";
private static final String OPTION_DESCRIPTION_APPLY_SIGNATURE =
"Apply any recovered function signature, in addition to the function name";
private boolean applyFunctionSignature = true;
private MicrosoftDemangler demangler = new MicrosoftDemangler();
public MicrosoftDemanglerAnalyzer() {
super(NAME, DESCRIPTION);
setDefaultEnablement(true);
}
@Override
public boolean canAnalyze(Program program) {
return demangler.canDemangle(program);
}
@Override
public void registerOptions(Options options, Program program) {
options.registerOption(OPTION_NAME_APPLY_SIGNATURE, applyFunctionSignature, null,
OPTION_DESCRIPTION_APPLY_SIGNATURE);
}
@Override
public void optionsChanged(Options options, Program program) {
applyFunctionSignature =
options.getBoolean(OPTION_NAME_APPLY_SIGNATURE, applyFunctionSignature);
}
@Override
protected DemangledObject doDemangle(String mangled, DemanglerOptions options, MessageLog log)
throws DemangledException {
DemangledObject demangled = demangler.demangle(mangled, options);
return demangled;
}
}

View file

@ -15,8 +15,6 @@
*/
package ghidra.app.util.demangler.microsoft;
import java.util.regex.Pattern;
import ghidra.app.util.demangler.*;
import ghidra.app.util.opinion.MSCoffLoader;
import ghidra.app.util.opinion.PeLoader;
@ -30,15 +28,6 @@ import util.demangler.GenericDemangledException;
*/
public class MicrosoftDemangler implements Demangler {
/**
* This represents an odd symbol that looks mangled, but we don't know what to do with. It
* is of the form:
* ?BobsStuffIO@344text__@@U_text@@?W
*
* where the last character is preceded by a special character, such as ?, *, -, etc
*/
private static Pattern INVALID_TRAILING_CHARS_PATTERN = Pattern.compile(".*@@[?*`%~+/-][A-Z]");
public MicrosoftDemangler() {
}
@ -61,6 +50,19 @@ public class MicrosoftDemangler implements Demangler {
}
}
@Override
public DemangledObject demangle(String mangled, DemanglerOptions options)
throws DemangledException {
try {
DemangledObject demangled = demangleMS(mangled, options.demangleOnlyKnownPatterns());
return demangled;
}
catch (GenericDemangledException e) {
throw new DemangledException(true);
}
}
private DemangledObject demangleMS(String mangled, boolean demangleOnlyKnownPatterns)
throws GenericDemangledException {
if (mangled == null || mangled.length() == 0) {
@ -69,7 +71,7 @@ public class MicrosoftDemangler implements Demangler {
MDMangGhidra demangler = new MDMangGhidra();
try {
demangler.demangle(mangled, demangleOnlyKnownPatterns); //not using return type here.
demangler.demangle(mangled, demangleOnlyKnownPatterns);
DemangledObject object = demangler.getObject();
return object;
}
@ -81,6 +83,15 @@ public class MicrosoftDemangler implements Demangler {
}
}
// /**
// * This represents an odd symbol that looks mangled, but we don't know what to do with. It
// * is of the form:
// * ?BobsStuffIO@344text__@@U_text@@?W
// *
// * where the last character is preceded by a special character, such as ?, *, -, etc
// */
// private static Pattern INVALID_TRAILING_CHARS_PATTERN = Pattern.compile(".*@@[?*`%~+/-][A-Z]");
// private boolean isMangled(String mangled) {
// int atpos = mangled.indexOf("@");
// boolean isMangled = mangled.charAt(0) == '?' && atpos != -1;

View file

@ -82,7 +82,8 @@ class LoadPdbTask extends Task {
}
try {
AutoAnalysisManager.getAnalysisManager(program).scheduleWorker(worker, null, true,
AutoAnalysisManager.getAnalysisManager(program)
.scheduleWorker(worker, null, true,
monitor);
}
catch (InterruptedException | CancelledException e1) {
@ -117,7 +118,7 @@ class LoadPdbTask extends Task {
private void analyzeSymbols(TaskMonitor monitor, MessageLog log) {
DemanglerAnalyzer demanglerAnalyzer = new DemanglerAnalyzer();
MicrosoftDemanglerAnalyzer demanglerAnalyzer = new MicrosoftDemanglerAnalyzer();
String analyzerName = demanglerAnalyzer.getName();
Options analysisProperties = program.getOptions(Program.ANALYSIS_PROPERTIES);

View file

@ -15,8 +15,6 @@
*/
package ghidra.framework.options;
import ghidra.util.HelpLocation;
import java.awt.Color;
import java.awt.Font;
import java.beans.PropertyEditor;
@ -26,6 +24,8 @@ import java.util.List;
import javax.swing.KeyStroke;
import ghidra.util.HelpLocation;
public interface Options {
public static final char DELIMITER = '.';
public final static String DELIMITER_STRING = new String(new char[] { DELIMITER });