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.
1208 lines
31 KiB
1208 lines
31 KiB
/*++
|
|
|
|
Copyright 1996 - 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
symedit.c
|
|
|
|
Abstract:
|
|
|
|
|
|
Author:
|
|
|
|
Wesley A. Witt (wesw) 19-April-1993
|
|
|
|
Environment:
|
|
|
|
Win32, User Mode
|
|
|
|
--*/
|
|
|
|
#include <windows.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <symcvt.h>
|
|
#include <cv.h>
|
|
#include "strings.h"
|
|
#include <imagehlp.h>
|
|
|
|
#undef UNICODE
|
|
|
|
#define MAX_PATH 260
|
|
|
|
// prototypes for this module
|
|
|
|
BOOL CalculateOutputFilePointers( PIMAGEPOINTERS pi, PIMAGEPOINTERS po );
|
|
void ProcessCommandLineArgs( int argc, WCHAR *argv[] );
|
|
void PrintCopyright( void );
|
|
void PrintUsage( void );
|
|
void FatalError( int, ... );
|
|
BOOL MapOutputFile ( PPOINTERS p, char *fname, int );
|
|
void ComputeChecksum( char *szExeFile );
|
|
void ReadDebugInfo( PPOINTERS p );
|
|
void WriteDebugInfo( PPOINTERS p, BOOL);
|
|
void MungeDebugHeadersCoffToCv( PPOINTERS p, BOOL fAddCV );
|
|
void MungeExeName( PPOINTERS p, char * szExeName );
|
|
void DoCoffToCv(char *, char *, BOOL);
|
|
void DoSymToCv(char *, char *, char *, char *);
|
|
void DoNameChange(char *, char *, char *);
|
|
void DoExtract(char *, char *, char *);
|
|
void DoStrip(char *, char *);
|
|
|
|
IMAGE_DEBUG_DIRECTORY DbgDirSpare;
|
|
|
|
#define AdjustPtr(ptr) (((ptr) != NULL) ? \
|
|
((DWORD)ptr - (DWORD)pi->fptr + (DWORD)po->fptr) : \
|
|
((DWORD)(ptr)))
|
|
|
|
|
|
int _cdecl
|
|
wmain(
|
|
int argc,
|
|
WCHAR * argv[]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Shell for this utility.
|
|
|
|
Arguments:
|
|
|
|
argc - argument count
|
|
argv - argument pointers
|
|
|
|
|
|
Return Value:
|
|
|
|
0 - image was converted
|
|
>0 - image could not be converted
|
|
|
|
--*/
|
|
|
|
{
|
|
// Scan the command line and check what operations we are doing
|
|
|
|
ProcessCommandLineArgs( argc, argv );
|
|
return 0;
|
|
}
|
|
|
|
|
|
__inline void PrintCopyright( void )
|
|
{
|
|
puts( "\nMicrosoft(R) Windows NT SymEdit Version 1.0\n"
|
|
"(C) 1989-1995 Microsoft Corp. All rights reserved.\n");
|
|
}
|
|
|
|
|
|
__inline void PrintUsage( void )
|
|
{
|
|
PrintCopyright();
|
|
puts ("\nUsage: SYMEDIT <OPERATION> -q -o<file out> <file in>\n\n"
|
|
"\t<OPERATION> is:\n"
|
|
"\tC\tModify CodeView symbol information\n"
|
|
"\tN\tEdit name field\n"
|
|
"\tX\tExtract debug information\n"
|
|
"\tS\tStrip all debug information\n\n"
|
|
"Options:\n"
|
|
"\t-a\t\tAdd CodeView debug info to file\n"
|
|
"\t-n<name>\tName to change to\n"
|
|
"\t-o<file>\tspecify output file\n"
|
|
"\t-q\t\tquiet mode\n"
|
|
"\t-r\t\tReplace COFF debug info with CV info\n"
|
|
"\t-s<file>\tSym file source");
|
|
}
|
|
|
|
|
|
void
|
|
ProcessCommandLineArgs(
|
|
int argc,
|
|
WCHAR *argv[]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes the command line arguments and sets global flags to
|
|
indicate the user's desired behavior.
|
|
|
|
Arguments:
|
|
|
|
argc - argument count
|
|
argv - argument pointers
|
|
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
|
|
{
|
|
int i;
|
|
BOOL fQuiet = FALSE;
|
|
BOOL fSilent = FALSE;
|
|
char szOutputFile[MAX_PATH];
|
|
char szInputFile[MAX_PATH];
|
|
char szExeName[MAX_PATH];
|
|
char szDbgFile[MAX_PATH];
|
|
char szSymFile[MAX_PATH];
|
|
int iOperation;
|
|
BOOLEAN fAddCV = FALSE;
|
|
|
|
// Minimun number of of arguments is 2 -- program and operation
|
|
|
|
if (argc < 2 ||
|
|
(wcscmp(argv[1], L"-?") == 0) ||
|
|
(wcscmp(argv[1], L"?") == 0) )
|
|
{
|
|
PrintUsage();
|
|
exit(1);
|
|
}
|
|
|
|
// All operations on 1 character wide
|
|
|
|
if (argv[1][1] != 0) {
|
|
FatalError(ERR_OP_UNKNOWN, argv[1]);
|
|
}
|
|
|
|
// Validate the operation
|
|
|
|
switch( argv[1][0] ) {
|
|
case L'C':
|
|
case L'N':
|
|
case L'X':
|
|
case L'S':
|
|
iOperation = argv[1][0];
|
|
break;
|
|
default:
|
|
FatalError(ERR_OP_UNKNOWN, argv[1]);
|
|
}
|
|
|
|
// Parse out any other switches on the command line
|
|
|
|
for (i=2; i<argc; i++) {
|
|
if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
|
|
switch (towupper(argv[i][1])) {
|
|
|
|
// Add the CV debug information section rather than
|
|
// replace the COFF section with the CV info.
|
|
|
|
case L'A':
|
|
fAddCV = TRUE;
|
|
break;
|
|
|
|
// Specify the output name for the DBG file
|
|
|
|
case L'D':
|
|
if (argv[i][2] == 0) {
|
|
i += 1;
|
|
wcstombs(szDbgFile, argv[i], MAX_PATH);
|
|
} else {
|
|
wcstombs(szDbgFile, &argv[i][2], MAX_PATH);
|
|
}
|
|
break;
|
|
|
|
// Specify a new name to shove into the name of the
|
|
// debuggee field in the Misc. Debug info field
|
|
|
|
case L'N':
|
|
if (argv[i][2] == 0) {
|
|
i += 1;
|
|
wcstombs(szExeName, argv[i], MAX_PATH);
|
|
} else {
|
|
wcstombs(szExeName, &argv[i][2], MAX_PATH);
|
|
}
|
|
break;
|
|
|
|
// Specify the name of the output file
|
|
|
|
case L'O':
|
|
if (argv[i][2] == 0) {
|
|
i += 1;
|
|
wcstombs(szOutputFile,argv[i], MAX_PATH);
|
|
} else {
|
|
wcstombs(szOutputFile, &argv[i][2], MAX_PATH);
|
|
}
|
|
break;
|
|
|
|
// Be quite and don't put out the banner
|
|
|
|
case L'Q':
|
|
fQuiet = TRUE;
|
|
fSilent = TRUE;
|
|
break;
|
|
|
|
// Replace COFF debug information with CODEVIEW debug information
|
|
|
|
case L'R':
|
|
break;
|
|
|
|
// Convert a Symbol File to CV info
|
|
|
|
case L'S':
|
|
if (argv[i][2] == 0) {
|
|
i += 1;
|
|
wcstombs(szSymFile, argv[i], MAX_PATH);
|
|
} else {
|
|
wcstombs(szSymFile, &argv[i][2], MAX_PATH);
|
|
}
|
|
break;
|
|
|
|
// Print the command line options
|
|
|
|
case L'?':
|
|
PrintUsage();
|
|
exit(1);
|
|
break;
|
|
|
|
// Unrecognized option
|
|
|
|
default:
|
|
FatalError( ERR_OP_UNKNOWN, argv[i] );
|
|
break;
|
|
}
|
|
} else {
|
|
// No leading switch character -- must be a file name
|
|
|
|
wcstombs(szInputFile, &argv[i][0], MAX_PATH);
|
|
|
|
// Process the file(s)
|
|
|
|
if (!fQuiet) {
|
|
PrintCopyright();
|
|
fQuiet = TRUE;
|
|
}
|
|
|
|
if (!fSilent) {
|
|
printf("processing file: %s\n", szInputFile );
|
|
}
|
|
|
|
// Do switch validation cheching and setup any missing global variables
|
|
|
|
switch ( iOperation ) {
|
|
|
|
// For conversions -- there are three types
|
|
//
|
|
// 1. Coff to CV -- add
|
|
// 2. Coff to CV -- replace
|
|
// 3. SYM to CV --- add
|
|
// 4. SYM to CV -- seperate file
|
|
//
|
|
// Optional input file (not needed for case 4)
|
|
// Optional output file
|
|
// Optional sym file (implys sym->CV)
|
|
// Optional DBG file
|
|
|
|
case 'C':
|
|
if (szSymFile == NULL) {
|
|
DoCoffToCv(szInputFile, szOutputFile, fAddCV);
|
|
} else {
|
|
DoSymToCv(szInputFile, szOutputFile, szDbgFile, szSymFile);
|
|
}
|
|
break;
|
|
|
|
// For changing the name of the debuggee --
|
|
// Must specify input file
|
|
// Must specify new name
|
|
// Optional output file
|
|
|
|
case 'N':
|
|
DoNameChange(szInputFile, szOutputFile, szExeName);
|
|
break;
|
|
|
|
// For extraction of debug information
|
|
// Must specify input file
|
|
// Optional output file name
|
|
// Optional debug file name
|
|
|
|
case 'X':
|
|
DoExtract(szInputFile, szOutputFile, szDbgFile);
|
|
break;
|
|
|
|
// For full strip of debug information
|
|
// Must specify input file
|
|
// Optional output file
|
|
|
|
case 'S':
|
|
DoStrip(szInputFile, szOutputFile);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
ReadDebugInfo(
|
|
PPOINTERS p
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function will go out and read in all of the debug information
|
|
into memory -- this is required because the input and output
|
|
files might be the same, if so then writing out informaiton may
|
|
destory data we need at a later time.
|
|
|
|
Arguments:
|
|
|
|
p - Supplies a pointer to the structure describing the debug info file
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
int i;
|
|
// int cb;
|
|
// char * pb;
|
|
// PIMAGE_COFF_SYMBOLS_HEADER pCoffDbgInfo;
|
|
|
|
// Allocate space to save pointers to debug info
|
|
|
|
p->iptrs.rgpbDebugSave = (PCHAR *) malloc(p->iptrs.cDebugDir * sizeof(PCHAR));
|
|
memset(p->iptrs.rgpbDebugSave, 0, p->iptrs.cDebugDir * sizeof(PCHAR));
|
|
|
|
// Check each possible debug type record
|
|
|
|
for (i=0; i<p->iptrs.cDebugDir; i++) {
|
|
|
|
// If there was debug information then copy over the
|
|
// description block and cache in the actual debug data.
|
|
|
|
if (p->iptrs.rgDebugDir[i] != NULL) {
|
|
p->iptrs.rgpbDebugSave[i] =
|
|
malloc( p->iptrs.rgDebugDir[i]->SizeOfData );
|
|
if (p->iptrs.rgpbDebugSave[i] == NULL) {
|
|
FatalError(ERR_NO_MEMORY);
|
|
}
|
|
__try {
|
|
memcpy(p->iptrs.rgpbDebugSave[i],
|
|
p->iptrs.fptr +
|
|
p->iptrs.rgDebugDir[i]->PointerToRawData,
|
|
p->iptrs.rgDebugDir[i]->SizeOfData );
|
|
} __except(EXCEPTION_EXECUTE_HANDLER ) {
|
|
free(p->iptrs.rgpbDebugSave[i]);
|
|
p->iptrs.rgpbDebugSave[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
WriteDebugInfo(
|
|
PPOINTERS p,
|
|
BOOL fAddCV
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function will go out and read in all of the debug information
|
|
into memory -- this is required because the input and output
|
|
files might be the same, if so then writing out informaiton may
|
|
destory data we need at a later time.
|
|
|
|
Arguments:
|
|
|
|
p - Supplies a pointer to the structure describing the debug info file
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG PointerToDebugData = 0; // Offset from the start of the file
|
|
// to the current location to write
|
|
// debug information out.
|
|
ULONG BaseOfDebugData = 0;
|
|
int i, flen;
|
|
PIMAGE_DEBUG_DIRECTORY pDir, pDbgDir = NULL;
|
|
|
|
if (p->optrs.debugSection) {
|
|
BaseOfDebugData = PointerToDebugData =
|
|
p->optrs.debugSection->PointerToRawData;
|
|
} else if (p->optrs.sepHdr) {
|
|
BaseOfDebugData = PointerToDebugData =
|
|
sizeof(IMAGE_SEPARATE_DEBUG_HEADER) +
|
|
p->optrs.sepHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) +
|
|
p->optrs.sepHdr->ExportedNamesSize;
|
|
}
|
|
|
|
// Step 2. If the debug information is mapped, we know this
|
|
// from the section headers, then we may need to write
|
|
// out a new debug director to point to the debug information
|
|
|
|
if (fAddCV) {
|
|
if (p->optrs.optHdr) {
|
|
p->optrs.optHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].
|
|
VirtualAddress = p->optrs.debugSection->VirtualAddress;
|
|
p->optrs.optHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size +=
|
|
sizeof(IMAGE_DEBUG_DIRECTORY);
|
|
} else if (p->optrs.sepHdr) {
|
|
p->optrs.sepHdr->DebugDirectorySize += sizeof(IMAGE_DEBUG_DIRECTORY);
|
|
} else {
|
|
exit(1);
|
|
}
|
|
|
|
if (p->optrs.sepHdr) {
|
|
pDbgDir = (PIMAGE_DEBUG_DIRECTORY) malloc(p->optrs.cDebugDir * sizeof(IMAGE_DEBUG_DIRECTORY));
|
|
for (i=0; i<p->optrs.cDebugDir; i++) {
|
|
if (p->optrs.rgDebugDir[i] != NULL) {
|
|
pDbgDir[i] = *(p->optrs.rgDebugDir[i]);
|
|
p->optrs.rgDebugDir[i] = &pDbgDir[i];
|
|
}
|
|
}
|
|
}
|
|
for (i=0; i<p->optrs.cDebugDir; i++) {
|
|
if (p->optrs.rgDebugDir[i]) {
|
|
pDir = (PIMAGE_DEBUG_DIRECTORY) (PointerToDebugData +
|
|
p->optrs.fptr);
|
|
*pDir = *(p->optrs.rgDebugDir[i]);
|
|
p->optrs.rgDebugDir[i] = pDir;
|
|
PointerToDebugData += sizeof(IMAGE_DEBUG_DIRECTORY);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Step 3. For every debug info type, write out the debug information
|
|
// and update any header information required
|
|
|
|
for (i=0; i<p->optrs.cDebugDir; i++) {
|
|
if (p->optrs.rgDebugDir[i] != NULL) {
|
|
if (p->optrs.rgpbDebugSave[i] != NULL) {
|
|
p->optrs.rgDebugDir[i]->PointerToRawData =
|
|
PointerToDebugData;
|
|
if (p->optrs.debugSection) {
|
|
p->optrs.rgDebugDir[i]->AddressOfRawData =
|
|
p->optrs.debugSection->VirtualAddress +
|
|
PointerToDebugData - BaseOfDebugData;
|
|
}
|
|
memcpy(p->optrs.fptr + PointerToDebugData,
|
|
p->optrs.rgpbDebugSave[i],
|
|
p->optrs.rgDebugDir[i]->SizeOfData);
|
|
|
|
if ((i == IMAGE_DEBUG_TYPE_COFF) &&
|
|
(p->optrs.fileHdr != NULL)) {
|
|
|
|
PIMAGE_COFF_SYMBOLS_HEADER pCoffDbgInfo;
|
|
pCoffDbgInfo = (PIMAGE_COFF_SYMBOLS_HEADER)p->optrs.rgpbDebugSave[i];
|
|
p->optrs.fileHdr->PointerToSymbolTable =
|
|
PointerToDebugData + pCoffDbgInfo->LvaToFirstSymbol;
|
|
}
|
|
}
|
|
PointerToDebugData += p->optrs.rgDebugDir[i]->SizeOfData;
|
|
}
|
|
}
|
|
|
|
// Step 4. Clean up any COFF structures if we are replacing
|
|
// the coff information with CV info.
|
|
|
|
if ((p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_COFF] == NULL) &&
|
|
(p->optrs.fileHdr != NULL)) {
|
|
|
|
// Since there is no coff debug information -- clean out
|
|
// both fields pointing to the debug info
|
|
|
|
p->optrs.fileHdr->PointerToSymbolTable = 0;
|
|
p->optrs.fileHdr->NumberOfSymbols = 0;
|
|
}
|
|
|
|
// Step 5. Correct the alignments if needed. If there is a real .debug
|
|
// section in the file (i.e. it is mapped) then update it.
|
|
|
|
if (p->optrs.debugSection) {
|
|
p->optrs.debugSection->SizeOfRawData =
|
|
FileAlign(PointerToDebugData - BaseOfDebugData);
|
|
|
|
// update the optional header with the new image size
|
|
|
|
p->optrs.optHdr->SizeOfImage =
|
|
SectionAlign(p->optrs.debugSection->VirtualAddress +
|
|
p->optrs.debugSection->SizeOfRawData);
|
|
p->optrs.optHdr->SizeOfInitializedData +=
|
|
p->optrs.debugSection->SizeOfRawData;
|
|
}
|
|
|
|
// calculate the new file size
|
|
|
|
if (p->optrs.optHdr != NULL) {
|
|
flen = FileAlign(PointerToDebugData);
|
|
} else {
|
|
flen = PointerToDebugData;
|
|
}
|
|
|
|
// finally, update the eof pointer and close the file
|
|
|
|
UnmapViewOfFile( p->optrs.fptr );
|
|
|
|
if (!SetFilePointer( p->optrs.hFile, flen, 0, FILE_BEGIN )) {
|
|
FatalError( ERR_FILE_PTRS );
|
|
}
|
|
|
|
if (!SetEndOfFile( p->optrs.hFile )) {
|
|
FatalError( ERR_SET_EOF );
|
|
}
|
|
|
|
CloseHandle( p->optrs.hFile );
|
|
|
|
// Exit -- we are done.
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
MungeDebugHeadersCoffToCv(
|
|
PPOINTERS p,
|
|
BOOL fAddCV
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
p - pointer to a POINTERS structure (see symcvt.h)
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
|
|
{
|
|
if (!fAddCV) {
|
|
CV_DIR(&p->optrs) = COFF_DIR(&p->optrs);
|
|
COFF_DIR(&p->optrs) = 0;
|
|
} else {
|
|
CV_DIR(&p->optrs) = &DbgDirSpare;
|
|
*(COFF_DIR(&p->optrs)) = *(COFF_DIR(&p->iptrs));
|
|
};
|
|
|
|
*CV_DIR(&p->optrs) = *(COFF_DIR(&p->iptrs));
|
|
CV_DIR(&p->optrs)->Type = IMAGE_DEBUG_TYPE_CODEVIEW;
|
|
CV_DIR(&p->optrs)->SizeOfData = p->pCvStart.size;
|
|
p->optrs.rgpbDebugSave[IMAGE_DEBUG_TYPE_CODEVIEW] = p->pCvStart.ptr;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
MapOutputFile (
|
|
PPOINTERS p,
|
|
char *fname,
|
|
int cb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Maps the output file specified by the fname argument and saves the
|
|
file handle & file pointer in the POINTERS structure.
|
|
|
|
|
|
Arguments:
|
|
|
|
p - pointer to a POINTERS structure (see symcvt.h)
|
|
fname - ascii string for the file name
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE - file mapped ok
|
|
FALSE - file could not be mapped
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL rval;
|
|
HANDLE hMap = NULL;
|
|
DWORD oSize;
|
|
|
|
rval = FALSE;
|
|
|
|
p->optrs.hFile = CreateFileA( fname,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
0,
|
|
NULL );
|
|
|
|
if (p->optrs.hFile == INVALID_HANDLE_VALUE) {
|
|
goto exit;
|
|
}
|
|
|
|
oSize = p->iptrs.fsize;
|
|
if (p->pCvStart.ptr != NULL) {
|
|
oSize += p->pCvStart.size;
|
|
}
|
|
oSize += cb;
|
|
oSize += p->iptrs.cDebugDir * sizeof(IMAGE_DEBUG_DIRECTORY);
|
|
|
|
hMap = CreateFileMapping( p->optrs.hFile, NULL, PAGE_READWRITE,
|
|
0, oSize, NULL );
|
|
|
|
if (hMap == NULL) {
|
|
goto exit;
|
|
}
|
|
|
|
p->optrs.fptr = MapViewOfFile( hMap, FILE_MAP_WRITE, 0, 0, 0 );
|
|
|
|
CloseHandle(hMap);
|
|
|
|
if (p->optrs.fptr == NULL) {
|
|
goto exit;
|
|
}
|
|
rval = TRUE;
|
|
exit:
|
|
return rval;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CalculateOutputFilePointers(
|
|
PIMAGEPOINTERS pi,
|
|
PIMAGEPOINTERS po
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function calculates the output file pointers based on the
|
|
input file pointers. The same address is used but they are all
|
|
re-based off the output file's file pointer.
|
|
|
|
Arguments:
|
|
|
|
p - pointer to a IMAGEPOINTERS structure (see symcvt.h)
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE - pointers were created
|
|
FALSE - pointers could not be created
|
|
|
|
--*/
|
|
{
|
|
int i;
|
|
|
|
// fixup the pointers relative the fptr for the output file
|
|
po->dosHdr = (PIMAGE_DOS_HEADER) AdjustPtr(pi->dosHdr);
|
|
po->ntHdr = (PIMAGE_NT_HEADERS) AdjustPtr(pi->ntHdr);
|
|
po->fileHdr = (PIMAGE_FILE_HEADER) AdjustPtr(pi->fileHdr);
|
|
po->optHdr = (PIMAGE_OPTIONAL_HEADER) AdjustPtr(pi->optHdr);
|
|
po->sectionHdrs = (PIMAGE_SECTION_HEADER) AdjustPtr(pi->sectionHdrs);
|
|
po->sepHdr = (PIMAGE_SEPARATE_DEBUG_HEADER) AdjustPtr(pi->sepHdr);
|
|
po->debugSection = (PIMAGE_SECTION_HEADER) AdjustPtr(pi->debugSection);
|
|
po->AllSymbols = (PIMAGE_SYMBOL) AdjustPtr(pi->AllSymbols);
|
|
po->stringTable = (PUCHAR) AdjustPtr(pi->stringTable);
|
|
|
|
// move the data from the input file to the output file
|
|
memcpy( po->fptr, pi->fptr, pi->fsize );
|
|
|
|
po->cDebugDir = pi->cDebugDir;
|
|
po->rgDebugDir = malloc(po->cDebugDir * sizeof(po->rgDebugDir[0]));
|
|
memset(po->rgDebugDir, 0, po->cDebugDir * sizeof(po->rgDebugDir[0]));
|
|
|
|
for (i=0; i<po->cDebugDir; i++) {
|
|
po->rgDebugDir[i] = (PIMAGE_DEBUG_DIRECTORY) AdjustPtr(pi->rgDebugDir[i]);
|
|
}
|
|
po->rgpbDebugSave = pi->rgpbDebugSave;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
FatalError(
|
|
int idMsg,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prints a message string to stderr and then exits.
|
|
|
|
Arguments:
|
|
|
|
s - message string to be printed
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
|
|
{
|
|
va_list marker;
|
|
char rgchFormat[256];
|
|
char rgch[256];
|
|
|
|
LoadStringA(GetModuleHandle(NULL), idMsg, rgchFormat, sizeof(rgchFormat));
|
|
|
|
va_start(marker, idMsg);
|
|
vsprintf(rgch, rgchFormat, marker);
|
|
va_end(marker);
|
|
|
|
fprintf(stderr, "%s\n", rgch);
|
|
|
|
exit(1);
|
|
}
|
|
|
|
|
|
void
|
|
ComputeChecksum(
|
|
char *szExeFile
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Computes a new checksum for the image by calling imagehlp.dll
|
|
|
|
Arguments:
|
|
|
|
szExeFile - exe file name
|
|
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwHeaderSum = 0;
|
|
DWORD dwCheckSum = 0;
|
|
HANDLE hFile;
|
|
DWORD cb;
|
|
IMAGE_DOS_HEADER dosHdr;
|
|
IMAGE_NT_HEADERS ntHdr;
|
|
|
|
if (MapFileAndCheckSumA(szExeFile, &dwHeaderSum, &dwCheckSum) != CHECKSUM_SUCCESS) {
|
|
FatalError( ERR_CHECKSUM_CALC );
|
|
}
|
|
|
|
hFile = CreateFileA( szExeFile,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
// seek to the beginning of the file
|
|
SetFilePointer( hFile, 0, 0, FILE_BEGIN );
|
|
|
|
// read in the dos header
|
|
if ((ReadFile(hFile, &dosHdr, sizeof(dosHdr), &cb, 0) == FALSE) || (cb != sizeof(dosHdr))) {
|
|
FatalError( ERR_CHECKSUM_CALC );
|
|
}
|
|
|
|
// read in the pe header
|
|
if ((dosHdr.e_magic != IMAGE_DOS_SIGNATURE) ||
|
|
(SetFilePointer(hFile, dosHdr.e_lfanew, 0, FILE_BEGIN) == -1L)) {
|
|
FatalError( ERR_CHECKSUM_CALC );
|
|
}
|
|
|
|
// read in the nt header
|
|
if ((!ReadFile(hFile, &ntHdr, sizeof(ntHdr), &cb, 0)) || (cb != sizeof(ntHdr))) {
|
|
FatalError( ERR_CHECKSUM_CALC );
|
|
}
|
|
|
|
if (SetFilePointer(hFile, dosHdr.e_lfanew, 0, FILE_BEGIN) == -1L) {
|
|
FatalError( ERR_CHECKSUM_CALC );
|
|
}
|
|
|
|
ntHdr.OptionalHeader.CheckSum = dwCheckSum;
|
|
|
|
if (!WriteFile(hFile, &ntHdr, sizeof(ntHdr), &cb, NULL)) {
|
|
FatalError( ERR_CHECKSUM_CALC );
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
MungeExeName(
|
|
PPOINTERS p,
|
|
char * szExeName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
description-of-function.
|
|
|
|
Arguments:
|
|
|
|
argument-name - Supplies | Returns description of argument.
|
|
.
|
|
.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIMAGE_DEBUG_MISC pMiscIn;
|
|
PIMAGE_DEBUG_MISC pMiscOut;
|
|
int cb;
|
|
int i;
|
|
|
|
for (i=0; i<p->iptrs.cDebugDir; i++) {
|
|
if (p->optrs.rgDebugDir[i] != 0) {
|
|
*(p->optrs.rgDebugDir[i]) = *(p->iptrs.rgDebugDir[i]);
|
|
}
|
|
}
|
|
|
|
pMiscIn = (PIMAGE_DEBUG_MISC)
|
|
p->iptrs.rgpbDebugSave[IMAGE_DEBUG_TYPE_MISC];
|
|
|
|
if (p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC] == NULL) {
|
|
p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC] = &DbgDirSpare;
|
|
memset(&DbgDirSpare, 0, sizeof(DbgDirSpare));
|
|
}
|
|
|
|
pMiscOut = (PIMAGE_DEBUG_MISC)
|
|
p->optrs.rgpbDebugSave[IMAGE_DEBUG_TYPE_MISC] =
|
|
malloc(p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC]->SizeOfData +
|
|
strlen(szExeName));
|
|
cb = p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC]->SizeOfData;
|
|
|
|
while ( cb > 0 ) {
|
|
if (pMiscIn->DataType == IMAGE_DEBUG_MISC_EXENAME) {
|
|
pMiscOut->DataType = IMAGE_DEBUG_MISC_EXENAME;
|
|
pMiscOut->Length = (sizeof(IMAGE_DEBUG_MISC) +
|
|
strlen(szExeName) + 3) & ~3;
|
|
pMiscOut->Unicode = FALSE;
|
|
strcpy(&pMiscOut->Data[0], szExeName);
|
|
szExeName = NULL;
|
|
} else {
|
|
memcpy(pMiscOut, pMiscIn, pMiscIn->Length);
|
|
}
|
|
|
|
p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC]->SizeOfData +=
|
|
(pMiscOut->Length - pMiscIn->Length);
|
|
|
|
cb -= pMiscIn->Length;
|
|
pMiscIn = (PIMAGE_DEBUG_MISC) (((char *) pMiscIn) + pMiscIn->Length);
|
|
pMiscOut = (PIMAGE_DEBUG_MISC) (((char *) pMiscOut) + pMiscOut->Length);
|
|
}
|
|
|
|
if (szExeName) {
|
|
pMiscOut->DataType = IMAGE_DEBUG_MISC_EXENAME;
|
|
pMiscOut->Length = (sizeof(IMAGE_DEBUG_MISC) +
|
|
strlen(szExeName) + 3) & ~3;
|
|
pMiscOut->Unicode = FALSE;
|
|
strcpy(&pMiscOut->Data[0], szExeName);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*** DoCoffToCv
|
|
*
|
|
*
|
|
*/
|
|
|
|
void DoCoffToCv(
|
|
char * szInput,
|
|
char * szOutput,
|
|
BOOL fAddCV
|
|
)
|
|
{
|
|
POINTERS p;
|
|
|
|
// Do default checking
|
|
|
|
if (szOutput == NULL) {
|
|
szOutput = szInput;
|
|
}
|
|
|
|
// Open the input file name and setup the pointers into the file
|
|
|
|
if (!MapInputFile( &p, NULL, szInput )) {
|
|
FatalError( ERR_OPEN_INPUT_FILE, szInput );
|
|
}
|
|
|
|
// Now, if we thing we are playing with PE exes then we need
|
|
// to setup the pointers into the map file
|
|
|
|
if (!CalculateNtImagePointers( &p.iptrs )) {
|
|
FatalError( ERR_INVALID_PE, szInput );
|
|
}
|
|
|
|
// We are about to try and do the coff to cv symbol conversion.
|
|
//
|
|
// Verify that the operation is legal.
|
|
//
|
|
// 1. We need to have coff debug information to start with
|
|
// 2. If the debug info is not mapped then we must not
|
|
// be trying to add CodeView info.
|
|
|
|
if ((p.iptrs.AllSymbols == NULL) || (COFF_DIR(&p.iptrs) == NULL)) {
|
|
FatalError( ERR_NO_COFF );
|
|
}
|
|
|
|
if (fAddCV && (p.iptrs.debugSection == 0) && (p.iptrs.sepHdr == NULL)) {
|
|
FatalError( ERR_NOT_MAPPED );
|
|
}
|
|
|
|
// Now go out an preform the acutal conversion.
|
|
|
|
if (!ConvertCoffToCv( &p )) {
|
|
FatalError( ERR_COFF_TO_CV );
|
|
}
|
|
|
|
// Read in any additional debug information in the file
|
|
|
|
ReadDebugInfo(&p);
|
|
|
|
// Open the output file and adjust the pointers so that we are ok.
|
|
|
|
if (!MapOutputFile( &p, szOutput, 0 )) {
|
|
FatalError( ERR_MAP_FILE, szOutput );
|
|
}
|
|
|
|
CalculateOutputFilePointers( &p.iptrs, &p.optrs );
|
|
|
|
// Munge the various debug information structures to preform the correct
|
|
// operations
|
|
|
|
MungeDebugHeadersCoffToCv( &p, fAddCV );
|
|
|
|
// Free our handles on the input file
|
|
|
|
UnMapInputFile(&p);
|
|
|
|
// Write out the debug information to the end of the exe
|
|
|
|
WriteDebugInfo( &p, fAddCV );
|
|
|
|
// and finally compute the checksum
|
|
|
|
if (p.iptrs.fileHdr != NULL) {
|
|
ComputeChecksum( szOutput );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*** DoSymToCv
|
|
*
|
|
*/
|
|
|
|
void
|
|
DoSymToCv(
|
|
char * szInput,
|
|
char * szOutput,
|
|
char * szDbg,
|
|
char * szSym
|
|
)
|
|
{
|
|
POINTERS p;
|
|
HANDLE hFile;
|
|
DWORD cb;
|
|
OFSTRUCT ofs;
|
|
|
|
// Open the input file name and setup the pointers into the file
|
|
|
|
if (!MapInputFile( &p, NULL, szSym )) {
|
|
FatalError(ERR_OPEN_INPUT_FILE, szSym);
|
|
}
|
|
|
|
// Now preform the desired operation
|
|
|
|
if ((szOutput == NULL) && (szDbg == NULL)) {
|
|
szOutput = szInput;
|
|
}
|
|
|
|
ConvertSymToCv( &p );
|
|
|
|
if (szOutput) {
|
|
if (szOutput != szInput) {
|
|
if (OpenFile(szInput, &ofs, OF_EXIST) == 0) {
|
|
FatalError(ERR_OPEN_INPUT_FILE, szInput);
|
|
}
|
|
if (CopyFileA(szInput, szOutput, FALSE) == 0) {
|
|
FatalError(ERR_OPEN_WRITE_FILE, szOutput);
|
|
}
|
|
}
|
|
hFile = CreateFileA(szOutput, GENERIC_WRITE, 0, NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
FatalError(ERR_OPEN_WRITE_FILE, szOutput);
|
|
}
|
|
SetFilePointer(hFile, 0, 0, FILE_END);
|
|
} else if (szDbg) {
|
|
hFile = CreateFileA(szDbg, GENERIC_WRITE, 0, NULL,
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
FatalError(ERR_OPEN_WRITE_FILE, szDbg);
|
|
}
|
|
}
|
|
|
|
WriteFile(hFile, p.pCvStart.ptr, p.pCvStart.size, &cb, NULL);
|
|
CloseHandle(hFile);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
DoNameChange(
|
|
char * szInput,
|
|
char * szOutput,
|
|
char * szNewName
|
|
)
|
|
{
|
|
POINTERS p;
|
|
|
|
// Open the input file name and setup the pointers into the file
|
|
|
|
if (!MapInputFile( &p, NULL, szInput )) {
|
|
FatalError(ERR_OPEN_INPUT_FILE, szInput);
|
|
}
|
|
|
|
// Now, if we thing we are playing with PE exes then we need
|
|
// to setup the pointers into the map file
|
|
|
|
if (!CalculateNtImagePointers( &p.iptrs )) {
|
|
FatalError(ERR_INVALID_PE, szInput);
|
|
}
|
|
|
|
// Now preform the desired operation
|
|
|
|
if (szOutput == NULL) {
|
|
szOutput = szInput;
|
|
}
|
|
|
|
if (szNewName == NULL) {
|
|
szNewName = szOutput;
|
|
}
|
|
|
|
if (p.iptrs.sepHdr != NULL) {
|
|
FatalError(ERR_EDIT_DBG_FILE);
|
|
}
|
|
|
|
// Read in all of the debug information
|
|
|
|
ReadDebugInfo(&p);
|
|
|
|
// Open the output file and adjust the pointers.
|
|
|
|
if (!MapOutputFile(&p, szOutput,
|
|
sizeof(szNewName) * 2 + sizeof(IMAGE_DEBUG_MISC))) {
|
|
FatalError(ERR_MAP_FILE, szOutput);
|
|
}
|
|
|
|
CalculateOutputFilePointers(&p.iptrs, &p.optrs);
|
|
|
|
// Munge the name of the file
|
|
|
|
MungeExeName(&p, szNewName);
|
|
|
|
// Close the input file
|
|
|
|
UnMapInputFile(&p);
|
|
|
|
// Write out the debug information to the end of the exe
|
|
|
|
WriteDebugInfo(&p, FALSE);
|
|
|
|
// and finally compute the checksum
|
|
|
|
if (p.iptrs.fileHdr != NULL) {
|
|
ComputeChecksum( szOutput );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
DoStrip(
|
|
char * szInput,
|
|
char * szOutput
|
|
)
|
|
{
|
|
char OutputFile[_MAX_PATH];
|
|
|
|
// Make sure we only have the path to the output file (it will always be
|
|
// named filename.DBG)
|
|
|
|
if (szOutput != NULL) {
|
|
CopyFileA(szInput, szOutput, FALSE);
|
|
}
|
|
|
|
SplitSymbols(szOutput, NULL, OutputFile, SPLITSYM_EXTRACT_ALL);
|
|
|
|
// Always delete the output file.
|
|
|
|
DeleteFileA(OutputFile);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
DoExtract(
|
|
char * szInput,
|
|
char * szOutput,
|
|
char * szDbgFile
|
|
)
|
|
{
|
|
char OutputFile[_MAX_PATH];
|
|
char szExt[_MAX_EXT];
|
|
char szFileName[_MAX_FNAME];
|
|
|
|
if (szOutput != NULL) {
|
|
CopyFileA(szInput, szOutput, FALSE);
|
|
szInput = _strdup(szOutput);
|
|
_splitpath(szOutput, NULL, NULL, szFileName, szExt);
|
|
*(szOutput + strlen(szOutput) - strlen(szFileName) - strlen(szExt)) = '\0';
|
|
}
|
|
|
|
SplitSymbols(szInput, szOutput, OutputFile, 0);
|
|
|
|
CopyFileA(szDbgFile, OutputFile, TRUE);
|
|
|
|
if (szOutput) {
|
|
free(szInput);
|
|
}
|
|
|
|
return;
|
|
}
|