/*
 * ************************************************************************************************************
 * Author: Marcin Załęczny
 * Copyright (C)2011 Marcin Załęczny <mzaleczny@hotmail.com>
 * License: The GNU Lesser General Public License, version 3.0 (LGPLv3)
 * Library Name: mzstddebug
 * Version: 1.02
 * First release date:   2011-02-03
 * Current release date: 2011-03-04
 *
 *
 * This file is part of mzstddebug library
 * 
 * mzstddebug library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * mzstddebug library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * ************************************************************************************************************
 */

#include "mzPEFile.h"
#include "mzFileUtils.h"
#include "mzFileMapping.h"
#include "mzMemoryView.h"

VOID CPEFile::_commonInit() {
    this->imageNtHeaders = NULL;
    this->imageDosExeData = NULL;
    this->imageDosHeader = NULL;
    //this->imageSectionHeaders = NULL;
    this->setRealClassName(L"CBinaryFile");
    this->_isValid = FALSE;
}

CPEFile::CPEFile() {
    this->_commonInit();
}

CPEFile::~CPEFile() {
    this->cleanup();
}

VOID CPEFile::cleanup() {
    if (this->imageSectionHeaders) {
        delete[] this->imageSectionHeaders;
        this->imageSectionHeaders = NULL;
    }
    if (this->imageNtHeaders) {
        delete this->imageNtHeaders;
        this->imageNtHeaders = NULL;
    }

    if (this->imageDosExeData) {
        delete this->imageDosExeData;
        this->imageDosExeData = NULL;
    }

    if (this->imageDosHeader) {
        delete this->imageDosHeader;
        this->imageDosHeader = NULL;
    }

    //this->cleanupSectionHeadersArray();
}

/*
VOID CPEFile::cleanupSectionHeadersArray() {
    if (this->imageSectionHeaders) {
        PIMAGE_SECTION_HEADER sectionHeader;
        for (LONG i = 0; i < this->imageSectionHeaders->size(); i++) {
            sectionHeader = (PIMAGE_SECTION_HEADER)this->imageSectionHeaders->getAt(i);
            if (sectionHeader) {
                delete sectionHeader;
            }
        }
        this->imageSectionHeaders->clear();
        delete this->imageSectionHeaders;
        this->imageSectionHeaders = NULL;
    }
}
*/
BOOL CPEFile::loadFileInfo(CString filename) {
    this->_isValid = FALSE;
    if (FileExists(filename)) {
        DWORD filesize = GetFileSize(filename);
        DWORD sizeofDosHeader = sizeof(IMAGE_DOS_HEADER);
        DWORD sizeofNtHeaders = sizeof(IMAGE_NT_HEADERS);
        DWORD sizeofSectionHeader = sizeof(IMAGE_SECTION_HEADER);
        if (filesize >= 0) {
            CFileMapping* fm = new CFileMapping(filename);
            if (fm) {
                DWORD bufferSize = 1024 * 512; // allocate 512KB buffer
                if (bufferSize > filesize) {
                    bufferSize = filesize;
                }
                BYTE* buffer = (BYTE*)fm->mapFileFragmentToMemory(0, bufferSize);
                if (!buffer) {
                    Stdout(L"Error during mapping file into memory.\n");
                    return FALSE;
                }

                this->cleanup();
                this->imageNtHeaders = new IMAGE_NT_HEADERS();
                this->imageDosHeader = new IMAGE_DOS_HEADER();
                this->imageSectionHeaders = NULL;

                DWORD pehdrOffset = 0;
                if (filesize >= sizeofDosHeader) {
                    this->_readDosHeaderSize = sizeofDosHeader;
                    CopyMemory(this->imageDosHeader, &buffer[0], this->_readDosHeaderSize);
                    pehdrOffset = this->imageDosHeader->e_lfanew;
                } else {
                    this->_readDosHeaderSize = filesize;
                    ZeroMemory(this->imageDosHeader, sizeofDosHeader);
                    CopyMemory(this->imageDosHeader, &buffer[0], this->_readDosHeaderSize);
                }
                this->_dosHeaderOffset = 0;


                this->_readDosExeDataSize = 0;
                if (filesize > sizeofDosHeader) {
                    if (filesize > pehdrOffset) {
                        this->_readDosExeDataSize = pehdrOffset - sizeofDosHeader;
                    } else {
                        this->_readDosExeDataSize = filesize - sizeofDosHeader;
                    }
                    this->imageDosExeData = new CDOSEXE_DATA(&buffer[sizeofDosHeader], this->_readDosExeDataSize);
                }
                this->_dosExeDataOffset = sizeofDosHeader;


                this->_readNtHeadersSize = 0;
                if (filesize > pehdrOffset) {
                    if (filesize >= pehdrOffset + sizeofNtHeaders) {
                        this->_readNtHeadersSize = sizeofNtHeaders;
                        CopyMemory(this->imageNtHeaders, &buffer[pehdrOffset], this->_readNtHeadersSize);
                    } else {
                        ZeroMemory(this->imageNtHeaders, sizeofNtHeaders);
                        if (filesize > pehdrOffset) {
                            this->_readNtHeadersSize = filesize - pehdrOffset;
                            CopyMemory(this->imageNtHeaders, &buffer[pehdrOffset], this->_readNtHeadersSize);
                        }
                    }
                }
                this->_ntHeadersOffset = pehdrOffset;


                this->_readSectionHeadersSize = 0;
                this->_sectionHeadersOffset = 0;
                if (pehdrOffset > 0 && filesize > pehdrOffset + sizeofNtHeaders) {
                    ULONG numberOfSections = this->imageNtHeaders->FileHeader.NumberOfSections;
                    // assumption for no more than 64 sections
                    if (numberOfSections > 0 && numberOfSections < 64) {
                        LONG totalSize = numberOfSections * sizeofSectionHeader;
                        this->imageSectionHeaders = new IMAGE_SECTION_HEADER[numberOfSections];
                        this->_sectionHeadersOffset = pehdrOffset + sizeofNtHeaders;
                        if (filesize >= this->_sectionHeadersOffset + totalSize) {
                            this->_readSectionHeadersSize = totalSize;
                            CopyMemory(this->imageSectionHeaders, &buffer[this->_sectionHeadersOffset], this->_readSectionHeadersSize);
                        } else {
                            ZeroMemory(this->imageSectionHeaders, totalSize);
                            LONG realSize = filesize - this->_sectionHeadersOffset;
                            if (realSize > 0) {
                                this->_readSectionHeadersSize = realSize;
                                CopyMemory(this->imageSectionHeaders, &buffer[this->_sectionHeadersOffset], this->_readSectionHeadersSize);
                            }
                        }
                    }
                }


                delete fm;
                return TRUE;
            }
        } else {
            Stdout(CString(L"File ") + filename + L" is empty.");
        }
    } else {
        Stdout(CString(L"File ") + filename + L" does not exists.\n");
    }
    return FALSE;
}

VOID CPEFile::stdoutImageHeadersDetails(CString indentation, BOOL dumpAsBytes, BOOL dumpAsWords, BOOL dumpAsDwords) {
    this->stdoutImageDosHeader(indentation, dumpAsBytes, dumpAsWords, dumpAsDwords);
    Stdout(L"\n\n");
    this->stdoutImageDosExeData(indentation, dumpAsBytes, dumpAsWords, dumpAsDwords);
    Stdout(L"\n\n");
    this->stdoutImageNtHeaders(indentation, dumpAsBytes, dumpAsWords, dumpAsDwords);
    Stdout(L"\n\n");
    this->stdoutImageSectionHeaders(indentation, dumpAsBytes, dumpAsWords, dumpAsDwords);
}

VOID CPEFile::stdoutImageDosHeader(CString indentation, BOOL dumpAsBytes, BOOL dumpAsWords, BOOL dumpAsDwords) {
    Stdout(indentation + L"DOS HEADER:\n");

    DWORD sizeofDosHeader = sizeof(IMAGE_DOS_HEADER);
    if (this->_readDosHeaderSize < sizeofDosHeader) {
        Stdout(indentation + L"  Invalid DOS Header. Should be size of: %d. Read in %d bytes\n", sizeofDosHeader, this->_readDosHeaderSize);
    }
    if (this->_readDosHeaderSize < sizeof(WORD)) {
        Stdout(indentation + L"  Invalid DOS Header. Not complete DOS signature read.\n");
    }
    if (this->_readDosHeaderSize >= sizeof(WORD) && this->imageDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
        WORD signature = IMAGE_DOS_SIGNATURE;
        BYTE* ptrHdr = (BYTE*)this->imageDosHeader;
        BYTE* ptrSig = (BYTE*)&signature;
        Stdout(indentation + L"  Invalid DOS Header signature: %#4x [%c%c, in file: %c%c], should be: %#4x [%c%c in file %c%c].\n",
                this->imageDosHeader->e_magic, ptrHdr[0], ptrHdr[1], ptrHdr[1], ptrHdr[0],
                signature, ptrSig[1], ptrSig[0], ptrSig[0], ptrSig[1]);
    }

    if (this->_readDosHeaderSize < 1) {
        return;
    }
    Stdout(indentation + L"  FILE OFFSET: %8ddx\n", this->_dosHeaderOffset);
    Stdout(indentation + L"  DATA SIZE:   %8ddx\n", this->_readDosHeaderSize);

    if (dumpAsBytes || dumpAsWords || dumpAsDwords) {
        CMemoryView mv(this->imageDosHeader, this->_readDosHeaderSize, this->_dosHeaderOffset);
        mv.print(indentation + L"  ", dumpAsBytes, dumpAsWords, dumpAsDwords);
    }

    CString details(indentation + L"  STRUCTURE DETAILS:\n");
    indentation += L"    ";
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_magic) + sizeof(this->imageDosHeader->e_magic) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_magic", this->imageDosHeader->e_magic, L"Magic number");
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_cblp) + sizeof(this->imageDosHeader->e_cblp) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_cblp", this->imageDosHeader->e_cblp, L"Bytes on last page of file");
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_cp) + sizeof(this->imageDosHeader->e_cp) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_cp", this->imageDosHeader->e_cp, L"Pages in file");
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_crlc) + sizeof(this->imageDosHeader->e_crlc) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_crlc", this->imageDosHeader->e_crlc, L"Relocations");
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_cparhdr) + sizeof(this->imageDosHeader->e_cparhdr) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_cparhdr", this->imageDosHeader->e_cparhdr, L"Size of header in paragraphs");
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_minalloc) + sizeof(this->imageDosHeader->e_minalloc) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_minalloc", this->imageDosHeader->e_minalloc, L"Minimum extra paragraphs needed");
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_maxalloc) + sizeof(this->imageDosHeader->e_maxalloc) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_maxalloc", this->imageDosHeader->e_maxalloc, L"Maximum extra paragraphs needed");
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_ss) + sizeof(this->imageDosHeader->e_ss) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_ss", this->imageDosHeader->e_ss, L"Initial (relative) SS value");
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_sp) + sizeof(this->imageDosHeader->e_sp) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_sp", this->imageDosHeader->e_sp, L"Initial SP value");
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_csum) + sizeof(this->imageDosHeader->e_csum) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_csum", this->imageDosHeader->e_csum, L"Checksum");
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_ip) + sizeof(this->imageDosHeader->e_ip) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_ip", this->imageDosHeader->e_ip, L"Initial IP value");
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_cs) + sizeof(this->imageDosHeader->e_cs) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_cs", this->imageDosHeader->e_cs, L"Initial (relative) CS value");
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_lfarlc) + sizeof(this->imageDosHeader->e_lfarlc) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_lfarlc", this->imageDosHeader->e_lfarlc, L"File address of relocation table");
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_ovno) + sizeof(this->imageDosHeader->e_ovno) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_ovno", this->imageDosHeader->e_ovno, L"Overlay number");
    }
    for (LONG i = 0; i < 4; i++) {
        if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_res[i]) + sizeof(this->imageDosHeader->e_res[i]) <= this->_readDosHeaderSize) {
            details += indentation + FormatMessage(L"e_res[%d]  :  %8dwx     %-40s\n", i, this->imageDosHeader->e_res[i], L"Reserved words");
        }
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_oemid) + sizeof(this->imageDosHeader->e_oemid) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_oemid", this->imageDosHeader->e_oemid, L"OEM identifier (for e_oeminfo)");
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_oeminfo) + sizeof(this->imageDosHeader->e_oeminfo) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8dwx     %-40s\n", L"e_oeminfo", this->imageDosHeader->e_oeminfo, L"OEM information; e_oemid specific");
    }
    for (LONG i = 0; i < 10; i++) {
        if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_res2[i]) + sizeof(this->imageDosHeader->e_res2[i]) <= this->_readDosHeaderSize) {
            details += indentation + FormatMessage(L"e_res2[%d] :  %8dwx     %-40s\n", i, this->imageDosHeader->e_res2[i], L"Reserved words");
        }
    }
    if (this->dosHeaderFieldOffset((BYTE*)&this->imageDosHeader->e_lfanew) + sizeof(this->imageDosHeader->e_lfanew) <= this->_readDosHeaderSize) {
        details += indentation + FormatMessage(L"%-10s:  %8ddx %-40s\n", L"e_lfanew", this->imageDosHeader->e_lfanew, L"File address of new exe header");
    }
    Stdout(details);
}

VOID CPEFile::stdoutImageDosExeData(CString indentation, BOOL dumpAsBytes, BOOL dumpAsWords, BOOL dumpAsDwords) {
    Stdout(indentation + L"DOS EXE DATA:\n");

    if (!this->imageDosExeData) {
        Stdout(indentation + L"  DOS Data not read in.\n");
        return;
    }

    if (this->_readDosExeDataSize < 1) {
        return;
    }
    Stdout(indentation + L"  FILE OFFSET: %8ddx\n", this->_dosExeDataOffset);
    Stdout(indentation + L"  DATA SIZE:   %8ddx\n", this->_readDosExeDataSize);

    if (dumpAsBytes || dumpAsWords || dumpAsDwords) {
        CMemoryView mv(this->imageDosExeData->data, this->_readDosExeDataSize, this->_dosExeDataOffset);
        mv.print(indentation + L"  ", dumpAsBytes, dumpAsWords, dumpAsDwords);
    }
}

VOID CPEFile::stdoutImageNtHeaders(CString indentation, BOOL dumpAsBytes, BOOL dumpAsWords, BOOL dumpAsDwords) {
    Stdout(indentation + L"NT HEADERS:\n");

    DWORD sizeofNtHeaders = sizeof(IMAGE_NT_HEADERS);
    if (this->_readNtHeadersSize < sizeofNtHeaders) {
        Stdout(indentation + L"  Invalid NT Headers. Should be size of: %d. Read in %d bytes\n", sizeofNtHeaders, this->_readNtHeadersSize);
    }
    if (this->_readNtHeadersSize < sizeof(DWORD)) {
        Stdout(indentation + L"  Invalid NT Headers. Not complete NT signature read.\n");
    }
    if (this->_readNtHeadersSize >= sizeof(DWORD) && this->imageNtHeaders->Signature != IMAGE_NT_SIGNATURE) {
        DWORD signature = IMAGE_NT_SIGNATURE;
        BYTE* ptrHdr = (BYTE*)this->imageNtHeaders;
        BYTE* ptrSig = (BYTE*)&signature;
        Stdout(indentation + L"  Invalid NT Headers signature: %#8x [%c%c%c%c, in file: %c%c%c%c], "
                L"should be: %#8x [%c%c%c%c in file %c%c%c%c].\n",
                this->imageNtHeaders->Signature, ptrHdr[0], ptrHdr[1], ptrHdr[2], ptrHdr[3],
                                                 ptrHdr[3], ptrHdr[2], ptrHdr[1], ptrHdr[0],
                signature, ptrSig[3], ptrSig[2], ptrSig[1], ptrSig[0],
                           ptrSig[0], ptrSig[1], ptrSig[2], ptrSig[3]);
    }

    if (this->_readNtHeadersSize < 1) {
        return;
    }
    Stdout(indentation + L"  FILE OFFSET: %8ddx\n", this->_ntHeadersOffset);
    Stdout(indentation + L"  DATA SIZE:   %8ddx\n", this->_readNtHeadersSize);

    if (dumpAsBytes || dumpAsWords || dumpAsDwords) {
        CMemoryView mv(this->imageNtHeaders, this->_readNtHeadersSize, this->_ntHeadersOffset);
        mv.print(indentation + L"  ", dumpAsBytes, dumpAsWords, dumpAsDwords);
    }

    CString details(indentation + L"  STRUCTURE DETAILS:\n");
    indentation += L"    ";
    if (this->ntHeaderFieldOffset((BYTE*)&this->imageNtHeaders->Signature) + sizeof(this->imageNtHeaders->Signature) <= this->_readNtHeadersSize) {
        details += indentation + FormatMessage(L"%-31s: %10ddx %-10s %-30s\n", L"Signature", this->imageNtHeaders->Signature, L"", L"");
    }

    PIMAGE_FILE_HEADER pifh = &this->imageNtHeaders->FileHeader;
    details += indentation + FormatMessage(L"FileHeader [size: %d]:\n", sizeof(this->imageNtHeaders->FileHeader));
    CString subinfo;
    if (this->ntHeaderFieldOffset((BYTE*)&pifh->Machine) + sizeof(pifh->Machine) <= this->_readNtHeadersSize) {
        switch (pifh->Machine) {
            case IMAGE_FILE_MACHINE_UNKNOWN:
                break;
            case IMAGE_FILE_MACHINE_I386:
                subinfo = L"(x86)";
                break;
            case IMAGE_FILE_MACHINE_R3000:
                subinfo = L"(R3000 - MIPS little-endian)";
                break;
            case IMAGE_FILE_MACHINE_R4000:
                subinfo = L"(R4000 - MIPS little-endian)";
                break;
            case IMAGE_FILE_MACHINE_R10000:
                subinfo = L"(R10000 - MIPS little-endian)";
                break;
            case IMAGE_FILE_MACHINE_WCEMIPSV2:
                subinfo = L"(WCEMIPSV2 - MIPS little-endian)";
                break;
            case IMAGE_FILE_MACHINE_ALPHA:
                subinfo = L"(Alpha_AXP)";
                break;
            case IMAGE_FILE_MACHINE_SH3:
                subinfo = L"(SH3 little-endian)";
                break;
            case IMAGE_FILE_MACHINE_SH3DSP:
                subinfo = L"(SH3DSP)";
                break;
            case IMAGE_FILE_MACHINE_SH3E:
                subinfo = L"(SH3E little-endian)";
                break;
            case IMAGE_FILE_MACHINE_SH4:
                subinfo = L"(SH4 little-endian)";
                break;
            case IMAGE_FILE_MACHINE_SH5:
                subinfo = L"(SH5)";
                break;
            case IMAGE_FILE_MACHINE_ARM:
                subinfo = L"(ARM)";
                break;
              case IMAGE_FILE_MACHINE_THUMB:
                subinfo = L"(THUMB)";
                break;
            case IMAGE_FILE_MACHINE_AM33:
                subinfo = L"(AM33)";
                break;
            case IMAGE_FILE_MACHINE_POWERPC:
                subinfo = L"(IBM PowerPC Little-Endian)";
                break;
            case IMAGE_FILE_MACHINE_POWERPCFP:
                subinfo = L"(POWERPCFP)";
                break;
            case IMAGE_FILE_MACHINE_IA64:
                subinfo = L"(ia64)";
                break;
            case IMAGE_FILE_MACHINE_MIPS16:
                subinfo = L"(MIPS16)";
                break;
            case IMAGE_FILE_MACHINE_ALPHA64:
                subinfo = L"(ALPHA64)";
                break;
            case IMAGE_FILE_MACHINE_MIPSFPU:
                subinfo = L"(MIPSFPU)";
                break;
            case IMAGE_FILE_MACHINE_MIPSFPU16:
                subinfo = L"(MIPSFPU16)";
                break;
            /*
            case IMAGE_FILE_MACHINE_AXP64: // we do not land here
                subinfo = L"(AXP64=ALPHA64)";
                break;
            */
            case IMAGE_FILE_MACHINE_TRICORE:
                subinfo = L"(TRICORE - Infineon)";
                break;
            case IMAGE_FILE_MACHINE_CEF:
                subinfo = L"(CEF)";
                break;
            case IMAGE_FILE_MACHINE_EBC:
                subinfo = L"(EBC - EFI Byte Code)";
                break;
            case IMAGE_FILE_MACHINE_AMD64:
                subinfo = L"(AMD64 - K8)";
                break;
            case IMAGE_FILE_MACHINE_M32R:
                subinfo = L"(M32R little-endian)";
                break;
            case IMAGE_FILE_MACHINE_CEE:
                subinfo = L"(CEE)";
                break;
            default:
                subinfo = L"(Unknown)";
                break;
        }
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dwx     %-12s: %-30s\n", L"Machine", pifh->Machine, subinfo.toWCHAR(), L"Target CPU");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pifh->NumberOfSections) + sizeof(pifh->NumberOfSections) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dwx     %-12s: %-30s\n", L"NumberOfSections", pifh->NumberOfSections, L"", L"Number of sections in the section table");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pifh->TimeDateStamp) + sizeof(pifh->TimeDateStamp) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-12s: %-30s\n", L"TimeDateStamp", pifh->TimeDateStamp, subinfo.toWCHAR(), L"Creation time (number of seconds since 1970-01-01 00:00 GMT)");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pifh->PointerToSymbolTable) + sizeof(pifh->PointerToSymbolTable) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-12s: %-30s\n", L"PointerToSymbolTable", pifh->PointerToSymbolTable, L"", L"Offset of the COFF symbol table");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pifh->NumberOfSymbols) + sizeof(pifh->NumberOfSymbols) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-12s: %-30s\n", L"NumberOfSymbols", pifh->NumberOfSymbols, L"", L"Number of symbols in the COFF symbol table");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pifh->SizeOfOptionalHeader) + sizeof(pifh->SizeOfOptionalHeader) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dwx     %-12s: %-30s\n", L"SizeOfOptionalHeader", pifh->SizeOfOptionalHeader, L"", L"Size of optional data in Optional Header");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pifh->Characteristics) + sizeof(pifh->Characteristics) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dwx     %-12s: %-30s\n", L"Characteristics", pifh->Characteristics, L"", L"Bit flags indicating attributes of the file");
        if ((pifh->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) == IMAGE_FILE_RELOCS_STRIPPED) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_RELOCS_STRIPPED", IMAGE_FILE_RELOCS_STRIPPED, L"Relocation info stripped from file");
        }
        if ((pifh->Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == IMAGE_FILE_EXECUTABLE_IMAGE) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_EXECUTABLE_IMAGE", IMAGE_FILE_EXECUTABLE_IMAGE, L"File is executable");
        }
        if ((pifh->Characteristics & IMAGE_FILE_LINE_NUMS_STRIPPED) == IMAGE_FILE_LINE_NUMS_STRIPPED) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_LINE_NUMS_STRIPPED", IMAGE_FILE_LINE_NUMS_STRIPPED, L"Line numbers stripped from file");
        }
        if ((pifh->Characteristics & IMAGE_FILE_LOCAL_SYMS_STRIPPED) == IMAGE_FILE_LOCAL_SYMS_STRIPPED) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_LOCAL_SYMS_STRIPPED", IMAGE_FILE_LOCAL_SYMS_STRIPPED, L"Local symbols stripped from file");
        }
        if ((pifh->Characteristics & IMAGE_FILE_AGGRESIVE_WS_TRIM) == IMAGE_FILE_AGGRESIVE_WS_TRIM) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_AGGRESIVE_WS_TRIM", IMAGE_FILE_AGGRESIVE_WS_TRIM, L"Agressively trim working set");
        }
        if ((pifh->Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) == IMAGE_FILE_LARGE_ADDRESS_AWARE) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_LARGE_ADDRESS_AWARE", IMAGE_FILE_LARGE_ADDRESS_AWARE, L"App can handle >2gb addresses");
        }
        if ((pifh->Characteristics & IMAGE_FILE_BYTES_REVERSED_LO) == IMAGE_FILE_BYTES_REVERSED_LO) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_BYTES_REVERSED_LO", IMAGE_FILE_BYTES_REVERSED_LO, L"Bytes of machine word are reversed");
        }
        if ((pifh->Characteristics & IMAGE_FILE_32BIT_MACHINE) == IMAGE_FILE_32BIT_MACHINE) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_32BIT_MACHINE", IMAGE_FILE_32BIT_MACHINE, L"For 32-bit machine");
        }
        if ((pifh->Characteristics & IMAGE_FILE_DEBUG_STRIPPED) == IMAGE_FILE_DEBUG_STRIPPED) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_DEBUG_STRIPPED", IMAGE_FILE_DEBUG_STRIPPED, L"Debugging info stripped from file => .dbg file");
        }
        if ((pifh->Characteristics & IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP) == IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP", IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP, L"If image is on removable media, copy and run from the swap file");
        }
        if ((pifh->Characteristics & IMAGE_FILE_NET_RUN_FROM_SWAP) == IMAGE_FILE_NET_RUN_FROM_SWAP) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_NET_RUN_FROM_SWAP", IMAGE_FILE_NET_RUN_FROM_SWAP, L"If image is on Net, copy and run from the swap file");
        }
        if ((pifh->Characteristics & IMAGE_FILE_SYSTEM) == IMAGE_FILE_SYSTEM) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_SYSTEM", IMAGE_FILE_SYSTEM, L"System file");
        }
        if ((pifh->Characteristics & IMAGE_FILE_DLL) == IMAGE_FILE_DLL) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_DLL", IMAGE_FILE_DLL, L"File is a DLL");
        }
        if ((pifh->Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY) == IMAGE_FILE_UP_SYSTEM_ONLY) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_UP_SYSTEM_ONLY", IMAGE_FILE_UP_SYSTEM_ONLY, L"File should only be run on a UP machine");
        }
        if ((pifh->Characteristics & IMAGE_FILE_BYTES_REVERSED_HI) == IMAGE_FILE_BYTES_REVERSED_HI) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %#8x  %-30s\n", L"", L"", L"IMAGE_FILE_BYTES_REVERSED_HI", IMAGE_FILE_BYTES_REVERSED_HI, L"Bytes of machine word are reversed");
        }
    }


    PIMAGE_OPTIONAL_HEADER pioh = &this->imageNtHeaders->OptionalHeader;
    details += indentation + FormatMessage(L"OptionalHeader [size: %d]:\n", sizeof(this->imageNtHeaders->OptionalHeader));
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->Magic) + sizeof(pioh->Magic) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dwx     %-6s: %-30s\n", L"Magic", pioh->Magic, L"", L"Signature identifying type of this header");
        if (pioh->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %-30s\n", L"", L"", L"HEADER 32", L"");
        }
        if (pioh->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %-30s\n", L"", L"", L"HEADER 64", L"");
        }
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->MajorLinkerVersion) + sizeof(pioh->MajorLinkerVersion) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dbx       %-6s: %-30s\n", L"MajorLinkerVersion", pioh->MajorLinkerVersion, L"", L"Major linker version");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->MinorLinkerVersion) + sizeof(pioh->MinorLinkerVersion) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dbx       %-6s: %-30s\n", L"MinorLinkerVersion", pioh->MinorLinkerVersion, L"", L"Minor linker version");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->SizeOfCode) + sizeof(pioh->SizeOfCode) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"SizeOfCode", pioh->SizeOfCode, L"", L"Total size of all sections with the IMAGE_SCN_CNT_CODE attribute");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->SizeOfInitializedData) + sizeof(pioh->SizeOfInitializedData) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"SizeOfInitializedData", pioh->SizeOfInitializedData, L"", L"Total size of all initialized data sections");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->SizeOfUninitializedData) + sizeof(pioh->SizeOfUninitializedData) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"SizeOfUninitializedData", pioh->SizeOfUninitializedData, L"", L"Total size of all sections with the uninitialized data attributes");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->AddressOfEntryPoint) + sizeof(pioh->AddressOfEntryPoint) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"AddressOfEntryPoint", pioh->AddressOfEntryPoint, L"", L"RVA of the first code byte ");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->BaseOfCode) + sizeof(pioh->BaseOfCode) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"BaseOfCode", pioh->BaseOfCode, L"", L"RVA of the first byte of code when loaded in memory");
    }
#ifndef _WIN64
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->BaseOfData) + sizeof(pioh->BaseOfData) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"BaseOfData", pioh->BaseOfData, L"", L"RVA of the first byte of data when loaded into memory");
    }
#endif
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->ImageBase) + sizeof(pioh->ImageBase) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"ImageBase", pioh->ImageBase, L"", L"Preferred load address of this file in memory");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->SectionAlignment) + sizeof(pioh->SectionAlignment) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"SectionAlignment", pioh->SectionAlignment, L"", L"Alignment of sections when loaded into memory");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->FileAlignment) + sizeof(pioh->FileAlignment) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"FileAlignment", pioh->FileAlignment, L"", L"Alignment of sections within the PE file");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->MajorOperatingSystemVersion) + sizeof(pioh->MajorOperatingSystemVersion) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dwx     %-6s: %-30s\n", L"MajorOperatingSystemVersion", pioh->MajorOperatingSystemVersion, L"", L"Major version number of the required operating system");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->MinorOperatingSystemVersion) + sizeof(pioh->MinorOperatingSystemVersion) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dwx     %-6s: %-30s\n", L"MinorOperatingSystemVersion", pioh->MinorOperatingSystemVersion, L"", L"Minor version number of the required operating system");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->MajorImageVersion) + sizeof(pioh->MajorImageVersion) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dwx     %-6s: %-30s\n", L"MajorImageVersion", pioh->MajorImageVersion, L"", L"Major version number of this file");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->MinorImageVersion) + sizeof(pioh->MinorImageVersion) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dwx     %-6s: %-30s\n", L"MinorImageVersion", pioh->MinorImageVersion, L"", L"Minor version number of this file");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->MajorSubsystemVersion) + sizeof(pioh->MajorSubsystemVersion) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dwx     %-6s: %-30s\n", L"MajorSubsystemVersion", pioh->MajorSubsystemVersion, L"", L"Major version of the operating subsystem needed for this executable");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->MinorSubsystemVersion) + sizeof(pioh->MinorSubsystemVersion) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dwx     %-6s: %-30s\n", L"MinorSubsystemVersion", pioh->MinorSubsystemVersion, L"", L"Minor version of the operating subsystem needed for this executable");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->Win32VersionValue) + sizeof(pioh->Win32VersionValue) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"Win32VersionValue", pioh->Win32VersionValue, L"", L"Unused (set to 0)");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->SizeOfImage) + sizeof(pioh->SizeOfImage) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"SizeOfImage", pioh->SizeOfImage, L"", L"Amount of memory required to reserve when loading this file");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->SizeOfHeaders) + sizeof(pioh->SizeOfHeaders) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"SizeOfHeaders", pioh->SizeOfHeaders, L"", L"Total size of the MS-DOS header, NT headers, and section table");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->CheckSum) + sizeof(pioh->CheckSum) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"CheckSum", pioh->CheckSum, L"", L"Checksum of the image");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->Subsystem) + sizeof(pioh->Subsystem) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dwx     %-6s: %-30s\n", L"Subsystem", pioh->Subsystem, L"", L"expected subsystem (user interface type)");
        if (pioh->Subsystem == IMAGE_SUBSYSTEM_NATIVE) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %-30s\n", L"", L"", L"IMAGE_SUBSYSTEM_NATIVE", L"Image doesn't require a subsystem");
        }
        if (pioh->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %-30s\n", L"", L"", L"IMAGE_SUBSYSTEM_WINDOWS_GUI", L"Use the Windows GUI");
        }
        if (pioh->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-34s %-30s\n", L"", L"", L"IMAGE_SUBSYSTEM_WINDOWS_CUI", L"Run as a console mode application");
        }
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->DllCharacteristics) + sizeof(pioh->DllCharacteristics) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10dwx     %-6s: %-30s\n", L"DllCharacteristics", pioh->DllCharacteristics, L"", L"Flags indicating characteristics of this DLL");
        if ((pioh->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) == IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-50s %-30s\n", L"", L"", L"IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE", L"DLL can move");
        }
        if ((pioh->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY) == IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-50s %-30s\n", L"", L"", L"IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY", L"Code Integrity Image");
        }
        if ((pioh->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NX_COMPAT) == IMAGE_DLLCHARACTERISTICS_NX_COMPAT) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-50s %-30s\n", L"", L"", L"IMAGE_DLLCHARACTERISTICS_NX_COMPAT", L"Image is NX compatible");
        }
        if ((pioh->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_ISOLATION) == IMAGE_DLLCHARACTERISTICS_NO_ISOLATION) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-50s %-30s\n", L"", L"", L"IMAGE_DLLCHARACTERISTICS_NO_ISOLATION", L"Image understands isolation and doesn't want it");
        }
        if ((pioh->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_SEH) == IMAGE_DLLCHARACTERISTICS_NO_SEH) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-50s %-30s\n", L"", L"", L"IMAGE_DLLCHARACTERISTICS_NO_SEH", L"Image does not use SEH");
        }
        if ((pioh->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_BIND) == IMAGE_DLLCHARACTERISTICS_NO_BIND) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-50s %-30s\n", L"", L"", L"IMAGE_DLLCHARACTERISTICS_NO_BIND", L"Do not bind this image");
        }
        if ((pioh->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_WDM_DRIVER) == IMAGE_DLLCHARACTERISTICS_WDM_DRIVER) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-50s %-30s\n", L"", L"", L"IMAGE_DLLCHARACTERISTICS_WDM_DRIVER", L"Driver uses WDM model");
        }
        if ((pioh->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE) == IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE) {
            details += indentation + L"    " + FormatMessage(L"%-23s   %-13s: %-50s %-30s\n", L"", L"", L"IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE", L"Terminal server aware");
        }
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->SizeOfStackReserve) + sizeof(pioh->SizeOfStackReserve) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"SizeOfStackReserve", pioh->SizeOfStackReserve, L"", L"Maximum size the initial thread in the process can grow to");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->SizeOfStackCommit) + sizeof(pioh->SizeOfStackCommit) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"SizeOfStackCommit", pioh->SizeOfStackCommit, L"", L"Amount of memory initially committed to the stack");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->SizeOfHeapReserve) + sizeof(pioh->SizeOfHeapReserve) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"SizeOfHeapReserve", pioh->SizeOfHeapReserve, L"", L"Initial reserved size of the default process heap");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->SizeOfHeapCommit) + sizeof(pioh->SizeOfHeapCommit) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"SizeOfHeapCommit", pioh->SizeOfHeapCommit, L"", L"Size of memory committed to the heap");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->LoaderFlags) + sizeof(pioh->LoaderFlags) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"LoaderFlags", pioh->LoaderFlags, L"", L"Obsolete");
    }
    if (this->ntHeaderFieldOffset((BYTE*)&pioh->NumberOfRvaAndSizes) + sizeof(pioh->NumberOfRvaAndSizes) <= this->_readNtHeadersSize) {
        details += indentation + L"    " + FormatMessage(L"%-27s: %10ddx %-6s: %-30s\n", L"NumberOfRvaAndSizes", pioh->NumberOfRvaAndSizes, L"", L"Number of entries in the DataDirectory array");
        details += indentation + L"    DataDirectory\n";
        if (this->ntHeaderFieldOffset((BYTE*)&pioh->DataDirectory) + sizeof(IMAGE_DATA_DIRECTORY) <= this->_readNtHeadersSize) {
            for (ULONG i = 0; i < pioh->NumberOfRvaAndSizes; i++) {
                if (this->ntHeaderFieldOffset((BYTE*)&pioh->DataDirectory) + (i+1)*sizeof(IMAGE_DATA_DIRECTORY) <= this->_readNtHeadersSize) {
                    details += indentation + L"        " + FormatMessage(L"DataDirectory[%2d]:    Virtual address: %p        Size: %8ddx\n", i, pioh->DataDirectory[i].VirtualAddress, pioh->DataDirectory[i].Size);
                } else {
                    break;
                }
            }
        }
    }

    Stdout(details);
}

