GP-42 Initial implementation of Pdb symbol store / symbol servers
@@ -17,6 +17,10 @@ eclipse.project.name = 'GPL CabExtract'
|
|||||||
|
|
||||||
project.ext.cabextract = "cabextract-1.6"
|
project.ext.cabextract = "cabextract-1.6"
|
||||||
|
|
||||||
|
/*********************************************************************************
|
||||||
|
* Deprecated - will be removed
|
||||||
|
*********************************************************************************/
|
||||||
|
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* CabExtract platform specific tasks
|
* CabExtract platform specific tasks
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1 +1,6 @@
|
|||||||
Internet,https://msdl.microsoft.com/download/symbols
|
Internet|https://msdl.microsoft.com/download/symbols/|WARNING: Check your organization's security policy before downloading files from the internet.
|
||||||
|
Internet|https://chromium-browser-symsrv.commondatastorage.googleapis.com|WARNING: Check your organization's security policy before downloading files from the internet.
|
||||||
|
Internet|https://symbols.mozilla.org/|WARNING: Check your organization's security policy before downloading files from the internet.
|
||||||
|
Internet|https://software.intel.com/sites/downloads/symbols/|WARNING: Check your organization's security policy before downloading files from the internet.
|
||||||
|
Internet|https://driver-symbols.nvidia.com/|WARNING: Check your organization's security policy before downloading files from the internet.
|
||||||
|
Internet|https://download.amd.com/dir/bin|WARNING: Check your organization's security policy before downloading files from the internet.
|
||||||
|
|||||||
@@ -1,156 +0,0 @@
|
|||||||
<!DOCTYPE doctype PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
|
|
||||||
|
|
||||||
<HTML>
|
|
||||||
<HEAD>
|
|
||||||
<META name="generator" content=
|
|
||||||
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
|
||||||
|
|
||||||
<TITLE>Load PDB File</TITLE>
|
|
||||||
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
|
|
||||||
</HEAD>
|
|
||||||
|
|
||||||
<BODY lang="EN-US">
|
|
||||||
<H1><A name="Load_PDB_File"></A>Load PDB File</H1>
|
|
||||||
|
|
||||||
<P>A program database (PDB) file holds debugging and project state information about a program
|
|
||||||
and can be created in a number of ways. Historically, it has been created using a Microsoft
|
|
||||||
compiler and written in <CODE>C/C++</CODE>, <CODE>C#</CODE>, and <CODE>Visual Basic</CODE>.
|
|
||||||
A user generates a PDB file using the <CODE>/ZI or /Zi</CODE> flag (for C/C++ programs) or the
|
|
||||||
<CODE>/debug</CODE> flag (for Visual Basic/C# programs).</P>
|
|
||||||
|
|
||||||
<P>There are two mechanisms for processing a PDB file. First, the platform-independent
|
|
||||||
PDB Universal Reader/Analyzer, which can read a raw PDB file and apply it. Its capabilities
|
|
||||||
are expected to be expanded in future releases. Second, the legacy capability that uses the
|
|
||||||
<A href="#dia">DIA SDK</A> to read information from the PDB file. This mechanism can only run
|
|
||||||
on a Windows platform, however it creates an XML representation of information gleaned using
|
|
||||||
the DIA SDK. These XML files can be saved and then used on Windows and non-Windows platforms
|
|
||||||
hosting Ghidra.</P>
|
|
||||||
|
|
||||||
<P>If loading a PDB, this should be done prior to other analysis, except in special cases,
|
|
||||||
such as when only loading data types.</P>
|
|
||||||
|
|
||||||
<P>Restricted loading of data types or public symbols is
|
|
||||||
supported by PDB Universal.</P>
|
|
||||||
|
|
||||||
<H2>To Load a PDB</H2>
|
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
|
||||||
<OL>
|
|
||||||
<LI>From the menu-bar of a tool, select <B>File <IMG src="../../shared/arrow.gif" alt=""
|
|
||||||
width="18" height="14"> Load PDB File</B></LI>
|
|
||||||
|
|
||||||
<LI>In the file chooser, select the PDB file (*.PDB or *.PDB.XML)</LI>
|
|
||||||
|
|
||||||
<LI>Click the "Select PDB" button</LI>
|
|
||||||
</OL>
|
|
||||||
|
|
||||||
<BLOCKQUOTE><UL>
|
|
||||||
<LI>PDB Universal is automatically used for *.PDB on non-Windows platforms</LI>
|
|
||||||
<LI>PDB MSDIA is used for *.PDB.XML files</LI>
|
|
||||||
</UL></BLOCKQUOTE>
|
|
||||||
|
|
||||||
<P>When a user chooses a PDB or XML file to load for a program, Ghidra will verify its
|
|
||||||
signature to be valid for the program. At this time, the PDB MSDIA loader cannot be used to
|
|
||||||
force-load a mismatched PDB. To perform a force-load of a PDB file, the user must choose the
|
|
||||||
PDB Universal loader if given the option. Force-loading an mismatched file can have
|
|
||||||
consequences, such as loading incorrect data types and symbols located at the wrong
|
|
||||||
addresses.</P>
|
|
||||||
|
|
||||||
<P>
|
|
||||||
PDB files may also be loaded using the PDB Analyzer, which is available through
|
|
||||||
<A HREF="help/topics/AutoAnalysisPlugin/AutoAnalysis.htm#Auto_Analyze">Auto Analysis</A> or as
|
|
||||||
a <A HREF="help/topics/AutoAnalysisPlugin/AutoAnalysis.htm#Analyze_One_Shot">One Shot Analyzer</A>.
|
|
||||||
</P>
|
|
||||||
</BLOCKQUOTE>
|
|
||||||
|
|
||||||
<H2>Information Loaded From PDB</H2>
|
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
|
||||||
<OL>
|
|
||||||
<LI>Structure and union definitions</LI>
|
|
||||||
|
|
||||||
<LI>Typedefs</LI>
|
|
||||||
|
|
||||||
<LI>Enumerations</LI>
|
|
||||||
|
|
||||||
<LI>Class definitions</LI>
|
|
||||||
|
|
||||||
<LI>Function prototypes</LI>
|
|
||||||
|
|
||||||
<LI>Stack variable names and data types</LI>
|
|
||||||
|
|
||||||
<LI>Source line numbers</LI>
|
|
||||||
|
|
||||||
<LI>Instruction and data symbols</LI>
|
|
||||||
</OL>
|
|
||||||
</BLOCKQUOTE>
|
|
||||||
|
|
||||||
<H2>Loading Errors</H2>
|
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
|
||||||
<P>Before the PDB file is loaded into the program, then PDB signature and age are matched
|
|
||||||
against the information stored in the executable. If these values do not match, then the PDB
|
|
||||||
will not be loaded.</P>
|
|
||||||
|
|
||||||
<P align="center"><IMG border="0" src="images/About_pdb.png"></P>
|
|
||||||
|
|
||||||
<P align="center">Figure 1</P>
|
|
||||||
</BLOCKQUOTE>
|
|
||||||
|
|
||||||
<H2>The DIA SDK-Based Capability</H2>
|
|
||||||
|
|
||||||
<P>*.PDB.XML files can be created in three different ways:
|
|
||||||
|
|
||||||
<BLOCKQUOTE><UL>
|
|
||||||
<LI>From the Ghidra GUI in Windows, use the
|
|
||||||
<A href="help/topics/GhidraScriptMgrPlugin/GhidraScriptMgrPlugin.htm">Ghidra Script Manager</A>
|
|
||||||
to run the <I>CreatePdbXmlFilesScript.java</I> script. Follow the prompts to choose
|
|
||||||
the .PDB file (or directory containing .PDB file(s)) to be converted to .PDB.XML form.
|
|
||||||
When given a directory, the script recursively traverses all subfolders to find .PDB
|
|
||||||
files. A created .PDB.XML file is placed in the same location as the corresponding original
|
|
||||||
.PDB file.</LI>
|
|
||||||
<br>
|
|
||||||
<LI>From a Windows command line, navigate to the following directory:
|
|
||||||
<I><ghidra install root>/support</I>
|
|
||||||
and run the <I>createPdbXmlFiles.bat</I> script. The script takes one argument representing
|
|
||||||
either one .PDB file or a directory of .PDB files. When given a directory, the script
|
|
||||||
recursively traverses all subdirectories to find .PDB files. A created .PDB.XML file is
|
|
||||||
placed in the same location as the corresponding original .PDB file. Sample calls to the
|
|
||||||
script are shown below.
|
|
||||||
<br><br>
|
|
||||||
<CODE> createPdbXmlFiles.bat C:\Symbols\samplePdb.pdb</CODE>
|
|
||||||
<br>
|
|
||||||
<CODE> createPdbXmlFiles.bat C:\Symbols</CODE>
|
|
||||||
<br>
|
|
||||||
</LI>
|
|
||||||
<br>
|
|
||||||
<LI>Run the included <I>pdb.exe</I> executable (found in the <I><ghidra install
|
|
||||||
root>/Ghidra/Features/PDB/os/win64</I> directory) and redirect (save) its output to an
|
|
||||||
XML file as shown below:
|
|
||||||
<br><br>
|
|
||||||
<CODE> pdb.exe samplePdb.pdb > samplePdb.pdb.xml</CODE>
|
|
||||||
</LI>
|
|
||||||
</UL></BLOCKQUOTE>
|
|
||||||
</P>
|
|
||||||
<P><B>NOTE:</B> Execution of <i>pdb.exe</i> has runtime dependencies which must be satisfied.
|
|
||||||
Please refer to the <a href="docs/README_PDB.html">README_PDB</a> document for details.</P>
|
|
||||||
|
|
||||||
<H2><A name="dia"></A>Debug Interface Access SDK</H2>
|
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
|
||||||
<P>The Microsoft Debug Interface Access Software Development Kit (DIA SDK) provides access to
|
|
||||||
debug information stored in program database (.PDB) files generated by Microsoft
|
|
||||||
post-compiler tools. Because the format of the .PDB file generated by the post-compiler tools
|
|
||||||
undergoes constant revision, exposing the format is impractical. Using the DIA API, you can
|
|
||||||
develop applications that search for and browse debug information stored in a .PDB file. Such
|
|
||||||
applications could, for example, report stack trace-back information and analyze performance
|
|
||||||
data.</P>
|
|
||||||
|
|
||||||
<P><IMG src="../../shared/note.png" border="0">If you are attempting to load a PDB on a
|
|
||||||
Windows machine and see an error message such as "Unable to locate the DIA SDK,"
|
|
||||||
you will need to add and register one or more files on your computer. Refer to the
|
|
||||||
<a href="docs/README_PDB.html">README_PDB</a> document for detailed instructions.
|
|
||||||
</P>
|
|
||||||
</BLOCKQUOTE>
|
|
||||||
</BODY>
|
|
||||||
</HTML>
|
|
||||||
@@ -66,11 +66,13 @@ public class BinaryReader {
|
|||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
setLittleEndian(isLittleEndian);
|
setLittleEndian(isLittleEndian);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a clone of this reader positioned at the new index.
|
* Returns a clone of this reader, with its own independent current position,
|
||||||
|
* positioned at the new index.
|
||||||
|
*
|
||||||
* @param newIndex the new index
|
* @param newIndex the new index
|
||||||
* @return a clone of this reader positioned at the new index
|
* @return an independent clone of this reader positioned at the new index
|
||||||
*/
|
*/
|
||||||
public BinaryReader clone(long newIndex) {
|
public BinaryReader clone(long newIndex) {
|
||||||
BinaryReader clone = new BinaryReader(provider, isLittleEndian());
|
BinaryReader clone = new BinaryReader(provider, isLittleEndian());
|
||||||
@@ -88,6 +90,36 @@ public class BinaryReader {
|
|||||||
return clone(currentIndex);
|
return clone(currentIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a BinaryReader that is in BigEndian mode.
|
||||||
|
*
|
||||||
|
* @return either this same instance (if already BigEndian), or a new instance
|
||||||
|
* (at the same location) in BigEndian mode
|
||||||
|
*/
|
||||||
|
public BinaryReader asBigEndian() {
|
||||||
|
if (isBigEndian()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
BinaryReader result = clone(currentIndex);
|
||||||
|
result.setLittleEndian(false);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a BinaryReader that is in LittleEndian mode.
|
||||||
|
*
|
||||||
|
* @return either this same instance (if already LittleEndian), or a new instance
|
||||||
|
* (at the same location) in LittleEndian mode
|
||||||
|
*/
|
||||||
|
public BinaryReader asLittleEndian() {
|
||||||
|
if (!isBigEndian()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
BinaryReader result = clone(currentIndex);
|
||||||
|
result.setLittleEndian(true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this reader will extract values in little endian,
|
* Returns true if this reader will extract values in little endian,
|
||||||
* otherwise in big endian.
|
* otherwise in big endian.
|
||||||
@@ -97,6 +129,15 @@ public class BinaryReader {
|
|||||||
return converter instanceof LittleEndianDataConverter;
|
return converter instanceof LittleEndianDataConverter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this reader will extract values in big endian.
|
||||||
|
*
|
||||||
|
* @return true is big endian, false is little endian
|
||||||
|
*/
|
||||||
|
public boolean isBigEndian() {
|
||||||
|
return converter instanceof BigEndianDataConverter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the endian of this binary reader.
|
* Sets the endian of this binary reader.
|
||||||
* @param isLittleEndian true for little-endian and false for big-endian
|
* @param isLittleEndian true for little-endian and false for big-endian
|
||||||
|
|||||||
@@ -1,53 +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.bin.format.pdb;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
import ghidra.app.util.bin.*;
|
|
||||||
import ghidra.framework.*;
|
|
||||||
|
|
||||||
public class PdbFactory {
|
|
||||||
static {
|
|
||||||
PluggableServiceRegistry.registerPluggableService(PdbFactory.class,
|
|
||||||
new PdbFactory());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PdbInfoDotNetIface getPdbInfoDotNetInstance(
|
|
||||||
BinaryReader reader, int ptr) throws IOException {
|
|
||||||
PdbFactory factory = PluggableServiceRegistry
|
|
||||||
.getPluggableService(PdbFactory.class);
|
|
||||||
return factory.doGetPdbInfoDotNetInstance(reader, ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PdbInfoIface getPdbInfoInstance(BinaryReader reader, int ptr)
|
|
||||||
throws IOException {
|
|
||||||
PdbFactory factory = PluggableServiceRegistry
|
|
||||||
.getPluggableService(PdbFactory.class);
|
|
||||||
return factory.doGetPdbInfoInstance(reader, ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected PdbInfoDotNetIface doGetPdbInfoDotNetInstance(
|
|
||||||
BinaryReader reader, int ptr) throws IOException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected PdbInfoIface doGetPdbInfoInstance(BinaryReader reader, int ptr)
|
|
||||||
throws IOException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.util.bin.format.pdb;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
|
import ghidra.framework.options.Options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bag of information about a Pdb symbol file, usually extracted from information present in a PE
|
||||||
|
* binary.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface PdbInfo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read either a {@link PdbInfoCodeView} object or a {@link PdbInfoDotNet} object
|
||||||
|
* from the BinaryReader of a PE binary.
|
||||||
|
*
|
||||||
|
* @param reader BinaryReader
|
||||||
|
* @param offset position of the debug info
|
||||||
|
* @return new PdbInfoCodeView or PdbInfoDotNet object
|
||||||
|
* @throws IOException if error
|
||||||
|
*/
|
||||||
|
public static PdbInfo read(BinaryReader reader, long offset) throws IOException {
|
||||||
|
if (PdbInfoCodeView.isMatch(reader, offset)) {
|
||||||
|
return PdbInfoCodeView.read(reader, offset);
|
||||||
|
}
|
||||||
|
if (PdbInfoDotNet.isMatch(reader, offset)) {
|
||||||
|
return PdbInfoDotNet.read(reader, offset);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this instance is valid.
|
||||||
|
*
|
||||||
|
* @return boolean true if valid (magic signature matches and fields have valid data)
|
||||||
|
*/
|
||||||
|
boolean isValid();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the various PDB info fields to a program's options.
|
||||||
|
*
|
||||||
|
* @param options Options of a Program to write to
|
||||||
|
*/
|
||||||
|
void serializeToOptions(Options options);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.util.bin.format.pdb;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
|
import ghidra.app.util.bin.StructConverter;
|
||||||
|
import ghidra.app.util.bin.format.pe.debug.DebugCodeViewConstants;
|
||||||
|
import ghidra.framework.options.Options;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.util.Conv;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Older style pdb information, using a simple 32bit hash to link the pdb to its binary.
|
||||||
|
*/
|
||||||
|
public class PdbInfoCodeView implements StructConverter, PdbInfo {
|
||||||
|
private static final int MAGIC =
|
||||||
|
DebugCodeViewConstants.SIGNATURE_NB << 16 | DebugCodeViewConstants.VERSION_10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the pdb information at the specified offset is a {@link PdbInfoCodeView}
|
||||||
|
* type (based on the signature at that offset).
|
||||||
|
*
|
||||||
|
* @param reader {@link BinaryReader}
|
||||||
|
* @param offset offset of the Pdb information
|
||||||
|
* @return boolean true if it is a {@link PdbInfoCodeView} type
|
||||||
|
* @throws IOException if error reading data
|
||||||
|
*/
|
||||||
|
public static boolean isMatch(BinaryReader reader, long offset) throws IOException {
|
||||||
|
//read value out as big endian
|
||||||
|
int value = reader.asBigEndian().readInt(offset);
|
||||||
|
return MAGIC == value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the pdb information from a PE binary.
|
||||||
|
*
|
||||||
|
* @param reader {@link BinaryReader}
|
||||||
|
* @param offset offset of the Pdb information
|
||||||
|
* @return new {@link PdbInfoCodeView} instance, never null
|
||||||
|
* @throws IOException if error reading data
|
||||||
|
*/
|
||||||
|
public static PdbInfoCodeView read(BinaryReader reader, long offset) throws IOException {
|
||||||
|
reader = reader.clone(offset);
|
||||||
|
|
||||||
|
PdbInfoCodeView result = new PdbInfoCodeView();
|
||||||
|
result.magic = reader.readNextByteArray(4);
|
||||||
|
result.offset = reader.readNextInt();
|
||||||
|
result.sig = reader.readNextInt();
|
||||||
|
result.age = reader.readNextInt();
|
||||||
|
result.pdbPath = reader.readNextAsciiString();
|
||||||
|
result.pdbName = FilenameUtils.getName(result.pdbPath);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] magic;
|
||||||
|
private int offset;
|
||||||
|
private int sig;
|
||||||
|
private int age;
|
||||||
|
private String pdbName;
|
||||||
|
private String pdbPath;
|
||||||
|
|
||||||
|
private PdbInfoCodeView() {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid() {
|
||||||
|
return magic.length == 4 && !pdbName.isBlank();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serializeToOptions(Options options) {
|
||||||
|
options.setString(PdbParserConstants.PDB_VERSION,
|
||||||
|
new String(magic, StandardCharsets.US_ASCII));
|
||||||
|
options.setString(PdbParserConstants.PDB_SIGNATURE, Conv.toHexString(sig));
|
||||||
|
options.setString(PdbParserConstants.PDB_AGE, Integer.toHexString(age));
|
||||||
|
options.setString(PdbParserConstants.PDB_FILE, pdbName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataType toDataType() {
|
||||||
|
StructureDataType struct = new StructureDataType("PdbInfo", 0);
|
||||||
|
struct.add(new StringDataType(), magic.length, "signature", null);
|
||||||
|
struct.add(new DWordDataType(), "offset", null);
|
||||||
|
struct.add(new DWordDataType(), "sig", null);
|
||||||
|
struct.add(new DWordDataType(), "age", null);
|
||||||
|
if (pdbName.length() > 0) {
|
||||||
|
struct.add(new StringDataType(), pdbName.length(), "pdbname", null);
|
||||||
|
}
|
||||||
|
struct.setCategoryPath(new CategoryPath("/PDB"));
|
||||||
|
return struct;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.util.bin.format.pdb;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
|
import ghidra.app.util.bin.StructConverter;
|
||||||
|
import ghidra.app.util.bin.format.pe.debug.DebugCodeViewConstants;
|
||||||
|
import ghidra.app.util.datatype.microsoft.GUID;
|
||||||
|
import ghidra.app.util.datatype.microsoft.GuidDataType;
|
||||||
|
import ghidra.framework.options.Options;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Newer style pdb information, using a GUID to link the pdb to its binary.
|
||||||
|
*/
|
||||||
|
public class PdbInfoDotNet implements StructConverter, PdbInfo {
|
||||||
|
private static final int MAGIC =
|
||||||
|
DebugCodeViewConstants.SIGNATURE_DOT_NET << 16 | DebugCodeViewConstants.VERSION_DOT_NET;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the pdb information at the specified offset is a {@link PdbInfoDotNet}
|
||||||
|
* type (based on the signature at that offset).
|
||||||
|
*
|
||||||
|
* @param reader {@link BinaryReader}
|
||||||
|
* @param offset offset of the Pdb information
|
||||||
|
* @return boolean true if it is a {@link PdbInfoDotNet} type
|
||||||
|
* @throws IOException if error reading data
|
||||||
|
*/
|
||||||
|
public static boolean isMatch(BinaryReader reader, long offset) throws IOException {
|
||||||
|
//read value out as big endian
|
||||||
|
int value = reader.asBigEndian().readInt(offset);
|
||||||
|
return MAGIC == value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads an instance from the stream.
|
||||||
|
*
|
||||||
|
* @param reader {@link BinaryReader} to read from
|
||||||
|
* @param offset position of the pdb info
|
||||||
|
* @return new instance, never null
|
||||||
|
* @throws IOException if IO error or data format error
|
||||||
|
*/
|
||||||
|
public static PdbInfoDotNet read(BinaryReader reader, long offset) throws IOException {
|
||||||
|
reader = reader.clone(offset);
|
||||||
|
|
||||||
|
PdbInfoDotNet result = new PdbInfoDotNet();
|
||||||
|
result.magic = reader.readNextByteArray(4);
|
||||||
|
result.guid = new GUID(reader);
|
||||||
|
result.age = reader.readNextInt();
|
||||||
|
result.pdbPath = reader.readNextAsciiString();
|
||||||
|
result.pdbName = FilenameUtils.getName(result.pdbPath);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance from explicit values.
|
||||||
|
*
|
||||||
|
* @param pdbPath String path / filename of the pdb file
|
||||||
|
* @param age age
|
||||||
|
* @param guid {@link GUID}
|
||||||
|
* @return new instance, never null
|
||||||
|
*/
|
||||||
|
public static PdbInfoDotNet fromValues(String pdbPath, int age, GUID guid) {
|
||||||
|
PdbInfoDotNet result = new PdbInfoDotNet();
|
||||||
|
result.pdbPath = pdbPath;
|
||||||
|
result.pdbName = FilenameUtils.getName(result.pdbPath);
|
||||||
|
result.age = age;
|
||||||
|
result.guid = guid;
|
||||||
|
result.magic = "????".getBytes();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private byte[] magic;
|
||||||
|
private GUID guid;
|
||||||
|
private int age;
|
||||||
|
private String pdbName;
|
||||||
|
private String pdbPath;
|
||||||
|
|
||||||
|
private PdbInfoDotNet() {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid() {
|
||||||
|
return magic.length == 4 && !pdbName.isBlank() && guid != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serializeToOptions(Options options) {
|
||||||
|
options.setString(PdbParserConstants.PDB_VERSION,
|
||||||
|
new String(magic, StandardCharsets.US_ASCII));
|
||||||
|
options.setString(PdbParserConstants.PDB_GUID, guid.toString());
|
||||||
|
options.setString(PdbParserConstants.PDB_AGE, Integer.toHexString(age));
|
||||||
|
options.setString(PdbParserConstants.PDB_FILE, pdbName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataType toDataType() {
|
||||||
|
StructureDataType struct = new StructureDataType("DotNetPdbInfo", 0);
|
||||||
|
struct.add(new StringDataType(), magic.length, "signature", null);
|
||||||
|
struct.add(new GuidDataType(), "guid", null);
|
||||||
|
struct.add(new DWordDataType(), "age", null);
|
||||||
|
if (pdbName.length() > 0) {
|
||||||
|
struct.add(new StringDataType(), pdbName.length(), "pdbname", null);
|
||||||
|
}
|
||||||
|
struct.setCategoryPath(new CategoryPath("/PDB"));
|
||||||
|
return struct;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,33 +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.bin.format.pdb;
|
|
||||||
|
|
||||||
import ghidra.app.util.bin.*;
|
|
||||||
import ghidra.app.util.datatype.microsoft.*;
|
|
||||||
|
|
||||||
public interface PdbInfoDotNetIface extends StructConverter {
|
|
||||||
|
|
||||||
public abstract String getPdbName();
|
|
||||||
|
|
||||||
public abstract int getAge();
|
|
||||||
|
|
||||||
public abstract int getSignature();
|
|
||||||
|
|
||||||
public abstract GUID getGUID();
|
|
||||||
|
|
||||||
public abstract byte[] getMagic();
|
|
||||||
}
|
|
||||||
@@ -1,32 +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.bin.format.pdb;
|
|
||||||
|
|
||||||
import ghidra.app.util.bin.*;
|
|
||||||
|
|
||||||
public interface PdbInfoIface extends StructConverter {
|
|
||||||
|
|
||||||
public abstract byte[] getMagic();
|
|
||||||
|
|
||||||
public abstract int getOffset();
|
|
||||||
|
|
||||||
public abstract int getSig();
|
|
||||||
|
|
||||||
public abstract int getAge();
|
|
||||||
|
|
||||||
public abstract String getPdbName();
|
|
||||||
}
|
|
||||||
@@ -19,8 +19,8 @@ import java.io.IOException;
|
|||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
|
|
||||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||||
import ghidra.app.util.bin.format.pdb.PdbInfoDotNetIface;
|
import ghidra.app.util.bin.format.pdb.PdbInfoCodeView;
|
||||||
import ghidra.app.util.bin.format.pdb.PdbInfoIface;
|
import ghidra.app.util.bin.format.pdb.PdbInfoDotNet;
|
||||||
import ghidra.app.util.bin.format.pe.debug.*;
|
import ghidra.app.util.bin.format.pe.debug.*;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
@@ -112,12 +112,12 @@ public class DebugDataDirectory extends DataDirectory {
|
|||||||
if (dcv != null) {
|
if (dcv != null) {
|
||||||
Address dataAddr = getDataAddress(dcv.getDebugDirectory(), isBinary, space, ntHeader);
|
Address dataAddr = getDataAddress(dcv.getDebugDirectory(), isBinary, space, ntHeader);
|
||||||
if (dataAddr != null) {
|
if (dataAddr != null) {
|
||||||
PdbInfoIface pdbInfo = dcv.getPdbInfo();
|
PdbInfoCodeView pdbInfo = dcv.getPdbInfo();
|
||||||
if (pdbInfo != null) {
|
if (pdbInfo != null) {
|
||||||
setPlateComment(program, dataAddr, "CodeView PDB Info");
|
setPlateComment(program, dataAddr, "CodeView PDB Info");
|
||||||
PeUtils.createData(program, dataAddr, pdbInfo.toDataType(), log);
|
PeUtils.createData(program, dataAddr, pdbInfo.toDataType(), log);
|
||||||
}
|
}
|
||||||
PdbInfoDotNetIface dotNetPdbInfo = dcv.getDotNetPdbInfo();
|
PdbInfoDotNet dotNetPdbInfo = dcv.getDotNetPdbInfo();
|
||||||
if (dotNetPdbInfo != null) {
|
if (dotNetPdbInfo != null) {
|
||||||
setPlateComment(program, dataAddr, ".NET PDB Info");
|
setPlateComment(program, dataAddr, ".NET PDB Info");
|
||||||
PeUtils.createData(program, dataAddr, dotNetPdbInfo.toDataType(), log);
|
PeUtils.createData(program, dataAddr, dotNetPdbInfo.toDataType(), log);
|
||||||
|
|||||||
@@ -15,30 +15,25 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.pe.debug;
|
package ghidra.app.util.bin.format.pe.debug;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import ghidra.app.util.bin.StructConverter;
|
import ghidra.app.util.bin.StructConverter;
|
||||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||||
import ghidra.app.util.bin.format.pdb.PdbFactory;
|
import ghidra.app.util.bin.format.pdb.PdbInfoCodeView;
|
||||||
import ghidra.app.util.bin.format.pdb.PdbInfoDotNetIface;
|
import ghidra.app.util.bin.format.pdb.PdbInfoDotNet;
|
||||||
import ghidra.app.util.bin.format.pdb.PdbInfoIface;
|
|
||||||
import ghidra.app.util.bin.format.pe.OffsetValidator;
|
import ghidra.app.util.bin.format.pe.OffsetValidator;
|
||||||
import ghidra.program.model.data.ArrayDataType;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.CategoryPath;
|
|
||||||
import ghidra.program.model.data.DataType;
|
|
||||||
import ghidra.program.model.data.Structure;
|
|
||||||
import ghidra.program.model.data.StructureDataType;
|
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to represent the code view debug information.
|
* A class to represent the code view debug information.
|
||||||
*/
|
*/
|
||||||
public class DebugCodeView implements StructConverter {
|
public class DebugCodeView implements StructConverter {
|
||||||
private DebugDirectory debugDir;
|
private DebugDirectory debugDir;
|
||||||
private DebugCodeViewSymbolTable symbolTable;
|
private DebugCodeViewSymbolTable symbolTable;
|
||||||
private PdbInfoIface pdbInfo;
|
private PdbInfoCodeView pdbInfo;
|
||||||
private PdbInfoDotNetIface dotNetPdbInfo;
|
private PdbInfoDotNet dotNetPdbInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@@ -70,8 +65,8 @@ public class DebugCodeView implements StructConverter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dotNetPdbInfo = PdbFactory.getPdbInfoDotNetInstance(reader, ptr);
|
dotNetPdbInfo = PdbInfoDotNet.isMatch(reader, ptr) ? PdbInfoDotNet.read(reader, ptr) : null;
|
||||||
pdbInfo = PdbFactory.getPdbInfoInstance(reader, ptr);
|
pdbInfo = PdbInfoCodeView.isMatch(reader, ptr) ? PdbInfoCodeView.read(reader, ptr) : null;
|
||||||
if (DebugCodeViewSymbolTable.isMatch(reader, ptr)) {
|
if (DebugCodeViewSymbolTable.isMatch(reader, ptr)) {
|
||||||
symbolTable =
|
symbolTable =
|
||||||
DebugCodeViewSymbolTable.createDebugCodeViewSymbolTable(reader,
|
DebugCodeViewSymbolTable.createDebugCodeViewSymbolTable(reader,
|
||||||
@@ -106,17 +101,18 @@ public class DebugCodeView implements StructConverter {
|
|||||||
* Returns the code view .PDB info.
|
* Returns the code view .PDB info.
|
||||||
* @return the code view .PDB info
|
* @return the code view .PDB info
|
||||||
*/
|
*/
|
||||||
public PdbInfoIface getPdbInfo() {
|
public PdbInfoCodeView getPdbInfo() {
|
||||||
return pdbInfo;
|
return pdbInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PdbInfoDotNetIface getDotNetPdbInfo() {
|
public PdbInfoDotNet getDotNetPdbInfo() {
|
||||||
return dotNetPdbInfo;
|
return dotNetPdbInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see ghidra.app.util.bin.StructConverter#toDataType()
|
* @see ghidra.app.util.bin.StructConverter#toDataType()
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public DataType toDataType() throws DuplicateNameException {
|
public DataType toDataType() throws DuplicateNameException {
|
||||||
Structure es = new StructureDataType("DebugCodeView", 0);
|
Structure es = new StructureDataType("DebugCodeView", 0);
|
||||||
es.add(WORD, "Signature", null);
|
es.add(WORD, "Signature", null);
|
||||||
|
|||||||
@@ -21,11 +21,7 @@ import java.util.Arrays;
|
|||||||
import ghidra.app.util.bin.BinaryReader;
|
import ghidra.app.util.bin.BinaryReader;
|
||||||
import ghidra.program.model.mem.MemBuffer;
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
import ghidra.util.BigEndianDataConverter;
|
import ghidra.util.*;
|
||||||
import ghidra.util.Conv;
|
|
||||||
import ghidra.util.DataConverter;
|
|
||||||
import ghidra.util.LittleEndianDataConverter;
|
|
||||||
import ghidra.util.NumericUtilities;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GUIDs identify objects such as interfaces, manager entry-point vectors (EPVs),
|
* GUIDs identify objects such as interfaces, manager entry-point vectors (EPVs),
|
||||||
@@ -61,54 +57,52 @@ public class GUID {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a GUID object using the GUID string form.
|
* Creates a GUID object using the GUID string form.
|
||||||
* @param guidString - "6B29FC40-CA47-1067-B31D-00DD010662DA"
|
* @param guidString - either with or without dashes between parts -
|
||||||
|
* "6B29FC40-CA47-1067-B31D-00DD010662DA", or "6B29FC40CA471067B31D00DD010662DA", and
|
||||||
|
* with or without leading and trailing "{" "}" characters
|
||||||
* @throws IllegalArgumentException if string does not represent a valid GUID
|
* @throws IllegalArgumentException if string does not represent a valid GUID
|
||||||
*/
|
*/
|
||||||
public GUID(String guidString) {
|
public GUID(String guidString) throws IllegalArgumentException {
|
||||||
if (guidString.length() != 36) {
|
String[] parts = getGUIDParts(guidString);
|
||||||
throw new IllegalArgumentException("Invalid GUID string.");
|
data1 = (int) NumericUtilities.parseHexLong(parts[0]);
|
||||||
}
|
data2 = (short) Integer.parseInt(parts[1], 16);
|
||||||
int pos = guidString.indexOf('-');
|
data3 = (short) Integer.parseInt(parts[2], 16);
|
||||||
if (pos == -1) {
|
int value = Integer.parseInt(parts[3], 16);
|
||||||
throw new IllegalArgumentException("Invalid GUID string.");
|
|
||||||
}
|
|
||||||
data1 = (int) NumericUtilities.parseHexLong(guidString.substring(0, pos));
|
|
||||||
|
|
||||||
guidString = guidString.substring(pos + 1);
|
|
||||||
pos = guidString.indexOf('-');
|
|
||||||
if (pos == -1) {
|
|
||||||
throw new IllegalArgumentException("Invalid GUID string.");
|
|
||||||
}
|
|
||||||
data2 = (short) Integer.parseInt(guidString.substring(0, pos), 16);
|
|
||||||
|
|
||||||
guidString = guidString.substring(pos + 1);
|
|
||||||
pos = guidString.indexOf('-');
|
|
||||||
if (pos == -1) {
|
|
||||||
throw new IllegalArgumentException("Invalid GUID string.");
|
|
||||||
}
|
|
||||||
data3 = (short) Integer.parseInt(guidString.substring(0, pos), 16);
|
|
||||||
|
|
||||||
guidString = guidString.substring(pos + 1);
|
|
||||||
pos = guidString.indexOf('-');
|
|
||||||
if (pos == -1) {
|
|
||||||
throw new IllegalArgumentException("Invalid GUID string.");
|
|
||||||
}
|
|
||||||
int value = Integer.parseInt(guidString.substring(0, pos), 16);
|
|
||||||
data4[0] = (byte) (value >> 8);
|
data4[0] = (byte) (value >> 8);
|
||||||
data4[1] = (byte) (value & 0xff);
|
data4[1] = (byte) (value & 0xff);
|
||||||
|
data4[2] = (byte) Integer.parseInt(parts[4].substring(0, 2), 16);
|
||||||
|
data4[3] = (byte) Integer.parseInt(parts[4].substring(2, 4), 16);
|
||||||
|
data4[4] = (byte) Integer.parseInt(parts[4].substring(4, 6), 16);
|
||||||
|
data4[5] = (byte) Integer.parseInt(parts[4].substring(6, 8), 16);
|
||||||
|
data4[6] = (byte) Integer.parseInt(parts[4].substring(8, 10), 16);
|
||||||
|
data4[7] = (byte) Integer.parseInt(parts[4].substring(10, 12), 16);
|
||||||
|
}
|
||||||
|
|
||||||
guidString = guidString.substring(pos + 1);
|
private String[] getGUIDParts(String guidString) throws IllegalArgumentException {
|
||||||
data4[2] = (byte) Integer.parseInt(guidString.substring(0, 2), 16);
|
String[] results = new String[5];
|
||||||
guidString = guidString.substring(2);
|
guidString = (guidString.startsWith("{") && guidString.endsWith("}"))
|
||||||
data4[3] = (byte) Integer.parseInt(guidString.substring(0, 2), 16);
|
? guidString.substring(1, guidString.length() - 1)
|
||||||
guidString = guidString.substring(2);
|
: guidString;
|
||||||
data4[4] = (byte) Integer.parseInt(guidString.substring(0, 2), 16);
|
if (guidString.length() == 36 && guidString.charAt(8) == '-' &&
|
||||||
guidString = guidString.substring(2);
|
guidString.charAt(13) == '-' && guidString.charAt(18) == '-' &&
|
||||||
data4[5] = (byte) Integer.parseInt(guidString.substring(0, 2), 16);
|
guidString.charAt(23) == '-') {
|
||||||
guidString = guidString.substring(2);
|
results[0] = guidString.substring(0, 8);
|
||||||
data4[6] = (byte) Integer.parseInt(guidString.substring(0, 2), 16);
|
results[1] = guidString.substring(9, 13);
|
||||||
guidString = guidString.substring(2);
|
results[2] = guidString.substring(14, 18);
|
||||||
data4[7] = (byte) Integer.parseInt(guidString.substring(0, 2), 16);
|
results[3] = guidString.substring(19, 23);
|
||||||
|
results[4] = guidString.substring(24);
|
||||||
|
}
|
||||||
|
else if (guidString.length() == 32) {
|
||||||
|
results[0] = guidString.substring(0, 8);
|
||||||
|
results[1] = guidString.substring(8, 12);
|
||||||
|
results[2] = guidString.substring(12, 16);
|
||||||
|
results[3] = guidString.substring(16, 20);
|
||||||
|
results[4] = guidString.substring(20);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("Invalid GUID string.");
|
||||||
|
}
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -172,23 +166,23 @@ public class GUID {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuffer buffer = new StringBuffer();
|
StringBuilder sb = new StringBuilder();
|
||||||
buffer.append(Conv.toHexString(data1));
|
sb.append(Conv.toHexString(data1));
|
||||||
buffer.append("-");
|
sb.append("-");
|
||||||
buffer.append(Conv.toHexString(data2));
|
sb.append(Conv.toHexString(data2));
|
||||||
buffer.append("-");
|
sb.append("-");
|
||||||
buffer.append(Conv.toHexString(data3));
|
sb.append(Conv.toHexString(data3));
|
||||||
buffer.append("-");
|
sb.append("-");
|
||||||
buffer.append(Conv.toHexString(data4[0]));
|
sb.append(Conv.toHexString(data4[0]));
|
||||||
buffer.append(Conv.toHexString(data4[1]));
|
sb.append(Conv.toHexString(data4[1]));
|
||||||
buffer.append("-");
|
sb.append("-");
|
||||||
buffer.append(Conv.toHexString(data4[2]));
|
sb.append(Conv.toHexString(data4[2]));
|
||||||
buffer.append(Conv.toHexString(data4[3]));
|
sb.append(Conv.toHexString(data4[3]));
|
||||||
buffer.append(Conv.toHexString(data4[4]));
|
sb.append(Conv.toHexString(data4[4]));
|
||||||
buffer.append(Conv.toHexString(data4[5]));
|
sb.append(Conv.toHexString(data4[5]));
|
||||||
buffer.append(Conv.toHexString(data4[6]));
|
sb.append(Conv.toHexString(data4[6]));
|
||||||
buffer.append(Conv.toHexString(data4[7]));
|
sb.append(Conv.toHexString(data4[7]));
|
||||||
return buffer.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ package ghidra.app.util.opinion;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import ghidra.app.util.bin.format.pdb.*;
|
import ghidra.app.util.bin.format.pdb.PdbInfoCodeView;
|
||||||
|
import ghidra.app.util.bin.format.pdb.PdbInfoDotNet;
|
||||||
import ghidra.app.util.bin.format.pe.FileHeader;
|
import ghidra.app.util.bin.format.pe.FileHeader;
|
||||||
import ghidra.app.util.bin.format.pe.SectionHeader;
|
import ghidra.app.util.bin.format.pe.SectionHeader;
|
||||||
import ghidra.app.util.bin.format.pe.debug.*;
|
import ghidra.app.util.bin.format.pe.debug.*;
|
||||||
import ghidra.app.util.datatype.microsoft.GUID;
|
|
||||||
import ghidra.app.util.demangler.DemangledObject;
|
import ghidra.app.util.demangler.DemangledObject;
|
||||||
import ghidra.app.util.demangler.DemanglerUtil;
|
import ghidra.app.util.demangler.DemanglerUtil;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
@@ -112,56 +112,14 @@ abstract class AbstractPeDebugLoader extends AbstractLibrarySupportLoader {
|
|||||||
|
|
||||||
Options proplist = program.getOptions(Program.PROGRAM_INFO);
|
Options proplist = program.getOptions(Program.PROGRAM_INFO);
|
||||||
|
|
||||||
PdbInfoIface cvPdbInfo = dcv.getPdbInfo();
|
PdbInfoCodeView cvPdbInfo = dcv.getPdbInfo();
|
||||||
if (cvPdbInfo != null) {
|
if (cvPdbInfo != null) {
|
||||||
byte[] magic = cvPdbInfo.getMagic();
|
cvPdbInfo.serializeToOptions(proplist);
|
||||||
int sig = cvPdbInfo.getSig();
|
|
||||||
int age = cvPdbInfo.getAge();
|
|
||||||
String name = cvPdbInfo.getPdbName();
|
|
||||||
|
|
||||||
proplist.setString(PdbParserConstants.PDB_VERSION, Conv.toString(magic));
|
|
||||||
proplist.setString(PdbParserConstants.PDB_SIGNATURE, Conv.toHexString(sig));
|
|
||||||
proplist.setString(PdbParserConstants.PDB_AGE, Integer.toHexString(age));
|
|
||||||
proplist.setString(PdbParserConstants.PDB_FILE, name);
|
|
||||||
/*
|
|
||||||
DebugDirectory dd = dcv.getDebugDirectory();
|
|
||||||
if (dd.getAddressOfRawData() > 0) {
|
|
||||||
Address address = space.getAddress(imageBase + dd.getAddressOfRawData());
|
|
||||||
listing.setComment(address, CodeUnit.PLATE_COMMENT, "CodeView PDB Info");
|
|
||||||
try {
|
|
||||||
listing.createData(address, cvPdbInfo.toDataType());
|
|
||||||
}
|
|
||||||
catch (IOException e) {}
|
|
||||||
catch (DuplicateNameException e) {}
|
|
||||||
catch (CodeUnitInsertionException e) {}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PdbInfoDotNetIface dotnetPdbInfo = dcv.getDotNetPdbInfo();
|
PdbInfoDotNet dotnetPdbInfo = dcv.getDotNetPdbInfo();
|
||||||
if (dotnetPdbInfo != null) {
|
if (dotnetPdbInfo != null) {
|
||||||
byte[] magic = dotnetPdbInfo.getMagic();
|
dotnetPdbInfo.serializeToOptions(proplist);
|
||||||
GUID guid = dotnetPdbInfo.getGUID();
|
|
||||||
int age = dotnetPdbInfo.getAge();
|
|
||||||
String name = dotnetPdbInfo.getPdbName();
|
|
||||||
|
|
||||||
proplist.setString(PdbParserConstants.PDB_VERSION, Conv.toString(magic));
|
|
||||||
proplist.setString(PdbParserConstants.PDB_GUID, guid.toString());
|
|
||||||
proplist.setString(PdbParserConstants.PDB_AGE, Integer.toHexString(age));
|
|
||||||
proplist.setString(PdbParserConstants.PDB_FILE, name);
|
|
||||||
/*
|
|
||||||
DebugDirectory dd = dcv.getDebugDirectory();
|
|
||||||
if (dd.getAddressOfRawData() > 0) {
|
|
||||||
Address address = space.getAddress(imageBase + dd.getAddressOfRawData());
|
|
||||||
listing.setComment(address, CodeUnit.PLATE_COMMENT, ".NET PDB Info");
|
|
||||||
try {
|
|
||||||
listing.createData(address, dotnetPdbInfo.toDataType());
|
|
||||||
}
|
|
||||||
catch (IOException e) {}
|
|
||||||
catch (DuplicateNameException e) {}
|
|
||||||
catch (CodeUnitInsertionException e) {}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugCodeViewSymbolTable dcvst = dcv.getSymbolTable();
|
DebugCodeViewSymbolTable dcvst = dcv.getSymbolTable();
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
##VERSION: 2.0
|
##VERSION: 2.0
|
||||||
|
##MODULE IP: Crystal Clear Icons - LGPL 2.1
|
||||||
|
##MODULE IP: FAMFAMFAM Icons - CC 2.5
|
||||||
|
##MODULE IP: Nuvola Icons - LGPL 2.1
|
||||||
##MODULE IP: Oxygen Icons - LGPL 3.0
|
##MODULE IP: Oxygen Icons - LGPL 3.0
|
||||||
Module.manifest||GHIDRA||||END|
|
Module.manifest||GHIDRA||||END|
|
||||||
src/global/docs/README_PDB.html||GHIDRA||||END|
|
src/global/docs/README_PDB.html||GHIDRA||||END|
|
||||||
@@ -13,13 +16,19 @@ src/main/help/help/shared/redo.png||GHIDRA||||END|
|
|||||||
src/main/help/help/shared/tip.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
src/main/help/help/shared/tip.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||||
src/main/help/help/shared/undo.png||GHIDRA||||END|
|
src/main/help/help/shared/undo.png||GHIDRA||||END|
|
||||||
src/main/help/help/shared/warning.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
src/main/help/help/shared/warning.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||||
|
src/main/help/help/topics/Pdb/LoadPDBNew.html||GHIDRA||||END|
|
||||||
src/main/help/help/topics/Pdb/PDB.htm||GHIDRA||||END|
|
src/main/help/help/topics/Pdb/PDB.htm||GHIDRA||||END|
|
||||||
src/main/help/help/topics/Pdb/download_pdb_file.html||GHIDRA||||END|
|
src/main/help/help/topics/Pdb/images/LoadPdb_Advanced_NeedsConfig.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/Pdb/images/KnownSymbolServerURLsDialog.png||GHIDRA||||END|
|
src/main/help/help/topics/Pdb/images/LoadPdb_Advanced_Screenshot.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/Pdb/images/PdbOrXmlDialog.png||GHIDRA||||END|
|
src/main/help/help/topics/Pdb/images/LoadPdb_Initial_Screenshot.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/Pdb/images/PeSpecifiedPathDialog.png||GHIDRA||||END|
|
src/main/help/help/topics/Pdb/images/Plus2.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/Pdb/images/SuccessDialog.png||GHIDRA||||END|
|
src/main/help/help/topics/Pdb/images/SymbolServerConfig_AddButtonMenu.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/Pdb/images/SymbolServerURLDialog.png||GHIDRA||||END|
|
src/main/help/help/topics/Pdb/images/SymbolServerConfig_Screenshot.png||GHIDRA||||END|
|
||||||
|
src/main/help/help/topics/Pdb/images/disk.png||FAMFAMFAM Icons - CC 2.5||||END|
|
||||||
|
src/main/help/help/topics/Pdb/images/down.png||GHIDRA||||END|
|
||||||
|
src/main/help/help/topics/Pdb/images/error.png||Nuvola Icons - LGPL 2.1||||END|
|
||||||
|
src/main/help/help/topics/Pdb/images/reload3.png||Crystal Clear Icons - LGPL 2.1||||END|
|
||||||
|
src/main/help/help/topics/Pdb/images/up.png||GHIDRA||||END|
|
||||||
src/pdb/README.txt||GHIDRA||||END|
|
src/pdb/README.txt||GHIDRA||||END|
|
||||||
src/pdb/pdb.sln||GHIDRA||||END|
|
src/pdb/pdb.sln||GHIDRA||||END|
|
||||||
src/pdb/pdb.vcxproj||GHIDRA||||END|
|
src/pdb/pdb.vcxproj||GHIDRA||||END|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,29 +13,23 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.pdb;
|
//Example preScript to force a PdbAnalyzer to use a custom PDB
|
||||||
|
//symbol file when analyzing a binary.
|
||||||
|
//@category PDB
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
import ghidra.app.util.bin.*;
|
import ghidra.app.plugin.core.analysis.PdbAnalyzer;
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
|
||||||
import java.io.*;
|
public class PdbExamplePrescript extends GhidraScript {
|
||||||
|
|
||||||
public class GhidraPdbFactory extends PdbFactory {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PdbInfoDotNetIface doGetPdbInfoDotNetInstance(
|
protected void run() throws Exception {
|
||||||
BinaryReader reader, int ptr) throws IOException {
|
// contrived example of choosing a pdb file with custom logic
|
||||||
if (PdbInfoDotNet.isMatch(reader, ptr)) {
|
File pdbFile = new File(getProgramFile().getPath() + ".pdb");
|
||||||
return new PdbInfoDotNet(reader, ptr);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
PdbAnalyzer.setPdbFileOption(currentProgram, pdbFile);
|
||||||
protected PdbInfoIface doGetPdbInfoInstance(BinaryReader reader, int ptr)
|
// or
|
||||||
throws IOException {
|
//PdbUniversalAnalyzer.setPdbFileOption(currentProgram, pdbFile);
|
||||||
if (PdbInfo.isMatch(reader, ptr)) {
|
|
||||||
return new PdbInfo(reader, ptr);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
//Example preScript to configure the PDB symbol server service to use the ~/symbols directory
|
||||||
|
//as the location to store symbol files, and to search Microsoft's public
|
||||||
|
//symbol server.
|
||||||
|
//The ~/symbols directory should already exist and be initialized as a symbol
|
||||||
|
//storage location.
|
||||||
|
//@category PDB
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.core.analysis.PdbAnalyzer;
|
||||||
|
import ghidra.app.plugin.core.analysis.PdbUniversalAnalyzer;
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
import pdb.PdbPlugin;
|
||||||
|
import pdb.symbolserver.*;
|
||||||
|
|
||||||
|
public class PdbSymbolServerExamplePrescript extends GhidraScript {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() throws Exception {
|
||||||
|
File homeDir = new File(System.getProperty("user.home"));
|
||||||
|
File symDir = new File(homeDir, "symbols");
|
||||||
|
LocalSymbolStore localSymbolStore = new LocalSymbolStore(symDir);
|
||||||
|
HttpSymbolServer msSymbolServer =
|
||||||
|
new HttpSymbolServer(URI.create("https://msdl.microsoft.com/download/symbols/"));
|
||||||
|
SymbolServerService symbolServerService =
|
||||||
|
new SymbolServerService(localSymbolStore, List.of(msSymbolServer));
|
||||||
|
|
||||||
|
PdbPlugin.saveSymbolServerServiceConfig(symbolServerService);
|
||||||
|
|
||||||
|
// You only need to enable the "allow remote" option on the specific
|
||||||
|
// analyzer you are using
|
||||||
|
PdbUniversalAnalyzer.setAllowRemoteOption(currentProgram, true);
|
||||||
|
PdbAnalyzer.setAllowRemoteOption(currentProgram, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ native execution issue and the use of an intermediate XML format.
|
|||||||
<p>Although GHIDRA has been primarily designed to utilize locally stored PDB files during analysis,
|
<p>Although GHIDRA has been primarily designed to utilize locally stored PDB files during analysis,
|
||||||
the ability to interactively download individual PDB files from a web-based Microsoft Symbol Server
|
the ability to interactively download individual PDB files from a web-based Microsoft Symbol Server
|
||||||
is also provided. This capability is accessed via the GUI while a program is open via the
|
is also provided. This capability is accessed via the GUI while a program is open via the
|
||||||
<B><I>File->Download PDB File...</I></B> action.
|
<B><I>File → Load PDB File...</I></B> action.
|
||||||
|
|
||||||
<H2><a name="DIASDK">DIA SDK Dependency</a></H2>
|
<H2><a name="DIASDK">DIA SDK Dependency</a></H2>
|
||||||
|
|
||||||
|
|||||||
@@ -51,8 +51,8 @@
|
|||||||
<tocroot>
|
<tocroot>
|
||||||
<tocref id="Program Annotation">
|
<tocref id="Program Annotation">
|
||||||
<tocdef id="PDB" sortgroup="q" text="PDB" target="help/topics/Pdb/PDB.htm" >
|
<tocdef id="PDB" sortgroup="q" text="PDB" target="help/topics/Pdb/PDB.htm" >
|
||||||
<tocdef id="README_PDB" sortgroup="a" text="PDB Parser (README_PDB)" target="docs/README_PDB.html" />
|
<tocdef id="LoadPDBNew" sortgroup="a" text="Load PDB File" target="help/topics/Pdb/LoadPDBNew.html" />
|
||||||
<tocdef id="Download PDB" sortgroup="b" text="Download PDB File" target="help/topics/Pdb/download_pdb_file.html" />
|
<tocdef id="README_PDB" sortgroup="b" text="PDB Parser (README_PDB)" target="docs/README_PDB.html" />
|
||||||
</tocdef>
|
</tocdef>
|
||||||
</tocref>
|
</tocref>
|
||||||
</tocroot>
|
</tocroot>
|
||||||
|
|||||||
@@ -0,0 +1,226 @@
|
|||||||
|
<!DOCTYPE doctype PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
|
||||||
|
|
||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<meta name="generator" content="Bluefish 2.2.11" >
|
||||||
|
<TITLE>Load PDB</TITLE>
|
||||||
|
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
|
||||||
|
</HEAD>
|
||||||
|
|
||||||
|
<BODY lang="EN-US">
|
||||||
|
|
||||||
|
<H1>Load PDB</H1>
|
||||||
|
|
||||||
|
|
||||||
|
<H2><A name="SymbolServers"></A>Symbol Servers and Symbol Storage</H2>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>In an effort to manage large collections of symbol files, Microsoft specified a scheme to organize
|
||||||
|
symbol files into directory structures.</P>
|
||||||
|
|
||||||
|
<P>Ghidra can search Microsoft-style symbol servers (web-based HTTP/HTTPS) or local file system symbol
|
||||||
|
storage directories as well as unorganized, non-MS symbol storage directories for the PE executable's
|
||||||
|
matching PDB symbol file.</P>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>Microsoft symbol servers / symbol storage directories:</LI>
|
||||||
|
<UL>
|
||||||
|
<LI>Organize symbol files (typically PDB files) into a directory hierarchy using information
|
||||||
|
from the symbol file being stored.</LI>
|
||||||
|
<LI><A name="OneLevel_SymbolDirectory"></A>In a single-level symbol server directory hierarchy, a symbol file named <CODE>myprogram.pdb</CODE> might be stored
|
||||||
|
as:</LI>
|
||||||
|
<UL>
|
||||||
|
<LI><CODE><FONT COLOR="BLUE">myprogram.pdb</FONT>/<FONT COLOR="RED">012345670123012301230123456789AB</FONT><FONT COLOR="GREEN">1</FONT>/<FONT COLOR="BLUE">myprogram.pdb</FONT></CODE>.</LI>
|
||||||
|
<LI><CODE><FONT COLOR="BLUE">myprogram.pdb</FONT></CODE> is the name of the file and the name of the initial subdirectory off the root of the server.</LI>
|
||||||
|
<LI><CODE><FONT COLOR="RED">012345670123012301230123456789AB</FONT></CODE> is the 32 character hexadecimal value (made up for this example) of the GUID
|
||||||
|
"012345678-0123-0123-0123-0123456789ABC" of the PDB file.</LI>
|
||||||
|
<UL><LI>This value might instead be a 8 character hexadecimal value of the ID of the symbol file if it was created by an older version of the tool chain.</LI></UL>
|
||||||
|
<LI><CODE><FONT COLOR="GREEN">1</FONT></CODE> is the hexadecimal value of the 'age' (build number) of the PDB file. Note: most PDB files will have an age value of 1.</LI>
|
||||||
|
</UL>
|
||||||
|
<LI><A name="TwoLevel_SymbolDirectory"></A>In a two-level symbol server directory hierarchy, the same symbol file would be stored
|
||||||
|
as: <CODE>my/myprogram.pdb/012345670123012301230123456789AB1/myprogram.pdb</CODE>, where the
|
||||||
|
first two letters of the symbol file's name are used to create a bucketing directory called "my" where all symbol files starting with
|
||||||
|
"my" are stored.</LI>
|
||||||
|
<UL><LI>A two-level symbol server directory hierarchy is indicated by the presence of a file called <CODE>index2.txt</CODE> in the root directory of
|
||||||
|
the hierarchy.</LI></UL>
|
||||||
|
<LI>Symbol storage directories are expected to have a <CODE>pingme.txt</CODE> file and a special directory called <CODE>000admin</CODE>.</LI>
|
||||||
|
<LI>Compressed symbol files (<CODE>*.pd_</CODE>):</LI>
|
||||||
|
<UL>
|
||||||
|
<LI>Microsoft symbol server tools can compress symbol files to save space. The compressed file is stored in place of the original file, renamed
|
||||||
|
to have a trailing underscore ("_") character in the file extension.</LI>
|
||||||
|
<LI>Before use, Ghidra will decompress the file into the <b>Local Symbol Storage</b> directory, using whatever organization scheme
|
||||||
|
that directory is configured with to store the uncompressed version of the file.</LI>
|
||||||
|
</UL>
|
||||||
|
<LI>Remote symbol files hosted on a HTTP server will be copied to and stored in the configured <b>Local Symbol Storage</b> directory before they
|
||||||
|
can be used.</LI>
|
||||||
|
</UL>
|
||||||
|
<LI><A name="Unorganized_SymbolDirectory"></A>Unorganized directories:</LI>
|
||||||
|
<UL>
|
||||||
|
<LI>Symbol files are are found by matching the leading part of the filename found in the unorganized directory with the sought-after symbol
|
||||||
|
file's name.</LI>
|
||||||
|
<UL>
|
||||||
|
<LI><CODE>helloworld.pdb</CODE> and <CODE>hellokitty.pdb</CODE> would both be found as possible matches when searching for
|
||||||
|
<CODE>hello.pdb</CODE>.</LI>
|
||||||
|
<LI>During searches, each possible matching PDB symbol file will be opened and partially parsed to extract its GUID and age values to determine if
|
||||||
|
it matches the user's full search criteria.</LI>
|
||||||
|
</UL>
|
||||||
|
</UL>
|
||||||
|
</UL>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H2>Menu Actions</H2>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<H3><A name="Load_PDB_File"></A>Load PDB File</H3>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>Allows the user to pick a PDB file or search for a PDB file and apply it to the currently open program in the CodeBrowser.</P>
|
||||||
|
<P>Use this action instead of the <b>PDB Analyzer</b> if the PDB file can't be found automatically with the currently configured
|
||||||
|
symbol server search locations, if you need to force load a non-exact PDB file, or you need to use other PDB options.</P>
|
||||||
|
<H4>Steps:</H4>
|
||||||
|
<UL>
|
||||||
|
<LI>Invoke <b>File → Load PDB File...</b>
|
||||||
|
<BLOCKQUOTE><IMG border="0" src="images/LoadPdb_Initial_Screenshot.png"></BLOCKQUOTE></LI>
|
||||||
|
<LI>The <B>PDB Location</B> field will either have the path of an existing matching PDB file, or
|
||||||
|
it will prompt the user to use the browse button to select a file or use the
|
||||||
|
<b>Advanced</b> screen to search for the file.</LI>
|
||||||
|
<UL><LI>A <CODE>PDB.XML</CODE> file can be selected using the browse button. This will limit the selected parser to be the MSDIA parser.</LI></UL>
|
||||||
|
<LI>Use the information displayed in the <B>Program PDB Information</B> panel to help you decide
|
||||||
|
which PDB file to choose.</LI>
|
||||||
|
<LI>If needed, click the <B>Advanced</B> button:
|
||||||
|
<BLOCKQUOTE><IMG border="0" src="images/LoadPdb_Advanced_NeedsConfig.png"></BLOCKQUOTE></LI>
|
||||||
|
<LI>The <b>Local Symbol Storage</b> location is required to enable searching. If missing, set it to a directory where Ghidra can store PDB files.</LI>
|
||||||
|
<UL>
|
||||||
|
<LI>For example, <CODE>/home/your_id/Symbols</CODE> or <CODE>C:\Users\your_name\Symbols</CODE>.</LI>
|
||||||
|
<LI>If the location is a new empty directory, the user will be prompted to initialize the directory as a Microsoft symbol storage directory.</LI>
|
||||||
|
</UL>
|
||||||
|
<LI><A href="#SymbolServerConfig_Add">Add</A> additional search locations by clicking the <img border="0" src="images/Plus2.png"> button.
|
||||||
|
The Microsoft symbol server and <b>Program's Import Location</b> are good defaults.</LI>
|
||||||
|
<LI>Save any changes to the configuration by clicking the <img border="0" src="images/disk.png"> button.</LI>
|
||||||
|
<LI>Set <A href="#PDB_Search_Search_Options">search options</A> as needed.</LI>
|
||||||
|
<LI>Click the <b>Search</b> button to search the configured locations.</LI>
|
||||||
|
<BLOCKQUOTE><IMG border="0" src="images/LoadPdb_Advanced_Screenshot.png"></BLOCKQUOTE></LI>
|
||||||
|
<LI>The <b>Local Symbol Storage</b> location is searched first, followed by any locations listed in the
|
||||||
|
<b>Additional Search Paths</b> list, in listed order.</LI>
|
||||||
|
<LI>Select one of the found PDB files and click the <b>Load</b> button to start the import process.</LI>
|
||||||
|
<LI>Remote symbol files will be downloaded to your <b>Local Symbol Storage</b> location before continuing.</LI>
|
||||||
|
</UL>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<H3><A name="Symbol_Server_Config"></A>Symbol Server Config</H3>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>Allows the user to configure the location where PDB symbol files are stored and additional locations to search for
|
||||||
|
existing PDB files. This is also available in the <b>Load PDB File</b>, <b>Advanced</b> screen.</P>
|
||||||
|
<H4>Steps:</H4>
|
||||||
|
<UL>
|
||||||
|
<LI>Invoke <b>Edit → Symbol Server Config</b>
|
||||||
|
<BLOCKQUOTE><IMG border="0" src="images/SymbolServerConfig_Screenshot.png"></BLOCKQUOTE></LI>
|
||||||
|
<LI>The <b>Local Symbol Storage</b> location is required to be able to search. If missing, set it to
|
||||||
|
a directory where Ghidra can store PDB files.</LI>
|
||||||
|
<UL>
|
||||||
|
<LI>For example, <CODE>/home/your_id/Symbols</CODE> or <CODE>C:\Users\your_name\Symbols</CODE>.</LI>
|
||||||
|
<LI>If the location is a new empty directory, the user will be prompted to initialize the directory as a Microsoft symbol storage directory.</LI>
|
||||||
|
</UL>
|
||||||
|
<LI><a href="#SymbolServerConfig_Add">Add</a> additional search locations by clicking the <img border="0" src="images/Plus2.png"> button.</LI>
|
||||||
|
<LI>Save any changes to the configuration by clicking the <img border="0" src="images/disk.png"> button.</LI>
|
||||||
|
<LI>Search locations can be disabled by toggling the <b>enabled</b> checkbox at the beginning of the row.</LI>
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H4><A name="SymbolServerConfig_Add"></A><img border="0" src="images/Plus2.png"> (Add)</H4>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>Allows the user to add a location to the search path list. Pick from the offered types of locations, or pick
|
||||||
|
a predefined location.</P>
|
||||||
|
<BLOCKQUOTE><IMG border="0" src="images/SymbolServerConfig_AddButtonMenu.png"></BLOCKQUOTE>
|
||||||
|
<UL>
|
||||||
|
<LI><B>Directory</B> - allows the user to pick an existing directory that will be searched for symbol files.
|
||||||
|
See <A href="#OneLevel_SymbolDirectory">level 1</A>/<A href="#TwoLevel_SymbolDirectory">level 2</A> or
|
||||||
|
<A href="#Unorganized_SymbolDirectory">unorganized</A> directory descriptions.</LI>
|
||||||
|
<LI><B>URL</B> - allows the user to enter a HTTP or HTTPS URL to a web-based symbol server.</LI>
|
||||||
|
<LI><B>Program's Import Location</B> - automatically references the directory from which the program was imported.</LI>
|
||||||
|
<LI><B>Import _NT_SYMBOL_PATH</B> - parses the current value of the <code>_NT_SYMBOL_PATH</code> system environment variable to extract
|
||||||
|
URLs and symbol directory locations to be added to the Ghidra configuration. If no environment value is present,
|
||||||
|
the user can paste their own value into the text field.</LI>
|
||||||
|
</UL>
|
||||||
|
<P>All items listed after the menu dividing line are automatically added from resource files that have a
|
||||||
|
<CODE>*.pdburl</CODE> extension. The default file included with Ghidra is called <CODE>PDB_SYMBOL_SERVER_URLS.pdburl</CODE> and
|
||||||
|
is located in the <CODE>Ghidra/Configurations/Public_Release/data</CODE> directory under the Ghidra install directory.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H4><A name="SymbolServerConfig_Delete"></A><img border="0" src="images/error.png"> (Delete)</H4>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>Deletes the currently selected locations from the <b>Additional Search Paths</b> table.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H4><A name="SymbolServerConfig_MoveUpDown"></A><img border="0" src="images/up.png"><img border="0" src="images/down.png"> (Up/Down)</H4>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>Moves the currently selected item up or down in the <b>Additional Search Paths</b> table.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H4><A name="SymbolServerConfig_Refresh_Status"></A><img border="0" src="images/reload3.png"> (Refresh)</H4>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>Updates the status column of the locations listed in the <b>Additional Search Paths</b>
|
||||||
|
table. Symbol servers or storage locations that are unreachable or misconfigured will show an error status in that column.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H4><A name="SymbolServerConfig_Save"></A><img border="0" src="images/disk.png"> (Save)</H4>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>Saves the currently displayed search and storage locations to the preferences file. This is shared between all Ghidra tools.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<H3><A name="PDB_Search_Search_Options"></A>PDB Search - Search Options</H3>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>These options control how PDB symbol files are found.</P>
|
||||||
|
<UL>
|
||||||
|
<LI><B>Ignore Age</B> - allows matching symbol files with the correct GUID, but incorrect age value. Only affects searches of local symbol directories.</LI>
|
||||||
|
<LI><B>Ignore GUID/ID</B> - allows matching symbol files with the correct name, but incorrect GUID or age. Only affects searches of local symbol directories.</LI>
|
||||||
|
<LI><B>Allow Remote</B> - allows the searching of web-based symbol servers. Off by default to prevent sharing possibly sensitive information (PDB name,
|
||||||
|
GUID, age) with web-based symbol servers outside your organization.</LI>
|
||||||
|
</UL>
|
||||||
|
<P>Additionally, there are <b>override</b> checkboxes in the <b>Program PDB Information</b> panel in the <b>Advanced</b> screen. These override values only
|
||||||
|
change the search criteria, they are not persisted to your program's metadata.</P>
|
||||||
|
<UL>
|
||||||
|
<LI><B>PDB Name Checkbox</B> - this checkbox allows entering a custom value for the desired PDB file name.</LI>
|
||||||
|
<LI><B>PDB Unique Id Checkbox</B> - this checkbox allows entering a custom GUID or ID value.</LI>
|
||||||
|
<LI><B>PDB Age Checkbox</B> - this checkbox allows entering a custom age value.</LI>
|
||||||
|
</UL>
|
||||||
|
<P>After changing a search option, you will need to perform another search to use the new options.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<H3><A name="PDB_Parser_Panel"></A>PDB Parser</H3>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>These options control which PDB parser will be used and any options used during parsing after the <b>Load</b> button is pressed.</P>
|
||||||
|
<UL>
|
||||||
|
<LI><B>Universal</B> - Platform-independent PDB analyzer (No PDB.XML support).</LI>
|
||||||
|
<LI><B>MSDIA</B> - Legacy PDB Analyzer. Requires MS DIA-SDK for raw PDB processing (Windows only), or preprocessed PDB.XML file.</LI>
|
||||||
|
<LI><B>Control</B> - All, Data Types Only, Public Symbols Only.</LI>
|
||||||
|
</UL>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H2>Troubleshooting</H2>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<UL>
|
||||||
|
<LI>If you are connecting to a Symbol Server that requires user authentication using PKI,
|
||||||
|
you must first set your PKI Certificate before attempting to download from the server. See
|
||||||
|
<A href="../../../help/topics/FrontEndPlugin/Ghidra_Front_end_Menus.htm#Set_PKI_Certificate">
|
||||||
|
PKI Certificate</A> for more details.</LI>
|
||||||
|
</UL>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<BR><BR><BR>
|
||||||
|
|
||||||
|
<P class="relatedtopic">Related Topics:</P>
|
||||||
|
<UL>
|
||||||
|
<LI><P class="relatedtopic"><A href="PDB.htm">PDB (general)</A></P></LI>
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<BR><BR><BR>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
||||||
@@ -1,36 +1,130 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
<!DOCTYPE doctype PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
|
||||||
|
|
||||||
<HTML>
|
<HTML>
|
||||||
<HEAD>
|
<HEAD>
|
||||||
<META name="generator" content=
|
<META name="generator" content=
|
||||||
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
||||||
|
|
||||||
<TITLE>Searching</TITLE>
|
<TITLE>Microsoft Program Databases (PDB)</TITLE>
|
||||||
<META http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
|
||||||
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
|
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
|
||||||
<META name="generator" content="Microsoft FrontPage 4.0">
|
|
||||||
</HEAD>
|
</HEAD>
|
||||||
|
|
||||||
<BODY lang="EN-US">
|
<BODY lang="EN-US">
|
||||||
<H1>PDB</H1>
|
<H1><A name="Using_Microsoft_PDB_Files"></A>Using Microsoft Program Database (PDB) Files</H1>
|
||||||
|
|
||||||
<P>Ghidra offers the ability to download and apply PDB debug information for programs that run
|
<P>A program database (PDB) file holds debugging and project state information about a program
|
||||||
on Microsoft Windows operating systems.
|
and can be created in a number of ways. Historically, it has been created using a Microsoft
|
||||||
The <I><A href="download_pdb_file.html">Download PDB File</A></I> feature allows users to
|
compiler and written in <CODE>C/C++</CODE>, <CODE>C#</CODE>, and <CODE>Visual Basic</CODE>.
|
||||||
download and optionally load/apply a PDB file that matches the user's current program, given an
|
A user generates a PDB file using the <CODE>/ZI or /Zi</CODE> flag (for C/C++ programs) or the
|
||||||
accessible Symbol Server.
|
<CODE>/debug</CODE> flag (for Visual Basic/C# programs).</P>
|
||||||
The <I><A href="help/topics/ImporterPlugin/load_pdb.html">Load PDB File</A></I> feature
|
|
||||||
allows users to apply a local PDB file to the current program. The <I>PDB Analyzer</I> also
|
|
||||||
automatically applies PDB symbols (attempting a search for matching PDB files locally) during
|
|
||||||
<A href="help/topics/AutoAnalysisPlugin/AutoAnalysis.htm">Auto-Analysis</A>.</P>
|
|
||||||
|
|
||||||
|
<P>There are two mechanisms for processing a PDB file. First, the platform-independent
|
||||||
|
PDB Universal Reader/Analyzer, which can read a raw PDB file and apply it. Its capabilities
|
||||||
|
are expected to be expanded in future releases. Second, the legacy capability that uses the
|
||||||
|
<A href="#dia">DIA SDK</A> to read information from the PDB file. This mechanism can only run
|
||||||
|
on a Windows platform, however it creates an XML representation of information gleaned using
|
||||||
|
the DIA SDK. These XML files can be saved and then used on Windows and non-Windows platforms
|
||||||
|
hosting Ghidra.</P>
|
||||||
|
|
||||||
|
<P>If loading a PDB, this should be done prior to other analysis, except in special cases,
|
||||||
|
such as when only loading data types.</P>
|
||||||
|
|
||||||
|
<P>Restricted loading of data types or public symbols is
|
||||||
|
supported by PDB Universal.</P>
|
||||||
|
|
||||||
|
<H2>To Load a PDB</H2>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>PDB files can be loaded in two ways:</P>
|
||||||
|
<UL>
|
||||||
|
<LI><B>File → <a href="LoadPDBNew.html#Load_PDB_File">Load PDB File</a></B></LI>
|
||||||
|
<LI>PDB Analyzer via <B>Analysis →
|
||||||
|
<A HREF="help/topics/AutoAnalysisPlugin/AutoAnalysis.htm#Auto_Analyze">Auto Analyze</A></B> or
|
||||||
|
<B>Analysis → <A HREF="help/topics/AutoAnalysisPlugin/AutoAnalysis.htm#Analyze_One_Shot">One Shot</A></B>.
|
||||||
|
</UL>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H2>Information Loaded From PDB</H2>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<OL>
|
||||||
|
<LI>Structure and union definitions</LI>
|
||||||
|
|
||||||
|
<LI>Typedefs</LI>
|
||||||
|
|
||||||
|
<LI>Enumerations</LI>
|
||||||
|
|
||||||
|
<LI>Class definitions</LI>
|
||||||
|
|
||||||
|
<LI>Function prototypes</LI>
|
||||||
|
|
||||||
|
<LI>Stack variable names and data types</LI>
|
||||||
|
|
||||||
|
<LI>Source line numbers</LI>
|
||||||
|
|
||||||
|
<LI>Instruction and data symbols</LI>
|
||||||
|
</OL>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H2>The DIA SDK-Based Capability</H2>
|
||||||
|
|
||||||
|
<P>*.PDB.XML files can be created in three different ways:
|
||||||
|
|
||||||
|
<BLOCKQUOTE><UL>
|
||||||
|
<LI>From the Ghidra GUI in Windows, use the
|
||||||
|
<A href="help/topics/GhidraScriptMgrPlugin/GhidraScriptMgrPlugin.htm">Ghidra Script Manager</A>
|
||||||
|
to run the <I>CreatePdbXmlFilesScript.java</I> script. Follow the prompts to choose
|
||||||
|
the .PDB file (or directory containing .PDB file(s)) to be converted to .PDB.XML form.
|
||||||
|
When given a directory, the script recursively traverses all subfolders to find .PDB
|
||||||
|
files. A created .PDB.XML file is placed in the same location as the corresponding original
|
||||||
|
.PDB file.</LI>
|
||||||
|
<br>
|
||||||
|
<LI>From a Windows command line, navigate to the following directory:
|
||||||
|
<I><ghidra install root>/support</I>
|
||||||
|
and run the <I>createPdbXmlFiles.bat</I> script. The script takes one argument representing
|
||||||
|
either one .PDB file or a directory of .PDB files. When given a directory, the script
|
||||||
|
recursively traverses all subdirectories to find .PDB files. A created .PDB.XML file is
|
||||||
|
placed in the same location as the corresponding original .PDB file. Sample calls to the
|
||||||
|
script are shown below.
|
||||||
|
<br><br>
|
||||||
|
<CODE> createPdbXmlFiles.bat C:\Symbols\samplePdb.pdb</CODE>
|
||||||
|
<br>
|
||||||
|
<CODE> createPdbXmlFiles.bat C:\Symbols</CODE>
|
||||||
|
<br>
|
||||||
|
</LI>
|
||||||
|
<br>
|
||||||
|
<LI>Run the included <I>pdb.exe</I> executable (found in the <I><ghidra install
|
||||||
|
root>/Ghidra/Features/PDB/os/win64</I> directory) and redirect (save) its output to an
|
||||||
|
XML file as shown below:
|
||||||
|
<br><br>
|
||||||
|
<CODE> pdb.exe samplePdb.pdb > samplePdb.pdb.xml</CODE>
|
||||||
|
</LI>
|
||||||
|
</UL></BLOCKQUOTE>
|
||||||
|
</P>
|
||||||
|
<P><B>NOTE:</B> Execution of <i>pdb.exe</i> has runtime dependencies which must be satisfied.
|
||||||
|
Please refer to the <a href="docs/README_PDB.html">README_PDB</a> document for details.</P>
|
||||||
|
|
||||||
|
<H2><A name="dia"></A>Debug Interface Access SDK</H2>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>The Microsoft Debug Interface Access Software Development Kit (DIA SDK) provides access to
|
||||||
|
debug information stored in program database (.PDB) files generated by Microsoft
|
||||||
|
post-compiler tools. Because the format of the .PDB file generated by the post-compiler tools
|
||||||
|
undergoes constant revision, exposing the format is impractical. Using the DIA API, you can
|
||||||
|
develop applications that search for and browse debug information stored in a .PDB file. Such
|
||||||
|
applications could, for example, report stack trace-back information and analyze performance
|
||||||
|
data.</P>
|
||||||
|
|
||||||
|
<P><IMG src="../../shared/note.png" border="0">If you are attempting to load a PDB on a
|
||||||
|
Windows machine and see an error message such as "Unable to locate the DIA SDK,"
|
||||||
|
you will need to add and register one or more files on your computer. Refer to the
|
||||||
|
<a href="docs/README_PDB.html">README_PDB</a> document for detailed instructions.
|
||||||
|
</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
<P class="relatedtopic">Related Topics:</P>
|
<P class="relatedtopic">Related Topics:</P>
|
||||||
|
|
||||||
<UL>
|
<UL>
|
||||||
<LI><A href="download_pdb_file.html">Download PDB File</A></LI>
|
<LI><A href="LoadPDBNew.html">Load PDB File</A></LI>
|
||||||
|
|
||||||
<LI><A href="help/topics/ImporterPlugin/load_pdb.html">Load PDB File</A></LI>
|
|
||||||
|
|
||||||
<LI><A href="help/topics/AutoAnalysisPlugin/AutoAnalysis.htm">Auto Analysis</A></LI>
|
<LI><A href="help/topics/AutoAnalysisPlugin/AutoAnalysis.htm">Auto Analysis</A></LI>
|
||||||
</UL>
|
</UL>
|
||||||
|
|
||||||
|
|||||||
@@ -1,184 +0,0 @@
|
|||||||
<!DOCTYPE doctype PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
|
|
||||||
|
|
||||||
<HTML>
|
|
||||||
<HEAD>
|
|
||||||
<META name="generator" content=
|
|
||||||
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
|
||||||
|
|
||||||
<TITLE>Download PDB File</TITLE>
|
|
||||||
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
|
|
||||||
</HEAD>
|
|
||||||
|
|
||||||
<BODY lang="EN-US">
|
|
||||||
<H1><A name="Download_PDB_File"></A>Download PDB File</H1>
|
|
||||||
|
|
||||||
<P>Ghidra offers the ability to download and apply a PDB file that corresponds to the program
|
|
||||||
currently open in the CodeBrowser. Successful downloading requires, at a minimum, that:</P>
|
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
|
||||||
<OL>
|
|
||||||
<LI>A Symbol Server URL is available and accessible from the client or computer where you are running Ghidra.</LI>
|
|
||||||
<LI>The program open in the CodeBrowser is a PE file that was compiled by a Microsoft compiler.</LI>
|
|
||||||
</OL>
|
|
||||||
</BLOCKQUOTE>
|
|
||||||
|
|
||||||
<H2>A Note for Windows Users</H2>
|
|
||||||
<BLOCKQUOTE>
|
|
||||||
<P>If set, Ghidra parses the <CODE>_NT_SYMBOL_PATH</CODE>
|
|
||||||
environment variable that is used to specify a PDB download location and Symbol Server URL(s).
|
|
||||||
The syntax for <CODE>_NT_SYMBOL_PATH</CODE> is shown below:</P>
|
|
||||||
|
|
||||||
<P><SPAN STYLE="margin-left:50px"><CODE>srv*[local symbols location]*[Symbol Server URL]</CODE></SPAN></P>
|
|
||||||
|
|
||||||
<P>The <CODE>_NT_SYMBOL_PATH</CODE> symbols location is used to pre-populate the dialog that asks
|
|
||||||
for the local storage location (as long as that location is valid). The <CODE>_NT_SYMBOL_PATH</CODE>
|
|
||||||
Symbol Server URL is used to pre-populate the dialog that asks for the Symbol Server location.</P>
|
|
||||||
|
|
||||||
<P><IMG src="../../shared/note.png" border="0">Although multiple Symbol Server URLs can be
|
|
||||||
specified in the <CODE>_NT_SYMBOL_PATH</CODE> variable, Ghidra only uses the first listed URL.</IMG></P>
|
|
||||||
</BLOCKQUOTE>
|
|
||||||
|
|
||||||
<H2>To Download a PDB</H2>
|
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
|
||||||
|
|
||||||
<OL>
|
|
||||||
<LI>From the menu-bar of a tool, select <B>File <IMG src="../../shared/arrow.gif" alt=""
|
|
||||||
width="18" height="14"> Download PDB File</B></LI>
|
|
||||||
|
|
||||||
<BR>
|
|
||||||
|
|
||||||
<LI>A dialog appears asking whether you want to download a PDB or XML (PDB.XML) file. Select
|
|
||||||
the type of file you want to download and click OK.</LI>
|
|
||||||
|
|
||||||
<P align="center"><IMG border="0" src="images/PdbOrXmlDialog.png"></P>
|
|
||||||
|
|
||||||
<P><IMG src="../../shared/note.png" border="0">A Symbol Server should always have PDB
|
|
||||||
files available for download. In contrast, .PDB.XML files are Ghidra-created files, and are
|
|
||||||
only available to download from the Symbol Server if Ghidra tools have been used to create
|
|
||||||
them and the server's admin has made them available. If you choose to download a .PDB.XML
|
|
||||||
file and it is not found on the server, you will see a dialog message telling you so. For
|
|
||||||
more information on creating and using .PDB.XML files, see the
|
|
||||||
<A href="../../../help/topics/ImporterPlugin/load_pdb.html">Load PDB File</A> section.</P>
|
|
||||||
|
|
||||||
<BR>
|
|
||||||
|
|
||||||
<LI>Before attempting to download the file, an attempt will first be made to locate it
|
|
||||||
using file and path names associated with the program. A dialog appears asking whether you
|
|
||||||
want to include the PE-Header-Specified Path, which could include a Universal Naming
|
|
||||||
Convention (UNC) path of a location that might not be trusted. Select OK if you want to
|
|
||||||
perform this potentially unsafe retrieval.</LI>
|
|
||||||
|
|
||||||
<P align="center"><IMG border="0" src="images/PeSpecifiedPathDialog.png"></P>
|
|
||||||
|
|
||||||
<BR>
|
|
||||||
|
|
||||||
<LI>A dialog appears asking where to save the downloaded file. Pick a location to store your
|
|
||||||
PDB files. A common location on Windows is <I>C:\Symbols</I>.</LI>
|
|
||||||
|
|
||||||
<BR>
|
|
||||||
|
|
||||||
<LI>At this point, if a PDB file of the type you have chosen (either .PDB or .PDB.XML) already
|
|
||||||
exists in the selected location, you will see a message indicating that a potential matching
|
|
||||||
PDB has been found. You will then be asked if you would like to continue with the download.
|
|
||||||
<BR><BR>
|
|
||||||
<UL>
|
|
||||||
<LI>If you select "No", jump to Step 7.</LI>
|
|
||||||
<BR>
|
|
||||||
<LI>If you select "Yes", please keep the following things in mind relating to a found
|
|
||||||
.PDB or .PDB.XML file:</LI>
|
|
||||||
<BR>
|
|
||||||
<UL>
|
|
||||||
<LI>If the found file is not in a directory that contains the current binary's GUID
|
|
||||||
(i.e., <I>C:\Symbols\<pdbfilename>\<GUID></I>), then the file is not
|
|
||||||
guaranteed to be an exact match for the current binary (when there is no GUID subfolder,
|
|
||||||
a matching file is found based on expected PDB filename).</LI>
|
|
||||||
|
|
||||||
<BR>
|
|
||||||
|
|
||||||
<LI>If there is any doubt about whether the found PDB file matches, it is a good
|
|
||||||
idea to try to download the matching file, anyway (the matching file will be saved
|
|
||||||
in a directory of the form <I><download location>/<pdbfilename>/
|
|
||||||
<GUID></I>).</LI>
|
|
||||||
|
|
||||||
<BR>
|
|
||||||
|
|
||||||
<LI>If you do choose to continue to apply the found PDB file, and its GUID does not
|
|
||||||
match the GUID of the current binary, you will be warned and given the option of
|
|
||||||
canceling the application of the PDB file.</LI>
|
|
||||||
</UL>
|
|
||||||
</UL>
|
|
||||||
|
|
||||||
<BR>
|
|
||||||
|
|
||||||
<LI>Next, you will see a dialog asking for the Symbol Server URL.
|
|
||||||
|
|
||||||
<BR><BR>
|
|
||||||
|
|
||||||
<P align="center"><IMG border="0" src="images/SymbolServerURLDialog.png"></P>
|
|
||||||
|
|
||||||
<BR>
|
|
||||||
|
|
||||||
If a list of known URLs exists in your distribution (the file will have the extension
|
|
||||||
<CODE>.pdburl</CODE>), the dialog will also include a button with the text "Choose from
|
|
||||||
known URLs". When this button is pressed, a separate dialog appears showing known Symbol
|
|
||||||
Server URLs.
|
|
||||||
|
|
||||||
<BR>
|
|
||||||
|
|
||||||
<P align="center"><IMG border="0" src="images/KnownSymbolServerURLsDialog.png"></P>
|
|
||||||
|
|
||||||
<BR>
|
|
||||||
|
|
||||||
You may choose any of these URLs or manually type one in. If manually typing in a URL,
|
|
||||||
be sure to include the protocol (<I>http</I> or <I>https</I>).
|
|
||||||
|
|
||||||
<BR>
|
|
||||||
|
|
||||||
<P><IMG src="../../shared/warning.png" border="0">Always be sure to check your organization's
|
|
||||||
security policy before downloading any file from the internet.</P>
|
|
||||||
</LI>
|
|
||||||
|
|
||||||
<BR>
|
|
||||||
|
|
||||||
<LI>Next, if the Symbol Server contains a matching PDB that is the same file type that you
|
|
||||||
chose earlier, it will return with a message indicating that the download was successful.
|
|
||||||
The message also contains the path where you can find the downloaded file.</LI>
|
|
||||||
|
|
||||||
<P align="center"><IMG border="0" src="images/SuccessDialog.png"></P>
|
|
||||||
<BR>
|
|
||||||
|
|
||||||
<LI>If the download was successful or an existing PDB file was found, you may be asked
|
|
||||||
whether you want to apply the PDB to the program.</LI>
|
|
||||||
|
|
||||||
<P><IMG src="../../shared/note.png" border="0">If Yes is chosen, see
|
|
||||||
<A href="help/topics/ImporterPlugin/load_pdb.html">Load PDB File</A> for continued help.</P>
|
|
||||||
|
|
||||||
</OL>
|
|
||||||
</BLOCKQUOTE>
|
|
||||||
|
|
||||||
<BR><BR>
|
|
||||||
<H2>Troubleshooting</H2>
|
|
||||||
<BLOCKQUOTE>
|
|
||||||
<UL>
|
|
||||||
<LI>If you are connecting to a Symbol Server that requires user authentication using PKI,
|
|
||||||
you must first set your PKI Certificate before attempting to download from the server. See
|
|
||||||
<A href="../../../help/topics/FrontEndPlugin/Ghidra_Front_end_Menus.htm#Set_PKI_Certificate">
|
|
||||||
PKI Certificate</A> for more details.</LI>
|
|
||||||
</UL>
|
|
||||||
</BLOCKQUOTE>
|
|
||||||
|
|
||||||
|
|
||||||
<BR><BR><BR>
|
|
||||||
|
|
||||||
<P class="relatedtopic">Related Topics:</P>
|
|
||||||
<UL>
|
|
||||||
<LI>
|
|
||||||
<P class="relatedtopic"><A href="../../../help/topics/ImporterPlugin/load_pdb.html">Load PDB</A></P>
|
|
||||||
</LI>
|
|
||||||
</UL>
|
|
||||||
|
|
||||||
<BR><BR><BR>
|
|
||||||
|
|
||||||
</BODY>
|
|
||||||
</HTML>
|
|
||||||
|
Before Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 752 B |
|
Before Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 620 B |
|
After Width: | Height: | Size: 192 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 979 B |
|
After Width: | Height: | Size: 193 B |
@@ -17,19 +17,14 @@ package ghidra.app.plugin.core.analysis;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.util.bin.format.pdb.*;
|
import ghidra.app.util.bin.format.pdb.PdbException;
|
||||||
|
import ghidra.app.util.bin.format.pdb.PdbParser;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.app.util.opinion.PeLoader;
|
|
||||||
import ghidra.app.util.pdb.PdbLocator;
|
|
||||||
import ghidra.framework.options.OptionType;
|
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.program.model.address.AddressSetView;
|
import ghidra.program.model.address.AddressSetView;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.SystemUtilities;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
@@ -40,25 +35,14 @@ public class PdbAnalyzer extends AbstractAnalyzer {
|
|||||||
static final String NAME = "PDB MSDIA";
|
static final String NAME = "PDB MSDIA";
|
||||||
static final boolean DEFAULT_ENABLEMENT = !PdbUniversalAnalyzer.DEFAULT_ENABLEMENT;
|
static final boolean DEFAULT_ENABLEMENT = !PdbUniversalAnalyzer.DEFAULT_ENABLEMENT;
|
||||||
private static final String DESCRIPTION =
|
private static final String DESCRIPTION =
|
||||||
"PDB Analyzer.\n" + "Requires MS DIA-SDK for raw PDB processing (Windows only).\n" +
|
"PDB Analyzer.\n" +
|
||||||
"Also supports pre-processed XML files.";
|
"Requires MS DIA-SDK for raw PDB processing (Windows only).\n" +
|
||||||
|
"Also supports pre-processed XML files.\n" +
|
||||||
|
"PDB Symbol Server searching is configured in Edit -> Symbol Server Config.\n";
|
||||||
|
|
||||||
private static final String ERROR_TITLE = "Error in PDB Analyzer";
|
private static final String ERROR_TITLE = "Error in PDB Analyzer";
|
||||||
|
|
||||||
private static final String SYMBOLPATH_OPTION_NAME = "Symbol Repository Path";
|
private boolean searchRemoteLocations = false;
|
||||||
private static final String SYMBOLPATH_OPTION_DESCRIPTION =
|
|
||||||
"Directory path to root of Microsoft Symbol Repository Directory";
|
|
||||||
|
|
||||||
private File symbolsRepositoryDir = PdbLocator.DEFAULT_SYMBOLS_DIR;
|
|
||||||
|
|
||||||
//==============================================================================================
|
|
||||||
// Include the PE-Header-Specified PDB path for searching for appropriate PDB file.
|
|
||||||
private static final String OPTION_NAME_INCLUDE_PE_PDB_PATH =
|
|
||||||
"Unsafe: Include PE PDB Path in PDB Search";
|
|
||||||
private static final String OPTION_DESCRIPTION_INCLUDE_PE_PDB_PATH =
|
|
||||||
"If checked, specifically searching for PDB in PE-Header-Specified Location.";
|
|
||||||
|
|
||||||
private boolean includePeSpecifiedPdbPath = false;
|
|
||||||
|
|
||||||
// only try once per transaction due to extensive error logging which may get duplicated
|
// only try once per transaction due to extensive error logging which may get duplicated
|
||||||
private long lastTransactionId = -1;
|
private long lastTransactionId = -1;
|
||||||
@@ -101,75 +85,21 @@ public class PdbAnalyzer extends AbstractAnalyzer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
File pdb = lookForPdb(program, log);
|
File pdbFile = PdbAnalyzerCommon.findPdb(this, program, searchRemoteLocations, monitor);
|
||||||
|
if (pdbFile == null) {
|
||||||
if (pdb == null) {
|
// warnings have already been logged
|
||||||
Msg.info(this, "PDB analyzer failed to locate PDB file");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Msg.info(this, "PDB analyzer parsing file: " + pdb.getAbsolutePath());
|
|
||||||
|
|
||||||
AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program);
|
AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program);
|
||||||
return parsePdb(pdb, program, mgr, monitor, log);
|
return parsePdb(pdbFile, program, mgr, monitor, log);
|
||||||
}
|
|
||||||
|
|
||||||
private static class PdbMissingState implements AnalysisState {
|
|
||||||
// object existence indicates missing PDB has already been reported
|
|
||||||
}
|
|
||||||
|
|
||||||
File lookForPdb(Program program, MessageLog log) {
|
|
||||||
String message = "";
|
|
||||||
File pdb;
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
pdb = PdbParser.findPDB(program, includePeSpecifiedPdbPath, symbolsRepositoryDir);
|
|
||||||
|
|
||||||
if (pdb == null) {
|
|
||||||
|
|
||||||
PdbMissingState missingState =
|
|
||||||
AnalysisStateInfo.getAnalysisState(program, PdbMissingState.class);
|
|
||||||
if (missingState != null) {
|
|
||||||
return null; // already notified user
|
|
||||||
}
|
|
||||||
AnalysisStateInfo.putAnalysisState(program, new PdbMissingState());
|
|
||||||
|
|
||||||
String pdbName = program.getOptions(Program.PROGRAM_INFO).getString(
|
|
||||||
PdbParserConstants.PDB_FILE, (String) null);
|
|
||||||
if (StringUtils.isBlank(pdbName)) {
|
|
||||||
message = "Program has no associated PDB file.";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
message = "Unable to locate PDB file \"" + pdbName + "\" with matching GUID.";
|
|
||||||
}
|
|
||||||
if (SystemUtilities.isInHeadlessMode()) {
|
|
||||||
message += "\n Use a script to set the PDB file location. I.e.,\n" +
|
|
||||||
" setAnalysisOption(currentProgram, \"PDB.Symbol Repository Path\", \"/path/to/pdb/folder\");\n" +
|
|
||||||
" This must be done using a pre-script (prior to analysis).";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
message += "\n You may set the PDB \"Symbol Repository Path\"" +
|
|
||||||
"\n using \"Edit->Options for [program]\" prior to analysis." +
|
|
||||||
"\nIt is important that a PDB is used during initial analysis " +
|
|
||||||
"\nif available.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pdb;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (message.length() > 0) {
|
|
||||||
log.appendMsg(getName(), message);
|
|
||||||
log.setStatus(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean parsePdb(File pdb, Program program, AutoAnalysisManager mgr, TaskMonitor monitor,
|
boolean parsePdb(File pdb, Program program, AutoAnalysisManager mgr, TaskMonitor monitor,
|
||||||
MessageLog log) {
|
MessageLog log) {
|
||||||
DataTypeManagerService dataTypeManagerService = mgr.getDataTypeManagerService();
|
DataTypeManagerService dataTypeManagerService = mgr.getDataTypeManagerService();
|
||||||
PdbParser parser = new PdbParser(pdb, program, dataTypeManagerService, true, monitor);
|
PdbParser parser =
|
||||||
|
new PdbParser(pdb, program, dataTypeManagerService, true, false, monitor);
|
||||||
|
|
||||||
String message;
|
String message;
|
||||||
|
|
||||||
@@ -201,32 +131,52 @@ public class PdbAnalyzer extends AbstractAnalyzer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canAnalyze(Program program) {
|
public boolean canAnalyze(Program program) {
|
||||||
return PeLoader.PE_NAME.equals(program.getExecutableFormat());
|
return PdbAnalyzerCommon.canAnalyzeProgram(program);
|
||||||
|
//return PeLoader.PE_NAME.equals(program.getExecutableFormat());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerOptions(Options options, Program program) {
|
public void registerOptions(Options options, Program program) {
|
||||||
|
|
||||||
symbolsRepositoryDir = PdbLocator.getDefaultPdbSymbolsDir();
|
options.registerOption(PdbAnalyzerCommon.OPTION_NAME_SEARCH_REMOTE_LOCATIONS,
|
||||||
|
searchRemoteLocations, null,
|
||||||
options.registerOption(SYMBOLPATH_OPTION_NAME, OptionType.FILE_TYPE, symbolsRepositoryDir,
|
PdbAnalyzerCommon.OPTION_DESCRIPTION_SEARCH_REMOTE_LOCATIONS);
|
||||||
null, SYMBOLPATH_OPTION_DESCRIPTION);
|
|
||||||
|
|
||||||
options.registerOption(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath, null,
|
|
||||||
OPTION_DESCRIPTION_INCLUDE_PE_PDB_PATH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void optionsChanged(Options options, Program program) {
|
public void optionsChanged(Options options, Program program) {
|
||||||
|
searchRemoteLocations = options.getBoolean(
|
||||||
File symbolsDir = options.getFile(SYMBOLPATH_OPTION_NAME, symbolsRepositoryDir);
|
PdbAnalyzerCommon.OPTION_NAME_SEARCH_REMOTE_LOCATIONS, searchRemoteLocations);
|
||||||
if (!symbolsDir.equals(symbolsRepositoryDir)) {
|
|
||||||
symbolsRepositoryDir = symbolsDir;
|
|
||||||
PdbLocator.setDefaultPdbSymbolsDir(symbolsDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
includePeSpecifiedPdbPath =
|
|
||||||
options.getBoolean(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the PDB file that will be used by the analyzer when it is next invoked
|
||||||
|
* on the specified program.
|
||||||
|
* <p>
|
||||||
|
* Normally the analyzer would locate the PDB file on its own, but if a
|
||||||
|
* headless script wishes to override the analyzer's behaivor, it can
|
||||||
|
* use this method to specify a file.
|
||||||
|
*
|
||||||
|
* @param program {@link Program}
|
||||||
|
* @param pdbFile the pdb file
|
||||||
|
*/
|
||||||
|
public static void setPdbFileOption(Program program, File pdbFile) {
|
||||||
|
PdbAnalyzerCommon.setPdbFileOption(NAME, program, pdbFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the "allow remote" option that will be used by the analyzer when it is next invoked
|
||||||
|
* on the specified program.
|
||||||
|
* <p>
|
||||||
|
* Normally when the analyzer attempts to locate a matching PDB file it
|
||||||
|
* will default to NOT searching remote symbol servers. A headless script could
|
||||||
|
* use this method to allow the analyzer to search remote symbol servers.
|
||||||
|
*
|
||||||
|
* @param program {@link Program}
|
||||||
|
* @param allowRemote boolean flag, true means analyzer can search remote symbol
|
||||||
|
* servers
|
||||||
|
*/
|
||||||
|
public static void setAllowRemoteOption(Program program, boolean allowRemote) {
|
||||||
|
PdbAnalyzerCommon.setAllowRemoteOption(NAME, program, allowRemote);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.io.File;
|
||||||
|
|
||||||
|
import ghidra.app.services.Analyzer;
|
||||||
|
import ghidra.app.util.opinion.PeLoader;
|
||||||
|
import ghidra.framework.options.Options;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.SystemUtilities;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
import pdb.PdbPlugin;
|
||||||
|
import pdb.symbolserver.FindOption;
|
||||||
|
import pdb.symbolserver.SymbolFileInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared configuration values and pdb searching logic
|
||||||
|
*/
|
||||||
|
public class PdbAnalyzerCommon {
|
||||||
|
static final String OPTION_DESCRIPTION_SEARCH_REMOTE_LOCATIONS =
|
||||||
|
"If checked, allow searching remote symbol servers for PDB files.";
|
||||||
|
static final String OPTION_NAME_SEARCH_REMOTE_LOCATIONS = "Search remote symbol servers";
|
||||||
|
|
||||||
|
static final String OPTION_DESCRIPTION_PDB_FILE = "Path to a manually chosen PDB file.";
|
||||||
|
static final String OPTION_NAME_PDB_FILE = "PDB File";
|
||||||
|
|
||||||
|
// TODO: I changed this method from what was lifted in the old code. I check for null string
|
||||||
|
// and I also check for MSCOFF_NAME (TODO: check on the validity of this!!!). Also, changed
|
||||||
|
// the comparison to a substring search from a .equals).
|
||||||
|
/**
|
||||||
|
* Returns true if the specified program is supported by either of the
|
||||||
|
* Pdb analyzers.
|
||||||
|
*
|
||||||
|
* @param program {@link Program}
|
||||||
|
* @return boolean true if program is supported by Pdb analyzers
|
||||||
|
*/
|
||||||
|
public static boolean canAnalyzeProgram(Program program) {
|
||||||
|
String executableFormat = program.getExecutableFormat();
|
||||||
|
return executableFormat != null && (executableFormat.indexOf(PeLoader.PE_NAME) != -1);
|
||||||
|
// TODO: Check for MSCOFF_NAME. Initial investigation shows that the .debug$T section of
|
||||||
|
// the MSCOFF (*.obj) file has type records and the .debug$S section has symbol records.
|
||||||
|
// More than that, in at least one instance, there has been a TypeServer2MsType type
|
||||||
|
// record that give the GUID, age, and name of the PDB file associated with the MSCOFF
|
||||||
|
// file. At this point in time, these two sections of the MSCOFF are read (header and
|
||||||
|
// raw data), but we do not interpret these sections any further. Suggest that we "might"
|
||||||
|
// want to parse some of these records at load time? Maybe not. We could, at analysis
|
||||||
|
// time, add the ability to process these two sections (as part of analysis (though we
|
||||||
|
// will not be aware of a PDB file yet), and upon discovery of a TypeServer2MsType (or
|
||||||
|
// perhaps other?), proceed to find the file (if possible) and also process that file.
|
||||||
|
// We posit that if a record indicates a separate PDB for the types (Note: MSFT indicates
|
||||||
|
// that only data types will be found in an MSCOFF PDB file), then that will likely be
|
||||||
|
// the only record in the .debug$T section.
|
||||||
|
// TODO: If the MSCOFF file is located in a MSCOFF ARCHIVE (*.lib), there can be a PDB
|
||||||
|
// associated with the archive. We currently do not pass on this association of the
|
||||||
|
// PDB archive to each underlying MSCOFF file. Moreover, we believe that we are not
|
||||||
|
// currently discovering the associated MSCOFF ARCHIVE PDB file when processing the
|
||||||
|
// MSCOFF ARCHIVE. Initial indication is that each MSCOFF within the archive will have
|
||||||
|
// the PDB file that it needs listed, even if redundant for each MSCOFF within the
|
||||||
|
// archive.
|
||||||
|
// return executableFormat != null && (executableFormat.indexOf(PeLoader.PE_NAME) != -1 ||
|
||||||
|
// executableFormat.indexOf(MSCoffLoader.MSCOFF_NAME) != -1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common logic to set a manual Pdb file that the specified analyzer will find and use
|
||||||
|
* when it is invoked later<p>
|
||||||
|
* Each specific analyzer has a public method that calls this to supply the
|
||||||
|
* actual analyzer name to make it easier for script writers to call.
|
||||||
|
*
|
||||||
|
* @param analyzerName name of analyzer
|
||||||
|
* @param program {@link Program}
|
||||||
|
* @param pdbFile the file
|
||||||
|
*/
|
||||||
|
static void setPdbFileOption(String analyzerName, Program program, File pdbFile) {
|
||||||
|
Options options = program.getOptions(Program.ANALYSIS_PROPERTIES);
|
||||||
|
options.setFile(analyzerName + "." + OPTION_NAME_PDB_FILE, pdbFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common logic to set the "allow remote" option that the specified analyzer will find and use
|
||||||
|
* when it is invoked later<p>
|
||||||
|
* Each specific analyzer has a public method that calls this to supply the
|
||||||
|
* actual analyzer name to make it easier for script writers to call.
|
||||||
|
*
|
||||||
|
* @param analyzerName name of analyzer
|
||||||
|
* @param program {@link Program}
|
||||||
|
* @param allowRemote boolean flag, true means the analyzer can search remote
|
||||||
|
* symbol servers
|
||||||
|
*/
|
||||||
|
static void setAllowRemoteOption(String analyzerName, Program program, boolean allowRemote) {
|
||||||
|
Options options = program.getOptions(Program.ANALYSIS_PROPERTIES);
|
||||||
|
options.setBoolean(analyzerName + "." + OPTION_NAME_SEARCH_REMOTE_LOCATIONS, allowRemote);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common pdb searching logic between both analyzers.
|
||||||
|
*
|
||||||
|
* @param pdbAnalyzer the analyzer doing the searching
|
||||||
|
* @param program the program
|
||||||
|
* @param allowRemote boolean flag, true means searching remote symbol servers
|
||||||
|
* is allowed
|
||||||
|
* @param monitor {@link TaskMonitor} to let user cancel
|
||||||
|
* @return File pointing to the found pdb, or null if not found or error
|
||||||
|
*/
|
||||||
|
static File findPdb(Analyzer pdbAnalyzer, Program program, boolean allowRemote,
|
||||||
|
TaskMonitor monitor) {
|
||||||
|
|
||||||
|
SymbolFileInfo symbolFileInfo = SymbolFileInfo.fromMetadata(program.getMetadata());
|
||||||
|
if (symbolFileInfo == null) {
|
||||||
|
Msg.info(pdbAnalyzer,
|
||||||
|
"Skipping PDB processing: missing PDB information in program metadata");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First look in the program's analysis options to see if there is a
|
||||||
|
// manually specified pdbFile. (see setPdbFileOption)
|
||||||
|
// If not set, then do a search using the currently configured symbol servers.
|
||||||
|
Options options = program.getOptions(Program.ANALYSIS_PROPERTIES);
|
||||||
|
String pdbFileOptionName = pdbAnalyzer.getName() + "." + OPTION_NAME_PDB_FILE;
|
||||||
|
|
||||||
|
// check existence first to avoid creating option value
|
||||||
|
File pdbFile = options.contains(pdbFileOptionName)
|
||||||
|
? options.getFile(pdbFileOptionName, null)
|
||||||
|
: null;
|
||||||
|
if (pdbFile == null) {
|
||||||
|
Set<FindOption> findOpts = allowRemote
|
||||||
|
? FindOption.of(FindOption.ALLOW_REMOTE)
|
||||||
|
: FindOption.NO_OPTIONS;
|
||||||
|
pdbFile = PdbPlugin.findPdb(program, findOpts, monitor);
|
||||||
|
}
|
||||||
|
if (pdbFile == null) {
|
||||||
|
Msg.info(pdbAnalyzer,
|
||||||
|
"Skipping PDB processing: failed to locate PDB file in configured locations");
|
||||||
|
if (SystemUtilities.isInHeadlessMode()) {
|
||||||
|
Msg.info(pdbAnalyzer,
|
||||||
|
"Use a script to set the PDB file location. I.e.,\n" +
|
||||||
|
" PdbAnalyzer.setPdbFileOption(currentProgram, new File(\"/path/to/pdb/file.pdb\")); or\n" +
|
||||||
|
" PdbUniversalAnalyzer.setPdbFileOption(currentProgram, new File(\"/path/to/pdb/file.pdb\"));\n" +
|
||||||
|
"Or set the symbol server search configuration using:" +
|
||||||
|
" PdbPlugin.saveSymbolServerServiceConfig(...);\n" +
|
||||||
|
" This must be done using a pre-script (prior to analysis).");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Msg.info(pdbAnalyzer,
|
||||||
|
"You may set the PDB \"Symbol Server Config\"" +
|
||||||
|
"\n using \"Edit->Symbol Server Config\" prior to analysis." +
|
||||||
|
"\nIt is important that a PDB is used during initial analysis " +
|
||||||
|
"\nif available.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Msg.info(pdbAnalyzer, "PDB analyzer parsing file: " + pdbFile);
|
||||||
|
if (!pdbFile.isFile()) {
|
||||||
|
Msg.error(pdbAnalyzer,
|
||||||
|
"Skipping PDB processing: specified file does not exist or is not readable: " +
|
||||||
|
pdbFile);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return pdbFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -19,12 +19,9 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
|
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.app.util.opinion.PeLoader;
|
|
||||||
import ghidra.app.util.pdb.PdbLocator;
|
import ghidra.app.util.pdb.PdbLocator;
|
||||||
import ghidra.app.util.pdb.PdbProgramAttributes;
|
import ghidra.app.util.pdb.PdbProgramAttributes;
|
||||||
import ghidra.app.util.pdb.pdbapplicator.PdbApplicator;
|
import ghidra.app.util.pdb.pdbapplicator.PdbApplicator;
|
||||||
@@ -35,7 +32,6 @@ import ghidra.framework.options.Options;
|
|||||||
import ghidra.program.model.address.AddressSetView;
|
import ghidra.program.model.address.AddressSetView;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.SystemUtilities;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
@@ -63,7 +59,8 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
|
|||||||
static final boolean DEFAULT_ENABLEMENT = true;
|
static final boolean DEFAULT_ENABLEMENT = true;
|
||||||
private static final String DESCRIPTION =
|
private static final String DESCRIPTION =
|
||||||
"Platform-independent PDB analyzer (No XML support).\n" +
|
"Platform-independent PDB analyzer (No XML support).\n" +
|
||||||
"NOTE: still undergoing development, so options may change.";
|
"NOTE: still undergoing development, so options may change.\n" +
|
||||||
|
"PDB Symbol Server searching is configured in Edit -> Symbol Server Config.\n";
|
||||||
|
|
||||||
//==============================================================================================
|
//==============================================================================================
|
||||||
// Force-load a PDB file.
|
// Force-load a PDB file.
|
||||||
@@ -79,18 +76,7 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
|
|||||||
private File DEFAULT_FORCE_LOAD_FILE = new File(PdbLocator.DEFAULT_SYMBOLS_DIR, "sample.pdb");
|
private File DEFAULT_FORCE_LOAD_FILE = new File(PdbLocator.DEFAULT_SYMBOLS_DIR, "sample.pdb");
|
||||||
private File forceLoadFile;
|
private File forceLoadFile;
|
||||||
|
|
||||||
// Symbol Repository Path.
|
private boolean searchRemoteLocations = false;
|
||||||
private static final String OPTION_NAME_SYMBOLPATH = "Symbol Repository Path";
|
|
||||||
private static final String OPTION_DESCRIPTION_SYMBOLPATH =
|
|
||||||
"Directory path to root of Microsoft Symbol Repository Directory";
|
|
||||||
private File symbolsRepositoryDir;
|
|
||||||
|
|
||||||
// Include the PE-Header-Specified PDB path for searching for appropriate PDB file.
|
|
||||||
private static final String OPTION_NAME_INCLUDE_PE_PDB_PATH =
|
|
||||||
"Unsafe: Include PE PDB Path in PDB Search";
|
|
||||||
private static final String OPTION_DESCRIPTION_INCLUDE_PE_PDB_PATH =
|
|
||||||
"If checked, specifically searching for PDB in PE-Header-Specified Location.";
|
|
||||||
private boolean includePeSpecifiedPdbPath = false;
|
|
||||||
|
|
||||||
//==============================================================================================
|
//==============================================================================================
|
||||||
// Additional instance data
|
// Additional instance data
|
||||||
@@ -162,34 +148,21 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (failMissingFilename(programAttributes, log) ||
|
File pdbFile = null;
|
||||||
failMissingAttributes(programAttributes, log)) {
|
if (doForceLoad && forceLoadFile != null) {
|
||||||
return true;
|
if (!forceLoadFile.isFile()) {
|
||||||
}
|
|
||||||
|
|
||||||
String pdbFilename;
|
|
||||||
if (doForceLoad) {
|
|
||||||
if (!confirmFile(forceLoadFile)) {
|
|
||||||
logFailure("Force-load PDB file does not exist: " + forceLoadFile, log);
|
logFailure("Force-load PDB file does not exist: " + forceLoadFile, log);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
pdbFilename = forceLoadFile.getAbsolutePath();
|
pdbFile = forceLoadFile;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PdbLocator locator = new PdbLocator(symbolsRepositoryDir);
|
pdbFile = PdbAnalyzerCommon.findPdb(this, program, searchRemoteLocations, monitor);
|
||||||
pdbFilename =
|
}
|
||||||
locator.findPdb(program, programAttributes, !SystemUtilities.isInHeadlessMode(),
|
if (pdbFile == null) {
|
||||||
includePeSpecifiedPdbPath, monitor, log, getName());
|
// warnings have already been logged
|
||||||
if (pdbFilename == null) {
|
return false;
|
||||||
if (!confirmDirectory(symbolsRepositoryDir)) {
|
|
||||||
logFailure("PDB symbol repository directory not found: " + symbolsRepositoryDir,
|
|
||||||
log);
|
|
||||||
}
|
|
||||||
Msg.info(this, "PDB analyzer failed to locate PDB file");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Msg.info(this, "PDB analyzer parsing file: " + pdbFilename);
|
|
||||||
|
|
||||||
PdbLog.message(
|
PdbLog.message(
|
||||||
"================================================================================");
|
"================================================================================");
|
||||||
@@ -197,61 +170,33 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
|
|||||||
PdbLog.message("Ghidra Version: " + Application.getApplicationVersion());
|
PdbLog.message("Ghidra Version: " + Application.getApplicationVersion());
|
||||||
PdbLog.message(NAME);
|
PdbLog.message(NAME);
|
||||||
PdbLog.message(DESCRIPTION);
|
PdbLog.message(DESCRIPTION);
|
||||||
PdbLog.message("PDB Filename: " + pdbFilename + "\n");
|
PdbLog.message("PDB Filename: " + pdbFile + "\n");
|
||||||
|
|
||||||
try (AbstractPdb pdb = PdbParser.parse(pdbFilename, pdbReaderOptions, monitor)) {
|
try (AbstractPdb pdb = PdbParser.parse(pdbFile.getPath(), pdbReaderOptions, monitor)) {
|
||||||
monitor.setMessage("PDB: Parsing " + pdbFilename + "...");
|
monitor.setMessage("PDB: Parsing " + pdbFile + "...");
|
||||||
pdb.deserialize(monitor);
|
pdb.deserialize(monitor);
|
||||||
PdbApplicator applicator = new PdbApplicator(pdbFilename, pdb);
|
PdbApplicator applicator = new PdbApplicator(pdbFile.getPath(), pdb);
|
||||||
applicator.applyTo(program, program.getDataTypeManager(), program.getImageBase(),
|
applicator.applyTo(program, program.getDataTypeManager(), program.getImageBase(),
|
||||||
pdbApplicatorOptions, monitor, log);
|
pdbApplicatorOptions, monitor, log);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (PdbException | IOException e) {
|
catch (PdbException | IOException e) {
|
||||||
log.appendMsg(getName(),
|
log.appendMsg(getName(),
|
||||||
"Issue processing PDB file: " + pdbFilename + ":\n " + e.toString());
|
"Issue processing PDB file: " + pdbFile + ":\n " + e.toString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: I changed this method from what was lifted in the old code. I check for null string
|
|
||||||
// and I also check for MSCOFF_NAME (TODO: check on the validity of this!!!). Also, changed
|
|
||||||
// the comparison to a substring search from a .equals).
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canAnalyze(Program program) {
|
public boolean canAnalyze(Program program) {
|
||||||
String executableFormat = program.getExecutableFormat();
|
return PdbAnalyzerCommon.canAnalyzeProgram(program);
|
||||||
return executableFormat != null && (executableFormat.indexOf(PeLoader.PE_NAME) != -1);
|
|
||||||
// TODO: Check for MSCOFF_NAME. Initial investigation shows that the .debug$T section of
|
|
||||||
// the MSCOFF (*.obj) file has type records and the .debug$S section has symbol records.
|
|
||||||
// More than that, in at least one instance, there has been a TypeServer2MsType type
|
|
||||||
// record that give the GUID, age, and name of the PDB file associated with the MSCOFF
|
|
||||||
// file. At this point in time, these two sections of the MSCOFF are read (header and
|
|
||||||
// raw data), but we do not interpret these sections any further. Suggest that we "might"
|
|
||||||
// want to parse some of these records at load time? Maybe not. We could, at analysis
|
|
||||||
// time, add the ability to process these two sections (as part of analysis (though we
|
|
||||||
// will not be aware of a PDB file yet), and upon discovery of a TypeServer2MsType (or
|
|
||||||
// perhaps other?), proceed to find the file (if possible) and also process that file.
|
|
||||||
// We posit that if a record indicates a separate PDB for the types (Note: MSFT indicates
|
|
||||||
// that only data types will be found in an MSCOFF PDB file), then that will likely be
|
|
||||||
// the only record in the .debug$T section.
|
|
||||||
// TODO: If the MSCOFF file is located in a MSCOFF ARCHIVE (*.lib), there can be a PDB
|
|
||||||
// associated with the archive. We currently do not pass on this association of the
|
|
||||||
// PDB archive to each underlying MSCOFF file. Moreover, we believe that we are not
|
|
||||||
// currently discovering the associated MSCOFF ARCHIVE PDB file when processing the
|
|
||||||
// MSCOFF ARCHIVE. Initial indication is that each MSCOFF within the archive will have
|
|
||||||
// the PDB file that it needs listed, even if redundant for each MSCOFF within the
|
|
||||||
// archive.
|
|
||||||
// return executableFormat != null && (executableFormat.indexOf(PeLoader.PE_NAME) != -1 ||
|
|
||||||
// executableFormat.indexOf(MSCoffLoader.MSCOFF_NAME) != -1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerOptions(Options options, Program program) {
|
public void registerOptions(Options options, Program program) {
|
||||||
|
|
||||||
symbolsRepositoryDir = PdbLocator.getDefaultPdbSymbolsDir();
|
|
||||||
|
|
||||||
// PDB file location information
|
// PDB file location information
|
||||||
if (developerMode) {
|
if (developerMode) {
|
||||||
options.registerOption(OPTION_NAME_DO_FORCELOAD, Boolean.FALSE, null,
|
options.registerOption(OPTION_NAME_DO_FORCELOAD, Boolean.FALSE, null,
|
||||||
@@ -259,10 +204,9 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
|
|||||||
options.registerOption(OPTION_NAME_FORCELOAD_FILE, OptionType.FILE_TYPE,
|
options.registerOption(OPTION_NAME_FORCELOAD_FILE, OptionType.FILE_TYPE,
|
||||||
DEFAULT_FORCE_LOAD_FILE, null, OPTION_DESCRIPTION_FORCELOAD_FILE);
|
DEFAULT_FORCE_LOAD_FILE, null, OPTION_DESCRIPTION_FORCELOAD_FILE);
|
||||||
}
|
}
|
||||||
options.registerOption(OPTION_NAME_SYMBOLPATH, OptionType.FILE_TYPE, symbolsRepositoryDir,
|
options.registerOption(PdbAnalyzerCommon.OPTION_NAME_SEARCH_REMOTE_LOCATIONS,
|
||||||
null, OPTION_DESCRIPTION_SYMBOLPATH);
|
searchRemoteLocations, null,
|
||||||
options.registerOption(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath, null,
|
PdbAnalyzerCommon.OPTION_DESCRIPTION_SEARCH_REMOTE_LOCATIONS);
|
||||||
OPTION_DESCRIPTION_INCLUDE_PE_PDB_PATH);
|
|
||||||
|
|
||||||
pdbReaderOptions.registerOptions(options);
|
pdbReaderOptions.registerOptions(options);
|
||||||
pdbApplicatorOptions.registerAnalyzerOptions(options);
|
pdbApplicatorOptions.registerAnalyzerOptions(options);
|
||||||
@@ -279,14 +223,8 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
|
|||||||
forceLoadFile = options.getFile(OPTION_NAME_FORCELOAD_FILE, forceLoadFile);
|
forceLoadFile = options.getFile(OPTION_NAME_FORCELOAD_FILE, forceLoadFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
File symbolsDir = options.getFile(OPTION_NAME_SYMBOLPATH, symbolsRepositoryDir);
|
searchRemoteLocations = options.getBoolean(
|
||||||
if (!symbolsDir.equals(symbolsRepositoryDir)) {
|
PdbAnalyzerCommon.OPTION_NAME_SEARCH_REMOTE_LOCATIONS, searchRemoteLocations);
|
||||||
symbolsRepositoryDir = symbolsDir;
|
|
||||||
PdbLocator.setDefaultPdbSymbolsDir(symbolsDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
includePeSpecifiedPdbPath =
|
|
||||||
options.getBoolean(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath);
|
|
||||||
|
|
||||||
pdbReaderOptions.loadOptions(options);
|
pdbReaderOptions.loadOptions(options);
|
||||||
pdbApplicatorOptions.loadAnalyzerOptions(options);
|
pdbApplicatorOptions.loadAnalyzerOptions(options);
|
||||||
@@ -294,51 +232,40 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
|
|||||||
|
|
||||||
//==============================================================================================
|
//==============================================================================================
|
||||||
|
|
||||||
private boolean failMissingFilename(PdbProgramAttributes attributes, MessageLog log) {
|
|
||||||
if (doForceLoad) {
|
|
||||||
return false; // PDB File property not used for forced load
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(attributes.getPdbFile())) {
|
|
||||||
logFailure("Missing 'PDB File' program property, unable to locate PDB", log);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logFailure(String msg, MessageLog log) {
|
private void logFailure(String msg, MessageLog log) {
|
||||||
log.appendMsg(getName(), msg);
|
log.appendMsg(getName(), msg);
|
||||||
log.appendMsg(getName(), "Skipping PDB processing");
|
log.appendMsg(getName(), "Skipping PDB processing");
|
||||||
log.setStatus(msg);
|
log.setStatus(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean failMissingAttributes(PdbProgramAttributes attributes, MessageLog log) {
|
/**
|
||||||
if (doForceLoad) {
|
* Sets the PDB file that will be used by the analyzer when it is next invoked
|
||||||
return false; // Attributes not used for forced load
|
* on the specified program.
|
||||||
}
|
* <p>
|
||||||
// RSDS version should only have GUID; non-RSDS version should only have Signature.
|
* Normally the analyzer would locate the PDB file on its own, but if a
|
||||||
String error;
|
* headless script wishes to override the analyzer's behaivor, it can
|
||||||
if ("RSDS".equals(attributes.getPdbVersion())) {
|
* use this method to specify a file.
|
||||||
if (!StringUtils.isEmpty(attributes.getPdbGuid())) {
|
*
|
||||||
return false; // Don't fail.
|
* @param program {@link Program}
|
||||||
}
|
* @param pdbFile the pdb file
|
||||||
error = "Missing 'PDB GUID' program property, unable to locate PDB.";
|
*/
|
||||||
}
|
public static void setPdbFileOption(Program program, File pdbFile) {
|
||||||
else {
|
PdbAnalyzerCommon.setPdbFileOption(NAME, program, pdbFile);
|
||||||
if (!StringUtils.isEmpty(attributes.getPdbSignature())) {
|
|
||||||
return false; // Don't fail.
|
|
||||||
}
|
|
||||||
error = "Missing 'PDB Signature' program property, unable to locate PDB.";
|
|
||||||
}
|
|
||||||
logFailure(error, log);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean confirmDirectory(File path) {
|
/**
|
||||||
return path.isDirectory();
|
* Sets the "allow remote" option that will be used by the analyzer when it is next invoked
|
||||||
|
* on the specified program.
|
||||||
|
* <p>
|
||||||
|
* Normally when the analyzer attempts to locate a matching PDB file it
|
||||||
|
* will default to NOT searching remote symbol servers. A headless script could
|
||||||
|
* use this method to allow the analyzer to search remote symbol servers.
|
||||||
|
*
|
||||||
|
* @param program {@link Program}
|
||||||
|
* @param allowRemote boolean flag, true means analyzer can search remote symbol
|
||||||
|
* servers
|
||||||
|
*/
|
||||||
|
public static void setAllowRemoteOption(Program program, boolean allowRemote) {
|
||||||
|
PdbAnalyzerCommon.setAllowRemoteOption(NAME, program, allowRemote);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean confirmFile(File path) {
|
|
||||||
return path.isFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,90 +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.util.bin.format.pdb;
|
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
|
||||||
import ghidra.app.util.bin.format.pe.debug.DebugCodeViewConstants;
|
|
||||||
import ghidra.program.model.data.*;
|
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class PdbInfo implements PdbInfoIface {
|
|
||||||
public final static int MAGIC =
|
|
||||||
DebugCodeViewConstants.SIGNATURE_NB << 16 |
|
|
||||||
DebugCodeViewConstants.VERSION_10;
|
|
||||||
|
|
||||||
public static boolean isMatch(BinaryReader reader, int ptr) throws IOException {
|
|
||||||
//read value out as big endian
|
|
||||||
int value = reader.readByte(ptr ) << 24 |
|
|
||||||
reader.readByte(ptr+1) << 16 |
|
|
||||||
reader.readByte(ptr+2) << 8 |
|
|
||||||
reader.readByte(ptr+3);
|
|
||||||
return MAGIC == value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte [] magic;
|
|
||||||
private int offset;
|
|
||||||
private int sig;
|
|
||||||
private int age;
|
|
||||||
private String pdbName;
|
|
||||||
|
|
||||||
public PdbInfo(BinaryReader reader, int ptr) throws IOException {
|
|
||||||
long origIndex = reader.getPointerIndex();
|
|
||||||
reader.setPointerIndex(ptr);
|
|
||||||
try {
|
|
||||||
magic = reader.readNextByteArray(4);
|
|
||||||
offset = reader.readNextInt();
|
|
||||||
sig = reader.readNextInt();
|
|
||||||
age = reader.readNextInt();
|
|
||||||
pdbName = reader.readNextAsciiString();
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
reader.setPointerIndex(origIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte [] getMagic() {
|
|
||||||
return magic;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOffset() {
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSig() {
|
|
||||||
return sig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAge() {
|
|
||||||
return age;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPdbName() {
|
|
||||||
return pdbName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
|
||||||
StructureDataType struct = new StructureDataType("PdbInfo", 0);
|
|
||||||
struct.add(new StringDataType(), magic.length, "signature", null);
|
|
||||||
struct.add(new DWordDataType(), "offset", null);
|
|
||||||
struct.add(new DWordDataType(), "sig", null);
|
|
||||||
struct.add(new DWordDataType(), "age", null);
|
|
||||||
struct.add(new StringDataType(), pdbName.length(), "pdbname", null);
|
|
||||||
struct.setCategoryPath(new CategoryPath("/PDB"));
|
|
||||||
return struct;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +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.util.bin.format.pdb;
|
|
||||||
|
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
|
||||||
import ghidra.app.util.bin.format.pe.debug.DebugCodeViewConstants;
|
|
||||||
import ghidra.app.util.datatype.microsoft.GUID;
|
|
||||||
import ghidra.app.util.datatype.microsoft.GuidDataType;
|
|
||||||
import ghidra.program.model.data.*;
|
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class PdbInfoDotNet implements PdbInfoDotNetIface {
|
|
||||||
public final static int MAGIC = DebugCodeViewConstants.SIGNATURE_DOT_NET << 16 |
|
|
||||||
DebugCodeViewConstants.VERSION_DOT_NET;
|
|
||||||
|
|
||||||
public static boolean isMatch(BinaryReader reader, int ptr) throws IOException {
|
|
||||||
//read value out as big endian
|
|
||||||
int value =
|
|
||||||
reader.readByte(ptr) << 24 | reader.readByte(ptr + 1) << 16 |
|
|
||||||
reader.readByte(ptr + 2) << 8 | reader.readByte(ptr + 3);
|
|
||||||
return MAGIC == value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] magic;
|
|
||||||
private GUID guid;
|
|
||||||
private int age;
|
|
||||||
private String pdbName;
|
|
||||||
|
|
||||||
public PdbInfoDotNet(BinaryReader reader, int ptr) throws IOException {
|
|
||||||
long origIndex = reader.getPointerIndex();
|
|
||||||
reader.setPointerIndex(ptr);
|
|
||||||
try {
|
|
||||||
magic = reader.readNextByteArray(4);
|
|
||||||
guid = new GUID(reader);
|
|
||||||
age = reader.readNextInt();
|
|
||||||
pdbName = reader.readNextAsciiString();
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
reader.setPointerIndex(origIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPdbName() {
|
|
||||||
return pdbName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAge() {
|
|
||||||
return age;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSignature() {
|
|
||||||
return guid.getData1();
|
|
||||||
}
|
|
||||||
|
|
||||||
public GUID getGUID() {
|
|
||||||
return guid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getMagic() {
|
|
||||||
return magic;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
|
||||||
StructureDataType struct = new StructureDataType("DotNetPdbInfo", 0);
|
|
||||||
struct.add(new StringDataType(), magic.length, "signature", null);
|
|
||||||
struct.add(new GuidDataType(), "guid", null);
|
|
||||||
struct.add(new DWordDataType(), "age", null);
|
|
||||||
if (pdbName.length() > 0) {
|
|
||||||
struct.add(new StringDataType(), pdbName.length(), "pdbname", null);
|
|
||||||
}
|
|
||||||
struct.setCategoryPath(new CategoryPath("/PDB"));
|
|
||||||
return struct;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,9 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.pdb;
|
package ghidra.app.util.bin.format.pdb;
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
@@ -27,9 +28,7 @@ import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility;
|
|||||||
import ghidra.app.services.DataTypeManagerService;
|
import ghidra.app.services.DataTypeManagerService;
|
||||||
import ghidra.app.util.NamespaceUtils;
|
import ghidra.app.util.NamespaceUtils;
|
||||||
import ghidra.app.util.SymbolPath;
|
import ghidra.app.util.SymbolPath;
|
||||||
import ghidra.app.util.importer.LibrarySearchPathManager;
|
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.app.util.pdb.PdbLocator;
|
|
||||||
import ghidra.app.util.pdb.PdbProgramAttributes;
|
import ghidra.app.util.pdb.PdbProgramAttributes;
|
||||||
import ghidra.framework.*;
|
import ghidra.framework.*;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
@@ -83,6 +82,7 @@ public class PdbParser {
|
|||||||
private PdbErrorHandler errHandler;
|
private PdbErrorHandler errHandler;
|
||||||
private PdbErrorReaderThread thread;
|
private PdbErrorReaderThread thread;
|
||||||
private boolean parsed = false;
|
private boolean parsed = false;
|
||||||
|
private boolean allowNonExactMatch;
|
||||||
|
|
||||||
private CategoryPath pdbCategory;
|
private CategoryPath pdbCategory;
|
||||||
|
|
||||||
@@ -94,13 +94,40 @@ public class PdbParser {
|
|||||||
private PdbDataTypeParser dataTypeParser;
|
private PdbDataTypeParser dataTypeParser;
|
||||||
private Map<SymbolPath, Boolean> namespaceMap = new TreeMap<>(); // false: simple namespace, true: class namespace
|
private Map<SymbolPath, Boolean> namespaceMap = new TreeMap<>(); // false: simple namespace, true: class namespace
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a PdbParser instance.
|
||||||
|
*
|
||||||
|
* @param pdbFile the pdb file to parse, either .pdb or .pdb.xml
|
||||||
|
* @param program the {@link Program} to modify
|
||||||
|
* @param service {@link DataTypeManagerService}
|
||||||
|
* @param forceAnalysis boolean flag, currently always true, needs to be refactored out
|
||||||
|
* @param allowNonExactMatch boolean flag, if true skips warning user about mismatch
|
||||||
|
* between the program's PDB guid/id/age and the specified PDB file's guid/id/age, which
|
||||||
|
* can terminate the pdb import in headless
|
||||||
|
* @param monitor {@link TaskMonitor}, null ok
|
||||||
|
*/
|
||||||
public PdbParser(File pdbFile, Program program, DataTypeManagerService service,
|
public PdbParser(File pdbFile, Program program, DataTypeManagerService service,
|
||||||
boolean forceAnalysis, TaskMonitor monitor) {
|
boolean forceAnalysis, boolean allowNonExactMatch, TaskMonitor monitor) {
|
||||||
this(pdbFile, program, service, getPdbAttributes(program), forceAnalysis, monitor);
|
this(pdbFile, program, service, getPdbAttributes(program), forceAnalysis,
|
||||||
|
allowNonExactMatch, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a PdbParser instance.
|
||||||
|
*
|
||||||
|
* @param pdbFile the pdb file to parse, either .pdb or .pdb.xml
|
||||||
|
* @param program the {@link Program} to modify
|
||||||
|
* @param service {@link DataTypeManagerService}
|
||||||
|
* @param programAttributes the PDB information specified by the program
|
||||||
|
* @param forceAnalysis boolean flag, currently always true, needs to be refactored out
|
||||||
|
* @param allowNonExactMatch boolean flag, if true skips warning user about mismatch
|
||||||
|
* between the program's PDB guid/id/age and the specified PDB file's guid/id/age, which
|
||||||
|
* can terminate the pdb import in headless
|
||||||
|
* @param monitor {@link TaskMonitor}, null ok
|
||||||
|
*/
|
||||||
public PdbParser(File pdbFile, Program program, DataTypeManagerService service,
|
public PdbParser(File pdbFile, Program program, DataTypeManagerService service,
|
||||||
PdbProgramAttributes programAttributes, boolean forceAnalysis, TaskMonitor monitor) {
|
PdbProgramAttributes programAttributes, boolean forceAnalysis,
|
||||||
|
boolean allowNonExactMatch, TaskMonitor monitor) {
|
||||||
this.pdbFile = pdbFile;
|
this.pdbFile = pdbFile;
|
||||||
this.pdbCategory = new CategoryPath(CategoryPath.ROOT, pdbFile.getName());
|
this.pdbCategory = new CategoryPath(CategoryPath.ROOT, pdbFile.getName());
|
||||||
this.program = program;
|
this.program = program;
|
||||||
@@ -108,8 +135,9 @@ public class PdbParser {
|
|||||||
this.service = service;
|
this.service = service;
|
||||||
this.forceAnalysis = forceAnalysis;
|
this.forceAnalysis = forceAnalysis;
|
||||||
this.monitor = monitor != null ? monitor : TaskMonitor.DUMMY;
|
this.monitor = monitor != null ? monitor : TaskMonitor.DUMMY;
|
||||||
this.isXML = pdbFile.getAbsolutePath().endsWith(PdbFileType.XML.toString());
|
this.isXML = pdbFile.getName().toLowerCase().endsWith(PdbFileType.XML.toString());
|
||||||
this.programAttributes = programAttributes;
|
this.programAttributes = programAttributes;
|
||||||
|
this.allowNonExactMatch = allowNonExactMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -184,12 +212,12 @@ public class PdbParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkFileType() throws PdbException {
|
private void checkFileType() throws PdbException {
|
||||||
String pdbFilename = pdbFile.getName();
|
String pdbFilename = pdbFile.getName().toLowerCase();
|
||||||
|
|
||||||
if (!pdbFilename.endsWith(PdbFileType.PDB.toString()) &&
|
if (!pdbFilename.endsWith(PdbFileType.PDB.toString()) &&
|
||||||
!pdbFilename.endsWith(PdbFileType.XML.toString())) {
|
!pdbFilename.endsWith(PdbFileType.XML.toString())) {
|
||||||
throw new PdbException(
|
throw new PdbException(
|
||||||
"\nInvalid file type (expecting .pdb or .pdb.xml): '" + pdbFilename + "'");
|
"\nInvalid file type (expecting .pdb or .pdb.xml): '" + pdbFile.getName() + "'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,18 +644,21 @@ public class PdbParser {
|
|||||||
pdbGuid = pdbGuid.toUpperCase();
|
pdbGuid = pdbGuid.toUpperCase();
|
||||||
pdbGuid = "{" + pdbGuid + "}";
|
pdbGuid = "{" + pdbGuid + "}";
|
||||||
|
|
||||||
if (!xmlGuid.equals(pdbGuid)) {
|
if (!allowNonExactMatch) {
|
||||||
warning = "PDB signature does not match.\n" + "Program GUID: " + pdbGuid +
|
if (!xmlGuid.equals(pdbGuid)) {
|
||||||
"\nXML GUID: " + xmlGuid; }
|
warning = "PDB signature does not match.\n" + "Program GUID: " + pdbGuid +
|
||||||
else {
|
"\nXML GUID: " + xmlGuid;
|
||||||
// Also check that PDB ages match, if they are both available
|
}
|
||||||
if ((xmlAge != null) && (pdbAge != null)) {
|
else {
|
||||||
|
// Also check that PDB ages match, if they are both available
|
||||||
|
if ((xmlAge != null) && (pdbAge != null)) {
|
||||||
|
|
||||||
int pdbAgeDecimal = Integer.parseInt(pdbAge, 16);
|
int pdbAgeDecimal = Integer.parseInt(pdbAge, 16);
|
||||||
int xmlAgeDecimal = Integer.parseInt(xmlAge);
|
int xmlAgeDecimal = Integer.parseInt(xmlAge);
|
||||||
|
|
||||||
if (xmlAgeDecimal != pdbAgeDecimal) {
|
if (xmlAgeDecimal != pdbAgeDecimal) {
|
||||||
warning = "PDB ages do not match.";
|
warning = "PDB ages do not match.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1042,17 +1073,6 @@ public class PdbParser {
|
|||||||
return new PdbProgramAttributes(program);
|
return new PdbProgramAttributes(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the PDB associated with the given program using its attributes.
|
|
||||||
* The PDB path information within the program information will not be used.
|
|
||||||
*
|
|
||||||
* @param program program for which to find a matching PDB
|
|
||||||
* @return matching PDB for program, or null
|
|
||||||
*/
|
|
||||||
public static File findPDB(Program program) {
|
|
||||||
return findPDB(getPdbAttributes(program), false, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the PDB has previously been loaded for the specified program.
|
* Determine if the PDB has previously been loaded for the specified program.
|
||||||
* @param program program for which to find a matching PDB
|
* @param program program for which to find a matching PDB
|
||||||
@@ -1062,273 +1082,6 @@ public class PdbParser {
|
|||||||
return getPdbAttributes(program).isPdbLoaded();
|
return getPdbAttributes(program).isPdbLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the PDB associated with the given program using its attributes, specifying the
|
|
||||||
* location where symbols are stored.
|
|
||||||
*
|
|
||||||
* @param program program for which to find a matching PDB
|
|
||||||
* @param includePeSpecifiedPdbPath to also check the PE-header-specified PDB path
|
|
||||||
* @param symbolsRepositoryDir location where downloaded symbols are stored
|
|
||||||
* @return matching PDB for program, or null
|
|
||||||
*/
|
|
||||||
public static File findPDB(Program program, boolean includePeSpecifiedPdbPath,
|
|
||||||
File symbolsRepositoryDir) {
|
|
||||||
return findPDB(getPdbAttributes(program), includePeSpecifiedPdbPath, symbolsRepositoryDir,
|
|
||||||
null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a matching PDB file using attributes associated with the program. User can specify the
|
|
||||||
* type of file to search from (.pdb or .pdb.xml).
|
|
||||||
*
|
|
||||||
* @param pdbAttributes PDB attributes associated with the program
|
|
||||||
* @param includePeSpecifiedPdbPath to also check the PE-header-specified PDB path
|
|
||||||
* @param symbolsRepositoryDir location of the local symbols repository (can be null)
|
|
||||||
* @param fileType type of file to search for (can be null)
|
|
||||||
* @return matching PDB file (or null, if not found)
|
|
||||||
*/
|
|
||||||
public static File findPDB(PdbProgramAttributes pdbAttributes,
|
|
||||||
boolean includePeSpecifiedPdbPath, File symbolsRepositoryDir, PdbFileType fileType) {
|
|
||||||
|
|
||||||
// Store potential names of PDB files and potential locations of those files,
|
|
||||||
// so that all possible combinations can be searched.
|
|
||||||
// LinkedHashSet is used when we need to preserve order
|
|
||||||
Set<String> guidSubdirPaths = new HashSet<>();
|
|
||||||
|
|
||||||
String guidAgeString = pdbAttributes.getGuidAgeCombo();
|
|
||||||
if (guidAgeString == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> potentialPdbNames = pdbAttributes.getPotentialPdbFilenames();
|
|
||||||
for (String potentialName : potentialPdbNames) {
|
|
||||||
guidSubdirPaths.add(File.separator + potentialName + File.separator + guidAgeString);
|
|
||||||
}
|
|
||||||
|
|
||||||
return checkPathsForPdb(symbolsRepositoryDir, guidSubdirPaths, potentialPdbNames, fileType,
|
|
||||||
pdbAttributes, includePeSpecifiedPdbPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check potential paths in a specific order. If the symbolsRepositoryPath parameter is
|
|
||||||
* supplied and the directory exists, that directory will be searched first for the
|
|
||||||
* matching PDB file.
|
|
||||||
*
|
|
||||||
* If the file type is supplied, then only that file type will be searched for. Otherwise,
|
|
||||||
* the search process depends on the current operating system that Ghidra is running from:
|
|
||||||
*
|
|
||||||
* - Windows: look in the symbolsRepositoryPath for a matching .pdb file. If one does not
|
|
||||||
* exist, look for a .pdb.xml file in symbolsRepositoryPath. If not found, then
|
|
||||||
* search for a matching .pdb file, then .pdb.xml file, in other directories.
|
|
||||||
* - non-Windows: look in the symbolsRepositoryPath for a matching .pdb.xml file. If one does
|
|
||||||
* not exist, look for a .pdb file. If a .pdb file is found, return an error saying
|
|
||||||
* that it was found, but could not be processed. If no matches found in
|
|
||||||
* symbolsRepositoryPath, then look for .pdb.xml file, then .pdb.xml file in other
|
|
||||||
* directories.
|
|
||||||
*
|
|
||||||
* @param symbolsRepositoryDir location of the local symbols repository (can be null)
|
|
||||||
* @param guidSubdirPaths subdirectory paths (that include the PDB's GUID) that may contain
|
|
||||||
* a matching PDB
|
|
||||||
* @param potentialPdbNames all potential filenames for the PDB file(s) that match the program
|
|
||||||
* @param fileType file type to search for (can be null)
|
|
||||||
* @param pdbAttributes PDB attributes associated with the program
|
|
||||||
* @return matching PDB file, if found (else null)
|
|
||||||
*/
|
|
||||||
private static File checkPathsForPdb(File symbolsRepositoryDir, Set<String> guidSubdirPaths,
|
|
||||||
List<String> potentialPdbNames, PdbFileType fileType,
|
|
||||||
PdbProgramAttributes pdbAttributes, boolean includePeSpecifiedPdbPath) {
|
|
||||||
|
|
||||||
File foundPdb = null;
|
|
||||||
Set<File> symbolsRepoPaths =
|
|
||||||
getSymbolsRepositoryPaths(symbolsRepositoryDir, guidSubdirPaths);
|
|
||||||
Set<File> predefinedPaths =
|
|
||||||
getPredefinedPaths(guidSubdirPaths, pdbAttributes, includePeSpecifiedPdbPath);
|
|
||||||
boolean fileTypeSpecified = (fileType != null);
|
|
||||||
boolean checkForXml;
|
|
||||||
|
|
||||||
// If the file type is specified, look for that type of file only.
|
|
||||||
if (fileTypeSpecified) {
|
|
||||||
checkForXml = (fileType == PdbFileType.XML) ? true : false;
|
|
||||||
|
|
||||||
foundPdb = checkForPDBorXML(symbolsRepoPaths, potentialPdbNames, checkForXml);
|
|
||||||
|
|
||||||
if (foundPdb != null) {
|
|
||||||
return foundPdb;
|
|
||||||
}
|
|
||||||
|
|
||||||
foundPdb = checkForPDBorXML(predefinedPaths, potentialPdbNames, checkForXml);
|
|
||||||
|
|
||||||
return foundPdb;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the file type is not specified, look for both file types, starting with the
|
|
||||||
// file type that's most appropriate for the Operating System (PDB for Windows, XML for
|
|
||||||
// non-Windows).
|
|
||||||
checkForXml = onWindows ? false : true;
|
|
||||||
|
|
||||||
// Start by searching in symbolsRepositoryPath, if available.
|
|
||||||
if (!symbolsRepoPaths.isEmpty()) {
|
|
||||||
foundPdb = checkSpecificPathsForPdb(symbolsRepoPaths, potentialPdbNames, checkForXml);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundPdb != null) {
|
|
||||||
return foundPdb;
|
|
||||||
}
|
|
||||||
|
|
||||||
return checkSpecificPathsForPdb(predefinedPaths, potentialPdbNames, checkForXml);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static File checkSpecificPathsForPdb(Set<File> paths, List<String> potentialPdbNames,
|
|
||||||
boolean checkForXmlFirst) {
|
|
||||||
|
|
||||||
File foundPdb = checkForPDBorXML(paths, potentialPdbNames, checkForXmlFirst);
|
|
||||||
|
|
||||||
if (foundPdb != null) {
|
|
||||||
return foundPdb;
|
|
||||||
}
|
|
||||||
|
|
||||||
foundPdb = checkForPDBorXML(paths, potentialPdbNames, !checkForXmlFirst);
|
|
||||||
|
|
||||||
return foundPdb;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Set<File> getSymbolsRepositoryPaths(File symbolsRepositoryDir,
|
|
||||||
Set<String> guidSubdirPaths) {
|
|
||||||
|
|
||||||
Set<File> symbolsRepoPaths = new LinkedHashSet<>();
|
|
||||||
|
|
||||||
// Collect sub-directories of the symbol repository that exist
|
|
||||||
if (symbolsRepositoryDir != null && symbolsRepositoryDir.isDirectory()) {
|
|
||||||
|
|
||||||
for (String guidSubdir : guidSubdirPaths) {
|
|
||||||
File testDir = new File(symbolsRepositoryDir, guidSubdir);
|
|
||||||
if (testDir.isDirectory()) {
|
|
||||||
symbolsRepoPaths.add(testDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check outer folder last
|
|
||||||
symbolsRepoPaths.add(symbolsRepositoryDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
return symbolsRepoPaths;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get list of "paths we know about" to search for PDBs
|
|
||||||
private static Set<File> getPredefinedPaths(Set<String> guidSubdirPaths,
|
|
||||||
PdbProgramAttributes pdbAttributes, boolean includePeSpecifiedPdbPath) {
|
|
||||||
|
|
||||||
Set<File> predefinedPaths = new LinkedHashSet<>();
|
|
||||||
|
|
||||||
getPathsFromAttributes(pdbAttributes, includePeSpecifiedPdbPath, predefinedPaths);
|
|
||||||
getSymbolPaths(PdbLocator.DEFAULT_SYMBOLS_DIR, guidSubdirPaths, predefinedPaths);
|
|
||||||
getSymbolPaths(PdbLocator.WINDOWS_SYMBOLS_DIR, guidSubdirPaths, predefinedPaths);
|
|
||||||
getLibraryPaths(guidSubdirPaths, predefinedPaths);
|
|
||||||
|
|
||||||
return predefinedPaths;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void getLibraryPaths(Set<String> guidSubdirPaths, Set<File> predefinedPaths) {
|
|
||||||
String[] libraryPaths = LibrarySearchPathManager.getLibraryPaths();
|
|
||||||
|
|
||||||
File libFile, subDir;
|
|
||||||
|
|
||||||
for (String path : libraryPaths) {
|
|
||||||
|
|
||||||
if ((libFile = new File(path)).isDirectory()) {
|
|
||||||
predefinedPaths.add(libFile);
|
|
||||||
|
|
||||||
// Check alternate locations
|
|
||||||
for (String guidSubdir : guidSubdirPaths) {
|
|
||||||
if ((subDir = new File(path, guidSubdir)).isDirectory()) {
|
|
||||||
predefinedPaths.add(subDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void getSymbolPaths(File symbolsDir, Set<String> guidSubdirPaths,
|
|
||||||
Set<File> predefinedPaths) {
|
|
||||||
// Don't have to call .exists(), since .isDirectory() does that already
|
|
||||||
if (symbolsDir == null || !symbolsDir.isDirectory()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
predefinedPaths.add(symbolsDir);
|
|
||||||
|
|
||||||
// Check alternate locations
|
|
||||||
String specialPdbPath = symbolsDir.getAbsolutePath();
|
|
||||||
|
|
||||||
for (String guidSubdir : guidSubdirPaths) {
|
|
||||||
File testDir = new File(specialPdbPath + guidSubdir);
|
|
||||||
if (testDir.isDirectory()) {
|
|
||||||
predefinedPaths.add(testDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void getPathsFromAttributes(PdbProgramAttributes pdbAttributes,
|
|
||||||
boolean includePeSpecifiedPdbPath, Set<File> predefinedPaths) {
|
|
||||||
if (pdbAttributes != null) {
|
|
||||||
|
|
||||||
String currentPath = pdbAttributes.getPdbFile();
|
|
||||||
|
|
||||||
if (currentPath != null && includePeSpecifiedPdbPath) {
|
|
||||||
File parentDir = new File(currentPath).getParentFile();
|
|
||||||
|
|
||||||
if (parentDir != null && parentDir.exists()) {
|
|
||||||
predefinedPaths.add(parentDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentPath = pdbAttributes.getExecutablePath();
|
|
||||||
|
|
||||||
if (currentPath != null && !currentPath.equals("unknown")) {
|
|
||||||
File parentDir = new File(currentPath).getParentFile();
|
|
||||||
|
|
||||||
if (parentDir != null && parentDir.exists()) {
|
|
||||||
predefinedPaths.add(parentDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the first PDB-type file found. Assumes list of potentialPdbDirs is in the order
|
|
||||||
* in which the directories should be searched.
|
|
||||||
*
|
|
||||||
* @param potentialPdbDirs potential PDB directories
|
|
||||||
* @param potentialPdbNames potential PDB names
|
|
||||||
* @param findXML - if true, only searches for the .pdb.xml version of the .pdb file
|
|
||||||
* @return the first file found
|
|
||||||
*/
|
|
||||||
private static File checkForPDBorXML(Set<File> potentialPdbDirs, List<String> potentialPdbNames,
|
|
||||||
boolean findXML) {
|
|
||||||
|
|
||||||
File pdb;
|
|
||||||
|
|
||||||
for (File pdbPath : potentialPdbDirs) {
|
|
||||||
|
|
||||||
for (String filename : potentialPdbNames) {
|
|
||||||
|
|
||||||
if (findXML) {
|
|
||||||
pdb = new File(pdbPath, filename + PdbFileType.XML.toString());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pdb = new File(pdbPath, filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: isFile() also checks for existence
|
|
||||||
if (pdb.isFile()) {
|
|
||||||
return pdb;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
PdbDataTypeParser getDataTypeParser() {
|
PdbDataTypeParser getDataTypeParser() {
|
||||||
if (program == null) {
|
if (program == null) {
|
||||||
throw new AssertException("Parser was not constructed with program");
|
throw new AssertException("Parser was not constructed with program");
|
||||||
|
|||||||
@@ -15,12 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.pdb2.pdbreader;
|
package ghidra.app.util.bin.format.pdb2.pdbreader;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import ghidra.app.util.datatype.microsoft.GUID;
|
import ghidra.app.util.datatype.microsoft.GUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class holds fields used to identify a PDB.
|
* This class holds fields used to identify a PDB.
|
||||||
* <P>
|
* <P>
|
||||||
* These are Version, Signature, Age, and GUID. Som identifiers can be null if not found in
|
* These are Version, Signature, Age, and GUID. Some identifiers can be null if not found in
|
||||||
* the specific version of the PDB.
|
* the specific version of the PDB.
|
||||||
*/
|
*/
|
||||||
public class PdbIdentifiers {
|
public class PdbIdentifiers {
|
||||||
@@ -38,7 +40,7 @@ public class PdbIdentifiers {
|
|||||||
* @param age age used to verify PDB against age stored in program
|
* @param age age used to verify PDB against age stored in program
|
||||||
* @param guid The GUID (can be null for older PDBs).
|
* @param guid The GUID (can be null for older PDBs).
|
||||||
*/
|
*/
|
||||||
PdbIdentifiers(int version, int signature, int age, GUID guid, Processor processor) {
|
public PdbIdentifiers(int version, int signature, int age, GUID guid, Processor processor) {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.signature = signature;
|
this.signature = signature;
|
||||||
this.age = age;
|
this.age = age;
|
||||||
@@ -78,4 +80,33 @@ public class PdbIdentifiers {
|
|||||||
return guid;
|
return guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return ((guid != null) ? guid.toString() : String.format("%08X", signature)) + ", " + age +
|
||||||
|
", " + version + ", " + processor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(age, guid, processor, signature, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PdbIdentifiers other = (PdbIdentifiers) obj;
|
||||||
|
return age == other.age && Objects.equals(guid, other.guid) &&
|
||||||
|
processor == other.processor && signature == other.signature &&
|
||||||
|
version == other.version;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,128 +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 pdb;
|
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Component;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
import docking.DialogComponentProvider;
|
|
||||||
import docking.DockingWindowManager;
|
|
||||||
import docking.widgets.combobox.GComboBox;
|
|
||||||
import ghidra.app.util.bin.format.pdb.PdbParser;
|
|
||||||
import ghidra.app.util.pdb.pdbapplicator.PdbApplicatorControl;
|
|
||||||
import ghidra.util.layout.PairLayout;
|
|
||||||
|
|
||||||
class AskPdbOptionsDialog extends DialogComponentProvider {
|
|
||||||
|
|
||||||
private boolean isCanceled;
|
|
||||||
|
|
||||||
private boolean useMsDiaParser;
|
|
||||||
private PdbApplicatorControl control = PdbApplicatorControl.ALL;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Popup PDB loader options
|
|
||||||
* @param parent parent component or null
|
|
||||||
* @param isPdbFile true if file to be loaded is a PDB file, false
|
|
||||||
* if MsDia XML file.
|
|
||||||
*/
|
|
||||||
AskPdbOptionsDialog(Component parent, boolean isPdbFile) {
|
|
||||||
super("Load PDB Options", true, true, true, false);
|
|
||||||
|
|
||||||
JPanel panel = new JPanel(new BorderLayout(10, 10));
|
|
||||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
|
||||||
|
|
||||||
JPanel optionsPanel = new JPanel(new PairLayout(10, 10));
|
|
||||||
|
|
||||||
final GComboBox<PdbApplicatorControl> controlCombo =
|
|
||||||
new GComboBox<>(PdbApplicatorControl.values());
|
|
||||||
controlCombo.setSelectedItem(PdbApplicatorControl.ALL);
|
|
||||||
controlCombo.addActionListener(e -> {
|
|
||||||
control = (PdbApplicatorControl) controlCombo.getSelectedItem();
|
|
||||||
});
|
|
||||||
|
|
||||||
optionsPanel.add(new JLabel("PDB Parser:"));
|
|
||||||
|
|
||||||
if (isPdbFile) {
|
|
||||||
useMsDiaParser = false; // Use PDB Universal by default
|
|
||||||
if (PdbParser.onWindows) {
|
|
||||||
final GComboBox<String> combo =
|
|
||||||
new GComboBox<>(new String[] { "PDB Universal", "PDB MSDIA" });
|
|
||||||
combo.setSelectedIndex(0);
|
|
||||||
controlCombo.setEnabled(!useMsDiaParser);
|
|
||||||
combo.addActionListener(e -> {
|
|
||||||
useMsDiaParser = (combo.getSelectedIndex() == 1);
|
|
||||||
controlCombo.setEnabled(!useMsDiaParser);
|
|
||||||
if (useMsDiaParser) {
|
|
||||||
controlCombo.setSelectedItem(PdbApplicatorControl.ALL);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
optionsPanel.add(combo);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
useMsDiaParser = false;
|
|
||||||
JLabel label = new JLabel("PDB Universal");
|
|
||||||
//label.setForeground(Color.red); // set color to emphasize prototype status
|
|
||||||
optionsPanel.add(label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
useMsDiaParser = true; // XML file only supported by MsDia parser
|
|
||||||
return; // no interaction currently required
|
|
||||||
}
|
|
||||||
|
|
||||||
optionsPanel.add(new JLabel("Control:"));
|
|
||||||
optionsPanel.add(controlCombo);
|
|
||||||
|
|
||||||
panel.add(optionsPanel, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
addWorkPanel(panel);
|
|
||||||
|
|
||||||
addApplyButton();
|
|
||||||
addCancelButton();
|
|
||||||
|
|
||||||
setDefaultButton(applyButton);
|
|
||||||
setRememberSize(false);
|
|
||||||
|
|
||||||
DockingWindowManager.showDialog(parent, AskPdbOptionsDialog.this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void applyCallback() {
|
|
||||||
isCanceled = false;
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void cancelCallback() {
|
|
||||||
isCanceled = true;
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isCanceled() {
|
|
||||||
return isCanceled;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean useMsDiaParser() {
|
|
||||||
return useMsDiaParser;
|
|
||||||
}
|
|
||||||
|
|
||||||
PdbApplicatorControl getApplicatorControl() {
|
|
||||||
return control;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,230 +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 pdb;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.*;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Scanner;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
import docking.DialogComponentProvider;
|
|
||||||
import docking.DockingWindowManager;
|
|
||||||
import docking.widgets.dialogs.ObjectChooserDialog;
|
|
||||||
import docking.widgets.label.GDLabel;
|
|
||||||
import generic.jar.ResourceFile;
|
|
||||||
import generic.util.WindowUtilities;
|
|
||||||
import ghidra.framework.Application;
|
|
||||||
import ghidra.framework.preferences.Preferences;
|
|
||||||
import ghidra.util.MessageType;
|
|
||||||
|
|
||||||
public class AskPdbUrlDialog extends DialogComponentProvider {
|
|
||||||
|
|
||||||
private boolean isCanceled;
|
|
||||||
private JLabel label;
|
|
||||||
private JTextField textField;
|
|
||||||
private KeyListener keyListener;
|
|
||||||
private List<URLChoice> choices = null;
|
|
||||||
|
|
||||||
protected AskPdbUrlDialog(String dialogTitle, String message) {
|
|
||||||
this(null, dialogTitle, message, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AskPdbUrlDialog(String dialogTitle, String message, Object defaultValue) {
|
|
||||||
this(null, dialogTitle, message, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AskPdbUrlDialog(Component parent, String title, String message) {
|
|
||||||
this(parent, title, message, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AskPdbUrlDialog(final Component parent, String title, String message,
|
|
||||||
Object defaultValue) {
|
|
||||||
super(title, true, true, true, false);
|
|
||||||
|
|
||||||
// create the key listener all the text fields will use
|
|
||||||
keyListener = new KeyAdapter() {
|
|
||||||
@Override
|
|
||||||
public void keyPressed(KeyEvent e) {
|
|
||||||
int keyCode = e.getKeyCode();
|
|
||||||
if (keyCode == KeyEvent.VK_ENTER) {
|
|
||||||
okCallback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
JPanel panel = new JPanel(new BorderLayout(10, 10));
|
|
||||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
|
||||||
|
|
||||||
label = new GDLabel(message);
|
|
||||||
panel.add(label, BorderLayout.WEST);
|
|
||||||
|
|
||||||
textField = new JTextField(40);
|
|
||||||
textField.setName("JTextField");//for JUnits...
|
|
||||||
textField.addKeyListener(keyListener);
|
|
||||||
textField.setText(defaultValue == null ? "" : defaultValue.toString());
|
|
||||||
textField.selectAll();
|
|
||||||
panel.add(textField, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
if (urlFileAvailable()) {
|
|
||||||
JButton urlButton = new JButton("Choose from known URLs");
|
|
||||||
urlButton.addActionListener(e -> urlCallback());
|
|
||||||
|
|
||||||
panel.add(urlButton, BorderLayout.EAST);
|
|
||||||
}
|
|
||||||
|
|
||||||
addWorkPanel(panel);
|
|
||||||
|
|
||||||
addOKButton();
|
|
||||||
addCancelButton();
|
|
||||||
|
|
||||||
setDefaultButton(okButton);
|
|
||||||
setRememberSize(false);
|
|
||||||
|
|
||||||
DockingWindowManager.showDialog(parent, AskPdbUrlDialog.this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void addOKButton() {
|
|
||||||
okButton = new JButton("Download from URL");
|
|
||||||
okButton.setMnemonic('K');
|
|
||||||
okButton.setName("OK");
|
|
||||||
okButton.addActionListener(e -> okCallback());
|
|
||||||
addButton(okButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean urlFileAvailable() {
|
|
||||||
List<ResourceFile> urlFiles = Application.findFilesByExtensionInApplication(".pdburl");
|
|
||||||
|
|
||||||
if (urlFiles.size() == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
InputStream urlFileContents = null;
|
|
||||||
String currentLine;
|
|
||||||
choices = new ArrayList<>();
|
|
||||||
|
|
||||||
for (ResourceFile urlFile : urlFiles) {
|
|
||||||
urlFileContents = urlFile.getInputStream();
|
|
||||||
|
|
||||||
Scanner scanner = new Scanner(urlFileContents);
|
|
||||||
try {
|
|
||||||
while (scanner.hasNextLine()) {
|
|
||||||
|
|
||||||
currentLine = scanner.nextLine();
|
|
||||||
|
|
||||||
// Find first comma, split on that
|
|
||||||
int commaIndex = currentLine.indexOf(',');
|
|
||||||
|
|
||||||
if (commaIndex > -1) {
|
|
||||||
choices.add(new URLChoice(currentLine.substring(0, commaIndex).trim(),
|
|
||||||
currentLine.substring(commaIndex + 1).trim()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
scanner.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ioe) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveCurrentDimensions() {
|
|
||||||
Rectangle bounds = getBounds();
|
|
||||||
Window window = WindowUtilities.windowForComponent(getComponent());
|
|
||||||
|
|
||||||
if (window != null) {
|
|
||||||
Point location = window.getLocation();
|
|
||||||
bounds.x = location.x;
|
|
||||||
bounds.y = location.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuffer buffer = new StringBuffer();
|
|
||||||
buffer.append(bounds.x).append(":");
|
|
||||||
buffer.append(bounds.y).append(":");
|
|
||||||
buffer.append(bounds.width).append(":");
|
|
||||||
buffer.append(bounds.height).append(":");
|
|
||||||
Preferences.setProperty("Ask Dialog Bounds", buffer.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getValue() {
|
|
||||||
return textField.getText();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void okCallback() {
|
|
||||||
isCanceled = false;
|
|
||||||
if (textField.getText().length() == 0) {
|
|
||||||
setStatusText("Please enter a valid URL.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
saveCurrentDimensions();
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void cancelCallback() {
|
|
||||||
isCanceled = true;
|
|
||||||
saveCurrentDimensions();
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void urlCallback() {
|
|
||||||
|
|
||||||
ObjectChooserDialog<URLChoice> urlDialog = new ObjectChooserDialog<>("Choose a URL",
|
|
||||||
URLChoice.class, choices, "getNetwork", "getUrl");
|
|
||||||
|
|
||||||
DockingWindowManager activeInstance = DockingWindowManager.getActiveInstance();
|
|
||||||
activeInstance.showDialog(urlDialog);
|
|
||||||
|
|
||||||
URLChoice pickedUrl = urlDialog.getSelectedObject();
|
|
||||||
|
|
||||||
if (pickedUrl != null) {
|
|
||||||
textField.setText(pickedUrl.getUrl());
|
|
||||||
|
|
||||||
if (pickedUrl.getNetwork().equalsIgnoreCase("internet")) {
|
|
||||||
setStatusText(
|
|
||||||
"WARNING: Check your organization's security policy before downloading files from the internet.",
|
|
||||||
MessageType.ERROR);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setStatusText(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCanceled() {
|
|
||||||
return isCanceled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValueAsString() {
|
|
||||||
Object val = getValue();
|
|
||||||
if ("".equals(val)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return val != null ? val.toString() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,6 @@ import java.io.IOException;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
import docking.DockingWindowManager;
|
import docking.DockingWindowManager;
|
||||||
import docking.widgets.OptionDialog;
|
|
||||||
import docking.widgets.dialogs.MultiLineMessageDialog;
|
import docking.widgets.dialogs.MultiLineMessageDialog;
|
||||||
import ghidra.app.plugin.core.analysis.*;
|
import ghidra.app.plugin.core.analysis.*;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DuplicateIdException;
|
import ghidra.app.plugin.core.datamgr.archive.DuplicateIdException;
|
||||||
@@ -29,8 +28,6 @@ import ghidra.app.util.bin.format.pdb.PdbException;
|
|||||||
import ghidra.app.util.bin.format.pdb.PdbParser;
|
import ghidra.app.util.bin.format.pdb.PdbParser;
|
||||||
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
|
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.app.util.pdb.PdbLocator;
|
|
||||||
import ghidra.app.util.pdb.PdbProgramAttributes;
|
|
||||||
import ghidra.app.util.pdb.pdbapplicator.*;
|
import ghidra.app.util.pdb.pdbapplicator.*;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.program.model.address.AddressSetView;
|
import ghidra.program.model.address.AddressSetView;
|
||||||
@@ -45,19 +42,21 @@ class LoadPdbTask extends Task {
|
|||||||
private final Program program;
|
private final Program program;
|
||||||
private final boolean useMsDiaParser;
|
private final boolean useMsDiaParser;
|
||||||
private final PdbApplicatorControl control; // PDB Universal Parser only
|
private final PdbApplicatorControl control; // PDB Universal Parser only
|
||||||
|
private boolean debugLogging;
|
||||||
|
|
||||||
LoadPdbTask(Program program, File pdbFile, boolean useMsDiaParser, PdbApplicatorControl control,
|
LoadPdbTask(Program program, File pdbFile, boolean useMsDiaParser, PdbApplicatorControl control,
|
||||||
DataTypeManagerService service) {
|
boolean debugLogging, DataTypeManagerService service) {
|
||||||
super("Load PDB", true, false, true, true);
|
super("Load PDB", true, false, true, true);
|
||||||
this.program = program;
|
this.program = program;
|
||||||
this.pdbFile = pdbFile;
|
this.pdbFile = pdbFile;
|
||||||
this.useMsDiaParser = useMsDiaParser;
|
this.useMsDiaParser = useMsDiaParser;
|
||||||
this.control = control;
|
this.control = control;
|
||||||
|
this.debugLogging = debugLogging;
|
||||||
this.service = service;
|
this.service = service;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(final TaskMonitor monitor) {
|
public void run(TaskMonitor monitor) {
|
||||||
|
|
||||||
WrappingTaskMonitor wrappedMonitor = new WrappingTaskMonitor(monitor) {
|
WrappingTaskMonitor wrappedMonitor = new WrappingTaskMonitor(monitor) {
|
||||||
@Override
|
@Override
|
||||||
@@ -134,7 +133,7 @@ class LoadPdbTask extends Task {
|
|||||||
|
|
||||||
private boolean parseWithMsDiaParser(MessageLog log, TaskMonitor monitor)
|
private boolean parseWithMsDiaParser(MessageLog log, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException {
|
throws IOException, CancelledException {
|
||||||
PdbParser parser = new PdbParser(pdbFile, program, service, true, monitor);
|
PdbParser parser = new PdbParser(pdbFile, program, service, true, true, monitor);
|
||||||
try {
|
try {
|
||||||
parser.parse();
|
parser.parse();
|
||||||
parser.openDataTypeArchives();
|
parser.openDataTypeArchives();
|
||||||
@@ -147,44 +146,19 @@ class LoadPdbTask extends Task {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: OptionDialog will not display an empty line
|
|
||||||
private static final String BLANK_LINE = " \n";
|
|
||||||
|
|
||||||
private boolean parseWithNewParser(MessageLog log, TaskMonitor monitor)
|
private boolean parseWithNewParser(MessageLog log, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException {
|
throws IOException, CancelledException {
|
||||||
|
|
||||||
|
PdbLog.setEnabled(debugLogging);
|
||||||
|
|
||||||
PdbReaderOptions pdbReaderOptions = new PdbReaderOptions(); // use defaults
|
PdbReaderOptions pdbReaderOptions = new PdbReaderOptions(); // use defaults
|
||||||
|
|
||||||
PdbApplicatorOptions pdbApplicatorOptions = new PdbApplicatorOptions();
|
PdbApplicatorOptions pdbApplicatorOptions = new PdbApplicatorOptions();
|
||||||
|
|
||||||
pdbApplicatorOptions.setProcessingControl(control);
|
pdbApplicatorOptions.setProcessingControl(control);
|
||||||
|
|
||||||
PdbProgramAttributes programAttributes = new PdbProgramAttributes(program);
|
|
||||||
|
|
||||||
try (AbstractPdb pdb = ghidra.app.util.bin.format.pdb2.pdbreader.PdbParser.parse(
|
try (AbstractPdb pdb = ghidra.app.util.bin.format.pdb2.pdbreader.PdbParser.parse(
|
||||||
pdbFile.getAbsolutePath(), pdbReaderOptions, monitor)) {
|
pdbFile.getAbsolutePath(), pdbReaderOptions, monitor)) {
|
||||||
|
|
||||||
PdbIdentifiers identifiers = pdb.getIdentifiers();
|
|
||||||
if (!PdbLocator.verifyPdbSignature(programAttributes, identifiers)) {
|
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
builder.append("Selected PDB does not match program's PDB specification!\n");
|
|
||||||
builder.append(BLANK_LINE);
|
|
||||||
builder.append("Program's PDB specification:\n");
|
|
||||||
builder.append(PdbLocator.formatPdbIdentifiers(programAttributes));
|
|
||||||
builder.append(BLANK_LINE);
|
|
||||||
builder.append("Selected PDB file specification:\n");
|
|
||||||
builder.append(
|
|
||||||
PdbLocator.formatPdbIdentifiers(pdbFile.getAbsolutePath(), identifiers));
|
|
||||||
builder.append(BLANK_LINE);
|
|
||||||
builder.append("Do you wish to force load this PDB?");
|
|
||||||
|
|
||||||
if (OptionDialog.YES_OPTION != OptionDialog.showYesNoDialog(null,
|
|
||||||
"Confirm PDB Load", builder.toString())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
monitor.setMessage("PDB: Parsing " + pdbFile + "...");
|
monitor.setMessage("PDB: Parsing " + pdbFile + "...");
|
||||||
pdb.deserialize(monitor);
|
pdb.deserialize(monitor);
|
||||||
PdbApplicator applicator = new PdbApplicator(pdbFile.getAbsolutePath(), pdb);
|
PdbApplicator applicator = new PdbApplicator(pdbFile.getAbsolutePath(), pdb);
|
||||||
|
|||||||
@@ -1,31 +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 pdb;
|
|
||||||
|
|
||||||
import ghidra.app.util.bin.format.pdb.*;
|
|
||||||
import ghidra.framework.*;
|
|
||||||
|
|
||||||
public class PdbInitializer implements ModuleInitializer {
|
|
||||||
public void run() {
|
|
||||||
PluggableServiceRegistry.registerPluggableService(PdbFactory.class,
|
|
||||||
new GhidraPdbFactory());
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "PDB Support Module";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,30 +16,33 @@
|
|||||||
package pdb;
|
package pdb;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
|
|
||||||
import docking.action.MenuData;
|
import docking.action.builder.ActionBuilder;
|
||||||
|
import docking.tool.ToolConstants;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.filechooser.GhidraFileChooser;
|
|
||||||
import docking.widgets.filechooser.GhidraFileChooserMode;
|
|
||||||
import ghidra.app.CorePluginPackage;
|
import ghidra.app.CorePluginPackage;
|
||||||
import ghidra.app.context.ProgramActionContext;
|
import ghidra.app.context.ProgramActionContext;
|
||||||
import ghidra.app.context.ProgramContextAction;
|
|
||||||
import ghidra.app.plugin.PluginCategoryNames;
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||||
|
import ghidra.app.plugin.core.analysis.PdbAnalyzerCommon;
|
||||||
import ghidra.app.services.DataTypeManagerService;
|
import ghidra.app.services.DataTypeManagerService;
|
||||||
import ghidra.app.util.bin.format.pdb.PdbParser;
|
|
||||||
import ghidra.app.util.pdb.pdbapplicator.PdbApplicatorControl;
|
|
||||||
import ghidra.framework.plugintool.*;
|
import ghidra.framework.plugintool.*;
|
||||||
import ghidra.framework.plugintool.util.PluginStatus;
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
|
import ghidra.framework.preferences.Preferences;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.util.GhidraProgramUtilities;
|
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.filechooser.ExtensionFileFilter;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskBuilder;
|
import ghidra.util.task.*;
|
||||||
import ghidra.util.task.TaskLauncher;
|
import pdb.symbolserver.*;
|
||||||
|
import pdb.symbolserver.ui.ConfigPdbDialog;
|
||||||
|
import pdb.symbolserver.ui.LoadPdbDialog;
|
||||||
|
import pdb.symbolserver.ui.LoadPdbDialog.LoadPdbResults;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@PluginInfo(
|
@PluginInfo(
|
||||||
@@ -51,9 +54,14 @@ import ghidra.util.task.TaskLauncher;
|
|||||||
)
|
)
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
public class PdbPlugin extends Plugin {
|
public class PdbPlugin extends Plugin {
|
||||||
|
private static final String PDB_SYMBOL_SERVER_OPTIONS = "PdbSymbolServer";
|
||||||
|
private static final String SYMBOL_STORAGE_DIR_OPTION =
|
||||||
|
PDB_SYMBOL_SERVER_OPTIONS + ".Symbol_Storage_Directory";
|
||||||
|
private static final String SYMBOL_SEARCH_PATH_OPTION =
|
||||||
|
PDB_SYMBOL_SERVER_OPTIONS + ".Symbol_Search_Path";
|
||||||
|
|
||||||
private ProgramContextAction loadPdbAction;
|
// the name of the help directory under src/main/help/help/topics
|
||||||
private GhidraFileChooser pdbChooser;
|
public static final String PDB_PLUGIN_HELP_TOPIC = "Pdb";
|
||||||
|
|
||||||
public PdbPlugin(PluginTool tool) {
|
public PdbPlugin(PluginTool tool) {
|
||||||
super(tool);
|
super(tool);
|
||||||
@@ -62,33 +70,34 @@ public class PdbPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void createActions() {
|
private void createActions() {
|
||||||
loadPdbAction = new ProgramContextAction("Load PDB File", this.getName()) {
|
new ActionBuilder("Load PDB File", this.getName())
|
||||||
|
.supportsDefaultToolContext(true)
|
||||||
|
.withContext(ProgramActionContext.class)
|
||||||
|
.validContextWhen(pac -> pac.getProgram() != null &&
|
||||||
|
PdbAnalyzerCommon.canAnalyzeProgram(pac.getProgram()))
|
||||||
|
.menuPath(ToolConstants.MENU_FILE, "Load PDB File...")
|
||||||
|
.menuGroup("Import PDB", "3")
|
||||||
|
.helpLocation(new HelpLocation(PDB_PLUGIN_HELP_TOPIC, "Load PDB File"))
|
||||||
|
.onAction(pac -> loadPDB(pac))
|
||||||
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
@Override
|
new ActionBuilder("Symbol Server Config", this.getName())
|
||||||
public boolean isEnabledForContext(ProgramActionContext context) {
|
.menuPath(ToolConstants.MENU_EDIT, "Symbol Server Config")
|
||||||
return context.getProgram() != null;
|
.menuGroup(ToolConstants.TOOL_OPTIONS_MENU_GROUP)
|
||||||
}
|
.helpLocation(new HelpLocation(PDB_PLUGIN_HELP_TOPIC, "Symbol Server Config"))
|
||||||
|
.onAction(ac -> configPDB())
|
||||||
@Override
|
.buildAndInstall(tool);
|
||||||
protected void actionPerformed(ProgramActionContext programContext) {
|
|
||||||
loadPDB();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
MenuData menuData =
|
|
||||||
new MenuData(new String[] { "&File", "Load PDB File..." }, null, "Import PDB");
|
|
||||||
menuData.setMenuSubGroup("3"); // below the major actions in the "Import/Export" group
|
|
||||||
loadPdbAction.setMenuBarData(menuData);
|
|
||||||
|
|
||||||
loadPdbAction.setEnabled(false);
|
|
||||||
loadPdbAction.setHelpLocation(new HelpLocation("ImporterPlugin", loadPdbAction.getName()));
|
|
||||||
tool.addAction(loadPdbAction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadPDB() {
|
private void configPDB() {
|
||||||
Program program = GhidraProgramUtilities.getCurrentProgram(tool);
|
ConfigPdbDialog.showSymbolServerConfig();
|
||||||
AutoAnalysisManager aam = AutoAnalysisManager.getAnalysisManager(program);
|
}
|
||||||
if (aam.isAnalyzing()) {
|
|
||||||
|
private void loadPDB(ProgramActionContext pac) {
|
||||||
|
Program program = pac.getProgram();
|
||||||
|
AutoAnalysisManager currentAutoAnalysisManager =
|
||||||
|
AutoAnalysisManager.getAnalysisManager(program);
|
||||||
|
if (currentAutoAnalysisManager.isAnalyzing()) {
|
||||||
Msg.showWarn(getClass(), null, "Load PDB",
|
Msg.showWarn(getClass(), null, "Load PDB",
|
||||||
"Unable to load PDB file while analysis is running.");
|
"Unable to load PDB file while analysis is running.");
|
||||||
return;
|
return;
|
||||||
@@ -110,26 +119,17 @@ public class PdbPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
File pdb = getPdbFile(program);
|
LoadPdbResults loadPdbResults = LoadPdbDialog.choosePdbForProgram(program);
|
||||||
if (pdb == null) {
|
if (loadPdbResults == null) {
|
||||||
tool.setStatusInfo("Loading PDB was cancelled.");
|
tool.setStatusInfo("Loading PDB was cancelled.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isPdbFile = pdb.getName().toLowerCase().endsWith(".pdb");
|
|
||||||
|
|
||||||
AskPdbOptionsDialog optionsDialog = new AskPdbOptionsDialog(null, isPdbFile);
|
|
||||||
if (optionsDialog.isCanceled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean useMsDiaParser = optionsDialog.useMsDiaParser();
|
|
||||||
PdbApplicatorControl control = optionsDialog.getApplicatorControl();
|
|
||||||
|
|
||||||
tool.setStatusInfo("");
|
tool.setStatusInfo("");
|
||||||
|
|
||||||
DataTypeManagerService service = tool.getService(DataTypeManagerService.class);
|
DataTypeManagerService dataTypeManagerService =
|
||||||
if (service == null) {
|
tool.getService(DataTypeManagerService.class);
|
||||||
|
if (dataTypeManagerService == null) {
|
||||||
Msg.showWarn(getClass(), null, "Load PDB",
|
Msg.showWarn(getClass(), null, "Load PDB",
|
||||||
"Unable to locate DataTypeService in the current tool.");
|
"Unable to locate DataTypeService in the current tool.");
|
||||||
return;
|
return;
|
||||||
@@ -138,34 +138,122 @@ public class PdbPlugin extends Plugin {
|
|||||||
// note: We intentionally use a 0-delay here. Our underlying task may show modal
|
// note: We intentionally use a 0-delay here. Our underlying task may show modal
|
||||||
// dialog prompts. We want the task progress dialog to be showing before any
|
// dialog prompts. We want the task progress dialog to be showing before any
|
||||||
// prompts appear.
|
// prompts appear.
|
||||||
|
LoadPdbTask loadPdbTask = new LoadPdbTask(program, loadPdbResults.pdbFile,
|
||||||
LoadPdbTask task = new LoadPdbTask(program, pdb, useMsDiaParser, control, service);
|
loadPdbResults.useMsDiaParser, loadPdbResults.control,
|
||||||
TaskBuilder.withTask(task)
|
loadPdbResults.debugLogging, dataTypeManagerService);
|
||||||
|
TaskBuilder.withTask(loadPdbTask)
|
||||||
.setStatusTextAlignment(SwingConstants.LEADING)
|
.setStatusTextAlignment(SwingConstants.LEADING)
|
||||||
.setLaunchDelay(0);
|
.setLaunchDelay(0);
|
||||||
new TaskLauncher(task, null, 0);
|
new TaskLauncher(loadPdbTask, null, 0);
|
||||||
}
|
}
|
||||||
catch (Exception pe) {
|
catch (Exception pe) {
|
||||||
Msg.showError(getClass(), null, "Error Loading PDB", pe.getMessage(), pe);
|
Msg.showError(getClass(), null, "Error Loading PDB", pe.getMessage(), pe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private File getPdbFile(Program program) {
|
//-------------------------------------------------------------------------------------------------------
|
||||||
File pdbFile = PdbParser.findPDB(program);
|
|
||||||
if (pdbChooser == null) {
|
|
||||||
pdbChooser = new GhidraFileChooser(tool.getToolFrame());
|
|
||||||
pdbChooser.setTitle("Select PDB file to load:");
|
|
||||||
pdbChooser.setApproveButtonText("Select PDB");
|
|
||||||
pdbChooser.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY);
|
|
||||||
pdbChooser.setFileFilter(new ExtensionFileFilter(new String[] { "pdb", "xml" },
|
|
||||||
"Program Database Files and PDB XML Representations"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pdbFile != null) {
|
/**
|
||||||
pdbChooser.setSelectedFile(pdbFile);
|
* Searches the currently configured symbol server paths for a Pdb symbol file.
|
||||||
}
|
*
|
||||||
|
* @param program the program associated with the requested pdb file
|
||||||
|
* @param findOptions options that control how to search for the symbol file
|
||||||
|
* @param monitor a {@link TaskMonitor} that allows the user to cancel
|
||||||
|
* @return a File that points to the found Pdb symbol file, or null if no file was found
|
||||||
|
*/
|
||||||
|
public static File findPdb(Program program, Set<FindOption> findOptions, TaskMonitor monitor) {
|
||||||
|
|
||||||
File selectedFile = pdbChooser.getSelectedFile();
|
try {
|
||||||
return selectedFile;
|
SymbolFileInfo symbolFileInfo = SymbolFileInfo.fromMetadata(program.getMetadata());
|
||||||
|
if (symbolFileInfo == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// make a copy and add in the ONLY_FIRST_RESULT option
|
||||||
|
findOptions = findOptions.isEmpty() ? EnumSet.noneOf(FindOption.class)
|
||||||
|
: EnumSet.copyOf(findOptions);
|
||||||
|
findOptions.add(FindOption.ONLY_FIRST_RESULT);
|
||||||
|
|
||||||
|
SymbolServerInstanceCreatorContext temporarySymbolServerInstanceCreatorContext =
|
||||||
|
SymbolServerInstanceCreatorRegistry.getInstance().getContext(program);
|
||||||
|
|
||||||
|
SymbolServerService temporarySymbolServerService =
|
||||||
|
getSymbolServerService(temporarySymbolServerInstanceCreatorContext);
|
||||||
|
|
||||||
|
List<SymbolFileLocation> results =
|
||||||
|
temporarySymbolServerService.find(symbolFileInfo, findOptions, monitor);
|
||||||
|
if (!results.isEmpty()) {
|
||||||
|
return temporarySymbolServerService.getSymbolFile(results.get(0), monitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (CancelledException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
Msg.error(PdbPlugin.class, "Error getting symbol file", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new instance of a {@link SymbolServerService} configured with values from the
|
||||||
|
* application's preferences, defaulting to a minimal instance if there is no config.
|
||||||
|
*
|
||||||
|
* @param symbolServerInstanceCreatorContext an object that provides the necessary context to
|
||||||
|
* the SymbolServerInstanceCreatorRegistry to create the SymbolServers that are listed in the
|
||||||
|
* config values
|
||||||
|
* @return a new {@link SymbolServerService} instance, never null
|
||||||
|
*/
|
||||||
|
public static SymbolServerService getSymbolServerService(
|
||||||
|
SymbolServerInstanceCreatorContext symbolServerInstanceCreatorContext) {
|
||||||
|
SymbolServer temporarySymbolServer =
|
||||||
|
symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry()
|
||||||
|
.newSymbolServer(Preferences.getProperty(SYMBOL_STORAGE_DIR_OPTION, "", true),
|
||||||
|
symbolServerInstanceCreatorContext);
|
||||||
|
SymbolStore symbolStore =
|
||||||
|
(temporarySymbolServer instanceof SymbolStore) ? (SymbolStore) temporarySymbolServer
|
||||||
|
: new SameDirSymbolStore(symbolServerInstanceCreatorContext.getRootDir());
|
||||||
|
List<SymbolServer> symbolServers =
|
||||||
|
symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry()
|
||||||
|
.createSymbolServersFromPathList(getSymbolSearchPaths(),
|
||||||
|
symbolServerInstanceCreatorContext);
|
||||||
|
return new SymbolServerService(symbolStore, symbolServers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persists the {@link SymbolStore} and {@link SymbolServer}s contained in the
|
||||||
|
* {@link SymbolServerService}.
|
||||||
|
*
|
||||||
|
* @param symbolServerService {@link SymbolServerService} to save, or null if clear p
|
||||||
|
* reference values
|
||||||
|
*/
|
||||||
|
public static void saveSymbolServerServiceConfig(SymbolServerService symbolServerService) {
|
||||||
|
if (symbolServerService != null) {
|
||||||
|
Preferences.setProperty(SYMBOL_STORAGE_DIR_OPTION,
|
||||||
|
symbolServerService.getSymbolStore().getName());
|
||||||
|
|
||||||
|
String path = symbolServerService.getSymbolServers()
|
||||||
|
.stream()
|
||||||
|
.map(SymbolServer::getName)
|
||||||
|
.collect(Collectors.joining(";"));
|
||||||
|
Preferences.setProperty(SYMBOL_SEARCH_PATH_OPTION, path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Preferences.setProperty(SYMBOL_STORAGE_DIR_OPTION, null);
|
||||||
|
Preferences.setProperty(SYMBOL_SEARCH_PATH_OPTION, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> getSymbolSearchPaths() {
|
||||||
|
String searchPathStr = Preferences.getProperty(SYMBOL_SEARCH_PATH_OPTION, "", true);
|
||||||
|
|
||||||
|
String[] pathParts = searchPathStr.split(";");
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
for (String part : pathParts) {
|
||||||
|
part = part.trim();
|
||||||
|
if (!part.isEmpty()) {
|
||||||
|
result.add(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||