ghidra/Ghidra/Features/GnuDemangler/ghidra_scripts/VxWorksSymTab_Finder.java

836 lines
25 KiB
Java

/* ###
* 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.
*/
// Locates and parses the VxWorks symbol table. Names the table "vxSymTbl"
// and names the table length variable "vxSymTblLen" (if the length variable
// appears either directly before or after the symbol table). Defines the
// symbol table as SYMBOL[vxSymTblLen].
//
// Extracts symbol name, location, and type from each entry. Disassembles,
// creates, and names functions. Names global variables.
//
// Any existing Ghidra symbol table entries that collide with VxWorks symbol
// table entries are deleted. Mangled C++ symbol names are demangled.
//
// The VxWorks symbol table is an array [0..n-1] of (struct SYMBOL) entries.
// The table may be immediately followed or preceeded by an (int) vxSymTblLen
// value.
//
// Prerequisites:
//
// - Program memory block(s) is(are) aligned with actual load addresses
// (run something like MemAlignARM_LE.java)
//
// - Symbol table cannot be in a memory block with a name that contains
// the string "text" or "bss"
//
// - Modify getVxSymbolClass() to recognize your program's VxWorks
// symbol table entry structure, if necessary
//
// @category VxWorks
import ghidra.app.cmd.disassemble.DisassembleCommand;
import ghidra.app.cmd.label.DemanglerCmd;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.script.GhidraScript;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.demangler.DemangledException;
import ghidra.app.util.demangler.gnu.GnuDemangler;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
public class VxWorksSymTab_Finder extends GhidraScript {
boolean debug = false;
//------------------------------------------------------------------------
// getDataTypeManagerByName
//
// Retrieves data type manager by name.
//
// Returns:
// Success: DataTypeManager
// Failure: null
//------------------------------------------------------------------------
private DataTypeManager getDataTypeManagerByName(String name) {
DataTypeManagerService service = state.getTool().getService(DataTypeManagerService.class);
// Loop through all managers in the data type manager service
for (DataTypeManager manager : service.getDataTypeManagers()) {
if (manager.getName().equals(name)) {
return manager;
}
}
return null;
}
//------------------------------------------------------------------------
// VxSymbol
//
// Contains a SYMBOL data type representing a VxWorks symbol table entry
// and several associated methods.
//------------------------------------------------------------------------
private class VxSymbol {
StructureDataType dt = null;
int nameOffset = 0;
int locOffset = 0;
int typeOffset = 0;
int length = 0;
public VxSymbol(StructureDataType struct) {
dt = struct;
nameOffset = getFieldOffset(dt, "symNameOff");
locOffset = getFieldOffset(dt, "symLocOff");
typeOffset = getFieldOffset(dt, "symType");
length = dt.getLength();
}
private int getFieldOffset(StructureDataType dataType, String name) {
for (DataTypeComponent comp : dataType.getComponents()) {
if (comp.getFieldName().equals(name)) {
return comp.getOffset();
}
}
return -1;
}
public DataType dataType() {
return dt;
}
public int length() {
return length;
}
public int nameOffset() {
return nameOffset;
}
public int locOffset() {
return locOffset;
}
public int typeOffset() {
return typeOffset;
}
// Add SYMBOL data type to Program DataTypeManager
// (if data type already exists, replace it)
public void createGhidraType() {
currentProgram.getDataTypeManager()
.addDataType(dt,
DataTypeConflictHandler.REPLACE_HANDLER);
}
}
//------------------------------------------------------------------------
// getVxSymbolClass
//
// Creates a SYMBOL structure data type and uses it to create a new
// VxSymbol class instance.
//
// Returns:
// Success: VxSymbol
// Failure: null
//------------------------------------------------------------------------
private VxSymbol getVxSymbolClass(int type) {
// Pre-define base data types used to define symbol table entry data type
DataTypeManager builtin = getDataTypeManagerByName("BuiltInTypes");
DataType charType = builtin.getDataType("/char");
DataType charPtrType = PointerDataType.getPointer(charType, 4);
DataType byteType = builtin.getDataType("/byte");
DataType ushortType = builtin.getDataType("/ushort");
DataType intType = builtin.getDataType("/int");
DataType uintType = builtin.getDataType("/uint");
DataType voidType = builtin.getDataType("/void");
DataType voidPtrType = PointerDataType.getPointer(voidType, 4);
// Define a SYMBOL data type (try to put most common first).
// Each SYMBOL data type must include at least 3 fields named
// symNameOff, symLocOff, and symType.
StructureDataType dt = null;
switch (type) {
case 0:
// Version 5.4, 6.4 and 6.8
//
// Total length: 0x14 bytes
// 0x00 uint symHashNode // NULL
// 0x04 char *symNameOff
// 0x08 void *symLocOff
// 0x0c int NULL
// 0x10 ushort symGroup
// 0x12 uchar symType
// 0x13 uchar undef
dt = new StructureDataType("SYMBOL", 0x14);
dt.replaceAtOffset(0, uintType, 4, "symHashNode", "");
dt.replaceAtOffset(4, charPtrType, 4, "symNameOff", "");
dt.replaceAtOffset(8, voidPtrType, 4, "symLocOff", "");
dt.replaceAtOffset(0x0c, intType, 4, "", "");
dt.replaceAtOffset(0x10, ushortType, 2, "symGroup", "");
dt.replaceAtOffset(0x12, byteType, 1, "symType", "");
break;
case 1:
// Version 6.1
//
// Total length: 0x18 bytes
// 0x00 uint symHashNode // NULL
// 0x04 char *symNameOff
// 0x08 void *symLocOff
// 0x0c int NULL
// 0x10 int NULL
// 0x14 uchar symType
// 0x15 uchar undef[3]
dt = new StructureDataType("SYMBOL", 0x18);
dt.replaceAtOffset(0, uintType, 4, "symHashNode", "");
dt.replaceAtOffset(4, charPtrType, 4, "symNameOff", "");
dt.replaceAtOffset(8, voidPtrType, 4, "symLocOff", "");
dt.replaceAtOffset(0x0c, intType, 4, "", "");
dt.replaceAtOffset(0x10, intType, 4, "", "");
dt.replaceAtOffset(0x14, byteType, 1, "symType", "");
break;
case 2:
// Unknown VxWorks version(s)
//
// Total length: 0x1c bytes
// 0x00 uint symHashNode // NULL
// 0x04 char *symNameOff
// 0x08 void *symLocOff
// 0x0c int unk; // no clear pattern to values
// 0x10 int NULL
// 0x14 int NULL
// 0x18 uchar symType
// 0x19 uchar undef[3]
dt = new StructureDataType("SYMBOL", 0x1c);
dt.replaceAtOffset(0, uintType, 4, "symHashNode", "");
dt.replaceAtOffset(4, charPtrType, 4, "symNameOff", "");
dt.replaceAtOffset(8, voidPtrType, 4, "symLocOff", "");
dt.replaceAtOffset(0x0c, intType, 4, "", "");
dt.replaceAtOffset(0x10, intType, 4, "", "");
dt.replaceAtOffset(0x14, intType, 4, "", "");
dt.replaceAtOffset(0x18, byteType, 1, "symType", "");
break;
case 3:
// Version 5.5
//
// Total length: 0x10 bytes
// 0x00 uint symHashNode // NULL
// 0x04 char *symNameOff
// 0x08 void *symLocOff
// 0x0c ushort symGroup // NULL
// 0x0e uchar symType
// 0x0f uchar undef
dt = new StructureDataType("SYMBOL", 0x10);
dt.replaceAtOffset(0, uintType, 4, "symHashNode", "");
dt.replaceAtOffset(4, charPtrType, 4, "symNameOff", "");
dt.replaceAtOffset(8, voidPtrType, 4, "symLocOff", "");
dt.replaceAtOffset(0x0c, ushortType, 2, "symGroup", "");
dt.replaceAtOffset(0x0e, byteType, 1, "symType", "");
break;
default:
return null;
}
// Return a VxSymbol class for this SYMBOL data type
return new VxSymbol(dt);
}
//------------------------------------------------------------------------
// isExecute
//
// Is address in an executable memory block?
//------------------------------------------------------------------------
private boolean isExecute(Address addr) {
// Search all program memory blocks
for (MemoryBlock block : getMemoryBlocks()) {
if (block.contains(addr)) {
return block.isExecute();
}
}
return false;
}
//------------------------------------------------------------------------
// isAddress
//
// Is offset in an existing memory block?
//------------------------------------------------------------------------
private boolean isAddress(long offset) {
// Search all program memory blocks
for (MemoryBlock block : getMemoryBlocks()) {
if (block.getStart().getOffset() <= offset && block.getEnd().getOffset() >= offset) {
return true;
}
}
return false; // no match
}
//------------------------------------------------------------------------
// isAddress
//
// Is offset in the specified memory block?
//------------------------------------------------------------------------
private boolean isAddress(long offset, MemoryBlock block) {
if (block.getStart().getOffset() <= offset && block.getEnd().getOffset() >= offset) {
return true;
}
return false;
}
//------------------------------------------------------------------------
// isString
//
// Are the bytes starting at addr a C string?
//
// Algorithm: Scan bytes until finding either an invalid char or null.
// If scan stops at null, return true -- else false.
//------------------------------------------------------------------------
private boolean isString(Address addr) {
byte _byte;
try {
_byte = getByte(addr);
}
catch (Exception except) {
return false;
}
while ( // May need to add valid character examples here.
(_byte == 0x09 || _byte == 0x0a || _byte == 0x0d || (_byte > 0x19 && _byte < 0x80)) &&
_byte != 0x00) {
if (monitor.isCancelled()) {
return false;
}
addr = addr.add(1);
try {
_byte = getByte(addr);
}
catch (Exception except) {
return false;
}
}
if (_byte == 0x00) {
return true; // Scan stopped at null.
}
return false; // Scan stopped at invalid char.
}
//------------------------------------------------------------------------
// clearString
//
// Remove data or instructions that overlap the null-terminated
// string at addr (may happen if disassembly creates invalid references
// or compiler optimization creates shared strings).
//
// Use get*Containing() in case a string that ends with the string
// at addr has already been defined (e.g., the string at addr is
// "CoolFunc" and the string "g_pfCoolFunc" overlaps it).
//------------------------------------------------------------------------
private void clearString(Address addr) throws Exception {
Data data;
Instruction inst;
// Clear the string, breaking on the terminating null character
while (getByte(addr) != 0) {
data = getDataContaining(addr);
if (data != null) {
removeDataAt(data.getAddress());
}
inst = getInstructionContaining(addr);
if (inst != null) {
removeInstructionAt(inst.getAddress());
}
addr = addr.add(1);
}
// Now clear at string's terminating null character
data = getDataContaining(addr);
if (data != null) {
removeDataAt(data.getAddress());
}
inst = getInstructionContaining(addr);
if (inst != null) {
removeInstructionAt(inst.getAddress());
}
}
//------------------------------------------------------------------------
// isSymTblEntry
//
// Does data pointed to by entry look like a VxWorks symbol table entry?
// Test is weak.
//------------------------------------------------------------------------
private boolean isSymTblEntry(Address entry, VxSymbol vxSymbol) throws Exception {
// First dword must be null or a valid ptr (typically into the sym table)
long value = getInt(entry) & 0xffffffffL;
if ((value != 0) && !isAddress(value)) {
if (debug) {
println("1: " + entry + " --> " + Long.toHexString(value));
}
return false;
}
// symNameOff field must point to a non-null C string
value = getInt(entry.add(vxSymbol.nameOffset())) & 0xffffffffL;
if (!isAddress(value)) {
if (debug) {
println("2: " + entry + " --> " + Long.toHexString(value));
}
return false;
}
Address symNameAddr = toAddr(value);
if (!isString(symNameAddr)) {
if (debug) {
println("3: " + entry + " --> " + Long.toHexString(value));
}
return false;
}
if (getByte(symNameAddr) == 0) {
return false;
}
// symLocOff field can be almost anything (e.g., external mem ref)
//value = (long)getInt(entry.add(vxSymbol.locOffset())) & 0xffffffffL;
//if (value == 0) {
// if (debug) println("4: " + entry);
// return false;
//}
// symType field must be recognized type code (this test is weak)
byte symType = getByte(entry.add(vxSymbol.typeOffset()));
if (!isValidSymType(symType)) {
if (debug) {
println("5: " + entry + " --> " + symType);
}
return false;
}
return true;
}
private boolean isValidSymType(byte symType) {
switch (symType) {
case 0: // Undefined Symbol
return false;
case 2: // Local Absolute
case 3: // Global Absolute
case 6: // Local Data
case 7: // Global Data
case 8: // Local BSS
case 9: // Global BSS
case 4: // Local .text
case 5: // Global .text
case 0x11: // External ref -- ignore
return true;
default:
return false;
}
}
//------------------------------------------------------------------------
// findSymTbl
//
// Searches all memory blocks for data that looks like a run of testLen
// VxWorks symbol table entries.
//
// Returns:
// Success: table address
// Failure: null
//------------------------------------------------------------------------
private Address findSymTbl(VxSymbol vxSymbol) throws Exception {
int testLen = 100; // number of symbol tbl entries to look for
boolean hasNonExecute = checkNonExecute();
// Iterate through all memory blocks
for (MemoryBlock block : currentProgram.getMemory().getBlocks()) {
// Skip code/execute blocks if there are non-execute blocks,
// otherwise search everything.
if (hasNonExecute && block.isExecute()) {
continue;
}
// skip uninit
if (!block.isInitialized()) {
continue;
}
// Search current block for run of testLen symbol table entries
int testBlkSize = vxSymbol.length * testLen;
printf(" block: " + block.getName() + " (" + block.getStart() + ", " +
block.getEnd() + ") ");
printf("testBlkSize = " + Integer.toHexString(testBlkSize) + " ");
System.out.flush();
long prevOffset = 0;
Address cursor = block.getStart();
while ((cursor != null) && isAddress(cursor.getOffset() + testBlkSize, block)) {
// Script cancel check and visual feedback
if (monitor.isCancelled()) {
return null;
}
if ((cursor.getOffset() - prevOffset) >= 0x100000) {
printf(".");
System.out.flush();
prevOffset = cursor.getOffset();
}
// Determine whether cursor now points to a symbol table
int i = 0;
for (Address entry = cursor; isSymTblEntry(entry, vxSymbol) &&
(i < testLen); entry = entry.add(vxSymbol.length()), i++) {
}
if (i == testLen) {
// May have symbol table -- verify length
if (getSymTblLen(cursor, vxSymbol) != 0) {
printf("\n");
System.out.flush();
return cursor; // found table -- stop searching
}
if (debug) {
printf("Possible symbol table at " + cursor + " has length error\n");
}
}
cursor = cursor.add(4);
}
printf("\n");
printf(" search terminated at: " + cursor + "\n");
System.out.flush();
}
return null;
}
private boolean checkNonExecute() {
for (MemoryBlock block : currentProgram.getMemory().getBlocks()) {
if (!block.isExecute()) {
return true;
}
}
return false;
}
//------------------------------------------------------------------------
// getSymTblLen
//
// Counts number of entries in VxWorks table at address symTbl.
//
// Returns:
// Success: number of table entries (> 0)
// Failure: 0
//------------------------------------------------------------------------
private int getSymTblLen(Address symTbl, VxSymbol vxSymbol) throws Exception {
Address entry = symTbl;
int j = 0;
while (isSymTblEntry(entry, vxSymbol)) {
entry = entry.add(vxSymbol.length());
j++;
}
return j;
// NOTE: Found an example of a VxWorks symbol table that was not
// directly adjacent to the symbol table length variable...
// so removed the following constraint.
/*
// Symbol table length entry may be at beginning or end of the symbol
// table...so compare computed length with both values. If either
// matches, table length is verified.
if ((j == getInt(entry)) || (j == getInt(symTbl.subtract(4)))) {
return j;
} else {
return 0;
}
*/
}
/**
* Look before/after the table to see if there is a size value there and mark it if it agrees with TableLen
*
* @param symTbl
* @param vxSymbol
* @param tableLen
* @throws Exception
*/
private void markSymbolTableLen(Address symTbl, VxSymbol vxSymbol, int symTblLen)
throws Exception {
// NOTE: Found an example of a VxWorks symbol table that was not
// directly adjacent to the symbol table length variable...
// Name the VxWorks symbol length variable
// (if it appears either directly before or after the symbol table)
Address symTblLenPtr = null;
long foreOff = symTbl.getOffset() - 4;
long aftOff = symTbl.getOffset() + symTblLen * vxSymbol.length();
if (isAddress(foreOff) && getInt(toAddr(foreOff)) == symTblLen) {
symTblLenPtr = toAddr(foreOff);
}
else if (isAddress(aftOff) && getInt(toAddr(aftOff)) == symTblLen) {
symTblLenPtr = toAddr(aftOff);
}
if (symTblLenPtr != null) {
removeConflictingSymbols("vxSymTblLen", symTblLenPtr);
createLabel(symTblLenPtr, "vxSymTblLen", true);
createDWord(symTblLenPtr);
}
else {
println("Warning: Symbol Table Size not found before of after table");
}
}
//------------------------------------------------------------------------
// removeConflictingSymbols
//
// Deletes all symbols with the same name and the primary symbol at addr.
//------------------------------------------------------------------------
private void removeConflictingSymbols(String name, Address addr) {
// Delete any existing symbols with the same name
for (Symbol sym : currentProgram.getSymbolTable().getSymbols(name)) {
sym.delete();
}
// Delete primary Ghidra symbol at the same address
Symbol sym = getSymbolAt(addr);
if (sym != null) {
sym.delete();
}
return;
}
//------------------------------------------------------------------------
// applyDemangled
//
// Apply demangled symbol name to symbol.
//------------------------------------------------------------------------
private void applyDemangled(Address addr, String mangled, String demangled) {
if (demangled != null) {
new DemanglerCmd(addr, mangled).applyTo(currentProgram, monitor);
currentProgram.getSymbolTable()
.removeSymbolSpecial(
getSymbol(mangled, currentProgram.getGlobalNamespace()));
}
return;
}
//------------------------------------------------------------------------
// doLocalDisassemble
//
// Do our own disassembly, and don't let auto-analysis start until after
// this script finishes. This speeds up the script substantially and
// allows auto-analysis to operate with more information (and code/data
// that isn't rapidly changing).
//------------------------------------------------------------------------
private void doLocalDisassemble(Address addr) {
// Only disassemble in memory blocks marked executable
if (!isExecute(addr)) {
return;
}
DisassembleCommand cmd = new DisassembleCommand(addr, null, true);
cmd.enableCodeAnalysis(false); // Queues changes up for later analysis
cmd.applyTo(currentProgram, monitor);
AddressSet set = cmd.getDisassembledAddressSet();
AutoAnalysisManager.getAnalysisManager(currentProgram).codeDefined(set);
return;
}
//------------------------------------------------------------------------
// getScriptAnalysisMode
//
// Force auto-analysis to wait until script completes.
//------------------------------------------------------------------------
@Override
public AnalysisMode getScriptAnalysisMode() {
return AnalysisMode.SUSPENDED;
}
//========================================================================
// Main
//========================================================================
@Override
public void run() throws Exception {
// Find VxWorks symbol table
Address symTbl = null;
VxSymbol vxSymbol = getVxSymbolClass(0);
for (int i = 0; ((vxSymbol != null) && !monitor.isCancelled()); i++, vxSymbol =
getVxSymbolClass(i)) {
println("Searching for symbol table variant " + i);
if ((symTbl = findSymTbl(vxSymbol)) != null) {
break;
}
}
if (vxSymbol == null) {
return;
}
int symTblLen = getSymTblLen(symTbl, vxSymbol);
println("Symbol table at " + symTbl + " (" + symTblLen + " entries)");
// Name the VxWorks symbol table
removeConflictingSymbols("vxSymTbl", symTbl);
createLabel(symTbl, "vxSymTbl", true);
markSymbolTableLen(symTbl, vxSymbol, symTblLen);
// Create symbol data type and symbol table structure
println("Creating SYMBOL data type and symbol table structure...");
vxSymbol.createGhidraType();
clearListing(symTbl, symTbl.add(symTblLen * vxSymbol.length() - 1));
createData(symTbl, new ArrayDataType(vxSymbol.dataType(), symTblLen, vxSymbol.length()));
// Create a GNU demangler instance
GnuDemangler demangler = new GnuDemangler();
if (!demangler.canDemangle(currentProgram)) {
println("Unable to create demangler.");
return;
}
// Process VxWorks symbol table entries
println("Processing symbol table entries.");
Address symEntry = symTbl;
for (int i = 0; (i < symTblLen) && !monitor.isCancelled(); i++, symEntry =
symEntry.add(vxSymbol.length())) {
// Extract symbol table entry values
Address symNameAddr = toAddr(getInt(symEntry.add(vxSymbol.nameOffset())) & 0xffffffffL);
Address symLoc = toAddr(getInt(symEntry.add(vxSymbol.locOffset())) & 0xffffffffL);
byte symType = getByte(symEntry.add(vxSymbol.typeOffset()));
// Remove any data or instructions that overlap string at *symNameAddr
clearString(symNameAddr);
// Turn *symNameAddr into a string and store it in symName
String symName;
try {
symName = (String) createAsciiString(symNameAddr).getValue();
}
catch (Exception e) {
println("createAsciiString: caught exception...");
println(e.getMessage());
println(e.toString());
return;
}
if (symName.length() > 2000) {
symName = symName.substring(0, 2000);
}
// Demangle symName
String symDemangledName = null;
try {
symDemangledName = demangler.demangle(symName).getSignature(false);
}
catch (DemangledException e) { // report demangling error
if (!e.isInvalidMangledName()) {
println("demangle: Demangling error");
}
}
catch (RuntimeException e) { // ignore unmangled symNames
}
// Status update
if (symDemangledName != null) {
println("i=" + i + ", nameAddr: " + symNameAddr + ", loc: " + symLoc + ", type: " +
symType + ", name: " + symName + ", demangled: " + symDemangledName);
}
else {
println("i=" + i + ", nameAddr: " + symNameAddr + ", loc: " + symLoc + ", type: " +
symType + ", name: " + symName);
}
// Clear any conflicting symbols from the Ghidra symbol table
removeConflictingSymbols(symName, symLoc);
// If entry type is data, simply create a Ghidra symbol for it.
// If entry type is code, disassemble it and create function.
switch (symType) {
case 0: // Undefined Symbol
println("NULL symType!");
break;
case 2: // Local Absolute
case 3: // Global Absolute
case 6: // Local Data
case 7: // Global Data
case 8: // Local BSS
case 9: // Global BSS
createLabel(symLoc, symName, true);
applyDemangled(symLoc, symName, symDemangledName);
break;
case 4: // Local .text
case 5: // Global .text
doLocalDisassemble(symLoc);
createFunction(symLoc, symName);
if (getFunctionAt(symLoc) != null) {
getFunctionAt(symLoc).setName(symName, SourceType.USER_DEFINED);
applyDemangled(symLoc, symName, symDemangledName);
}
else {
println("createFunction: Failed to create function");
createLabel(symLoc, symName, true);
applyDemangled(symLoc, symName, symDemangledName);
}
break;
case 0x11: // External ref -- ignore
break;
default:
println("Invalid symType!");
break;
}
}
}
}