[CREATESPEC]

- Implement retrieving export names from forwarders
- Add error() function to print errors
- Refactor image/symbol loading
- Fix some bugs and add some hacks, so that it compiles in out tree
- Add to build

svn path=/trunk/; revision=70569
This commit is contained in:
Timo Kreuzer 2016-01-10 18:50:36 +00:00
parent 3c1003aaf5
commit 1c0099452e
2 changed files with 350 additions and 130 deletions

View file

@ -1,4 +1,5 @@
add_subdirectory(bootvid_font_generator)
add_subdirectory(createspec)
add_subdirectory(gdb2)
add_subdirectory(gdihv)
add_subdirectory(genguid)

View file

@ -8,18 +8,31 @@
- Resolve forwarders
*/
#define MINGW_HAS_SECURE_API
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#ifdef __REACTOS__
#include <dbghelp.h>
#include <cvconst.h>
// dirty hacks!
#define sprintf_s(dst, size, format, ...) sprintf(dst, format, __VA_ARGS__)
#define vsprintf_s(dst, size, format, ap) vsprintf(dst, format, ap)
#define fopen_s(pfile, name, mode) ((*pfile = fopen(name, mode)), (*pfile != 0) ? 0 : -1)
#define strcpy_s(dst, size, src) strncpy(dst, src, size)
#define strcat_s(dst, size, src) strncat(dst, src, size)
#else
#ifdef _MSC_VER
#pragma warning(disable:4091)
#endif
#define _NO_CVCONST_H
#include <dbghelp.h>
// doesn't seem to be defined anywhere
enum BasicType {
// This is from cvconst.h, but win sdk lacks this file
enum BasicType {
btNoType = 0,
btVoid = 1,
btChar = 2,
@ -40,7 +53,7 @@ enum BasicType {
btHresult = 31
};
typedef enum CV_call_e {
typedef enum CV_call_e {
CV_CALL_NEAR_C = 0x00,
CV_CALL_NEAR_FAST = 0x04,
CV_CALL_NEAR_STD = 0x07,
@ -49,13 +62,21 @@ typedef enum CV_call_e {
CV_CALL_CLRCALL = 0x16
} CV_call_e;
#define MAX_SYMBOL_NAME 1024
#endif // __REACTOS__
#define MAX_SYMBOL_NAME 1024
typedef struct _SYMINFO_EX
{
SYMBOL_INFO si;
CHAR achName[MAX_SYMBOL_NAME];
} SYMINFO_EX;
typedef struct _SYMBOL64_EX
{
IMAGEHLP_SYMBOL64 sym64;
CHAR achName[MAX_SYMBOL_NAME];
} SYMBOL64_EX, *PSYMBOL64_EX;
typedef enum _PARAM_TYPES
{
TYPE_NONE,
@ -99,57 +120,203 @@ typedef struct _EXPORT_DATA
} EXPORT_DATA, *PEXPORT_DATA;
HANDLE ghProcess;
CHAR gszModuleFileName[MAX_PATH+1];
HRESULT
OpenFileFromName(
_In_ PCSTR pszDllName,
_Out_ PHANDLE phFile)
void
error(
_In_ const char* pszFormat,
...)
{
HANDLE hFile;
CHAR szBuffer[512];
SIZE_T cchBuffer;
DWORD dwLastError;
va_list argptr;
/* Try current directory */
GetCurrentDirectoryA(MAX_PATH, gszModuleFileName);
strcat_s(gszModuleFileName, sizeof(gszModuleFileName), "\\");
strcat_s(gszModuleFileName, sizeof(gszModuleFileName), pszDllName);
hFile = CreateFileA(gszModuleFileName,
FILE_READ_DATA,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile != INVALID_HANDLE_VALUE)
/* Get last error */
dwLastError = GetLastError();
va_start(argptr, pszFormat);
cchBuffer = vsprintf_s(szBuffer, sizeof(szBuffer), pszFormat, argptr);
va_end(argptr);
/* Strip trailing newlines */
_Analysis_assume_(cchBuffer < sizeof(szBuffer));
while ((cchBuffer >= 1) &&
((szBuffer[cchBuffer - 1] == '\r') ||
(szBuffer[cchBuffer - 1] == '\n')))
{
*phFile = hFile;
return S_OK;
szBuffer[cchBuffer - 1] = '\0';
cchBuffer--;
}
/* Try system32 directory */
strcat_s(gszModuleFileName, sizeof(gszModuleFileName), "%systemroot%\\system32\\");
strcat_s(gszModuleFileName, sizeof(gszModuleFileName), pszDllName);
hFile = CreateFileA(gszModuleFileName,
FILE_READ_DATA,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile != INVALID_HANDLE_VALUE)
/* Check if we have an error */
if (dwLastError != ERROR_SUCCESS)
{
*phFile = hFile;
return S_OK;
/* Append error code */
cchBuffer += sprintf_s(szBuffer + cchBuffer,
sizeof(szBuffer) - cchBuffer,
" [error %lu: ", dwLastError);
/* Format the last error code */
cchBuffer += FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwLastError,
0,
szBuffer + cchBuffer,
(DWORD)(sizeof(szBuffer) - cchBuffer),
NULL);
/* Strip trailing newlines */
_Analysis_assume_(cchBuffer < sizeof(szBuffer));
while ((cchBuffer >= 1) &&
((szBuffer[cchBuffer - 1] == '\r') ||
(szBuffer[cchBuffer - 1] == '\n')))
{
szBuffer[cchBuffer - 1] = '\0';
cchBuffer--;
}
fprintf(stderr, "%s]\n", szBuffer);
}
else
{
fprintf(stderr, "%s\n", szBuffer);
}
}
BOOL
InitDbgHelp(
VOID)
{
static const char *pszMsSymbolServer = "srv**symbols*http://msdl.microsoft.com/download/symbols";
DWORD Options;
/* Save current process ;-) */
ghProcess = GetCurrentProcess();
/* Initialize dbghelp */
if (!SymInitialize(ghProcess, 0, FALSE))
{
error("SymInitialize() failed.");
return FALSE;
}
return HRESULT_FROM_WIN32(GetLastError());
/* Set options */
Options = SymGetOptions();
Options |= SYMOPT_ALLOW_ABSOLUTE_SYMBOLS | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEBUG;// | SYMOPT_NO_PROMPTS;
Options &= ~SYMOPT_DEFERRED_LOADS;
SymSetOptions(Options);
/* Test if we can reach the MS symbol server */
if (!SymSrvIsStore(ghProcess, pszMsSymbolServer))
{
error("Failed to connect to symbol server.");
return FALSE;
}
/* Set MS symbol server as symbol search path */
SymSetSearchPath(ghProcess, pszMsSymbolServer);
return TRUE;
}
HMODULE
LoadModuleWithSymbolsFullPath(
_In_ PSTR pszFullModuleFileName)
{
HMODULE hmod;
DWORD64 dwModuleBase;
/* Load the DLL */
hmod = LoadLibraryExA(pszFullModuleFileName,
NULL,
LOAD_IGNORE_CODE_AUTHZ_LEVEL |
DONT_RESOLVE_DLL_REFERENCES |
LOAD_WITH_ALTERED_SEARCH_PATH);
if (hmod == NULL)
{
return NULL;
}
/* Load symbols for this module */
dwModuleBase = SymLoadModule64(ghProcess,
NULL,
pszFullModuleFileName,
NULL,
(DWORD_PTR)hmod,
0);
if (dwModuleBase == 0)
{
/* ERROR_SUCCESS means, we have symbols already */
if (GetLastError() != ERROR_SUCCESS)
{
return NULL;
}
}
else
{
printf("Successfully loaded symbols for '%s'\n",
pszFullModuleFileName);
}
return hmod;
}
HMODULE
LoadModuleWithSymbols(
_In_ PSTR pszModuleName)
{
CHAR szFullFileName[MAX_PATH];
HMODULE hmod;
/* Check if the file name has a path */
if (strchr(pszModuleName, '\\') != NULL)
{
/* Try as it is */
hmod = LoadModuleWithSymbolsFullPath(pszModuleName);
if (hmod != NULL)
{
return hmod;
}
}
/* Try current directory */
GetCurrentDirectoryA(MAX_PATH, szFullFileName);
strcat_s(szFullFileName, sizeof(szFullFileName), "\\");
strcat_s(szFullFileName, sizeof(szFullFileName), pszModuleName);
hmod = LoadModuleWithSymbolsFullPath(szFullFileName);
if (hmod != NULL)
{
return hmod;
}
/* Try system32 */
strcpy_s(szFullFileName, sizeof(szFullFileName), "%systemroot%\\system32");
strcat_s(szFullFileName, sizeof(szFullFileName), pszModuleName);
hmod = LoadModuleWithSymbolsFullPath(szFullFileName);
if (hmod != NULL)
{
return hmod;
}
#ifdef _WIN64
/* Try SysWoW64 */
strcpy_s(szFullFileName, sizeof(szFullFileName), "%systemroot%\\system32");
strcat_s(szFullFileName, sizeof(szFullFileName), pszModuleName);
hmod = LoadModuleWithSymbolsFullPath(szFullFileName);
if (hmod != NULL)
{
return hmod;
}
#endif // _WIN64
return NULL;
}
HRESULT
GetExportsFromFile(
_In_ HANDLE hFile,
_In_ HMODULE hmod,
_Out_ PEXPORT_DATA* ppExportData)
{
HANDLE hMap;
PBYTE pjImageBase;
PIMAGE_EXPORT_DIRECTORY pExportDir;
ULONG i, cjExportSize, cFunctions, cjTableSize;
@ -157,28 +324,18 @@ GetExportsFromFile(
PULONG pulAddressTable, pulNameTable;
PUSHORT pusOrdinalTable;
/* Create an image file mapping */
hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
if (!hMap)
{
fprintf(stderr, "CreateFileMapping() failed: %ld\n", GetLastError());
return HRESULT_FROM_WIN32(GetLastError());
}
pjImageBase = (PBYTE)hmod;
/* Map the file */
pjImageBase = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
if(pjImageBase == NULL)
{
fprintf(stderr, "MapViewOfFile() failed: %ld\n", GetLastError());
CloseHandle(hMap);
return HRESULT_FROM_WIN32(GetLastError());
}
/* Get the export directory */
pExportDir = ImageDirectoryEntryToData(pjImageBase,
/* Get the export directory */
pExportDir = ImageDirectoryEntryToData(pjImageBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&cjExportSize);
if (pExportDir == NULL)
{
fprintf(stderr, "Failed to get export directory\n");
return E_FAIL;
}
cFunctions = pExportDir->NumberOfFunctions;
cjTableSize = FIELD_OFFSET(EXPORT_DATA, aExports[cFunctions]);
@ -186,6 +343,7 @@ GetExportsFromFile(
pExportData = malloc(cjTableSize);
if (pExportData == NULL)
{
error("Failed to allocate %u bytes of memory for export table\n", cjTableSize);
return E_OUTOFMEMORY;
}
@ -199,7 +357,8 @@ GetExportsFromFile(
for (i = 0; i < cFunctions; i++)
{
PVOID pvFunction = (pjImageBase + pulAddressTable[i]);
/* Check if this is a forwarder */
if ((ULONG_PTR)((PUCHAR)pvFunction - (PUCHAR)pExportDir) < cjExportSize)
{
pExportData->aExports[i].pszForwarder = _strdup(pvFunction);
@ -223,8 +382,6 @@ GetExportsFromFile(
}
*ppExportData = pExportData;
UnmapViewOfFile(pjImageBase);
CloseHandle(hMap);
return S_OK;
}
@ -233,7 +390,7 @@ CALLBACK
EnumParametersCallback(
_In_ PSYMBOL_INFO pSymInfo,
_In_ ULONG SymbolSize,
_In_opt_ PVOID UserContext)
_In_ PVOID UserContext)
{
PEXPORT pExport = (PEXPORT)UserContext;
enum SymTagEnum eSymTag;
@ -282,7 +439,8 @@ EnumParametersCallback(
break;
}
}
/* 'long' type */
/* Default to 'long' type */
pExport->aeParameters[pExport->cParameters - 1] = TYPE_LONG;
break;
@ -309,12 +467,15 @@ EnumParametersCallback(
TI_GET_BASETYPE,
&eBaseType))
{
/* Check for string types */
if (eBaseType == btChar)
{
/* 'str' type */
pExport->aeParameters[pExport->cParameters - 1] = TYPE_STR;
}
else if (eBaseType == btWChar)
{
/* 'wstr' type */
pExport->aeParameters[pExport->cParameters - 1] = TYPE_WSTR;
}
}
@ -335,46 +496,92 @@ EnumParametersCallback(
return TRUE;
}
ULONG64
GetFunctionFromForwarder(
_In_ PCSTR pszForwarder)
{
CHAR szDllName[MAX_SYMBOL_NAME];
PCH pchDot, pszName;
ULONG64 ullFunction;
HMODULE hmod;
/* Copy the forwarder name */
strcpy_s(szDllName, sizeof(szDllName), pszForwarder);
/* Find the '.' */
pchDot = strchr(szDllName, '.');
if (pchDot == NULL)
{
error("Invalid name for forwarder '%s'!", pszForwarder);
return 0;
}
/* Terminate DLL name */
*pchDot = '\0';
/* Load the DLL */
hmod = LoadModuleWithSymbols(szDllName);
if (hmod == NULL)
{
error("Failed to load module for forwarder '%s'!", pszForwarder);
return 0;
}
/* Get the function name and check for ordinal */
pszName = pchDot + 1;
if (pszName[0] == '#')
{
ULONG iOrdinal = strtoul(pszName + 1, NULL, 10);
if ((iOrdinal == 0) || (iOrdinal > 0xFFFF))
{
error("Got invalid ordinal %u for ''", iOrdinal, pszForwarder);
return 0;
}
pszName = (PSTR)(ULONG_PTR)iOrdinal;
}
/* Get the function address */
ullFunction = (ULONG_PTR)GetProcAddress(hmod, pszName);
if (ullFunction == 0)
{
error("Failed to resolve '%s' in '%s'.", pchDot + 1, szDllName);
return 0;
}
return ullFunction;
}
HRESULT
ParseExportSymbols(
_In_ HANDLE hFile,
ParseImageSymbols(
_In_ HMODULE hmod,
_Inout_ PEXPORT_DATA pExportData)
{
DWORD Options;
DWORD64 dwModuleBase;
ULONG i;
IMAGEHLP_STACK_FRAME StackFrame;
SYMINFO_EX sym;
dwModuleBase = (DWORD_PTR)hmod;
/* Initialize dbghelp */
if (!SymInitialize(ghProcess, 0, FALSE))
return E_FAIL;
Options = SymGetOptions();
Options |= SYMOPT_ALLOW_ABSOLUTE_SYMBOLS | SYMOPT_DEBUG;// | SYMOPT_NO_PROMPTS;
Options &= ~SYMOPT_DEFERRED_LOADS;
SymSetOptions(Options);
SymSetSearchPath(ghProcess, "srv**symbols*http://msdl.microsoft.com/download/symbols");
printf("Loading symbols, please wait...\n");
dwModuleBase = SymLoadModule64(ghProcess, 0, gszModuleFileName, 0, 0, 0);
if (dwModuleBase == 0)
{
fprintf(stderr, "SymLoadModule64() failed: %ld\n", GetLastError());
return E_FAIL;
}
/* Loop through all exports */
for (i = 0; i < pExportData->cNumberOfExports; i++)
{
PEXPORT pExport = &pExportData->aExports[i];
ULONG64 ullFunction = dwModuleBase + pExportData->aExports[i].ulRva;
ULONG64 ullDisplacement;
/* Skip forwarder */
/* Check if this is a forwarder */
if (pExport->pszForwarder != NULL)
continue;
{
/* Load the module and get the function address */
ullFunction = GetFunctionFromForwarder(pExport->pszForwarder);
if (ullFunction == 0)
{
printf("Failed to get function for forwarder '%s'. Skipping.\n", pExport->pszForwarder);
continue;
}
}
RtlZeroMemory(&sym, sizeof(sym));
sym.si.SizeOfStruct = sizeof(SYMBOL_INFO);
@ -383,16 +590,16 @@ ParseExportSymbols(
/* Try to find the symbol */
if (!SymFromAddr(ghProcess, ullFunction, &ullDisplacement, &sym.si))
{
printf("Error: SymFromAddr() failed. Error code: %u \n", GetLastError());
error("Error: SymFromAddr() failed.");
continue;
}
/* Symbol found. Check if it is a function */
/* Get the symbol name */
pExport->pszSymbol = _strdup(sym.si.Name);
/* Check if it is a function */
if (sym.si.Tag == SymTagFunction)
{
/* If we don't have a name yet, get one */
pExport->pszSymbol = _strdup(sym.si.Name);
/* Get the calling convention */
if (!SymGetTypeInfo(ghProcess,
dwModuleBase,
@ -401,7 +608,7 @@ ParseExportSymbols(
&pExport->dwCallingConvention))
{
/* Fall back to __stdcall */
pExport->dwCallingConvention = 0x07; // CV_CALL_NEAR_STD
pExport->dwCallingConvention = CV_CALL_NEAR_STD;
}
/* Set the context to the function address */
@ -409,8 +616,7 @@ ParseExportSymbols(
StackFrame.InstructionOffset = ullFunction;
if (!SymSetContext(ghProcess, &StackFrame, NULL))
{
DWORD dwLastError = GetLastError();
__debugbreak();
error("SymSetContext failed for i = %u.", i);
continue;
}
@ -421,11 +627,14 @@ ParseExportSymbols(
EnumParametersCallback,
pExport))
{
DWORD dwLastError = GetLastError();
__debugbreak();
error("SymEnumSymbols failed for i = %u.", i);
continue;
}
}
else if (sym.si.Tag == SymTagPublicSymbol)
{
pExport->dwCallingConvention = CV_CALL_NEAR_STD;
}
else if (sym.si.Tag == SymTagData)
{
pExport->fData = TRUE;
@ -457,8 +666,6 @@ GetCallingConvention(
return "syscall";
case CV_CALL_THISCALL:
return "thiscall";
case CV_CALL_CLRCALL:
return "clrcall";
default:
__debugbreak();
}
@ -478,7 +685,7 @@ CreateSpecFile(
/* Create the spec file */
if (fopen_s(&file, pszSpecFile, "w") != 0)
{
fprintf(stderr, "Failed to open spec file: '%s'\n", pszSpecFile);
error("Failed to open spec file: '%s'\n", pszSpecFile);
return E_FAIL;
}
@ -487,7 +694,7 @@ CreateSpecFile(
{
pExport = &pExportData->aExports[i];
fprintf(file, "%u %s ", i + 1, GetCallingConvention(pExport));
fprintf(file, "%lu %s ", i + 1, GetCallingConvention(pExport));
//if (pExport->fNoName)
if (pExport->pszName == NULL)
{
@ -504,7 +711,7 @@ CreateSpecFile(
}
else
{
fprintf(file, "NamelessExport_%u", i);
fprintf(file, "NamelessExport_%lu", i);
}
if (!pExport->fData)
@ -539,43 +746,24 @@ int main(int argc, char* argv[])
HRESULT hr;
CHAR szSpecFile[MAX_PATH];
PSTR pszSpecFile;
HANDLE hFile;
PEXPORT_DATA pExportData;
HMODULE hmod;
// check params
// help
ghProcess = GetCurrentProcess();
/* Open the file */
hr = OpenFileFromName(argv[1], &hFile);
if (!SUCCEEDED(hr))
/* Check parameters */
if ((argc < 2) || !strcmp(argv[1], "/?"))
{
fprintf(stderr, "Failed to open file: %lx\n", hr);
return hr;
}
/* Get the exports */
hr = GetExportsFromFile(hFile, &pExportData);
if (!SUCCEEDED(hr))
{
fprintf(stderr, "Failed to get exports: %lx\n", hr);
return hr;
}
/* Get additional info from symbols */
hr = ParseExportSymbols(hFile, pExportData);
if (!SUCCEEDED(hr))
{
fprintf(stderr, "Failed to get symbol information: %lx\n", hr);
printf("syntax: createspec <image file> [<spec file>]\n");
return 0;
}
/* Check if we have a spec file name */
if (argc > 2)
{
pszSpecFile = argv[2];
}
else
{
/* Create spec file name from image file name */
PSTR pszStart = strrchr(argv[1], '\\');
if (pszStart == 0)
pszStart = argv[1];
@ -585,9 +773,40 @@ int main(int argc, char* argv[])
pszSpecFile = szSpecFile;
}
hr = CreateSpecFile(pszSpecFile, pExportData);
/* Initialize dbghelp.dll */
if (!InitDbgHelp())
{
error("Failed to init dbghelp!\n"
"Make sure you have dbghelp.dll and symsrv.dll in the same folder.\n");
return E_FAIL;
}
CloseHandle(hFile);
/* Load the file including symbols */
printf("Loading symbols for '%s', please wait...\n", argv[1]);
hmod = LoadModuleWithSymbols(argv[1]);
if (hmod == NULL)
{
error("Failed to load module '%s'!", argv[1]);
return E_FAIL;
}
/* Get the exports */
hr = GetExportsFromFile(hmod, &pExportData);
if (!SUCCEEDED(hr))
{
error("Failed to get exports: %lx\n", hr);
return hr;
}
/* Get additional info from symbols */
hr = ParseImageSymbols(hmod, pExportData);
if (!SUCCEEDED(hr))
{
error("Failed to get symbol information: hr=%lx\n", hr);
}
/* Write the spec file */
hr = CreateSpecFile(pszSpecFile, pExportData);
printf("Spec file '%s' was successfully written.\n", szSpecFile);