You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
427 lines
12 KiB
427 lines
12 KiB
//
|
|
// cvtodbg.cpp
|
|
//
|
|
// Takes a PE file and a file containing CV info, and jams the CV info
|
|
// into the PE file (trashing it). However, The Jammed PE File when
|
|
// splitsym'ed gives a dbg file, which can be used for debugging.
|
|
//
|
|
//
|
|
#undef UNICODE
|
|
|
|
#include "windows.h"
|
|
#include "imagehlp.h"
|
|
#include "stdio.h"
|
|
#include "stdlib.h"
|
|
////////////////////////////////////////
|
|
//
|
|
// Data
|
|
//
|
|
char szImageName[MAX_PATH];
|
|
char szCVName[MAX_PATH];
|
|
char szPdbName[MAX_PATH];
|
|
char szPdbCurrentPath[MAX_PATH];
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
HANDLE hMappedFile = INVALID_HANDLE_VALUE;
|
|
LPVOID pvImageBase = NULL;
|
|
|
|
BOOL fVerbose = FALSE;
|
|
BOOL fForce = FALSE;
|
|
|
|
typedef struct NB10I // NB10 debug info
|
|
{
|
|
DWORD nb10; // NB10
|
|
DWORD off; // offset, always 0
|
|
DWORD sig;
|
|
DWORD age;
|
|
} NB10I;
|
|
|
|
typedef struct cvinfo
|
|
{
|
|
NB10I nb10;
|
|
char rgb[0x200 - sizeof(NB10I)];
|
|
} CVINFO;
|
|
|
|
|
|
////////////////////////////////////////
|
|
//
|
|
// Forward declarations
|
|
//
|
|
BOOL ParseArgs(int argc, WCHAR* argv[]);
|
|
void UpdateCodeViewInfo();
|
|
void Usage();
|
|
void Message(const char* szFormat, ...);
|
|
void Error(const char *sz, ...);
|
|
void ErrorThrow(DWORD, const char *sz, ...);
|
|
void Throw(DWORD);
|
|
void MapImage();
|
|
void UnmapImage(BOOL fTouch);
|
|
BOOL DebugDirectoryIsUseful(LPVOID, ULONG);
|
|
void RecalculateChecksum();
|
|
ULONG FileSize(HANDLE);
|
|
|
|
|
|
class FileMapping {
|
|
public:
|
|
FileMapping()
|
|
: hFile(NULL), hMapping(NULL), pView(NULL)
|
|
{
|
|
}
|
|
|
|
~FileMapping()
|
|
{
|
|
Cleanup();
|
|
}
|
|
|
|
void Cleanup()
|
|
{
|
|
if (pView != NULL)
|
|
UnmapViewOfFile(pView);
|
|
if (hMapping != NULL)
|
|
CloseHandle(hMapping);
|
|
if (hFile != NULL && hFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
bool Open(LPCTSTR szFile)
|
|
{
|
|
hFile = CreateFile(szFile,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
if (hMapping != NULL) {
|
|
pView = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Cleanup();
|
|
return false;
|
|
}
|
|
|
|
PVOID GetDataPtr()
|
|
{
|
|
return pView;
|
|
}
|
|
|
|
DWORD GetSize()
|
|
{
|
|
return GetFileSize(hFile, NULL);
|
|
}
|
|
|
|
bool IsValid()
|
|
{
|
|
return (pView != NULL);
|
|
}
|
|
|
|
private:
|
|
HANDLE hFile;
|
|
HANDLE hMapping;
|
|
PVOID pView;
|
|
};
|
|
|
|
|
|
|
|
////////////////////////////////////////
|
|
//
|
|
// Code
|
|
//
|
|
void __cdecl wmain(int argc, WCHAR* argv[])
|
|
// Main entry point
|
|
//
|
|
{
|
|
szPdbName[0] = 0;
|
|
szPdbCurrentPath[0] = 0;
|
|
if (ParseArgs(argc, argv))
|
|
{
|
|
__try
|
|
{
|
|
UpdateCodeViewInfo();
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
// nothing, just don't propagate it higher to the user
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// find the code view info;
|
|
// if new info fits in old space, rewrite; else append new cv record and fix up
|
|
// debug directory to point to the new record; append cv info to file.
|
|
void UpdateCodeViewInfo()
|
|
{
|
|
PIMAGE_NT_HEADERS pntHeaders;
|
|
ULONG cbWritten;
|
|
|
|
MapImage();
|
|
|
|
FileMapping cvMapping;
|
|
if (!cvMapping.Open(szCVName))
|
|
ErrorThrow(666, "Couldn't open CV file");
|
|
|
|
pntHeaders = ImageNtHeader(pvImageBase);
|
|
if (pntHeaders)
|
|
{
|
|
if (pntHeaders->OptionalHeader.MajorLinkerVersion >= 3 ||
|
|
pntHeaders->OptionalHeader.MinorLinkerVersion >= 5)
|
|
{
|
|
// make it non vc generated image, we are trashing the binary anyway
|
|
if ( pntHeaders->OptionalHeader.MajorLinkerVersion > 5)
|
|
pntHeaders->OptionalHeader.MajorLinkerVersion = 5;
|
|
|
|
// put dbg info back in if its already stripped.
|
|
if (pntHeaders->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED)
|
|
pntHeaders->FileHeader.Characteristics ^= IMAGE_FILE_DEBUG_STRIPPED;
|
|
|
|
ULONG ibFileCvStart = FileSize(hFile);
|
|
SetFilePointer(hFile, 0, NULL, FILE_END);
|
|
|
|
while (ibFileCvStart & 7) // Align file length to 8-bytes. Slow, but clearly
|
|
{ // works! And for 7 bytes, who cares.
|
|
BYTE zero = 0;
|
|
WriteFile(hFile, &zero, 1, &cbWritten, NULL);
|
|
ibFileCvStart++;
|
|
}
|
|
|
|
// Write out the CV info.
|
|
WriteFile(hFile, cvMapping.GetDataPtr(), cvMapping.GetSize(), &cbWritten, NULL);
|
|
|
|
// Make up a debug directory
|
|
IMAGE_DEBUG_DIRECTORY dbgdirs[2];
|
|
|
|
dbgdirs[0].Characteristics = pntHeaders->FileHeader.Characteristics;
|
|
dbgdirs[0].TimeDateStamp = pntHeaders->FileHeader.TimeDateStamp;
|
|
dbgdirs[0].MajorVersion = 0;
|
|
dbgdirs[0].MinorVersion = 0;
|
|
dbgdirs[0].Type = IMAGE_DEBUG_TYPE_MISC;
|
|
dbgdirs[0].SizeOfData = 0;
|
|
dbgdirs[0].AddressOfRawData = ibFileCvStart;
|
|
dbgdirs[0].PointerToRawData = ibFileCvStart;
|
|
|
|
dbgdirs[1].Characteristics = pntHeaders->FileHeader.Characteristics;
|
|
dbgdirs[1].TimeDateStamp = pntHeaders->FileHeader.TimeDateStamp;
|
|
dbgdirs[1].MajorVersion = 0;
|
|
dbgdirs[1].MinorVersion = 0;
|
|
dbgdirs[1].Type = IMAGE_DEBUG_TYPE_CODEVIEW;
|
|
dbgdirs[1].SizeOfData = cvMapping.GetSize();
|
|
dbgdirs[1].AddressOfRawData = ibFileCvStart;
|
|
dbgdirs[1].PointerToRawData = ibFileCvStart;
|
|
|
|
// Find the beginning of the first section and stick the debug directory there
|
|
// (did we mention we're trashing the file?)
|
|
|
|
IMAGE_SECTION_HEADER* pFirstSection = IMAGE_FIRST_SECTION(pntHeaders);
|
|
|
|
memcpy((PBYTE)((DWORD)pvImageBase + pFirstSection->PointerToRawData), &dbgdirs, sizeof(dbgdirs));
|
|
|
|
pntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress = pFirstSection->VirtualAddress;
|
|
pntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size = sizeof(dbgdirs);
|
|
|
|
}
|
|
}
|
|
UnmapImage(TRUE);
|
|
}
|
|
|
|
|
|
|
|
void MapImage()
|
|
// Map the image into memory for read-write. Caller MUST call
|
|
// Unmapimage to clean up even on failure.
|
|
{
|
|
if (fForce)
|
|
SetFileAttributesA(szImageName, FILE_ATTRIBUTE_NORMAL);
|
|
|
|
hFile = CreateFileA( szImageName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
ErrorThrow(GetLastError(), "unable to open '%s'\n", szImageName);
|
|
}
|
|
|
|
|
|
hMappedFile = CreateFileMapping( hFile,
|
|
NULL,
|
|
PAGE_READWRITE,
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
if (!hMappedFile)
|
|
{
|
|
ErrorThrow(GetLastError(), "un`able to create file mapping for '%s'\n", szImageName);
|
|
}
|
|
|
|
pvImageBase = MapViewOfFile(hMappedFile, FILE_MAP_WRITE, 0, 0, 0);
|
|
|
|
if (!pvImageBase)
|
|
{
|
|
ErrorThrow(GetLastError(), "unable to map view of '%s'\n", szImageName);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void UnmapImage(BOOL fTouch)
|
|
// Clean up whatever MapImage does
|
|
{
|
|
if (pvImageBase)
|
|
{
|
|
FlushViewOfFile(pvImageBase, 0);
|
|
UnmapViewOfFile(pvImageBase);
|
|
pvImageBase = NULL;
|
|
}
|
|
|
|
if (hMappedFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(hMappedFile);
|
|
hMappedFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (fTouch)
|
|
{
|
|
TouchFileTimes(hFile, NULL);
|
|
}
|
|
CloseHandle(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL ParseArgs(int argc, WCHAR* argv[])
|
|
// Parse the arguments and set our flags appropriately
|
|
{
|
|
WCHAR* wszString;
|
|
WCHAR c;
|
|
|
|
szImageName[0] = L'\0';
|
|
szCVName[0] = L'\0';
|
|
|
|
while (--argc)
|
|
{
|
|
wszString = *++argv;
|
|
if (*wszString == L'/' || *wszString == L'-')
|
|
{
|
|
while ((c = *++wszString) != L'\0')
|
|
{
|
|
switch (towupper( c ))
|
|
{
|
|
case L'?':
|
|
Usage();
|
|
return FALSE;
|
|
|
|
case L'V':
|
|
fVerbose = TRUE;
|
|
break;
|
|
|
|
default:
|
|
Error("invalid switch - /%c\n", c );
|
|
Usage();
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (szImageName[0] == L'\0')
|
|
{
|
|
wcstombs(szImageName, wszString, MAX_PATH);
|
|
}
|
|
else if (szCVName[0] == L'\0')
|
|
{
|
|
wcstombs(szCVName, wszString, MAX_PATH);
|
|
}
|
|
else
|
|
{
|
|
Error("too many files specified\n");
|
|
Usage();
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (szImageName==NULL)
|
|
{
|
|
Error("no image name specified\n");
|
|
Usage();
|
|
return FALSE;
|
|
}
|
|
|
|
if (szCVName==NULL)
|
|
{
|
|
Error("no CV filename specified\n");
|
|
Usage();
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void Usage()
|
|
{
|
|
fprintf(stderr, "Usage: cvtodbg [options] imageName cvFile\n"
|
|
" [-?] display this message\n"
|
|
" [-f] overwrite readonly files\n");
|
|
}
|
|
|
|
void Message(const char* szFormat, ...)
|
|
{
|
|
va_list va;
|
|
va_start(va, szFormat);
|
|
fprintf (stdout, "resetpdb: ");
|
|
vfprintf(stdout, szFormat, va);
|
|
va_end(va);
|
|
}
|
|
|
|
void Error(const char* szFormat, ...)
|
|
{
|
|
va_list va;
|
|
va_start(va, szFormat);
|
|
fprintf (stderr, "resetpdb: error: ");
|
|
vfprintf(stderr, szFormat, va);
|
|
va_end(va);
|
|
}
|
|
|
|
void ErrorThrow(DWORD dw, const char* szFormat, ...)
|
|
{
|
|
va_list va;
|
|
va_start(va, szFormat);
|
|
fprintf (stderr, "resetpdb: error: ");
|
|
vfprintf(stderr, szFormat, va);
|
|
va_end(va);
|
|
Throw(dw);
|
|
}
|
|
|
|
void Throw(DWORD dw)
|
|
{
|
|
RaiseException(dw, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
|
}
|
|
|
|
BOOL DebugDirectoryIsUseful(LPVOID Pointer, ULONG Size)
|
|
{
|
|
return (Pointer != NULL) &&
|
|
(Size >= sizeof(IMAGE_DEBUG_DIRECTORY)) &&
|
|
((Size % sizeof(IMAGE_DEBUG_DIRECTORY)) == 0);
|
|
}
|
|
|
|
ULONG FileSize(HANDLE h)
|
|
// Answer the size of the file with this handle
|
|
{
|
|
BY_HANDLE_FILE_INFORMATION info;
|
|
GetFileInformationByHandle(h, &info);
|
|
return info.nFileSizeLow;
|
|
}
|