GP-42 Initial implementation of Pdb symbol store / symbol servers

This commit is contained in:
dev747368
2020-07-21 18:34:35 -04:00
parent df72f24b58
commit 425667e640
93 changed files with 7715 additions and 3874 deletions
+4
View File
@@ -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>&lt;ghidra install root&gt;/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>&nbsp;&nbsp;&nbsp;&nbsp;createPdbXmlFiles.bat C:\Symbols\samplePdb.pdb</CODE>
<br>
<CODE>&nbsp;&nbsp;&nbsp;&nbsp;createPdbXmlFiles.bat C:\Symbols</CODE>
<br>
</LI>
<br>
<LI>Run the included <I>pdb.exe</I> executable (found in the <I>&lt;ghidra install
root&gt;/Ghidra/Features/PDB/os/win64</I> directory) and redirect (save) its output to an
XML file as shown below:
<br><br>
<CODE>&nbsp;&nbsp;&nbsp;&nbsp;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 &quot;Unable to locate the DIA SDK,&quot;
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();
+15 -6
View File
@@ -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 &rarr; 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 &rarr; 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 &rarr; 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">&nbsp;(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">&nbsp;(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">&nbsp;(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">&nbsp;(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">&nbsp;(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 &rarr; <a href="LoadPDBNew.html#Load_PDB_File">Load PDB File</a></B></LI>
<LI>PDB Analyzer via <B>Analysis &rarr;
<A HREF="help/topics/AutoAnalysisPlugin/AutoAnalysis.htm#Auto_Analyze">Auto Analyze</A></B> or
<B>Analysis &rarr; <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>&lt;ghidra install root&gt;/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>&nbsp;&nbsp;&nbsp;&nbsp;createPdbXmlFiles.bat C:\Symbols\samplePdb.pdb</CODE>
<br>
<CODE>&nbsp;&nbsp;&nbsp;&nbsp;createPdbXmlFiles.bat C:\Symbols</CODE>
<br>
</LI>
<br>
<LI>Run the included <I>pdb.exe</I> executable (found in the <I>&lt;ghidra install
root&gt;/Ghidra/Features/PDB/os/win64</I> directory) and redirect (save) its output to an
XML file as shown below:
<br><br>
<CODE>&nbsp;&nbsp;&nbsp;&nbsp;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 &quot;Unable to locate the DIA SDK,&quot;
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\&lt;pdbfilename&gt;\&lt;GUID&gt;</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>&lt;download location&gt;/&lt;pdbfilename&gt;/
&lt;GUID&gt;</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>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 979 B

Binary file not shown.

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;
} }
} }

Some files were not shown because too many files have changed in this diff Show More