mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GT-3235 Correct parsing of *.ord MS ordinal symbol map files
This commit is contained in:
parent
8eef7ddc0a
commit
cf32f48605
8 changed files with 286 additions and 148 deletions
|
@ -35,6 +35,7 @@ data/parserprofiles/objc_mac_carbon.prf||GHIDRA||reviewed||END|
|
||||||
data/parserprofiles/vs12Local.prf||GHIDRA||||END|
|
data/parserprofiles/vs12Local.prf||GHIDRA||||END|
|
||||||
data/pcodetest/EmuTesting.gdt||GHIDRA||||END|
|
data/pcodetest/EmuTesting.gdt||GHIDRA||||END|
|
||||||
data/stringngrams/StringModel.sng||GHIDRA||reviewed||END|
|
data/stringngrams/StringModel.sng||GHIDRA||reviewed||END|
|
||||||
|
data/symbols/README.txt||GHIDRA||||END|
|
||||||
data/symbols/win32/kernel32.hints||GHIDRA||||END|
|
data/symbols/win32/kernel32.hints||GHIDRA||||END|
|
||||||
data/symbols/win32/mfc100.exports||GHIDRA||||END|
|
data/symbols/win32/mfc100.exports||GHIDRA||||END|
|
||||||
data/symbols/win32/mfc100u.exports||GHIDRA||||END|
|
data/symbols/win32/mfc100u.exports||GHIDRA||||END|
|
||||||
|
@ -1186,3 +1187,4 @@ src/test.slow/resources/filterTestDirList.txt||GHIDRA||||END|
|
||||||
src/test.slow/resources/ghidra/app/plugin/core/datamgr/TestDataType.txt||GHIDRA||||END|
|
src/test.slow/resources/ghidra/app/plugin/core/datamgr/TestDataType.txt||GHIDRA||||END|
|
||||||
src/test.slow/resources/ghidra/app/script/GhidraScriptAsk.properties||GHIDRA||||END|
|
src/test.slow/resources/ghidra/app/script/GhidraScriptAsk.properties||GHIDRA||||END|
|
||||||
src/test/resources/defaultTools/TestCodeBrowser.tool||GHIDRA||||END|
|
src/test/resources/defaultTools/TestCodeBrowser.tool||GHIDRA||||END|
|
||||||
|
src/test/resources/ghidra/app/util/opinion/test.ord||GHIDRA||||END|
|
||||||
|
|
26
Ghidra/Features/Base/data/symbols/README.txt
Normal file
26
Ghidra/Features/Base/data/symbols/README.txt
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
The use of library symbol table information is currently limited to Windows
|
||||||
|
x86 (32-bit and 64-bit). Currently, the use of these library symbol files are
|
||||||
|
best suited for library names which incorporate an API version (e.g., mfc100.dll)
|
||||||
|
since the .exports and .ord files will utilize this same name for identification.
|
||||||
|
|
||||||
|
When the PE loader detects a library dependency (i.e., DLL) and the library is
|
||||||
|
not loaded, the subdirectories win32 or win64 will be searched for a corresponding
|
||||||
|
.exports or .ord file to provide ordinal-to-symbol name mappings. User generated
|
||||||
|
.exports and .ord files may also be stored/read from the user's 'symbols' resource
|
||||||
|
directory contained within the user's version-specific .ghidra directory (e.g.,
|
||||||
|
%HOMEPATH%/.ghidra/.ghidra-9.0/symbols/win64).
|
||||||
|
|
||||||
|
The .exports files can be generated from a loaded library program and can also
|
||||||
|
provide function stack purge and function comment information. The Ghidra script
|
||||||
|
'CreateExportFileForDLL' may be used to generate a .exports file for the current
|
||||||
|
program which will be stored within the user's 'symbols' resource directory
|
||||||
|
mentioned above.
|
||||||
|
|
||||||
|
Many library functions are referenced through the use of ordinals and may be
|
||||||
|
missing real symbol names. In such cases it may be desirable to rely on ordinal
|
||||||
|
to symbol name map .ord files which may be generated with the following command:
|
||||||
|
|
||||||
|
DUMPBIN /EXPORTS <DLL-FILEPATH>
|
||||||
|
|
||||||
|
The DUMPBIN utility is provided with Microsoft Visual Studio. The resulting output
|
||||||
|
should be stored within a .ord file using the DLL name (e.g., mfc100.ord).
|
|
@ -29,14 +29,22 @@
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
import ghidra.app.util.opinion.LibraryLookupTable;
|
import ghidra.app.util.opinion.LibraryLookupTable;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.SystemUtilities;
|
||||||
|
|
||||||
public class CreateExportFileForDLL extends GhidraScript {
|
public class CreateExportFileForDLL extends GhidraScript {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() throws Exception {
|
public void run() throws Exception {
|
||||||
|
|
||||||
|
if (currentProgram == null) {
|
||||||
|
Msg.error(this, "Script requires active program");
|
||||||
|
}
|
||||||
|
|
||||||
// push this .dll into the location of the system .exports files.
|
// push this .dll into the location of the system .exports files.
|
||||||
// must have write permissions.
|
// must have write permissions.
|
||||||
ResourceFile file = LibraryLookupTable.createFile(currentProgram, false, true, monitor);
|
ResourceFile file = LibraryLookupTable.createFile(currentProgram, false,
|
||||||
|
SystemUtilities.isInDevelopmentMode(), monitor);
|
||||||
|
|
||||||
println("Created .exports file : " + file.getAbsolutePath());
|
println("Created .exports file : " + file.getAbsolutePath());
|
||||||
}
|
}
|
|
@ -139,7 +139,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
|
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||||
|
Program program) {
|
||||||
|
|
||||||
if (options != null) {
|
if (options != null) {
|
||||||
for (Option option : options) {
|
for (Option option : options) {
|
||||||
|
@ -336,8 +337,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
|
|
||||||
Address imageBaseAddr = language.getAddressFactory().getDefaultAddressSpace().getAddress(
|
Address imageBaseAddr = language.getAddressFactory().getDefaultAddressSpace().getAddress(
|
||||||
loadSpec.getDesiredImageBase());
|
loadSpec.getDesiredImageBase());
|
||||||
Program program = createProgram(provider, programName, imageBaseAddr, getName(),
|
Program program = createProgram(provider, programName, imageBaseAddr, getName(), language,
|
||||||
language, compilerSpec, consumer);
|
compilerSpec, consumer);
|
||||||
|
|
||||||
int transactionID = program.startTransaction("importing");
|
int transactionID = program.startTransaction("importing");
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
|
@ -623,8 +624,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
*/
|
*/
|
||||||
protected boolean importLibrary(String libName, DomainFolder libFolder, File libFile,
|
protected boolean importLibrary(String libName, DomainFolder libFolder, File libFile,
|
||||||
LoadSpec loadSpec, List<Option> options, MessageLog log, Object consumer,
|
LoadSpec loadSpec, List<Option> options, MessageLog log, Object consumer,
|
||||||
Set<String> unprocessedLibs, List<Program> programList,
|
Set<String> unprocessedLibs, List<Program> programList, TaskMonitor monitor)
|
||||||
TaskMonitor monitor) throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
|
|
||||||
if (!libFile.isFile()) {
|
if (!libFile.isFile()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -658,8 +659,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
protected boolean importLibrary(String libName, DomainFolder libFolder, File libFile,
|
protected boolean importLibrary(String libName, DomainFolder libFolder, File libFile,
|
||||||
ByteProvider provider, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
ByteProvider provider, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
||||||
Object consumer, Set<String> unprocessedLibs, List<Program> programList,
|
Object consumer, Set<String> unprocessedLibs, List<Program> programList,
|
||||||
TaskMonitor monitor)
|
TaskMonitor monitor) throws CancelledException, IOException {
|
||||||
throws CancelledException, IOException {
|
|
||||||
|
|
||||||
Program lib = null;
|
Program lib = null;
|
||||||
int size = loadSpec.getLanguageCompilerSpec().getLanguageDescription().getSize();
|
int size = loadSpec.getLanguageCompilerSpec().getLanguageDescription().getSize();
|
||||||
|
@ -670,6 +670,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!isLoadLibraries(options)) {
|
if (!isLoadLibraries(options)) {
|
||||||
|
// TODO: LibraryLookupTable support currently assumes Windows for x86 (32 or 64 bit).
|
||||||
|
// Need to investigate adding support for other architectures
|
||||||
if (LibraryLookupTable.hasFileAndPathAndTimeStampMatch(libFile, size)) {
|
if (LibraryLookupTable.hasFileAndPathAndTimeStampMatch(libFile, size)) {
|
||||||
return true;// no need to really import it
|
return true;// no need to really import it
|
||||||
}
|
}
|
||||||
|
@ -710,9 +712,11 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @param monitor the task monitor
|
* @param monitor the task monitor
|
||||||
* @param size the language size
|
* @param size the language size
|
||||||
* @param program the loaded library program
|
* @param program the loaded library program
|
||||||
|
* @throws CancelledException thrown is task cancelled
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
protected void createExportsFile(String libName, File libFile, MessageLog log,
|
protected void createExportsFile(String libName, File libFile, MessageLog log,
|
||||||
TaskMonitor monitor, int size, Program program) {
|
TaskMonitor monitor, int size, Program program) throws CancelledException {
|
||||||
|
|
||||||
if (!LibraryLookupTable.libraryLookupTableFileExists(libName, size) ||
|
if (!LibraryLookupTable.libraryLookupTableFileExists(libName, size) ||
|
||||||
!LibraryLookupTable.hasFileAndPathAndTimeStampMatch(libFile, size)) {
|
!LibraryLookupTable.hasFileAndPathAndTimeStampMatch(libFile, size)) {
|
||||||
|
@ -728,8 +732,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected LoadSpec getLoadSpec(LoadSpec loadSpec, ByteProvider provider)
|
protected LoadSpec getLoadSpec(LoadSpec loadSpec, ByteProvider provider) throws IOException {
|
||||||
throws IOException {
|
|
||||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||||
Collection<LoadSpec> loadSpecs = findSupportedLoadSpecs(provider);
|
Collection<LoadSpec> loadSpecs = findSupportedLoadSpecs(provider);
|
||||||
if (loadSpecs != null) { // shouldn't be null, but protect against rogue loaders
|
if (loadSpecs != null) { // shouldn't be null, but protect against rogue loaders
|
||||||
|
@ -758,9 +761,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
// Check based on the original program name, not on the name I gave this program
|
// Check based on the original program name, not on the name I gave this program
|
||||||
int size = program.getLanguage().getLanguageDescription().getSize();
|
int size = program.getLanguage().getLanguageDescription().getSize();
|
||||||
|
|
||||||
LibrarySymbolTable symtab =
|
LibrarySymbolTable symtab = LibraryLookupTable.getSymbolTable(
|
||||||
LibraryLookupTable.getSymbolTable(new File(program.getExecutablePath()).getName(),
|
new File(program.getExecutablePath()).getName(), size);
|
||||||
size);
|
|
||||||
if (symtab == null) {
|
if (symtab == null) {
|
||||||
// now try based on the name given to the program
|
// now try based on the name given to the program
|
||||||
symtab = LibraryLookupTable.getSymbolTable(program.getName(), size);
|
symtab = LibraryLookupTable.getSymbolTable(program.getName(), size);
|
||||||
|
|
|
@ -25,9 +25,14 @@ import ghidra.framework.options.Options;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.datastruct.FixedSizeHashMap;
|
import ghidra.util.datastruct.FixedSizeHashMap;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class LibraryLookupTable {
|
public class LibraryLookupTable {
|
||||||
|
|
||||||
|
static final String EXPORTS_FILE_EXTENSION = ".exports";
|
||||||
|
static final String ORDINAL_MAPPING_FILE_EXTENSION = ".ord";
|
||||||
|
|
||||||
private static final int MAX_CACHE_ITEMS = 10;
|
private static final int MAX_CACHE_ITEMS = 10;
|
||||||
|
|
||||||
private static Map<String, LibrarySymbolTable> cacheMap =
|
private static Map<String, LibrarySymbolTable> cacheMap =
|
||||||
|
@ -111,12 +116,12 @@ public class LibraryLookupTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized static ResourceFile createFile(Program program, boolean overwrite,
|
public synchronized static ResourceFile createFile(Program program, boolean overwrite,
|
||||||
TaskMonitor monitor) throws IOException {
|
TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
return createFile(program, overwrite, false, monitor);
|
return createFile(program, overwrite, false, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized static ResourceFile createFile(Program program, boolean overwrite,
|
public synchronized static ResourceFile createFile(Program program, boolean overwrite,
|
||||||
boolean inSystem, TaskMonitor monitor) throws IOException {
|
boolean inSystem, TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
ResourceFile file = null;
|
ResourceFile file = null;
|
||||||
int size = program.getLanguage().getLanguageDescription().getSize();
|
int size = program.getLanguage().getLanguageDescription().getSize();
|
||||||
|
|
||||||
|
@ -152,7 +157,8 @@ public class LibraryLookupTable {
|
||||||
symTab.applyOrdinalFile(existingDefFile, false);
|
symTab.applyOrdinalFile(existingDefFile, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!monitor.isCancelled()) {
|
monitor.checkCanceled();
|
||||||
|
|
||||||
File f = file.getFile(true);
|
File f = file.getFile(true);
|
||||||
if (f == null) {
|
if (f == null) {
|
||||||
Msg.warn(LibraryLookupTable.class, "Can't write to installation directory");
|
Msg.warn(LibraryLookupTable.class, "Can't write to installation directory");
|
||||||
|
@ -160,14 +166,26 @@ public class LibraryLookupTable {
|
||||||
else {
|
else {
|
||||||
symTab.write(f, new File(program.getExecutablePath()), version);
|
symTab.write(f, new File(program.getExecutablePath()), version);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the symbol table associated with the DLL name.
|
* Get the symbol table associated with the DLL name. If not previously
|
||||||
*
|
* generated for the given dllName, it will be constructed from a .exports
|
||||||
|
* file found within the 'symbols' resource area. If a .exports file
|
||||||
|
* is not found a similarly named .ord file will be used if found. The
|
||||||
|
* .exports file is a Ghidra XML file formatted file, while the .ord file
|
||||||
|
* is produced with the Visual Studio DUMPBIN /EXPORTS command. The default
|
||||||
|
* resource area is located within the directory
|
||||||
|
* <pre>
|
||||||
|
* Ghidra/Features/Base/data/symbols/[win32|win64]
|
||||||
|
* </pre>
|
||||||
|
* Alternatively, a user specific resource directory may be used which
|
||||||
|
* is located at
|
||||||
|
* <pre>
|
||||||
|
* <USER_HOME>/.ghidra/<.ghidraVersion>/symbols/[win32|win64]
|
||||||
|
* </pre>
|
||||||
* The cacheMap is a static cache which always returns the same
|
* The cacheMap is a static cache which always returns the same
|
||||||
* instance for a given DLL name.
|
* instance for a given DLL name.
|
||||||
*
|
*
|
||||||
|
@ -212,19 +230,19 @@ public class LibraryLookupTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized static ResourceFile getExistingExportsFile(String dllName, int size) {
|
synchronized static ResourceFile getExistingExportsFile(String dllName, int size) {
|
||||||
return getExistingExtensionedFile(dllName, ".exports", size);
|
return getExistingExtensionedFile(dllName, EXPORTS_FILE_EXTENSION, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized static ResourceFile getNewExportsFile(String dllName, int size) {
|
synchronized static ResourceFile getNewExportsFile(String dllName, int size) {
|
||||||
return getNewExtensionedFile(dllName, ".exports", size);
|
return getNewExtensionedFile(dllName, EXPORTS_FILE_EXTENSION, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResourceFile getNewSystemExportsFile(String name, int size) {
|
private static ResourceFile getNewSystemExportsFile(String name, int size) {
|
||||||
return getNewSystemExtensionedFile(name, ".exports", size);
|
return getNewSystemExtensionedFile(name, EXPORTS_FILE_EXTENSION, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized static ResourceFile getExistingOrdinalFile(String dllName, int size) {
|
synchronized static ResourceFile getExistingOrdinalFile(String dllName, int size) {
|
||||||
return getExistingExtensionedFile(dllName, ".ord", size);
|
return getExistingExtensionedFile(dllName, ORDINAL_MAPPING_FILE_EXTENSION, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized static boolean hasFileAndPathAndTimeStampMatch(File libraryFile, int size) {
|
synchronized static boolean hasFileAndPathAndTimeStampMatch(File libraryFile, int size) {
|
||||||
|
|
|
@ -33,6 +33,7 @@ import ghidra.program.model.mem.MemoryBlock;
|
||||||
import ghidra.program.model.scalar.Scalar;
|
import ghidra.program.model.scalar.Scalar;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import ghidra.util.xml.GenericXMLOutputter;
|
import ghidra.util.xml.GenericXMLOutputter;
|
||||||
import ghidra.util.xml.XmlUtilities;
|
import ghidra.util.xml.XmlUtilities;
|
||||||
|
@ -62,6 +63,11 @@ class LibrarySymbolTable {
|
||||||
private HashMap<Integer, LibraryExportedSymbol> ordMap = new HashMap<>();
|
private HashMap<Integer, LibraryExportedSymbol> ordMap = new HashMap<>();
|
||||||
private Set<String> forwards = new HashSet<>();
|
private Set<String> forwards = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an empty library symbol table
|
||||||
|
* @param tableName symbol table name (generally same as associated library name)
|
||||||
|
* @param size the architecture size of the DLL (e.g., 32 or 64).
|
||||||
|
*/
|
||||||
LibrarySymbolTable(String tableName, int size) {
|
LibrarySymbolTable(String tableName, int size) {
|
||||||
this.tableName = tableName.toLowerCase();
|
this.tableName = tableName.toLowerCase();
|
||||||
this.size = size;
|
this.size = size;
|
||||||
|
@ -69,11 +75,24 @@ class LibrarySymbolTable {
|
||||||
tempPurge = size <= 32 ? -1 : 0; // assume 0 purge for 64-bit
|
tempPurge = size <= 32 ? -1 : 0; // assume 0 purge for 64-bit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct library symbol table from an existing symbol exports file in XML format
|
||||||
|
* @param libraryFile existing symbol exports file
|
||||||
|
* @param size the architecture size of the DLL (e.g., 32 or 64).
|
||||||
|
* @throws IOException thrown if file IO error occurs
|
||||||
|
*/
|
||||||
LibrarySymbolTable(ResourceFile libraryFile, int size) throws IOException {
|
LibrarySymbolTable(ResourceFile libraryFile, int size) throws IOException {
|
||||||
read(libraryFile, size);
|
read(libraryFile, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
LibrarySymbolTable(Program library, TaskMonitor monitor) {
|
/**
|
||||||
|
* Construct a library symbol table based upon a specified library in the
|
||||||
|
* form of a {@link Program} object.
|
||||||
|
* @param library library program
|
||||||
|
* @param monitor task monitor
|
||||||
|
* @throws CancelledException thrown if task cancelled
|
||||||
|
*/
|
||||||
|
LibrarySymbolTable(Program library, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
tableName = new File(library.getExecutablePath()).getName().toLowerCase();
|
tableName = new File(library.getExecutablePath()).getName().toLowerCase();
|
||||||
size = library.getLanguage().getLanguageDescription().getSize();
|
size = library.getLanguage().getLanguageDescription().getSize();
|
||||||
|
@ -85,7 +104,8 @@ class LibrarySymbolTable {
|
||||||
// go through all the symbols looking for Ordinal_#
|
// go through all the symbols looking for Ordinal_#
|
||||||
// get the number and name for the symbol
|
// get the number and name for the symbol
|
||||||
SymbolIterator iter = symTab.getSymbolIterator(SymbolUtilities.ORDINAL_PREFIX + "*", true);
|
SymbolIterator iter = symTab.getSymbolIterator(SymbolUtilities.ORDINAL_PREFIX + "*", true);
|
||||||
while (iter.hasNext() && !monitor.isCancelled()) {
|
while (iter.hasNext()) {
|
||||||
|
monitor.checkCanceled();
|
||||||
Symbol sym = iter.next();
|
Symbol sym = iter.next();
|
||||||
int ordinal = SymbolUtilities.getOrdinalValue(sym.getName());
|
int ordinal = SymbolUtilities.getOrdinalValue(sym.getName());
|
||||||
if (ordinal == -1) {
|
if (ordinal == -1) {
|
||||||
|
@ -219,68 +239,99 @@ class LibrarySymbolTable {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a ordinal exports file produced by Microsoft DUMPBIN /EXPORTS <DLL>
|
||||||
|
* It is expected to start parsing lines following the table header containing the 'ordinal' header.
|
||||||
|
* Each ordinal mapping line is expected to have the format, starting with ordinal number and
|
||||||
|
* ending with symbol name:
|
||||||
|
* <ordinal> <other-column-data> <name>
|
||||||
|
* The name column contains the symbol name followed by an optional demangled form. If the name starts with
|
||||||
|
* [NONAME] this will be stripped.
|
||||||
|
* @param ordinalExportsFile file path to ordinal mapping file produced by DUMPBIN /EXPORTS
|
||||||
|
* @param addMissingOrdinals if true new entries will be created for ordinal mappings
|
||||||
|
* not already existing within this symbol table, otherwise only those which already
|
||||||
|
* exist will be updated with a name if specified by mapping file.
|
||||||
|
*/
|
||||||
public void applyOrdinalFile(ResourceFile ordinalExportsFile, boolean addMissingOrdinals) {
|
public void applyOrdinalFile(ResourceFile ordinalExportsFile, boolean addMissingOrdinals) {
|
||||||
try {
|
try (BufferedReader in =
|
||||||
InputStreamReader ir = new InputStreamReader(ordinalExportsFile.getInputStream());
|
new BufferedReader(new InputStreamReader(ordinalExportsFile.getInputStream()))) {
|
||||||
BufferedReader in = new BufferedReader(ir);
|
|
||||||
|
int ordinalColumnEndIndex = -1;
|
||||||
|
int nameColumnStartIndex = -1;
|
||||||
|
|
||||||
int mode = NONE;
|
int mode = NONE;
|
||||||
String inString;
|
String inString;
|
||||||
while ((inString = in.readLine()) != null) {
|
while ((inString = in.readLine()) != null) {
|
||||||
StringTokenizer tok = new StringTokenizer(inString);
|
|
||||||
if (!tok.hasMoreElements()) {
|
if (mode == NONE && inString.trim().startsWith("ordinal")) {
|
||||||
|
// rely on column header labels to establish ordinal and name column start/end
|
||||||
|
int ordinalColumnStartIndex = inString.indexOf("ordinal");
|
||||||
|
if (ordinalColumnStartIndex < 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String str = tok.nextToken();
|
ordinalColumnEndIndex = ordinalColumnStartIndex + 7;
|
||||||
|
|
||||||
if (str.startsWith(";")) {
|
nameColumnStartIndex = inString.indexOf("name");
|
||||||
continue; // comment - skip line
|
if (nameColumnStartIndex < 1) {
|
||||||
|
Msg.error(this,
|
||||||
|
"Failed to parse ordinal symbol file: " + ordinalExportsFile);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (str.equals("ordinal")) {
|
|
||||||
mode = ORDINAL;
|
mode = ORDINAL;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode != ORDINAL) {
|
if (mode != ORDINAL) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// must be a definition line
|
inString = stripComment(inString);
|
||||||
// ordinal Name DemangledName
|
if (inString.length() < nameColumnStartIndex) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
String ordStr = str;
|
// parse ordinal from first token on line, if bad parse, then done
|
||||||
|
String ordinalStr = inString.substring(0, ordinalColumnEndIndex).trim();
|
||||||
// parse ordinal, if bad parse, then done
|
|
||||||
int ordinal;
|
int ordinal;
|
||||||
try {
|
try {
|
||||||
ordinal = Integer.parseInt(ordStr);
|
ordinal = Integer.parseInt(ordinalStr);
|
||||||
}
|
}
|
||||||
catch (NumberFormatException exc) {
|
catch (NumberFormatException exc) {
|
||||||
// done parsing
|
// done parsing
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tok.hasMoreElements()) {
|
String nameStr = inString.substring(nameColumnStartIndex).trim();
|
||||||
break;
|
if (nameStr.length() == 0) {
|
||||||
|
break; // unexpected
|
||||||
}
|
}
|
||||||
|
|
||||||
String entryName = tok.nextToken();
|
// if [NONAME] present strip-off and use next field as name (i.e., mangled name)
|
||||||
|
if (nameStr.startsWith("[NONAME]")) {
|
||||||
|
nameStr = nameStr.substring(8).trim();
|
||||||
|
}
|
||||||
|
|
||||||
LibraryExportedSymbol sym = ordMap.get(new Integer(ordinal));
|
int index = nameStr.indexOf(' ');
|
||||||
|
if (index > 0) {
|
||||||
|
nameStr = nameStr.substring(0, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nameStr.length() == 0) {
|
||||||
|
continue; // skip if no name
|
||||||
|
}
|
||||||
|
|
||||||
|
LibraryExportedSymbol sym = ordMap.get(Integer.valueOf(ordinal));
|
||||||
if (sym != null) {
|
if (sym != null) {
|
||||||
symMap.remove(sym.getName());
|
symMap.remove(sym.getName());
|
||||||
sym.setName(entryName);
|
sym.setName(nameStr);
|
||||||
}
|
}
|
||||||
else if (addMissingOrdinals) {
|
else if (addMissingOrdinals) {
|
||||||
sym = new LibraryExportedSymbol(tableName, size, ordinal, entryName, null, null,
|
sym = new LibraryExportedSymbol(tableName, size, ordinal, nameStr, null, null,
|
||||||
tempPurge, false, null);
|
tempPurge, false, null);
|
||||||
symMap.put(entryName, sym);
|
symMap.put(nameStr, sym);
|
||||||
ordMap.put(ordinal, sym);
|
ordMap.put(ordinal, sym);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
in.close();
|
|
||||||
ir.close();
|
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException e) {
|
catch (FileNotFoundException e) {
|
||||||
return;
|
return;
|
||||||
|
@ -290,92 +341,9 @@ class LibrarySymbolTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyDefdFile(ResourceFile defFile) {
|
private String stripComment(String str) {
|
||||||
try {
|
int index = str.indexOf(';');
|
||||||
InputStreamReader ir = new InputStreamReader(defFile.getInputStream());
|
return index < 0 ? str : str.substring(0, index);
|
||||||
BufferedReader in = new BufferedReader(ir);
|
|
||||||
|
|
||||||
int mode = NONE;
|
|
||||||
String inString;
|
|
||||||
while ((inString = in.readLine()) != null) {
|
|
||||||
StringTokenizer tok = new StringTokenizer(inString);
|
|
||||||
if (!tok.hasMoreElements()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String cmd = tok.nextToken();
|
|
||||||
|
|
||||||
if (cmd.startsWith(";")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (cmd.equals("LIBRARY")) {
|
|
||||||
mode = LIBRARY;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (cmd.equals("EXPORTS")) {
|
|
||||||
mode = EXPORTS;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (mode != EXPORTS) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be a definition line
|
|
||||||
// entryname[=internalName] [@Ordinal [NONAME]] [PRIVATE] [DATA]
|
|
||||||
|
|
||||||
String entryName = cmd;
|
|
||||||
// search for '='
|
|
||||||
// none, then no internalName
|
|
||||||
int eqPos = entryName.indexOf('=');
|
|
||||||
if (eqPos > 0) {
|
|
||||||
entryName = entryName.substring(0, eqPos - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// search for '@'
|
|
||||||
// none, then no ordinalName and no NONAME
|
|
||||||
// @, might be NONAME
|
|
||||||
// optional PRIVATE and DATA
|
|
||||||
String nxtStr = tok.nextToken();
|
|
||||||
String ordStr = null;
|
|
||||||
if (nxtStr.startsWith("@")) {
|
|
||||||
if (nxtStr.length() > 1) {
|
|
||||||
ordStr = nxtStr.substring(1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!tok.hasMoreElements()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ordStr = tok.nextToken();
|
|
||||||
}
|
|
||||||
if (!tok.hasMoreElements()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
nxtStr = tok.nextToken();
|
|
||||||
// if (nxtStr.equals("NONAME")) {
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
int ordinal = Integer.parseInt(ordStr);
|
|
||||||
|
|
||||||
LibraryExportedSymbol sym = ordMap.get(new Integer(ordinal));
|
|
||||||
if (sym != null) {
|
|
||||||
symMap.remove(sym.getName());
|
|
||||||
sym.setName(entryName);
|
|
||||||
symMap.put(entryName, sym);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Msg.info(this, "* " + ordinal + " : " + entryName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
in.close();
|
|
||||||
ir.close();
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> getForwards() {
|
List<String> getForwards() {
|
||||||
|
@ -390,14 +358,14 @@ class LibrarySymbolTable {
|
||||||
* exist.
|
* exist.
|
||||||
*/
|
*/
|
||||||
LibraryExportedSymbol getSymbol(int ordinal) {
|
LibraryExportedSymbol getSymbol(int ordinal) {
|
||||||
return ordMap.get(new Integer(ordinal));
|
return ordMap.get(ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the symbol for the specified name
|
* Returns the symbol for the specified name
|
||||||
*
|
*
|
||||||
* @param symbol the name of the desired symbol
|
* @param symbol the name of the desired symbol
|
||||||
* @return
|
* @return symbol map entry or null if not found
|
||||||
*/
|
*/
|
||||||
LibraryExportedSymbol getSymbol(String symbol) {
|
LibraryExportedSymbol getSymbol(String symbol) {
|
||||||
return symMap.get(symbol);
|
return symMap.get(symbol);
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
/* ###
|
||||||
|
* 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.opinion;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import generic.jar.ResourceFile;
|
||||||
|
import resources.ResourceManager;
|
||||||
|
|
||||||
|
public class OrdinalFileSymbolLoadTest {
|
||||||
|
|
||||||
|
protected static final String ORD_TEST_FILE = "ghidra/app/util/opinion/test.ord";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testORDFileParse() {
|
||||||
|
|
||||||
|
File ordFile = ResourceManager.getResourceFile(ORD_TEST_FILE);
|
||||||
|
assertNotNull(ordFile);
|
||||||
|
|
||||||
|
ResourceFile ordResourceFile = new ResourceFile(ordFile);
|
||||||
|
|
||||||
|
LibrarySymbolTable symTable = new LibrarySymbolTable("test", 32);
|
||||||
|
symTable.applyOrdinalFile(ordResourceFile, true);
|
||||||
|
|
||||||
|
LibraryExportedSymbol symbol = symTable.getSymbol(1);
|
||||||
|
assertNotNull(symbol);
|
||||||
|
assertEquals("SymbolName1", symbol.getName());
|
||||||
|
assertEquals(-1, symbol.getPurge());
|
||||||
|
assertEquals("test", symbol.getLibraryName());
|
||||||
|
|
||||||
|
symbol = symTable.getSymbol(2);
|
||||||
|
assertNotNull(symbol);
|
||||||
|
assertEquals("SymbolName2", symbol.getName());
|
||||||
|
assertEquals(-1, symbol.getPurge());
|
||||||
|
assertEquals("test", symbol.getLibraryName());
|
||||||
|
|
||||||
|
symbol = symTable.getSymbol(20);
|
||||||
|
assertNotNull(symbol);
|
||||||
|
assertEquals("SymbolName3", symbol.getName());
|
||||||
|
assertEquals(-1, symbol.getPurge());
|
||||||
|
assertEquals("test", symbol.getLibraryName());
|
||||||
|
|
||||||
|
symbol = symTable.getSymbol(30);
|
||||||
|
assertNotNull(symbol);
|
||||||
|
assertEquals("SymbolName4", symbol.getName());
|
||||||
|
assertEquals(-1, symbol.getPurge());
|
||||||
|
assertEquals("test", symbol.getLibraryName());
|
||||||
|
|
||||||
|
symbol = symTable.getSymbol(40);
|
||||||
|
assertNotNull(symbol);
|
||||||
|
assertEquals("SymbolName5", symbol.getName());
|
||||||
|
assertEquals(-1, symbol.getPurge());
|
||||||
|
assertEquals("test", symbol.getLibraryName());
|
||||||
|
|
||||||
|
symbol = symTable.getSymbol(50);
|
||||||
|
assertNull(symbol);
|
||||||
|
|
||||||
|
symbol = symTable.getSymbol(60);
|
||||||
|
assertNotNull(symbol);
|
||||||
|
assertEquals("SymbolName6", symbol.getName());
|
||||||
|
assertEquals(-1, symbol.getPurge());
|
||||||
|
assertEquals("test", symbol.getLibraryName());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
Microsoft ordinal to symbol mapping file
|
||||||
|
produced using the DUMPBIN /EXPORTS command
|
||||||
|
|
||||||
|
This test file is intended to be representitive
|
||||||
|
of the ordinal mapping table format which whose
|
||||||
|
start is identified by the table header starting
|
||||||
|
with the word 'ordinal'
|
||||||
|
|
||||||
|
ordinal hint RVA name
|
||||||
|
|
||||||
|
1 xyz SymbolName1
|
||||||
|
2 2 abc SymbolName2
|
||||||
|
|
||||||
|
; comments
|
||||||
|
|
||||||
|
20 123 [NONAME] SymbolName3 x y z
|
||||||
|
30 3 456 [NONAME] SymbolName4 a b c
|
||||||
|
25 18 AppPolicyGetClrCompat (forwarded to kernelbase.AppPolicyGetClrCompat)
|
||||||
|
|
||||||
|
40 567 SymbolName5 (forwarded to ...
|
||||||
|
|
||||||
|
; the following symbol will be discarded since it has no name mapping
|
||||||
|
50 910 [NONAME]
|
||||||
|
|
||||||
|
60 899 [NONAME] SymbolName6 x y z
|
||||||
|
|
||||||
|
Summary
|
||||||
|
|
||||||
|
other summary information
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue