//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CRM project
//	
// module: pesup.h
//   october 2009
// description: 
//	PE file header support functions and types.

#pragma once

typedef union _SECTION_NAME
{
	UCHAR	Byte[8];
	ULONG	Dword[2];
} SECTION_NAME, *PSECTION_NAME;

typedef struct _LINKED_BUFFER  LINKED_BUFFER, *PLINKED_BUFFER;

typedef struct _LINKED_BUFFER
{
	PLINKED_BUFFER	Next;		// Next buffer in list, 0 if current is the last one
	PCHAR			Buffer;		// Pointer to memory buffer
	ULONG			Size;		// Size of the memory buffer in bytes
} LINKED_BUFFER, *PLINKED_BUFFER;

typedef struct _IMAGE_DELAY_IMPORT_DESCRIPTOR
{
	DWORD		Flags;
	DWORD		Name;
	DWORD		Module;
	DWORD		FirstThunk;
	DWORD		OriginalFirstThunk;
	DWORD		BoundIAT;
	DWORD		UnloadIAT;
	DWORD		TimeDateStamp;
} IMAGE_DELAY_IMPORT_DESCRIPTOR, *PIMAGE_DELAY_IMPORT_DESCRIPTOR;


typedef ULONG_PTR	IAT_ENTRY;
typedef IAT_ENTRY*	PIAT_ENTRY;

typedef ULONG			EXPORT_ENTRY;
typedef EXPORT_ENTRY*	PEXPORT_ENTRY;


_inline PVOID PeSupGetImagePeHeader(PVOID DosHeader)
{
	PIMAGE_NT_HEADERS pe = (PIMAGE_NT_HEADERS)((PCHAR)DosHeader + ((PIMAGE_DOS_HEADER)DosHeader)->e_lfanew);
	return((PVOID)pe);
}


#define		PeSupGetOptionalField(PeHeader, Field)												\
				(FIELD_OFFSET(IMAGE_OPTIONAL_HEADER32, ##Field) != FIELD_OFFSET(IMAGE_OPTIONAL_HEADER64, ##Field) && \
				((PIMAGE_NT_HEADERS32)PeHeader)->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 ?	\
				((PIMAGE_NT_HEADERS64)PeHeader)->OptionalHeader.##Field :								\
				((PIMAGE_NT_HEADERS32)PeHeader)->OptionalHeader.##Field)

#define		PeSupSetOptionalField(PeHeader, Field, Value)										\
				(FIELD_OFFSET(IMAGE_OPTIONAL_HEADER32, ##Field) != FIELD_OFFSET(IMAGE_OPTIONAL_HEADER64, ##Field) && \
				((PIMAGE_NT_HEADERS32)PeHeader)->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 ?	\
				((PIMAGE_NT_HEADERS64)PeHeader)->OptionalHeader.##Field = Value :						\
				((PIMAGE_NT_HEADERS32)PeHeader)->OptionalHeader.##Field = Value)

#define		PeSupPtrOptionalField(PeHeader, Field)										\
				(FIELD_OFFSET(IMAGE_OPTIONAL_HEADER32, ##Field) != FIELD_OFFSET(IMAGE_OPTIONAL_HEADER64, ##Field) && \
				((PIMAGE_NT_HEADERS32)PeHeader)->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 ?	\
				(PVOID)&((PIMAGE_NT_HEADERS64)PeHeader)->OptionalHeader.##Field:						\
				(PVOID)&((PIMAGE_NT_HEADERS32)PeHeader)->OptionalHeader.##Field)


#define		PeSupGetDirectoryEntryPtr(PeHeader, Entry)												\
				(((PIMAGE_NT_HEADERS32)PeHeader)->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 ?		\
				&((PIMAGE_NT_HEADERS64)PeHeader)->OptionalHeader.DataDirectory[##Entry] :					\
				&((PIMAGE_NT_HEADERS32)PeHeader)->OptionalHeader.DataDirectory[##Entry])


_inline ULONG_PTR PeSupGetImageBase(PCHAR DosHeader)
{
	PIMAGE_NT_HEADERS pe = (PIMAGE_NT_HEADERS)(DosHeader + ((PIMAGE_DOS_HEADER)DosHeader)->e_lfanew);
	return((ULONG_PTR)pe->OptionalHeader.ImageBase);

}

_inline ULONG_PTR* PeSupGetImageBaseOfs(PVOID PeHeader)
{
	PIMAGE_NT_HEADERS pe = (PIMAGE_NT_HEADERS)PeHeader;
	return(&pe->OptionalHeader.ImageBase);

}


_inline ULONG PeSupGetImageSize(PCHAR DosHeader)
{
	PIMAGE_NT_HEADERS32		PEHeader32	= (PIMAGE_NT_HEADERS32)PeSupGetImagePeHeader(DosHeader);
	PIMAGE_NT_HEADERS64		PEHeader64	= (PIMAGE_NT_HEADERS64)PEHeader32;
	ULONG	ImageSize;

	if (PEHeader32->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
		ImageSize = PEHeader64->OptionalHeader.SizeOfImage;
	else
		ImageSize = PEHeader32->OptionalHeader.SizeOfImage;

	return(ImageSize);
}



_inline ULONG_PTR PeSupGetPeImageBase(PVOID PeHeader)
{
	PIMAGE_NT_HEADERS pe = (PIMAGE_NT_HEADERS)PeHeader;
	return((ULONG_PTR)pe->OptionalHeader.ImageBase);

}

_inline ULONG PeSupGetSectionRva(PVOID Section)
{
	PIMAGE_SECTION_HEADER sections = (PIMAGE_SECTION_HEADER)Section;
	return(sections->VirtualAddress);
}


_inline ULONG PeSupGetSectionVSize(PVOID Section)
{
	PIMAGE_SECTION_HEADER sections = (PIMAGE_SECTION_HEADER)Section;
	return(sections->Misc.VirtualSize);
}

_inline ULONG PeSupGetSectionRSize(PVOID Section)
{
	PIMAGE_SECTION_HEADER sections = (PIMAGE_SECTION_HEADER)Section;
	return(sections->SizeOfRawData);
}

_inline PVOID PeSupGetFirstWritableSection(PCHAR DosHeader)
{
	PIMAGE_NT_HEADERS pe = (PIMAGE_NT_HEADERS)(DosHeader + ((PIMAGE_DOS_HEADER)DosHeader)->e_lfanew);
	PIMAGE_SECTION_HEADER sections = (PIMAGE_SECTION_HEADER)((PCHAR)&pe->OptionalHeader + pe->FileHeader.SizeOfOptionalHeader);
	ULONG NumberOfSections = pe->FileHeader.NumberOfSections;

	do 
	{
		if (sections->Characteristics & IMAGE_SCN_MEM_WRITE)
			return((PVOID)sections);

		sections += 1;
		NumberOfSections -= 1;
	} while (NumberOfSections);

	return(NULL);
}	

_inline ULONG PeSupGetNumberOfSections(PVOID PeHeader)
{
	PIMAGE_NT_HEADERS pe = (PIMAGE_NT_HEADERS)PeHeader;
	return((ULONG)pe->FileHeader.NumberOfSections);
}

_inline ULONG PeSupGetImageIatVa(PVOID PeHeader)
{
	PIMAGE_NT_HEADERS pe = (PIMAGE_NT_HEADERS)PeHeader;
	return((ULONG)pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress);
}

_inline ULONG PeSupGetImageIatSize(PVOID PeHeader)
{
	PIMAGE_NT_HEADERS pe = (PIMAGE_NT_HEADERS)PeHeader;
	return((ULONG)pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size);
}


_inline PVOID PeSupGetSectionTable(PVOID PeHeader)
{
	PIMAGE_NT_HEADERS pe = (PIMAGE_NT_HEADERS)PeHeader;
	return((PVOID)((PCHAR)&pe->OptionalHeader + pe->FileHeader.SizeOfOptionalHeader));
}

_inline ULONG PeSupGetSectionTableSize(PVOID PeHeader)
{
	PIMAGE_NT_HEADERS pe = (PIMAGE_NT_HEADERS)PeHeader;
	return(pe->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
}


_inline PVOID PeSupGetNextSection(PVOID Section)
{
	return((PVOID)((ULONG_PTR)Section + sizeof(IMAGE_SECTION_HEADER)));
}


_inline ULONG PeSubGetSectionFlags(PVOID Section)
{
	return(((PIMAGE_SECTION_HEADER)Section)->Characteristics);
}

_inline ULONG PeSupGetSectionOffset(PVOID Section)
{
	return(((PIMAGE_SECTION_HEADER)Section)->PointerToRawData);
}

_inline ULONG PeSupGetSectionAlignment(PVOID PeHeader)
{
	PIMAGE_NT_HEADERS pe = (PIMAGE_NT_HEADERS)PeHeader;
	return(pe->OptionalHeader.SectionAlignment);
}


_inline ULONG PeSupAlign(ULONG Size, ULONG Alignment)
{
	ULONG AlignedSize = Size & ~(Alignment-1);
				
	if (Size != AlignedSize)
		AlignedSize += Alignment;

	return(AlignedSize);
}

_inline PVOID PeSupFindSectionByName(PCHAR DosHeader, PSECTION_NAME SecName)
{
	PIMAGE_NT_HEADERS pe = (PIMAGE_NT_HEADERS)(DosHeader + ((PIMAGE_DOS_HEADER)DosHeader)->e_lfanew);
	ULONG NumberOfSections = pe->FileHeader.NumberOfSections;
	PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((PCHAR)&pe->OptionalHeader + pe->FileHeader.SizeOfOptionalHeader);
	PVOID pFound = NULL;

	do
	{
		PSECTION_NAME pName = (PSECTION_NAME)&pSection->Name;
		if (pName->Dword[0] == SecName->Dword[0] && pName->Dword[1] == SecName->Dword[1])
			pFound = (PVOID)pSection;

		pSection += 1;
		NumberOfSections -=1;
	} while((NumberOfSections) && (pFound == NULL));
	
	return(pFound);
}

_inline LONG PeSupIsPe64(PVOID PeHeader)
{
	PIMAGE_NT_HEADERS pe = (PIMAGE_NT_HEADERS)PeHeader;
	if (pe->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
		return(TRUE);
	else
		return(FALSE);
}

_inline PVOID PeSupRvaToVa(ULONG Rva, PCHAR ImageBase)
{
	return((PVOID)((ULONG_PTR)Rva + (ULONG_PTR)ImageBase));

}


_inline ULONG	PeSupGetFileAlign(PVOID	PeHeader)
{
	PIMAGE_NT_HEADERS32	Pe = (PIMAGE_NT_HEADERS32)PeHeader;
	if (Pe->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
		return(((PIMAGE_NT_HEADERS64)Pe)->OptionalHeader.FileAlignment);
	else
		return(Pe->OptionalHeader.FileAlignment);
}







#define PeSupGetFreeCodeSpace(x)	PeSupGetSectionFreeBuffers(x, IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_DISCARDABLE)