singrdk/base/Windows/mkcore/mkcore.cpp

1419 lines
41 KiB
C++

//////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: mkcore.cpp
//
// Contents: Creates a core file from one or more PE image by physically
// aligning and allocating all of the sections.
//
// Owner:
//
// History: 2003/10/10 Created from mkflat.
//
#define UNICODE
#define _UNICODE
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <winlean.h>
#include <assert.h>
//////////////////////////////////////////////////////////////////////////////
//
static BOOL s_fVerbose = FALSE;
typedef signed int INT;
typedef signed char INT8, *LPINT8;
typedef signed short INT16, *LPINT16;
typedef unsigned int UINT;
typedef unsigned char UINT8, *LPUINT8;
typedef unsigned short UINT16, *LPUINT16;
#include "minidump.h"
#define arrayof(a) (sizeof(a)/sizeof(a[0]))
//////////////////////////////////////////////////////////////////////////////
//
static inline UINT Max(UINT a, UINT b)
{
return a > b ? a : b;
}
static inline UINT Min(UINT a, UINT b)
{
return a < b ? a : b;
}
static inline UINT Align(UINT nValue, UINT nPowerOf2)
{
return (nValue + nPowerOf2 - 1) & ~(nPowerOf2 - 1);
}
DWORD RvaToFileOffset(DWORD nRva, UINT nSecs, PIMAGE_SECTION_HEADER pSecs)
{
DWORD n;
for (n = 0; n < nSecs; n++) {
DWORD vaStart = pSecs[n].VirtualAddress;
DWORD vaEnd = vaStart + pSecs[n].Misc.VirtualSize;
if (nRva >= vaStart && nRva < vaEnd) {
return pSecs[n].PointerToRawData + nRva - pSecs[n].VirtualAddress;
}
}
return 0;
}
BOOL error(PCSTR pwzError)
{
fprintf(stderr, "%s\n", pwzError);
fclose(stderr);
return FALSE;
}
void Dump(PBYTE pbData, ULONG cbData)
{
for (ULONG n = 0; n < cbData; n += 16) {
printf(" ");
for (ULONG o = n; o < n + 16; o++) {
if (o >= cbData) {
printf(" ");
}
else {
printf("%02x", pbData[o]);
}
if (o % 4 == 3) {
printf(" ");
}
}
printf(" ");
for (ULONG o = n; o < n + 16; o++) {
if (o >= cbData) {
printf(" ");
}
else {
if (pbData[o] >= ' ' && pbData[o] < 127) {
printf("%c", pbData[o]);
}
else {
printf(".");
}
}
}
printf("\n");
}
}
//////////////////////////////////////////////////////////////////// CFileMap.
//
class CFileMap
{
public:
CFileMap()
{
m_pbData = NULL;
m_cbData = 0;
}
~CFileMap()
{
Close();
}
public:
BOOL Load(PCWSTR pwzFile);
PBYTE Seek(UINT32 cbPos);
UINT32 Size();
VOID Close();
protected:
PBYTE m_pbData;
UINT32 m_cbData;
};
VOID CFileMap::Close()
{
if (m_pbData) {
UnmapViewOfFile(m_pbData);
m_pbData = NULL;
}
m_cbData = 0;
}
UINT32 CFileMap::Size()
{
return m_cbData;
}
PBYTE CFileMap::Seek(UINT32 cbPos)
{
if (m_pbData && cbPos <= m_cbData) {
return m_pbData + cbPos;
}
return NULL;
}
BOOL CFileMap::Load(PCWSTR pwzFile)
{
Close();
HANDLE hFile = CreateFile(pwzFile,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
ULONG cbInFileData = GetFileSize(hFile, NULL);
if (cbInFileData == 0xffffffff) {
CloseHandle(hFile);
return FALSE;
}
HANDLE hInFileMap = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL);
CloseHandle(hFile);
if (hInFileMap == NULL) {
return FALSE;
}
m_pbData = (PBYTE)MapViewOfFile(hInFileMap, FILE_MAP_COPY, 0, 0, 0);
CloseHandle(hInFileMap);
if (m_pbData == NULL) {
return FALSE;
}
m_cbData = cbInFileData;
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
//
class CArrayInternal
{
public:
CArrayInternal(UINT cbEach)
{
m_pbData = new BYTE [cbEach * 32];
m_cbData = cbEach * 32;
assert(m_pbData);
m_cbEach = cbEach;
m_cbUsed = 0;
}
~CArrayInternal()
{
if (m_pbData != NULL) {
delete[] m_pbData;
m_pbData = NULL;
}
}
PVOID Seek(UINT nEntry)
{
return m_pbData + m_cbEach * nEntry;
}
PVOID Add()
{
if (m_cbUsed + m_cbEach >= m_cbData) {
UINT cbData = m_cbData + m_cbEach * 32;
PBYTE pbData = new BYTE [cbData];
assert(pbData);
CopyMemory(pbData, m_pbData, m_cbUsed);
delete[] m_pbData;
m_pbData = pbData;
m_cbData = cbData;
}
PBYTE pb = m_pbData + m_cbUsed;
m_cbUsed += m_cbEach;
ZeroMemory(pb, m_cbEach);
return pb;
}
UINT Count()
{
return m_cbUsed / m_cbEach;
}
UINT Size()
{
return m_cbUsed;
}
protected:
PBYTE m_pbData;
UINT m_cbData;
UINT m_cbEach;
UINT m_cbUsed;
};
template<class T> class CArray : public CArrayInternal
{
public:
CArray()
: CArrayInternal(sizeof (T)) {}
T* Seek(UINT nEntry)
{ return (T*)CArrayInternal::Seek(nEntry); }
T* Add()
{ return (T*)CArrayInternal::Add(); }
};
//////////////////////////////////////////////////////////////////// CFileOut.
//
class CFileOut
{
public:
CFileOut();
~CFileOut();
public:
BOOL Create(PCWSTR pwzFile);
BOOL Seek(UINT32 cbPos);
BOOL Write(PBYTE pbData, UINT32 cbData);
BOOL Read(PBYTE pbData, UINT32 cbData);
BOOL Zero(UINT32 cbData);
BOOL Delete();
UINT32 Size();
UINT32 Checksum();
VOID Close();
protected:
HANDLE m_hFile;
WCHAR m_wzFile[MAX_PATH];
UINT32 m_cbPos;
};
CFileOut::CFileOut()
{
m_hFile = INVALID_HANDLE_VALUE;
m_wzFile[0] = '\0';
m_cbPos = 0;
}
CFileOut::~CFileOut()
{
Close();
}
VOID CFileOut::Close()
{
if (m_hFile != INVALID_HANDLE_VALUE) {
CloseHandle(m_hFile);
m_hFile = INVALID_HANDLE_VALUE;
}
}
BOOL CFileOut::Seek(UINT32 cbPos)
{
if (m_hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
if (SetFilePointer(m_hFile, cbPos, NULL, FILE_BEGIN) != cbPos) {
return FALSE;
}
m_cbPos = cbPos;
return TRUE;
}
BOOL CFileOut::Write(PBYTE pbData, UINT32 cbData)
{
if (m_hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
DWORD dwWrote = 0;
if (!WriteFile(m_hFile, pbData, cbData, &dwWrote, NULL) || dwWrote != cbData) {
return FALSE;
}
m_cbPos += cbData;
return TRUE;
}
BOOL CFileOut::Zero(UINT32 cbData)
{
if (m_hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
if (cbData == 0) {
return TRUE;
}
UINT zero_size = cbData < 65536 ? cbData : 65536;
PBYTE buf = new BYTE [zero_size];
assert(buf);
if (!buf) {
return FALSE;
}
ZeroMemory(buf, zero_size);
for (; cbData > 0; cbData -= zero_size) {
if (zero_size > cbData) {
zero_size = cbData;
}
if (!Write(buf, zero_size)) {
delete[] buf;
return FALSE;
}
}
delete[] buf;
buf = NULL;
return TRUE;
}
BOOL CFileOut::Delete()
{
if (m_hFile != INVALID_HANDLE_VALUE) {
Close();
return DeleteFile(m_wzFile);
}
return FALSE;
}
UINT32 CFileOut::Size()
{
return SetFilePointer(m_hFile, 0, NULL, FILE_END);
}
BOOL CFileOut::Read(PBYTE pbData, UINT32 cbData)
{
if (m_hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
DWORD dwRead = 0;
if (!ReadFile(m_hFile, pbData, cbData, &dwRead, NULL) || dwRead != cbData) {
return FALSE;
}
m_cbPos += cbData;
return TRUE;
}
BOOL CFileOut::Create(PCWSTR pwzFile)
{
Close();
m_wzFile[0] = '\0';
m_hFile = CreateFile(pwzFile,
GENERIC_READ | GENERIC_WRITE,
0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (m_hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
wcscpy(m_wzFile, pwzFile);
m_cbPos = 0;
return TRUE;
}
UINT32 CFileOut::Checksum()
{
if (m_hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
PBYTE buf = new BYTE [65536];
assert(buf);
if (!buf) {
return ~0u;
}
UINT32 size = Size();
UINT32 sum = 0;
if (!Seek(0)) {
assert(!"Couldn't seek.");
delete[] buf;
return ~0u;
}
for (UINT32 pos = 0; pos < size;) {
UINT32 read = size - pos < 65536 ? size - pos : 65536;
if (!Read(buf, read)) {
printf("Pos: %d/%d\n", pos, m_cbPos);
printf("Len: %d\n", size);
__asm int 3;
assert(!"Couldn't read.");
delete[] buf;
return ~0u;
}
UINT32 *pBeg = (UINT32*)(buf);
UINT32 *pEnd = (UINT32*)(buf + read);
while (pBeg < pEnd) {
sum += *pBeg++;
}
pos += read;
}
delete[] buf;
buf = NULL;
return sum;
}
//////////////////////////////////////////////////////////////////////////////
//
class CMiniDump
{
public:
struct MODULE
{
PUINT8 pbImageMap;
UINT64 vaImageBase;
UINT64 vaImageHead;
UINT64 vaImageEntry;
PCWSTR pwzImage;
UINT32 nRegions;
PUINT8 pbCvData;
UINT32 cbCvData;
};
struct BLOB
{
UINT64 vaBlob;
PUINT8 pbBlob;
UINT32 cbBlob;
PCWSTR pwzImage;
};
public:
CMiniDump();
~CMiniDump();
BOOL Create(PCWSTR pwzFile);
BOOL Write();
VOID Close();
BOOL Delete();
BOOL Stack(UINT64 vaStack, UINT32 cbStack);
BOOL AddModule(PCWSTR pwzImage);
BOOL AddBlob(PCWSTR pwzImage, UINT64 vaBlob, UINT32 *pcbBlob);
protected:
BOOL AddMemory(ULONG64 address,
PBYTE pbData, UINT32 cbData, UINT32 cbFull,
PMINIDUMP_MEMORY_DESCRIPTOR pOut);
BOOL AddString(PCWSTR pwzString, UINT32 *pRva);
protected:
ULONG Allocate(UINT32 cbData);
BOOL WriteDebug(MODULE *pSelf,
PMINIDUMP_MODULE pModule);
BOOL WriteModule(MODULE *pSelf,
PMINIDUMP_MODULE pModule,
PMINIDUMP_MEMORY_DESCRIPTOR pRegions);
BOOL WriteBlob(BLOB *pBlob,
PMINIDUMP_MEMORY_DESCRIPTOR pRegions);
BOOL AddData(PBYTE pbData, UINT32 cbData, UINT32 cbFull,
MINIDUMP_LOCATION_DESCRIPTOR *pOut);
PBYTE AddCreate(UINT32 cbData, UINT32 *pRva);
PBYTE AddCreate(UINT32 cbData, MINIDUMP_LOCATION_DESCRIPTOR* pOut);
BOOL Write(PVOID pvData, MINIDUMP_LOCATION_DESCRIPTOR Out);
protected:
static int __cdecl CompareModules(const void *p1, const void *p2);
static int __cdecl CompareBlobs(const void *p1, const void *p2);
static int __cdecl CompareMinidumpModules(const void *p1, const void *p2);
protected:
CFileOut m_Out;
ULONG m_cbAllocated;
MINIDUMP_HEADER m_Header;
MINIDUMP_DIRECTORY m_Directory[8];
UINT64 m_vaImageBase;
UINT64 m_vaImageHead;
UINT64 m_vaImageEntry;
UINT64 m_vaStack;
UINT32 m_cbStack;
CArray<MODULE> m_Modules;
CArray<BLOB> m_Blobs;
static UINT64 s_vaImageBase;
public:
static const UINT64 c_vaBlob = 0x7b00;
static const UINT64 c_vaStack = 0x2fff00;
static const UINT32 c_cbStack = 0x100;
};
UINT64 CMiniDump::s_vaImageBase = 0;
CMiniDump::CMiniDump()
{
m_cbAllocated = sizeof(m_Header) + sizeof(m_Directory);
ZeroMemory(&m_Header, sizeof(m_Header));
ZeroMemory(&m_Directory, sizeof(m_Directory));
m_Header.Signature = MINIDUMP_SIGNATURE;
m_Header.Version = MINIDUMP_VERSION;
m_Header.NumberOfStreams = arrayof(m_Directory);
m_Header.StreamDirectoryRva = sizeof(MINIDUMP_HEADER);
m_Header.CheckSum = 0;
m_Header.TimeDateStamp = 0;
m_Header.Flags = (MiniDumpNormal | MiniDumpWithDataSegs);
m_vaImageBase = 0;
m_vaImageHead = 0;
m_vaImageEntry = 0;
m_vaStack = c_vaStack;
m_cbStack = c_cbStack;
}
CMiniDump::~CMiniDump()
{
Close();
}
BOOL CMiniDump::Create(PCWSTR pwzFile)
{
if (!m_Out.Create(pwzFile)) {
return FALSE;
}
return TRUE;
}
VOID CMiniDump::Close()
{
m_Out.Close();
}
BOOL CMiniDump::Stack(UINT64 vaStack, UINT32 cbStack)
{
m_vaStack = vaStack;
m_cbStack = cbStack;
return TRUE;
}
int __cdecl CMiniDump::CompareModules(const void *p1, const void *p2)
{
const MODULE *m1 = (const MODULE *)p1;
const MODULE *m2 = (const MODULE *)p2;
return (m1->vaImageBase < m2->vaImageBase) ? -1 : 1;
}
int __cdecl CMiniDump::CompareBlobs(const void *p1, const void *p2)
{
const BLOB *m1 = (const BLOB *)p1;
const BLOB *m2 = (const BLOB *)p2;
return (m1->vaBlob < m2->vaBlob) ? -1 : 1;
}
int __cdecl CMiniDump::CompareMinidumpModules(const void *p1, const void *p2)
{
const MINIDUMP_MODULE *m1 = (const MINIDUMP_MODULE *)p1;
const MINIDUMP_MODULE *m2 = (const MINIDUMP_MODULE *)p2;
UINT64 vaImageBase1 = m1->BaseOfImage == s_vaImageBase ? 0 : m1->BaseOfImage;
UINT64 vaImageBase2 = m2->BaseOfImage == s_vaImageBase ? 0 : m2->BaseOfImage;
return (vaImageBase1 < vaImageBase2) ? -1 : 1;
}
BOOL CMiniDump::Write()
{
s_vaImageBase = m_vaImageBase;
qsort(m_Modules.Seek(0), m_Modules.Count(), sizeof(MODULE), CompareModules);
qsort(m_Blobs.Seek(0), m_Blobs.Count(), sizeof(BLOB), CompareBlobs);
UINT32 nRegions = 0;
nRegions += 1; // one for the thread stack.
for (UINT m = 0; m < m_Modules.Count(); m++) {
nRegions += m_Modules.Seek(m)->nRegions;
}
for (UINT m = 0; m < m_Blobs.Count(); m++) {
nRegions++;
}
//
m_Directory[0].StreamType = ThreadListStream;
m_Directory[1].StreamType = ModuleListStream;
m_Directory[2].StreamType = MemoryListStream;
m_Directory[3].StreamType = SystemInfoStream;
m_Directory[4].StreamType = 0xcafeface;
// First we must allocate all of the dump before the memory regions...
PMINIDUMP_THREAD_LIST pThreads = (PMINIDUMP_THREAD_LIST)AddCreate
(sizeof(MINIDUMP_THREAD_LIST) + sizeof(MINIDUMP_THREAD),
&m_Directory[0].Location);
PMINIDUMP_MODULE_LIST pModules = (PMINIDUMP_MODULE_LIST)AddCreate
(sizeof(MINIDUMP_MODULE_LIST) + m_Modules.Count() * sizeof(MINIDUMP_MODULE),
&m_Directory[1].Location);
PMINIDUMP_MEMORY_LIST pRegions = (PMINIDUMP_MEMORY_LIST)AddCreate
(sizeof(MINIDUMP_MEMORY_LIST) + nRegions * sizeof(MINIDUMP_MEMORY_DESCRIPTOR),
&m_Directory[2].Location);
PMINIDUMP_SYSTEM_INFO pSystems = (PMINIDUMP_SYSTEM_INFO)AddCreate
(sizeof(MINIDUMP_SYSTEM_INFO),
&m_Directory[3].Location);
pThreads->NumberOfThreads = 1;
PCONTEXT pContext = (PCONTEXT)AddCreate(sizeof(CONTEXT),
&pThreads->Threads[0].ThreadContext);
for (UINT m = 0; m < m_Modules.Count(); m++) {
MODULE *pSelf = m_Modules.Seek(m);
AddString(pSelf->pwzImage, &pModules->Modules[m].ModuleNameRva);
WriteDebug(pSelf, &pModules->Modules[m]);
}
// Now, we can write the memory regions...
PMINIDUMP_MEMORY_DESCRIPTOR pRegion = pRegions->MemoryRanges;
UINT module = 0;
UINT blob = 0;
UINT stack = 0;
while (stack < 1 || module < m_Modules.Count() || blob < m_Blobs.Count()) {
UINT64 vaModule = (module < m_Modules.Count())
? m_Modules.Seek(module)->vaImageBase : 0xffffffffffffffff;
UINT64 vaBlob = (blob < m_Blobs.Count())
? m_Blobs.Seek(blob)->vaBlob : 0xffffffffffffffff;
UINT64 vaStack = (stack < 1)
? m_vaStack : 0xffffffffffffffff;
if (vaModule <= vaBlob && vaModule <= vaStack) {
// Module next.
WriteModule(m_Modules.Seek(module), &pModules->Modules[module], pRegion);
pRegion += m_Modules.Seek(module)->nRegions;
module++;
}
else if (vaBlob < vaModule && vaBlob < vaStack) {
// Blob next.
WriteBlob(m_Blobs.Seek(blob), pRegion);
pRegion++;
blob++;
}
else if (vaStack < vaModule && vaStack < vaBlob) {
// Stack next.
AddMemory(m_vaStack, NULL, 0, 0x100, pRegion);
pThreads->Threads[0].Stack = *pRegion;
if (s_fVerbose) {
printf(" add %08I64x..%08I64x %-8.8s\n",
m_vaStack, m_vaStack + 0x100, "stack");
}
stack++;
pRegion++;
}
}
pThreads->Threads[0].ThreadId = 1001;
pThreads->Threads[0].SuspendCount = 0;
pThreads->Threads[0].PriorityClass = 0;
pThreads->Threads[0].Priority = 0;
pThreads->Threads[0].Teb = 0;
Write(pThreads, m_Directory[0].Location);
pModules->NumberOfModules = m_Modules.Count();
s_vaImageBase = m_vaImageBase;
qsort(pModules->Modules, pModules->NumberOfModules,
sizeof(MINIDUMP_MODULE), CompareMinidumpModules);
Write(pModules, m_Directory[1].Location);
pRegions->NumberOfMemoryRanges = nRegions;
Write(pRegions, m_Directory[2].Location);
pSystems->NumberOfProcessors = 1;
// TODO64 add command-line machine flag
if (false) {
pSystems->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
}
else {
pSystems->ProcessorArchitecture = 0;
}
pSystems->ProcessorLevel = 15;
pSystems->ProcessorRevision = 519;
pSystems->MajorVersion = 1;
pSystems->MinorVersion = 1;
pSystems->BuildNumber = 0xdead;
pSystems->PlatformId = 7;
Write(pSystems, m_Directory[3].Location);
pContext->Esp = (UINT32)m_vaStack;
pContext->Ebx = (UINT32)m_vaImageBase;
pContext->Eax = (UINT32)m_vaImageHead;
pContext->Eip = (UINT32)m_vaImageEntry;
if (s_fVerbose) {
printf(" eip: %x, ebx: %x, eax: %x, esp: %x\n",
pContext->Eip, pContext->Ebx, pContext->Eax, pContext->Esp);
}
Write(pContext, pThreads->Threads[0].ThreadContext);
// Finally, the trailer must follow all of the memory regions.
PUINT32 pTrailer = (PUINT32)AddCreate(4 * sizeof(UINT32), &m_Directory[4].Location);
pTrailer[0] = 0xfeedbeef;
pTrailer[1] = 0xcafeface;
pTrailer[2] = 0x00000000;
pTrailer[3] = 0x00000000;
Write(pTrailer, m_Directory[4].Location);
m_Out.Seek(0);
m_Out.Write((PBYTE)&m_Header, sizeof(m_Header));
m_Out.Write((PBYTE)&m_Directory, sizeof(m_Directory));
UINT32 sum = m_Out.Checksum();
pTrailer[3] = -(INT32)sum;
Write(pTrailer, m_Directory[4].Location);
return TRUE;
}
BOOL CMiniDump::Delete()
{
return m_Out.Delete();
}
ULONG CMiniDump::Allocate(UINT32 cbData)
{
ULONG cbWhere = m_cbAllocated;
m_cbAllocated += (cbData + 7) & ~7; // Align to UINT64.
return cbWhere;
}
BOOL CMiniDump::Write(PVOID pvData, MINIDUMP_LOCATION_DESCRIPTOR Out)
{
m_Out.Seek(Out.Rva);
return m_Out.Write((PBYTE)pvData, Out.DataSize);
}
BOOL CMiniDump::AddData(PBYTE pbData, UINT32 cbData, UINT32 cbFull,
MINIDUMP_LOCATION_DESCRIPTOR* pOut)
{
ULONG cbWhere = Allocate(cbData);
ULONG cbZero = cbFull - cbData;
if (!m_Out.Seek(cbWhere)) {
return FALSE;
}
if (cbData > 0 && !m_Out.Write(pbData, cbData)) {
return FALSE;
}
if (!m_Out.Zero(cbZero)) {
return FALSE;
}
if (pOut) {
pOut->DataSize = cbFull;
pOut->Rva = cbWhere;
}
return TRUE;
}
PBYTE CMiniDump::AddCreate(UINT32 cbData, UINT32 * pRva)
{
PBYTE pbData = new BYTE [cbData];
assert(pbData);
ZeroMemory(pbData, cbData);
*pRva = Allocate(cbData);
return pbData;
}
PBYTE CMiniDump::AddCreate(UINT32 cbData,
MINIDUMP_LOCATION_DESCRIPTOR* pOut)
{
pOut->DataSize = cbData;
return AddCreate(cbData, &pOut->Rva);
}
BOOL CMiniDump::AddString(PCWSTR pwzString, UINT32 *pRva)
{
ULONG cbData = sizeof(MINIDUMP_STRING) + wcslen(pwzString) * 2 + 2;
PMINIDUMP_STRING pString = (PMINIDUMP_STRING)AddCreate(cbData, pRva);
pString->Length = wcslen(pwzString) * 2;
wcscpy(pString->Buffer, pwzString);
m_Out.Seek(*pRva);
m_Out.Write((PBYTE)pString, cbData);
return TRUE;
}
BOOL CMiniDump::AddMemory(ULONG64 address,
PBYTE pbData, UINT32 cbData, UINT32 cbFull,
PMINIDUMP_MEMORY_DESCRIPTOR pOut)
{
if (AddData(pbData, cbData, cbFull, &pOut->Memory)) {
pOut->StartOfMemoryRange = address;
return TRUE;
}
return FALSE;
}
BOOL CMiniDump::AddModule(PCWSTR pwzImage)
{
bool isPe64 = false;
CFileMap *pfImage = new CFileMap();
assert(pfImage);
if (!pfImage->Load(pwzImage)) {
fprintf(stderr, "Could not open image file: %ls\n", pwzImage);
abort:
delete pfImage;
return FALSE;
}
MODULE * pModule = m_Modules.Add();
PIMAGE_DOS_HEADER pdos;
PIMAGE_NT_HEADERS ppe;
PIMAGE_NT_HEADERS64 ppe64;
// Read in the PE image header
//
pdos = (PIMAGE_DOS_HEADER)pfImage->Seek(0);
if (pdos == NULL || pdos->e_magic != IMAGE_DOS_SIGNATURE) {
error("Image doesn't have MZ signature.");
goto abort;
}
ppe = (PIMAGE_NT_HEADERS)pfImage->Seek(pdos->e_lfanew);
if (ppe == NULL || ppe->Signature != IMAGE_NT_SIGNATURE) {
error("Image doesn't have PE signature.");
goto abort;
}
if (ppe->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) {
fprintf(stderr, "mkcore: 64-bit PE files!\n");
isPe64 = true;
ppe64 = (PIMAGE_NT_HEADERS64) ppe;
}
pModule->nRegions = 1+ ppe->FileHeader.NumberOfSections;
// this is not right we could be truncating addresses here
DWORD debugAddr;
DWORD debugSize;
if (isPe64) {
debugAddr = ppe64->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
debugSize = ppe64->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
}
else {
debugAddr = ppe->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
debugSize = ppe->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
}
DWORD debugPos = (debugAddr && debugSize)
? RvaToFileOffset(debugAddr, ppe->FileHeader.NumberOfSections,
IMAGE_FIRST_SECTION(ppe))
: 0;
////////////////////////////////////////////////// Adjust Debug Directory.
//
if (debugPos) {
PIMAGE_DEBUG_DIRECTORY pDir = (PIMAGE_DEBUG_DIRECTORY)pfImage->Seek(debugPos);
assert(pDir);
DWORD nEntries = debugSize / sizeof(*pDir);
for (DWORD n = 0; n < nEntries; n++) {
PBYTE pbData = pfImage->Seek(pDir[n].PointerToRawData);
if ((pbData[0] == 'R' && pbData[1] == 'S') ||
(pbData[0] == 'N' && pbData[1] == 'B')) {
pModule->pbCvData = pbData;
pModule->cbCvData = pDir[n].SizeOfData;
break;
}
}
if (nEntries > 1) {
printf(" Dropped %d DBG entries.\n", nEntries - 1);
}
}
pModule->pbImageMap = pfImage->Seek(0);
pModule->pwzImage = pwzImage;
if (isPe64) {
pModule->vaImageBase = ppe64->OptionalHeader.ImageBase;
pModule->vaImageHead = ppe64->OptionalHeader.ImageBase + pdos->e_lfanew;
pModule->vaImageEntry = ppe64->OptionalHeader.ImageBase
+ ppe64->OptionalHeader.AddressOfEntryPoint;
}
else {
pModule->vaImageBase = ppe->OptionalHeader.ImageBase;
pModule->vaImageHead = ppe->OptionalHeader.ImageBase + pdos->e_lfanew;
pModule->vaImageEntry = ppe->OptionalHeader.ImageBase
+ ppe->OptionalHeader.AddressOfEntryPoint;
}
// Copy the image name into the image...
PCWSTR pwzName = pwzImage;
PCWSTR pwz;
if ((pwz = wcsrchr(pwzName, ':')) != NULL) {
pwzName = pwz + 1;
}
if ((pwz = wcsrchr(pwzName, '\\')) != NULL) {
pwzName = pwz + 1;
}
ZeroMemory(pfImage->Seek(64), 64);
wcscpy((PWCHAR)pfImage->Seek(68), pwzName);
_wcslwr((PWCHAR)pfImage->Seek(68));
if (m_vaImageBase == 0) {
m_vaImageBase = pModule->vaImageBase;
m_vaImageHead = pModule->vaImageHead;
m_vaImageEntry = pModule->vaImageEntry;
}
return TRUE;
}
BOOL CMiniDump::AddBlob(PCWSTR pwzImage, UINT64 vaBlob, UINT32 *pcbBlob)
{
CFileMap *pfImage = new CFileMap();
assert(pfImage);
if (!pfImage->Load(pwzImage)) {
fprintf(stderr, "Could not open blob file: %ls\n", pwzImage);
delete pfImage;
return FALSE;
}
BLOB * pBlob = m_Blobs.Add();
pBlob->pwzImage = pwzImage;
pBlob->pbBlob = pfImage->Seek(0);
pBlob->cbBlob = pfImage->Size();
pBlob->vaBlob = vaBlob;
*pcbBlob = pBlob->cbBlob;
return TRUE;
}
BOOL CMiniDump::WriteDebug(MODULE *pSelf,
PMINIDUMP_MODULE pModule)
{
AddString(pSelf->pwzImage, &pModule->ModuleNameRva);
if (pSelf->cbCvData > 0) {
AddData(pSelf->pbCvData, pSelf->cbCvData, pSelf->cbCvData, &pModule->CvRecord);
}
return TRUE;
}
BOOL CMiniDump::WriteModule(MODULE *pSelf,
PMINIDUMP_MODULE pModule,
PMINIDUMP_MEMORY_DESCRIPTOR pRegions)
{
UINT32 cbHeader;
UINT64 vaImageBase;
PIMAGE_DOS_HEADER pdos;
PIMAGE_NT_HEADERS ppe;
PIMAGE_SECTION_HEADER psec;
PIMAGE_SECTION_HEADER pfsec;
PIMAGE_SECTION_HEADER plsec;
bool isPe64;
PIMAGE_NT_HEADERS64 ppe64;
// Read in the PE image header
//
pdos = (PIMAGE_DOS_HEADER)(pSelf->pbImageMap);
ppe = (PIMAGE_NT_HEADERS)(pSelf->pbImageMap + pdos->e_lfanew);
if (ppe->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) {
fprintf(stderr, "WriteModule: 64-bit PE files!\n");
isPe64 = true;
ppe64 = (PIMAGE_NT_HEADERS64) ppe;
}
pfsec = IMAGE_FIRST_SECTION(ppe);
plsec = pfsec + ppe->FileHeader.NumberOfSections;
if (isPe64) {
cbHeader = ppe64->OptionalHeader.SizeOfHeaders;
vaImageBase = ppe64->OptionalHeader.ImageBase;
}
else {
cbHeader = ppe->OptionalHeader.SizeOfHeaders;
vaImageBase = ppe->OptionalHeader.ImageBase;
}
AddMemory(vaImageBase, pSelf->pbImageMap, cbHeader, cbHeader, pRegions++);
if (s_fVerbose) {
printf(" add %08I64x..%08I64x %-8.8s %ls\n",
vaImageBase,
vaImageBase + cbHeader,
"pe",
pSelf->pwzImage);
}
for (psec = pfsec; psec < plsec; psec++) {
DWORD cbData = Min(psec->SizeOfRawData, psec->Misc.VirtualSize);
DWORD cbFull = Max(psec->SizeOfRawData, psec->Misc.VirtualSize);
PBYTE pbData = (pSelf->pbImageMap + psec->PointerToRawData);
if (s_fVerbose) {
printf(" add %08I64x..%08I64x %-8.8s %ls\n",
vaImageBase + psec->VirtualAddress,
vaImageBase + psec->VirtualAddress + cbFull,
psec->Name,
pSelf->pwzImage);
}
AddMemory(vaImageBase + psec->VirtualAddress, pbData, cbData, cbFull,
pRegions++);
}
pModule->BaseOfImage = ppe->OptionalHeader.ImageBase;
pModule->SizeOfImage = ppe->OptionalHeader.SizeOfImage;
pModule->CheckSum = ppe->OptionalHeader.CheckSum;
pModule->TimeDateStamp = ppe->FileHeader.TimeDateStamp;
return TRUE;
}
BOOL CMiniDump::WriteBlob(BLOB *pBlob,
PMINIDUMP_MEMORY_DESCRIPTOR pRegions)
{
if (s_fVerbose) {
printf(" add %08I64x..%08I64x %-8.8s %ls\n",
pBlob->vaBlob,
pBlob->vaBlob + pBlob->cbBlob,
"blob",
pBlob->pwzImage);
}
return AddMemory(pBlob->vaBlob, pBlob->pbBlob, pBlob->cbBlob, pBlob->cbBlob, pRegions);
}
//
//////////////////////////////////////////////////////////////////////////////
BOOL DumpFile(PCWSTR file)
{
CFileMap cfImage;
if (!cfImage.Load(file)) {
fprintf(stderr, "Could not open dump file: %ls\n", file);
return FALSE;
}
PMINIDUMP_HEADER pHeader = (PMINIDUMP_HEADER)cfImage.Seek(0);
printf("Header: [%08x..%08x]\n", 0, sizeof(MINIDUMP_HEADER));
printf(" Signature: %c%c%c%c\n",
((char *)&pHeader->Signature)[0],
((char *)&pHeader->Signature)[1],
((char *)&pHeader->Signature)[2],
((char *)&pHeader->Signature)[3]);
printf(" Version: %08x\n", pHeader->Version);
printf(" Streams: %d\n", pHeader->NumberOfStreams);
printf(" Dir RVA: %08x\n", pHeader->StreamDirectoryRva);
printf(" CheckSum: %08x\n", pHeader->CheckSum);
printf(" TimeDate: %08x\n", pHeader->TimeDateStamp);
printf(" Flags: %016I64x\n", pHeader->Flags);
PMINIDUMP_DIRECTORY pDir
= (PMINIDUMP_DIRECTORY)cfImage.Seek(pHeader->StreamDirectoryRva);
printf("Directory [%08x..%08x]\n", pHeader->StreamDirectoryRva,
pHeader->StreamDirectoryRva
+ sizeof(MINIDUMP_DIRECTORY) * pHeader->NumberOfStreams);
for (UINT i = 0; i < pHeader->NumberOfStreams; i++) {
printf("%4d. Type: %8x, RVA: %8x, Size: %8x\n",
i, pDir[i].StreamType,
(UINT32)pDir[i].Location.Rva, pDir[i].Location.DataSize);
}
for (UINT i = 0; i < pHeader->NumberOfStreams; i++) {
switch (pDir[i].StreamType) {
case UnusedStream:
break;
case ThreadListStream:
{
PMINIDUMP_THREAD_LIST pl =
(PMINIDUMP_THREAD_LIST)cfImage.Seek(pDir[i].Location.Rva);
printf("ThreadList:\n");
printf(" Thread: %d\n", pl->NumberOfThreads);
for (UINT t = 0; t < pl->NumberOfThreads; t++) {
printf(" %8d: Stack=%08I64x [%08x..%08x] [%08x..%08x] %d\n",
pl->Threads[t].ThreadId,
pl->Threads[t].Stack.StartOfMemoryRange,
pl->Threads[t].Stack.Memory.Rva,
pl->Threads[t].Stack.Memory.Rva +
pl->Threads[t].Stack.Memory.DataSize,
pl->Threads[t].ThreadContext.Rva,
pl->Threads[t].ThreadContext.Rva +
pl->Threads[t].ThreadContext.DataSize,
pl->Threads[t].ThreadContext.DataSize);
}
}
break;
case ModuleListStream:
{
PMINIDUMP_MODULE_LIST pl =
(PMINIDUMP_MODULE_LIST)cfImage.Seek(pDir[i].Location.Rva);
printf("ModuleList: (%x)\n", pDir[i].Location.Rva);
printf(" Modules: %d\n", pl->NumberOfModules);
UINT t = 0;
for (; t < pl->NumberOfModules && t < 10; t++) {
printf(" %08I64x Size=%08x NameRva=%08x chks=%08x date=%08x \n"
" `%.60ls'(%d.%d)\n",
pl->Modules[t].BaseOfImage,
pl->Modules[t].SizeOfImage,
pl->Modules[t].ModuleNameRva,
pl->Modules[t].CheckSum,
pl->Modules[t].TimeDateStamp,
cfImage.Seek(pl->Modules[t].ModuleNameRva)+4,
*(PULONG)cfImage.Seek(pl->Modules[t].ModuleNameRva),
2* wcslen((PWCHAR)cfImage.Seek(pl->Modules[t].ModuleNameRva)+4)
);
#if 0
if (pl->Modules[t].CvRecord.Rva) {
printf(" CV [%08x..%08x]\n",
pl->Modules[t].CvRecord.Rva,
pl->Modules[t].CvRecord.Rva +
pl->Modules[t].CvRecord.DataSize);
Dump(cfImage.Seek(pl->Modules[t].CvRecord.Rva),
pl->Modules[t].CvRecord.DataSize);
}
if (pl->Modules[t].MiscRecord.Rva) {
printf(" Misc [%08x..%08x]\n",
pl->Modules[t].MiscRecord.Rva,
pl->Modules[t].MiscRecord.Rva +
pl->Modules[t].MiscRecord.DataSize);
Dump(cfImage.Seek(pl->Modules[t].MiscRecord.Rva),
pl->Modules[t].MiscRecord.DataSize);
}
#endif
}
if (t < pl->NumberOfModules) {
printf(" ...\n");
}
}
break;
case MemoryListStream:
{
PMINIDUMP_MEMORY_LIST pl =
(PMINIDUMP_MEMORY_LIST)cfImage.Seek(pDir[i].Location.Rva);
printf("MemoryList:\n");
printf(" Ranges: %d\n", pl->NumberOfMemoryRanges);
UINT t = 0;
for (; t < pl->NumberOfMemoryRanges /* && t < 10 */; t++) {
printf(" %08I64x..%08I64x [%08x..%08x]\n",
pl->MemoryRanges[t].StartOfMemoryRange,
pl->MemoryRanges[t].StartOfMemoryRange
+ pl->MemoryRanges[t].Memory.DataSize,
pl->MemoryRanges[t].Memory.Rva,
pl->MemoryRanges[t].Memory.Rva
+ pl->MemoryRanges[t].Memory.DataSize);
}
if (t < pl->NumberOfMemoryRanges) {
printf(" ...\n");
}
}
break;
case SystemInfoStream:
{
PMINIDUMP_SYSTEM_INFO pi =
(PMINIDUMP_SYSTEM_INFO)cfImage.Seek(pDir[i].Location.Rva);
printf("SystemInfo:\n");
printf(" Processors: %d\n", pi->NumberOfProcessors);
printf(" Processor: %d.%d.%d\n",
pi->ProcessorArchitecture,
pi->ProcessorLevel,
pi->ProcessorRevision);
printf(" Version: %d.%d.%d %d\n",
pi->MajorVersion,
pi->MinorVersion,
pi->BuildNumber,
pi->PlatformId);
printf(" VendorId: %-12.12s\n", pi->Cpu.X86CpuInfo.VendorId);
printf(" Version: %08x\n", pi->Cpu.X86CpuInfo.VersionInformation);
printf(" Feature: %08x\n", pi->Cpu.X86CpuInfo.FeatureInformation);
printf(" AMDFeat: %08x\n", pi->Cpu.X86CpuInfo.AMDExtendedCpuFeatures);
}
break;
}
}
return TRUE;
}
int __cdecl wmain(int argc, WCHAR **argv)
{
BOOL fNeedHelp = FALSE;
BOOL fGood = FALSE;
PCWSTR pwzOutput = NULL;
CMiniDump miniDump;
UINT64 vaStack = CMiniDump::c_vaStack;
UINT32 cbStack = CMiniDump::c_cbStack;
UINT64 vaBlob = CMiniDump::c_vaBlob;
UINT32 cbBlob = 0;
for (int arg = 1; arg < argc && !fNeedHelp; arg++) {
if (argv[arg][0] == '-' || argv[arg][0] == '/') {
WCHAR *argn = argv[arg]+1; // Argument name
WCHAR *argp = argn; // Argument parameter
while (*argp && *argp != ':') {
argp++;
}
if (*argp == ':')
*argp++ = '\0';
switch (argn[0]) {
case 'a': // Set next blob address
case 'A':
vaBlob = _wcstoi64(argp, NULL, 0);
break;
case 'b':
case 'B':
if (!miniDump.AddBlob(argp, vaBlob, &cbBlob)) {
break;
}
vaBlob += cbBlob;
break;
case 'c': // Stack size
case 'C':
cbStack = (UINT32)_wcstoi64(argp, NULL, 0);
break;
case 's': // Stack base
case 'S':
vaStack = _wcstoi64(argp, NULL, 0);
break;
case 'd': // Dump
case 'D':
DumpFile(argp);
break;
case 'o': // Output file.
case 'O':
pwzOutput = argp;
if (!miniDump.Create(pwzOutput)) {
fprintf(stderr, "Could not open output file: %ls\n", pwzOutput);
break;
}
printf("%ls:\n", pwzOutput);
break;
case 'v': // Verbose
case 'V':
s_fVerbose = TRUE;
break;
case '?': // Help
fNeedHelp = TRUE;
break;
default:
printf("Unknown argument: %ls\n", argv[arg]);
fNeedHelp = TRUE;
break;
}
}
else {
if (pwzOutput == NULL) {
fprintf(stderr, "Must specify output file before input files.\n");
fNeedHelp = TRUE;
break;
}
if (!miniDump.AddModule(argv[arg])) {
break;
}
else {
fGood = TRUE;
}
}
}
if (!fGood) {
miniDump.Delete();
}
else {
miniDump.Stack(vaStack, cbStack);
miniDump.Write();
miniDump.Close();
}
if (argc == 1) {
fNeedHelp = TRUE;
}
if (fNeedHelp) {
printf(
"Usage:\n"
" mkcore [options] images...\n"
"or:\n"
" mkcore /d:minidump\n"
"Options:\n"
" /a:address -- Set virtual address for subsequent blobs.\n"
" /b:file -- Add a blob file to the minidump.\n"
" /d:minidump -- Display the context of minidump file.\n"
" /o:out_file -- Specify output file (defaults to image.exe).\n"
" /s:stackbase -- Set the base address stack (defaults to 0x%I64x).\n"
" /c:stacksize -- Set the size of zeroed stack data (defaults to 0x%x).\n"
" /v -- Verbose.\n"
" /? -- Display this help screen.\n"
"Summary:\n"
" Creates a minidump file from the sections of one or more PEs.\n"
" The first PE file is marked for entry on startup.\n",
(UINT64)CMiniDump::c_vaStack,
(UINT32)CMiniDump::c_cbStack
);
}
return 0;
}
//
///////////////////////////////////////////////////////////////// End of File.