1019 lines
22 KiB
C++
1019 lines
22 KiB
C++
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dc.cpp
|
|
|
|
Abstract:
|
|
|
|
Command line utility for dumping importing information from DLL's
|
|
and executables.
|
|
|
|
Command Line Options:
|
|
/v Verbose mode
|
|
/s:Func Only display functions matching search string "Func"
|
|
/o:File Send output to File
|
|
/f Sort by function, not by module.
|
|
|
|
History:
|
|
|
|
05/10/2000 t-michkr Created
|
|
|
|
--*/
|
|
|
|
#include <windows.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
// For some reason, this is needed to compile in
|
|
// the sdktools tree.
|
|
#define strdup _strdup
|
|
#define stricmp _stricmp
|
|
#define strnicmp _strnicmp
|
|
|
|
// Structure containing all info relevent to an imported function.
|
|
struct SFunction
|
|
{
|
|
// The name this function is imported by.
|
|
char* m_szName;
|
|
|
|
// Ordinal number, Win3.1 compatibility.
|
|
int m_iOrdinal;
|
|
|
|
// Lookup index into a DLL's export table
|
|
// for quick patching.
|
|
int m_iHint;
|
|
|
|
// Starting address of the function.
|
|
DWORD m_dwAddress;
|
|
|
|
// Whether or not it is a delayed import.
|
|
bool m_fDelayedImport;
|
|
|
|
// Forwarded function name
|
|
char* m_szForward;
|
|
|
|
// Link to next function
|
|
SFunction* m_pNext;
|
|
|
|
SFunction()
|
|
{
|
|
m_szName = m_szForward = 0;
|
|
m_iOrdinal = m_iHint = -1;
|
|
m_dwAddress = static_cast<DWORD>(-1);
|
|
m_fDelayedImport = false;
|
|
m_pNext = 0;
|
|
}
|
|
};
|
|
|
|
// A module used by the executable (ie, DLL's)
|
|
struct SModule
|
|
{
|
|
// The name of this module
|
|
char* m_szName;
|
|
|
|
// All functions imported from this module
|
|
SFunction* m_pFunctions;
|
|
|
|
// Link to next module
|
|
SModule* m_pNext;
|
|
|
|
SModule()
|
|
{
|
|
m_szName = 0;
|
|
m_pFunctions = 0;
|
|
m_pNext = 0;
|
|
}
|
|
};
|
|
|
|
// All modules imported by the executable.
|
|
SModule* g_pModules = 0;
|
|
|
|
void InsertFunctionSorted(SModule* pMod, SFunction* pFunc)
|
|
{
|
|
|
|
// Special case, insert at front
|
|
if(pMod->m_pFunctions == 0
|
|
|| stricmp(pMod->m_pFunctions->m_szName, pFunc->m_szName) > 0)
|
|
{
|
|
pFunc->m_pNext = pMod->m_pFunctions;
|
|
pMod->m_pFunctions = pFunc;
|
|
return;
|
|
}
|
|
|
|
SFunction* pfPrev = pMod->m_pFunctions;
|
|
SFunction* pfTemp = pMod->m_pFunctions->m_pNext;
|
|
while(pfTemp)
|
|
{
|
|
if(stricmp(pfTemp->m_szName, pFunc->m_szName) > 0)
|
|
{
|
|
pFunc->m_pNext = pfTemp;
|
|
pfPrev->m_pNext = pFunc;
|
|
return;
|
|
}
|
|
pfPrev = pfTemp;
|
|
pfTemp = pfTemp->m_pNext;
|
|
}
|
|
|
|
// Insert at end.
|
|
pFunc->m_pNext = 0;
|
|
pfPrev->m_pNext = pFunc;
|
|
}
|
|
|
|
void InsertModuleSorted(SModule* pMod)
|
|
{
|
|
// Special case, insert at front
|
|
if(g_pModules == 0
|
|
|| stricmp(g_pModules->m_szName, pMod->m_szName) > 0)
|
|
{
|
|
pMod->m_pNext = g_pModules;
|
|
g_pModules = pMod;
|
|
return;
|
|
}
|
|
|
|
SModule* pmPrev = g_pModules;
|
|
SModule* pmTemp = g_pModules->m_pNext;
|
|
while(pmTemp)
|
|
{
|
|
if(stricmp(pmTemp->m_szName, pMod->m_szName) > 0)
|
|
{
|
|
pMod->m_pNext = pmTemp;
|
|
pmPrev->m_pNext = pMod;
|
|
return;
|
|
}
|
|
pmPrev = pmTemp;
|
|
pmTemp = pmTemp->m_pNext;
|
|
}
|
|
|
|
// Insert at end.
|
|
pMod->m_pNext = 0;
|
|
pmPrev->m_pNext = pMod;
|
|
}
|
|
|
|
// Print a message about the last error that occurred.
|
|
void PrintLastError()
|
|
{
|
|
// Get the message string
|
|
void* pvMsgBuf;
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS, 0, GetLastError(),
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
reinterpret_cast<PTSTR>(&pvMsgBuf),0, 0);
|
|
|
|
// Print it.
|
|
fprintf(stderr, "%s\n", pvMsgBuf);
|
|
|
|
// Free the buffer.
|
|
LocalFree(pvMsgBuf);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* LinkName2Name
|
|
*
|
|
*************************************************************************/
|
|
void
|
|
LinkName2Name(
|
|
char* szLinkName,
|
|
char* szName)
|
|
{
|
|
/*
|
|
* the link name is expected like ?Function@Class@@Params
|
|
* to be converted to Class::Function
|
|
*/
|
|
|
|
static CHAR arrOperators[][8] =
|
|
{
|
|
"",
|
|
"",
|
|
"new",
|
|
"delete",
|
|
"=",
|
|
">>",
|
|
"<<",
|
|
"!",
|
|
"==",
|
|
"!="
|
|
};
|
|
|
|
DWORD dwCrr = 0;
|
|
DWORD dwCrrFunction = 0;
|
|
DWORD dwCrrClass = 0;
|
|
DWORD dwSize;
|
|
BOOL fIsCpp = FALSE;
|
|
BOOL fHasClass = FALSE;
|
|
BOOL fIsContructor = FALSE;
|
|
BOOL fIsDestructor = FALSE;
|
|
|
|
BOOL fIsOperator = FALSE;
|
|
DWORD dwOperatorIndex = 0;
|
|
char szFunction[1024];
|
|
char szClass[1024];
|
|
|
|
if (*szLinkName == '@')
|
|
szLinkName++;
|
|
|
|
dwSize = lstrlen(szLinkName);
|
|
|
|
/*
|
|
* skip '?'
|
|
*/
|
|
while (dwCrr < dwSize) {
|
|
if (szLinkName[dwCrr] == '?') {
|
|
|
|
dwCrr++;
|
|
fIsCpp = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* check to see if this is a special function (like ??0)
|
|
*/
|
|
if (fIsCpp) {
|
|
|
|
if (szLinkName[dwCrr] == '?') {
|
|
|
|
dwCrr++;
|
|
|
|
/*
|
|
* the next digit should tell as the function type
|
|
*/
|
|
if (isdigit(szLinkName[dwCrr])) {
|
|
|
|
switch (szLinkName[dwCrr]) {
|
|
|
|
case '0':
|
|
fIsContructor = TRUE;
|
|
break;
|
|
case '1':
|
|
fIsDestructor = TRUE;
|
|
break;
|
|
default:
|
|
fIsOperator = TRUE;
|
|
dwOperatorIndex = szLinkName[dwCrr] - '0';
|
|
break;
|
|
}
|
|
dwCrr++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* get the function name
|
|
*/
|
|
while (dwCrr < dwSize) {
|
|
|
|
if (szLinkName[dwCrr] != '@') {
|
|
|
|
szFunction[dwCrrFunction] = szLinkName[dwCrr];
|
|
dwCrrFunction++;
|
|
dwCrr++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
szFunction[dwCrrFunction] = '\0';
|
|
|
|
if (fIsCpp) {
|
|
/*
|
|
* skip '@'
|
|
*/
|
|
if (dwCrr < dwSize) {
|
|
|
|
if (szLinkName[dwCrr] == '@') {
|
|
dwCrr++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* get the class name (if any)
|
|
*/
|
|
while (dwCrr < dwSize) {
|
|
|
|
if (szLinkName[dwCrr] != '@') {
|
|
|
|
fHasClass = TRUE;
|
|
szClass[dwCrrClass] = szLinkName[dwCrr];
|
|
dwCrrClass++;
|
|
dwCrr++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
szClass[dwCrrClass] = '\0';
|
|
}
|
|
|
|
/*
|
|
* print the new name
|
|
*/
|
|
if (fIsContructor) {
|
|
sprintf(szName, "%s::%s", szFunction, szFunction);
|
|
} else if (fIsDestructor) {
|
|
sprintf(szName, "%s::~%s", szFunction, szFunction);
|
|
} else if (fIsOperator) {
|
|
sprintf(szName, "%s::operator %s", szFunction, arrOperators[dwOperatorIndex]);
|
|
} else if (fHasClass) {
|
|
sprintf(szName, "%s::%s", szClass, szFunction);
|
|
} else {
|
|
sprintf(szName, "%s", szFunction);
|
|
}
|
|
}
|
|
|
|
// Get function forwarding information for
|
|
// imported functions.
|
|
// This is done by loading the module and sniffing
|
|
// its export table.
|
|
bool GetForwardFunctions(SModule* pModule)
|
|
{
|
|
// Open the DLL module.
|
|
char szFileName[1024];
|
|
char* pstr;
|
|
|
|
// Search the path and windows directories for it.
|
|
if(SearchPath(0, pModule->m_szName, 0, 1024, szFileName, &pstr)==0)
|
|
return false;
|
|
|
|
HANDLE hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ,
|
|
0, OPEN_EXISTING, 0, 0);
|
|
|
|
if(hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
PrintLastError();
|
|
return false;
|
|
}
|
|
|
|
HANDLE hMap = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);
|
|
|
|
if(hMap == 0)
|
|
{
|
|
PrintLastError();
|
|
return false;
|
|
}
|
|
|
|
void* pvFileBase = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
|
if(!pvFileBase)
|
|
{
|
|
PrintLastError();
|
|
return false;
|
|
}
|
|
|
|
// Get the MS-DOS compatible header
|
|
PIMAGE_DOS_HEADER pidh = reinterpret_cast<PIMAGE_DOS_HEADER>(pvFileBase);
|
|
if(pidh->e_magic != IMAGE_DOS_SIGNATURE)
|
|
{
|
|
fprintf(stderr, "File is not a valid executable\n");
|
|
return false;
|
|
}
|
|
|
|
// Get the NT header
|
|
PIMAGE_NT_HEADERS pinth = reinterpret_cast<PIMAGE_NT_HEADERS>(
|
|
reinterpret_cast<DWORD>(pvFileBase) + pidh->e_lfanew);
|
|
|
|
if(pinth->Signature != IMAGE_NT_SIGNATURE)
|
|
{
|
|
// Not a valid Win32 executable, may be a Win16 or OS/2 exe
|
|
fprintf(stderr, "File is not a valid executable\n");
|
|
return false;
|
|
}
|
|
|
|
|
|
// Get the other headers
|
|
PIMAGE_FILE_HEADER pifh = &pinth->FileHeader;
|
|
PIMAGE_OPTIONAL_HEADER pioh = &pinth->OptionalHeader;
|
|
PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION(pinth);
|
|
|
|
// If no exports, we're done.
|
|
if(pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size == 0)
|
|
return true;
|
|
|
|
DWORD dwVAImageDir =
|
|
pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
|
|
|
|
|
// Locate the section with this image directory.
|
|
for(int i = 0; i < pifh->NumberOfSections; i++)
|
|
{
|
|
if( (dwVAImageDir >= pish[i].VirtualAddress) &&
|
|
(dwVAImageDir < (pish[i].VirtualAddress + pish[i].SizeOfRawData)))
|
|
{
|
|
pish = &pish[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(i == pifh->NumberOfSections)
|
|
{
|
|
fprintf(stderr, "Could not locate export directory section\n");
|
|
return false;
|
|
}
|
|
|
|
DWORD dwBase = reinterpret_cast<DWORD>(pvFileBase) +
|
|
pish->PointerToRawData - pish->VirtualAddress;
|
|
|
|
PIMAGE_EXPORT_DIRECTORY pied =
|
|
reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(dwBase + dwVAImageDir);
|
|
|
|
DWORD* pdwNames = reinterpret_cast<DWORD*>(dwBase +
|
|
pied->AddressOfNames);
|
|
|
|
WORD* pwOrdinals = reinterpret_cast<WORD*>(dwBase +
|
|
pied->AddressOfNameOrdinals);
|
|
|
|
DWORD* pdwAddresses = reinterpret_cast<DWORD*>(dwBase +
|
|
pied->AddressOfFunctions);
|
|
|
|
for(unsigned hint = 0; hint < pied->NumberOfNames; hint++)
|
|
{
|
|
char* szFunction = reinterpret_cast<PSTR>(dwBase + pdwNames[hint]);
|
|
|
|
// Duck out early if this function isn't used in the executable.
|
|
SFunction* pFunc = pModule->m_pFunctions;
|
|
while(pFunc)
|
|
{
|
|
if(strcmp(pFunc->m_szName, szFunction)==0)
|
|
break;
|
|
|
|
pFunc = pFunc->m_pNext;
|
|
}
|
|
|
|
if(pFunc == 0)
|
|
continue;
|
|
|
|
int ordinal = pied->Base + static_cast<DWORD>(pwOrdinals[hint]);
|
|
DWORD dwAddress = pdwAddresses[ordinal-pied->Base];
|
|
|
|
// Check if this function has been forwarded to another DLL
|
|
if( ((dwAddress) >= dwVAImageDir) &&
|
|
((dwAddress) <
|
|
(dwVAImageDir + pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)))
|
|
{
|
|
char* szForward = reinterpret_cast<char*>(dwBase + dwAddress);
|
|
|
|
pFunc->m_szForward = strdup(szForward);
|
|
}
|
|
}
|
|
UnmapViewOfFile(pvFileBase);
|
|
|
|
CloseHandle(hMap);
|
|
CloseHandle(hFile);
|
|
return true;
|
|
}
|
|
|
|
// Look through the import directory, and build a list of all imported functions
|
|
bool ParseImportDirectory(PIMAGE_FILE_HEADER pifh, PIMAGE_OPTIONAL_HEADER pioh,
|
|
PIMAGE_SECTION_HEADER pish, void* pvFileBase,
|
|
bool fDelayed)
|
|
{
|
|
// Get which directory we want (normal imports or delayed imports)
|
|
DWORD dwDir = (fDelayed) ?
|
|
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT : IMAGE_DIRECTORY_ENTRY_IMPORT;
|
|
|
|
// Bail if no imports
|
|
if(pioh->DataDirectory[dwDir].Size == 0)
|
|
return true;
|
|
|
|
// Locate the import directory in the image.
|
|
PIMAGE_SECTION_HEADER pishImportDirectory = 0;
|
|
|
|
DWORD dwVAImageDir = pioh->DataDirectory[dwDir].VirtualAddress;
|
|
for(int i = 0; i < pifh->NumberOfSections; i++)
|
|
{
|
|
if((dwVAImageDir >= pish[i].VirtualAddress) &&
|
|
dwVAImageDir < (pish[i].VirtualAddress + pish[i].SizeOfRawData))
|
|
{
|
|
// This is it.
|
|
pishImportDirectory = &pish[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(pishImportDirectory == 0)
|
|
{
|
|
fprintf(stderr, "Cannot locate %s%s\n",((fDelayed) ? "delayed " : ""),
|
|
"import directory section");
|
|
|
|
return false;
|
|
}
|
|
|
|
// Get the base address for the image.
|
|
DWORD dwBase = reinterpret_cast<DWORD>(pvFileBase)
|
|
+ pishImportDirectory->PointerToRawData
|
|
- pishImportDirectory->VirtualAddress;
|
|
|
|
// Get the import descriptor array
|
|
PIMAGE_IMPORT_DESCRIPTOR piid =
|
|
reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(dwBase + dwVAImageDir);
|
|
|
|
// Loop through all imported modules
|
|
while(piid->FirstThunk || piid->OriginalFirstThunk)
|
|
{
|
|
SModule* psm = new SModule;
|
|
psm->m_szName = strdup(reinterpret_cast<char*>(dwBase + piid->Name));
|
|
|
|
// Check if it is already in the list
|
|
SModule* pTemp = g_pModules;
|
|
while(pTemp)
|
|
{
|
|
if(strcmp(pTemp->m_szName, psm->m_szName) == 0)
|
|
break;
|
|
|
|
pTemp = pTemp->m_pNext;
|
|
}
|
|
|
|
// If not, insert it
|
|
if(pTemp == 0)
|
|
{
|
|
InsertModuleSorted(psm);
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, get rid of it.
|
|
pTemp = g_pModules;
|
|
while(pTemp)
|
|
{
|
|
if(strcmp(pTemp->m_szName, psm->m_szName)==0)
|
|
break;
|
|
|
|
pTemp = pTemp->m_pNext;
|
|
}
|
|
assert(pTemp);
|
|
free(psm->m_szName);
|
|
delete psm;
|
|
psm = pTemp;
|
|
}
|
|
|
|
// Get the function imports from this module.
|
|
PIMAGE_THUNK_DATA pitdf = 0;
|
|
PIMAGE_THUNK_DATA pitda = 0;
|
|
|
|
// Check for MS or Borland format
|
|
if(piid->OriginalFirstThunk)
|
|
{
|
|
// MS format, function array is in original first thunk.
|
|
pitdf = reinterpret_cast<PIMAGE_THUNK_DATA>(dwBase +
|
|
piid->OriginalFirstThunk);
|
|
|
|
// If the time stamp is set, the module has been bound,
|
|
// and first thunk is the bound address array.
|
|
if(piid->TimeDateStamp)
|
|
{
|
|
pitda = reinterpret_cast<PIMAGE_THUNK_DATA>(dwBase +
|
|
piid->FirstThunk);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Borland format uses first thunk for function array
|
|
pitdf = reinterpret_cast<PIMAGE_THUNK_DATA>(dwBase +
|
|
piid->FirstThunk);
|
|
}
|
|
|
|
while(pitdf->u1.Ordinal)
|
|
{
|
|
SFunction* psf = new SFunction;
|
|
|
|
if(IMAGE_SNAP_BY_ORDINAL(pitdf->u1.Ordinal))
|
|
{
|
|
psf->m_iOrdinal = static_cast<int>(IMAGE_ORDINAL(pitdf->u1.Ordinal));
|
|
psf->m_iHint = -1;
|
|
|
|
char szTemp[1024];
|
|
sprintf(szTemp, "Unnamed%6d", psf->m_iOrdinal);
|
|
|
|
psf->m_szName = strdup(szTemp);
|
|
}
|
|
else
|
|
{
|
|
PIMAGE_IMPORT_BY_NAME piibn =
|
|
reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(dwBase +
|
|
(DWORD)(pitdf->u1.AddressOfData));
|
|
char* szName = reinterpret_cast<char*>(piibn->Name);
|
|
char szBuffer[512];
|
|
LinkName2Name(szName, szBuffer);
|
|
psf->m_szName = strdup(szBuffer);
|
|
|
|
psf->m_iOrdinal = -1;
|
|
psf->m_iHint = piibn->Hint;
|
|
}
|
|
|
|
psf->m_fDelayedImport = fDelayed;
|
|
psf->m_dwAddress = pitda ? (DWORD) pitda->u1.Function :
|
|
reinterpret_cast<DWORD>(INVALID_HANDLE_VALUE);
|
|
|
|
// Do a sorted insert of the function
|
|
InsertFunctionSorted(psm, psf);
|
|
|
|
// Go to next function
|
|
pitdf++;
|
|
|
|
if(pitda)
|
|
pitda++;
|
|
}
|
|
|
|
// Go to next entry
|
|
piid++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GetImports(char* szExecutable)
|
|
{
|
|
// Open the file
|
|
HANDLE hFile = CreateFile(szExecutable, GENERIC_READ, FILE_SHARE_READ, 0,
|
|
OPEN_EXISTING, 0, 0);
|
|
if(hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
PrintLastError();
|
|
return false;
|
|
}
|
|
|
|
// Map this file into memory
|
|
HANDLE hMap = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);
|
|
if(hMap == 0)
|
|
{
|
|
PrintLastError();
|
|
return false;
|
|
}
|
|
|
|
void* pvFileBase;
|
|
pvFileBase = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
|
if(pvFileBase == 0)
|
|
{
|
|
PrintLastError();
|
|
return false;
|
|
}
|
|
|
|
// Get the MS-DOS compatible header
|
|
PIMAGE_DOS_HEADER pidh = reinterpret_cast<PIMAGE_DOS_HEADER>(pvFileBase);
|
|
if(pidh->e_magic != IMAGE_DOS_SIGNATURE)
|
|
{
|
|
fprintf(stderr,"File is not a valid executable\n");
|
|
return false;
|
|
}
|
|
|
|
// Get the NT header
|
|
PIMAGE_NT_HEADERS pinth = reinterpret_cast<PIMAGE_NT_HEADERS>(
|
|
reinterpret_cast<DWORD>(pvFileBase) + pidh->e_lfanew);
|
|
|
|
if(pinth->Signature != IMAGE_NT_SIGNATURE)
|
|
{
|
|
// Not a valid Win32 executable, may be a Win16 or OS/2 exe
|
|
fprintf(stderr, "File is not a valid executable\n");
|
|
return false;
|
|
}
|
|
|
|
|
|
// Get the other headers
|
|
PIMAGE_FILE_HEADER pifh = &pinth->FileHeader;
|
|
PIMAGE_OPTIONAL_HEADER pioh = &pinth->OptionalHeader;
|
|
PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION(pinth);
|
|
|
|
// Get normal imports
|
|
if(!ParseImportDirectory(pifh, pioh, pish, pvFileBase, false))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Get delayed imports
|
|
if(!ParseImportDirectory(pifh, pioh, pish, pvFileBase, true))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Resolve forwarded functions
|
|
SModule* pModule = g_pModules;
|
|
while(pModule)
|
|
{
|
|
GetForwardFunctions(pModule);
|
|
pModule = pModule->m_pNext;
|
|
}
|
|
|
|
// We're done with the file.
|
|
if(!UnmapViewOfFile(pvFileBase))
|
|
{
|
|
PrintLastError();
|
|
return false;
|
|
}
|
|
|
|
CloseHandle(hMap);
|
|
CloseHandle(hFile);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Return true if function name matches search string, false otherwise.
|
|
bool MatchFunction(const char* szFunc, const char* szSearch)
|
|
{
|
|
if(strcmp(szSearch, "*") == 0)
|
|
return true;
|
|
|
|
while(*szSearch != '\0' && *szFunc != '\0')
|
|
{
|
|
// If we get a ?, we don't care and move on to the next
|
|
// character.
|
|
if(*szSearch == '?')
|
|
{
|
|
szSearch++;
|
|
szFunc++;
|
|
continue;
|
|
}
|
|
|
|
// If we have a wildcard, move to next search string and search for substring
|
|
if(*szSearch == '*')
|
|
{
|
|
const char* szCurrSearch;
|
|
szSearch++;
|
|
|
|
if(*szSearch == '\0')
|
|
return true;
|
|
|
|
// Don't change starting point.
|
|
szCurrSearch = szSearch;
|
|
for(;;)
|
|
{
|
|
// We're done if we hit another wildcard
|
|
if(*szCurrSearch == '*' ||
|
|
*szCurrSearch == '?')
|
|
{
|
|
// Update the permanent search position.
|
|
szSearch = szCurrSearch;
|
|
break;
|
|
}
|
|
// At end of both strings, return true.
|
|
if((*szCurrSearch == '\0') && (*szFunc == '\0'))
|
|
return true;
|
|
|
|
// We never found it
|
|
if(*szFunc == '\0')
|
|
return false;
|
|
|
|
// If it doesn't match, start over
|
|
if(toupper(*szFunc) != toupper(*szCurrSearch))
|
|
{
|
|
// If mismatch on first character
|
|
// of search string, move to next
|
|
// character in function string.
|
|
if(szCurrSearch == szSearch)
|
|
szFunc++;
|
|
else
|
|
szCurrSearch = szSearch;
|
|
}
|
|
else
|
|
{
|
|
szFunc++;
|
|
szCurrSearch++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(toupper(*szFunc) != toupper(*szSearch))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
szFunc++;
|
|
szSearch++;
|
|
}
|
|
}
|
|
|
|
if((*szFunc == 0) && ((*szSearch == '\0') || (strcmp(szSearch,"*")==0)))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void PrintModule(SModule* pMod, char* szSearch, bool fVerbose, FILE* pfOut)
|
|
{
|
|
bool fModNamePrinted = false;
|
|
|
|
SFunction* pFunc = pMod->m_pFunctions;
|
|
while(pFunc)
|
|
{
|
|
if(!MatchFunction(pFunc->m_szName, szSearch))
|
|
{
|
|
pFunc = pFunc->m_pNext;
|
|
continue;
|
|
}
|
|
|
|
if(!fModNamePrinted)
|
|
{
|
|
fModNamePrinted = true;
|
|
fprintf(pfOut, "\n%s:\n", pMod->m_szName);
|
|
|
|
if(fVerbose)
|
|
fprintf(pfOut, "%-42s%-8s%-5s%-12s%s\n", "Function", "Ordinal",
|
|
"Hint", "Address", "Delayed");
|
|
}
|
|
|
|
if(fVerbose)
|
|
{
|
|
fprintf(pfOut,"%-45s", (pFunc->m_szForward == 0)
|
|
? pFunc->m_szName : pFunc->m_szForward);
|
|
|
|
if(pFunc->m_iOrdinal==-1)
|
|
fprintf(pfOut, "%-5s", "N/A");
|
|
else
|
|
fprintf(pfOut, "%-5d", pFunc->m_iOrdinal);
|
|
|
|
if(pFunc->m_iHint == -1)
|
|
fprintf(pfOut, "%-5s", "N/A");
|
|
else
|
|
fprintf(pfOut, "%-5d", pFunc->m_iHint);
|
|
|
|
|
|
if(pFunc->m_dwAddress == static_cast<DWORD>(-1))
|
|
fprintf(pfOut, "%-12s", "Not Bound");
|
|
else
|
|
fprintf(pfOut, "%-#12x", pFunc->m_dwAddress);
|
|
|
|
fprintf(pfOut,"%s\n", pFunc->m_fDelayedImport ? "Yes" : "No");
|
|
|
|
}
|
|
else
|
|
fprintf(pfOut, "%s\n", pFunc->m_szName);
|
|
|
|
pFunc = pFunc->m_pNext;
|
|
}
|
|
}
|
|
|
|
int _cdecl main(int argc, char** argv)
|
|
{
|
|
if(argc < 2)
|
|
{
|
|
fprintf(stderr,"Usage: dc executable [/v /s: func /f]\n");
|
|
return 0;
|
|
}
|
|
|
|
// Parse command line
|
|
char* szFileName = argv[1];
|
|
if( (strnicmp(szFileName, "/?", 2) == 0) ||
|
|
(strncmp(szFileName, "/h", 2) == 0))
|
|
{
|
|
printf("Usage: dc executable [/v /s: func /f]\n");
|
|
printf("executable:\t\tName of executable file to check\n");
|
|
printf("/v:\t\t\tVerbose\n");
|
|
printf("/s: func\t\tDisplay all functions matching func search string");
|
|
printf(", * and ? allowed.\n");
|
|
printf("/f:\t\t\tDisplay alphabetically by function, not module.\n");
|
|
printf("/o: File\t\tRedirect all output to File.\n");
|
|
return 0;
|
|
}
|
|
|
|
FILE* pfOutput = 0;
|
|
|
|
// If no extension, just add .exe
|
|
if(strchr(szFileName, '.') == 0)
|
|
{
|
|
szFileName = new char[strlen(argv[1]) + 5];
|
|
strcpy(szFileName, argv[1]);
|
|
strcat(szFileName, ".exe");
|
|
}
|
|
|
|
bool fVerbose = false;
|
|
bool fUseStdout = true;
|
|
char* szSearch = "*";
|
|
bool fSortByFunction = false;
|
|
|
|
// Get flags.
|
|
for(int i = 2; i < argc; i++)
|
|
{
|
|
char* szFlag = argv[i];
|
|
if(stricmp(szFlag, "/v") == 0)
|
|
{
|
|
fVerbose = true;
|
|
}
|
|
else if(strnicmp(szFlag, "/s:", 3) == 0)
|
|
{
|
|
if((i == argc-1) && (strlen(szFlag) <= 3))
|
|
{
|
|
fprintf(stderr,"Missing search string\n");
|
|
return 0;
|
|
}
|
|
|
|
if(strlen(szFlag) > 3)
|
|
{
|
|
szSearch = strdup(&szFlag[3]);
|
|
}
|
|
else
|
|
{
|
|
szSearch = argv[i+1];
|
|
i++;
|
|
}
|
|
}
|
|
else if(stricmp(szFlag, "/f") == 0)
|
|
{
|
|
fSortByFunction = true;
|
|
}
|
|
else if( (strnicmp(szFlag, "/h",2) == 0) ||
|
|
(strnicmp(szFlag, "/?",2) == 0))
|
|
{
|
|
printf("Usage: dc executable [/v /s: func /f]\n");
|
|
printf("executable:\t\tName of executable file to check\n");
|
|
printf("/v:\t\t\tVerbose\n");
|
|
printf("/s: func\t\tDisplay all functions matching func search string");
|
|
printf(", * and ? allowed.\n");
|
|
printf("/f:\t\t\tDisplay alphabetically by function, not module.\n");
|
|
printf("/o: File\t\tRedirect all output to File.\n");
|
|
return 0;
|
|
}
|
|
else if(strnicmp(szFlag, "/o:", 3) == 0)
|
|
{
|
|
fUseStdout = false;
|
|
if( (i == argc-1) && (strlen(szFlag) <= 3))
|
|
{
|
|
fprintf(stderr, "Missing output file name\n");
|
|
return 0;
|
|
}
|
|
if(strlen(szFlag) > 3)
|
|
{
|
|
pfOutput = fopen(&szFlag[3], "wt");
|
|
}
|
|
else
|
|
{
|
|
pfOutput = fopen(argv[i+1], "wt");
|
|
i++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr,"Unknown command line option, %s\n", szFlag);
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
if(fUseStdout)
|
|
pfOutput = stdout;
|
|
|
|
if(!pfOutput)
|
|
{
|
|
fprintf(stderr,"Unable to open output file\n");
|
|
return 0;
|
|
}
|
|
|
|
// We wrap this code in a try block, because
|
|
// we map the file into memory. If the file
|
|
// is invalid and if the pointers in it are garbage,
|
|
// we should get an access violation, which is caught.
|
|
try
|
|
{
|
|
if(!GetImports(szFileName))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
catch(...)
|
|
{
|
|
fprintf(stderr, "Invalid executable file\n");
|
|
return 0;
|
|
}
|
|
|
|
if(fSortByFunction)
|
|
{
|
|
// Create a global list of functions
|
|
SModule* pGlobal = new SModule;
|
|
pGlobal->m_szName = "All Imported Functions";
|
|
|
|
SModule* pMod = g_pModules;
|
|
while(pMod)
|
|
{
|
|
SFunction* pFunc = pMod->m_pFunctions;
|
|
while(pFunc)
|
|
{
|
|
// Create a copy of this function
|
|
// This is a shallow copy, but
|
|
// it should be ok, since we don't
|
|
// delete the original
|
|
SFunction* pNew = new SFunction;
|
|
memcpy(pNew, pFunc, sizeof(*pFunc));
|
|
|
|
InsertFunctionSorted(pGlobal, pNew);
|
|
|
|
pFunc = pFunc->m_pNext;
|
|
}
|
|
pMod = pMod->m_pNext;
|
|
}
|
|
|
|
PrintModule(pGlobal, szSearch, fVerbose, pfOutput);
|
|
|
|
}
|
|
else
|
|
{
|
|
SModule* pMod = g_pModules;
|
|
while(pMod)
|
|
{
|
|
PrintModule(pMod, szSearch, fVerbose, pfOutput);
|
|
pMod = pMod->m_pNext;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
} |