VOID CPEFile::stdoutImageSectionHeaders(CString indentation, BOOL dumpAsBytes, BOOL dumpAsWords, BOOL dumpAsDwords) {
    Stdout(indentation + L"SECTIONS HEADER:\n");

    DWORD sizeofSectionHeader = sizeof(IMAGE_SECTION_HEADER);
    if (!this->imageSectionHeaders || this->_readSectionHeadersSize < sizeofSectionHeader) {
        Stdout(indentation + L"  Invalid Sections Header. Should be minimum size of: %d. Read in %d bytes\n", sizeofSectionHeader, this->_readSectionHeadersSize);
        return;
    }
    ULONG numberOfSections = this->imageNtHeaders->FileHeader.NumberOfSections;
    Stdout(indentation + L"  FILE OFFSET: %8ddx\n", this->_sectionHeadersOffset);
    Stdout(indentation + L"  DATA SIZE:   %8ddx,  NUMBER OF ENTRIES: %ddx  SIZE OF ENTRY: %ddx\n", this->_readSectionHeadersSize, numberOfSections, sizeofSectionHeader);

    if (dumpAsBytes || dumpAsWords || dumpAsDwords) {
        CMemoryView mv(this->imageSectionHeaders, this->_readSectionHeadersSize, this->_sectionHeadersOffset);
        mv.print(indentation + L"  ", dumpAsBytes, dumpAsWords, dumpAsDwords);
    }

    CString details(indentation + L"  STRUCTURE DETAILS:\n");
    indentation += L"    ";
    PIMAGE_SECTION_HEADER pish;
    for (UINT i = 0; i < numberOfSections; i++) {
        if (i > 0) {
            details += L"\n";
        }
        
        pish = &this->imageSectionHeaders[i];        
        details += indentation + FormatMessage(L"      SECTION_HEADER[%d]  offset: %ddx  size: %ddx\n", i, this->_sectionHeadersOffset + i*sizeofSectionHeader, sizeofSectionHeader);
        
        CMemoryView mv(pish, sizeofSectionHeader, 0);
        details += mv.getDump(indentation + L"            ", dumpAsBytes, dumpAsWords, dumpAsDwords);
        
        if (this->sectionHeaderFieldOffset((BYTE*)&pish->Name) + sizeof(pish->Name) <= this->_readSectionHeadersSize) {
            details += indentation + L"            " + FormatMessage(L"%-30s:  %s\n", L"Name", CString((char*)pish->Name).toWCHAR());
        }
        if (this->sectionHeaderFieldOffset((BYTE*)&pish->Misc) + sizeof(pish->Misc) <= this->_readSectionHeadersSize) {
            //details += indentation + L"            " + FormatMessage(L"%-30s:  %11ddx\n", L"Physical address", pish->Misc.PhysicalAddress); // - union field with below
            details += indentation + L"            " + FormatMessage(L"%-30s:  %11ddx\n", L"Virtual size", pish->Misc.VirtualSize);
        }
        if (this->sectionHeaderFieldOffset((BYTE*)&pish->VirtualAddress) + sizeof(pish->VirtualAddress) <= this->_readSectionHeadersSize) {
            details += indentation + L"            " + FormatMessage(L"%-30s:  %11ddx\n", L"Virtual address", pish->VirtualAddress);
        }
        if (this->sectionHeaderFieldOffset((BYTE*)&pish->SizeOfRawData) + sizeof(pish->SizeOfRawData) <= this->_readSectionHeadersSize) {
            details += indentation + L"            " + FormatMessage(L"%-30s:  %11ddx    %s\n", L"Size of raw data", pish->SizeOfRawData, L"Size in bytes of section data hold in the file");
        }
        if (this->sectionHeaderFieldOffset((BYTE*)&pish->PointerToRawData) + sizeof(pish->PointerToRawData) <= this->_readSectionHeadersSize) {
            details += indentation + L"            " + FormatMessage(L"%-30s:  %11ddx    %s\n", L"Pointer to raw data", pish->PointerToRawData, L"File offset of section data");
        }
        if (this->sectionHeaderFieldOffset((BYTE*)&pish->PointerToRelocations) + sizeof(pish->PointerToRelocations) <= this->_readSectionHeadersSize) {
            details += indentation + L"            " + FormatMessage(L"%-30s:  %11ddx\n", L"Pointer to relocations", pish->PointerToRelocations);
        }
        if (this->sectionHeaderFieldOffset((BYTE*)&pish->PointerToLinenumbers) + sizeof(pish->PointerToLinenumbers) <= this->_readSectionHeadersSize) {
            details += indentation + L"            " + FormatMessage(L"%-30s:  %11ddx\n", L"Pointer to line numbers", pish->PointerToLinenumbers);
        }
        if (this->sectionHeaderFieldOffset((BYTE*)&pish->NumberOfRelocations) + sizeof(pish->NumberOfRelocations) <= this->_readSectionHeadersSize) {
            details += indentation + L"            " + FormatMessage(L"%-30s:  %11dwx\n", L"Number of relocations", pish->NumberOfRelocations);
        }
        if (this->sectionHeaderFieldOffset((BYTE*)&pish->NumberOfLinenumbers) + sizeof(pish->NumberOfLinenumbers) <= this->_readSectionHeadersSize) {
            details += indentation + L"            " + FormatMessage(L"%-30s:  %11dwx\n", L"Number of line numbers", pish->NumberOfLinenumbers);
        }
        if (this->sectionHeaderFieldOffset((BYTE*)&pish->Characteristics) + sizeof(pish->Characteristics) <= this->_readSectionHeadersSize) {
            details += indentation + L"            " + FormatMessage(L"%-30s:  %-11ddx\n", L"Characteristics", pish->Characteristics);
            #ifdef IMAGE_SCN_TYPE_REG
            if (pish->Characteristics == IMAGE_SCN_TYPE_REG) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_TYPE_REG", L"");
            }
            #endif
            #ifdef IMAGE_SCN_TYPE_DSECT
            if ((pish->Characteristics & IMAGE_SCN_TYPE_DSECT) == IMAGE_SCN_TYPE_DSECT) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_TYPE_DSECT", L"");
            }
            #endif
            #ifdef IMAGE_SCN_TYPE_NOLOAD
            if ((pish->Characteristics & IMAGE_SCN_TYPE_NOLOAD) == IMAGE_SCN_TYPE_NOLOAD) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_TYPE_NOLOAD", L"");
            }
            #endif
            #ifdef   IMAGE_SCN_TYPE_GROUP
            if ((pish->Characteristics &   IMAGE_SCN_TYPE_GROUP) ==   IMAGE_SCN_TYPE_GROUP) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_TYPE_GROUP", L"");
            }
            #endif
            #ifdef IMAGE_SCN_TYPE_NO_PAD
            if ((pish->Characteristics & IMAGE_SCN_TYPE_NO_PAD) == IMAGE_SCN_TYPE_NO_PAD) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_TYPE_NO_PAD", L"");
            }
            #endif
            #ifdef IMAGE_SCN_TYPE_COPY
            if ((pish->Characteristics & IMAGE_SCN_TYPE_COPY) == IMAGE_SCN_TYPE_COPY) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_TYPE_COPY", L"");
            }
            #endif
            #ifdef IMAGE_SCN_CNT_CODE
            if ((pish->Characteristics & IMAGE_SCN_CNT_CODE) == IMAGE_SCN_CNT_CODE) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_CNT_CODE", L"Section contains code");
            }
            #endif
            #ifdef IMAGE_SCN_CNT_INITIALIZED_DATA
            if ((pish->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) == IMAGE_SCN_CNT_INITIALIZED_DATA) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_CNT_INITIALIZED_DATA", L"Section contains initialized data");
            }
            #endif
            #ifdef IMAGE_SCN_CNT_UNINITIALIZED_DATA
            if ((pish->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) == IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_CNT_UNINITIALIZED_DATA", L"Section contains uninitialized data");
            }
            #endif
            #ifdef IMAGE_SCN_LNK_OTHER
            if ((pish->Characteristics & IMAGE_SCN_LNK_OTHER) == IMAGE_SCN_LNK_OTHER) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_LNK_OTHER", L"");
            }
            #endif
            #ifdef IMAGE_SCN_LNK_INFO
            if ((pish->Characteristics & IMAGE_SCN_LNK_INFO) == IMAGE_SCN_LNK_INFO) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_LNK_INFO", L"Section contains comments or some other type of information");
            }
            #endif
            #ifdef IMAGE_SCN_TYPE_OVER
            if ((pish->Characteristics & IMAGE_SCN_TYPE_OVER) == IMAGE_SCN_TYPE_OVER) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_TYPE_OVER", L"");
            }
            #endif
            #ifdef IMAGE_SCN_LNK_REMOVE
            if ((pish->Characteristics & IMAGE_SCN_LNK_REMOVE) == IMAGE_SCN_LNK_REMOVE) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_LNK_REMOVE", L"Section contents will not become part of image");
            }
            #endif
            #ifdef IMAGE_SCN_LNK_COMDAT
            if ((pish->Characteristics & IMAGE_SCN_LNK_COMDAT) == IMAGE_SCN_LNK_COMDAT) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_LNK_COMDAT", L"Section contents comdat");
            }
            #endif
            #ifdef IMAGE_SCN_MEM_PROTECTED
            if ((pish->Characteristics & IMAGE_SCN_MEM_PROTECTED) == IMAGE_SCN_MEM_PROTECTED) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_MEM_PROTECTED", L"Obsolete flag");
            }
            #endif
            #ifdef IMAGE_SCN_NO_DEFER_SPEC_EXC
            if ((pish->Characteristics & IMAGE_SCN_NO_DEFER_SPEC_EXC) == IMAGE_SCN_NO_DEFER_SPEC_EXC) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_NO_DEFER_SPEC_EXC", L"Reset speculative exceptions handling bits in the TLB entries for this section");
            }
            #endif
            #ifdef IMAGE_SCN_GPREL
            if ((pish->Characteristics & IMAGE_SCN_GPREL) == IMAGE_SCN_GPREL) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_GPREL", L"Section content can be accessed relative to GP");
            }
            #endif
            #ifdef IMAGE_SCN_MEM_FARDATA
            if ((pish->Characteristics & IMAGE_SCN_MEM_FARDATA) == IMAGE_SCN_MEM_FARDATA) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_MEM_FARDATA", L"Section contents will not become part of image");
            }
            #endif
            #ifdef IMAGE_SCN_MEM_SYSHEAP
            if ((pish->Characteristics & IMAGE_SCN_MEM_SYSHEAP) == IMAGE_SCN_MEM_SYSHEAP) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_MEM_SYSHEAP", L"Obsolete flag");
            }
            #endif
            #ifdef IMAGE_SCN_MEM_PURGEABLE
            if ((pish->Characteristics & IMAGE_SCN_MEM_PURGEABLE) == IMAGE_SCN_MEM_PURGEABLE) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_MEM_PURGEABLE", L"");
            }
            #endif
            #ifdef IMAGE_SCN_MEM_16BIT
            if ((pish->Characteristics & IMAGE_SCN_MEM_16BIT) == IMAGE_SCN_MEM_16BIT) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_MEM_16BIT", L"");
            }
            #endif
            #ifdef IMAGE_SCN_MEM_LOCKED
            if ((pish->Characteristics & IMAGE_SCN_MEM_LOCKED) == IMAGE_SCN_MEM_LOCKED) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_MEM_LOCKED", L"");
            }
            #endif
            #ifdef IMAGE_SCN_MEM_PRELOAD
            if ((pish->Characteristics & IMAGE_SCN_MEM_PRELOAD) == IMAGE_SCN_MEM_PRELOAD) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_MEM_PRELOAD", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_1BYTES
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_1BYTES) == IMAGE_SCN_ALIGN_1BYTES) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_1BYTES", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_2BYTES
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_2BYTES) == IMAGE_SCN_ALIGN_2BYTES) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_2BYTES", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_4BYTES
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_4BYTES) == IMAGE_SCN_ALIGN_4BYTES) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_4BYTES", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_8BYTES
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_8BYTES) == IMAGE_SCN_ALIGN_8BYTES) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_8BYTES", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_16BYTES
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_16BYTES) == IMAGE_SCN_ALIGN_16BYTES) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_16BYTES", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_32BYTES
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_32BYTES) == IMAGE_SCN_ALIGN_32BYTES) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_32BYTES", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_64BYTES
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_64BYTES) == IMAGE_SCN_ALIGN_64BYTES) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_64BYTES", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_128BYTES
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_128BYTES) == IMAGE_SCN_ALIGN_128BYTES) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_128BYTES", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_256BYTES
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_256BYTES) == IMAGE_SCN_ALIGN_256BYTES) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_256BYTES", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_512BYTES
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_512BYTES) == IMAGE_SCN_ALIGN_512BYTES) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_512BYTES", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_1024BYTES
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_1024BYTES) == IMAGE_SCN_ALIGN_1024BYTES) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_1024BYTES", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_2048BYTES
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_2048BYTES) == IMAGE_SCN_ALIGN_2048BYTES) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_2048BYTES", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_4096BYTES
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_4096BYTES) == IMAGE_SCN_ALIGN_4096BYTES) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_4096BYTES", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_8192BYTES
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_8192BYTES) == IMAGE_SCN_ALIGN_8192BYTES) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_8192BYTES", L"");
            }
            #endif
            #ifdef IMAGE_SCN_ALIGN_MASK
            if ((pish->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_MASK) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_ALIGN_MASK", L"");
            }
            #endif
            #ifdef IMAGE_SCN_LNK_NRELOC_OVFL
            if ((pish->Characteristics & IMAGE_SCN_LNK_NRELOC_OVFL) == IMAGE_SCN_LNK_NRELOC_OVFL) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_LNK_NRELOC_OVFL", L"Section contains extended relocations");
            }
            #endif
            #ifdef IMAGE_SCN_MEM_DISCARDABLE
            if ((pish->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == IMAGE_SCN_MEM_DISCARDABLE) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_MEM_DISCARDABLE", L"Section can be discarded");
            }
            #endif
            #ifdef IMAGE_SCN_MEM_NOT_CACHED
            if ((pish->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) == IMAGE_SCN_MEM_NOT_CACHED) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_MEM_NOT_CACHED", L"Section is not cachable");
            }
            #endif
            #ifdef IMAGE_SCN_MEM_NOT_PAGED
            if ((pish->Characteristics & IMAGE_SCN_MEM_NOT_PAGED) == IMAGE_SCN_MEM_NOT_PAGED) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_MEM_NOT_PAGED", L"Section is not pageable");
            }
            #endif
            #ifdef IMAGE_SCN_MEM_SHARED
            if ((pish->Characteristics & IMAGE_SCN_MEM_SHARED) == IMAGE_SCN_MEM_SHARED) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_MEM_SHARED", L"Section is shareable");
            }
            #endif
            #ifdef IMAGE_SCN_MEM_EXECUTE
            if ((pish->Characteristics & IMAGE_SCN_MEM_EXECUTE) == IMAGE_SCN_MEM_EXECUTE) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_MEM_EXECUTE", L"Section is executable");
            }
            #endif
            #ifdef IMAGE_SCN_MEM_READ
            if ((pish->Characteristics & IMAGE_SCN_MEM_READ) == IMAGE_SCN_MEM_READ) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_MEM_READ", L"Section is readable");
            }
            #endif
            #ifdef IMAGE_SCN_MEM_WRITE
            if ((pish->Characteristics & IMAGE_SCN_MEM_WRITE) == IMAGE_SCN_MEM_WRITE) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_MEM_WRITE", L"Section is writeable");
            }
            #endif
            #ifdef IMAGE_SCN_SCALE_INDEX
            if ((pish->Characteristics & IMAGE_SCN_SCALE_INDEX) == IMAGE_SCN_SCALE_INDEX) {
                details += indentation + L"            " + FormatMessage(L"%-4s  %-35s    %s\n", L"", L"IMAGE_SCN_SCALE_INDEX", L"Tls index is scaled");
            }
            #endif
        }
    }
    
    Stdout(details);
}
