病毒实验五

title: viruslab5
date: 2016-01-13 15:47:40
categories: virus
tags: virus

PE文件注入

  • part1
    • 要求:附带程序将一个完整的PE文件(hellow.exe)读到内存中
      并将其分解为PE头、节表、以及各个节。
      试编写void OutputPEInMem()函数,用其打印下列信息:
      • PE头中的ImageBase字段、AddressOfEntryPoint字段、
        NumberOfSections字段与SizeOfImage字段;
      • 节表(Section Table)中每一项的Name字段、Virtual Size字段、Virtual Address字段、
        RawData Size字段、RawData Offset字段、Characteristics字段。
	#include <windows.h>
	#include <stdio.h>
	#define MAX_SECTION_NUM   16
	#define MAX_IMPDESC_NUM   64

	HANDLE  hHeap;
	DWORD dwBaseAddress;
	PIMAGE_DOS_HEADER pDosHeader;
	PCHAR   pDosStub;
	DWORD   dwDosStubSize;
	DWORD   dwDosStubOffset;
	PIMAGE_NT_HEADERS           pNtHeaders;
	PIMAGE_FILE_HEADER          pFileHeader;
	PIMAGE_OPTIONAL_HEADER32    pOptHeader;
	PIMAGE_SECTION_HEADER   pSecHeaders;
	PIMAGE_SECTION_HEADER   pSecHeader[MAX_SECTION_NUM];
	WORD  wSecNum;
	PBYTE pSecData[MAX_SECTION_NUM];
	DWORD dwSecSize[MAX_SECTION_NUM];
	DWORD dwFileSize;

	static DWORD PEAlign(DWORD dwTarNum,DWORD dwAlignTo)
	{	
	//PEAlign参数1:源节表 文件中的块大小
	//PEAlign参数2:文件对齐值
	//返回对齐后的文件大小
	return (((dwTarNum+dwAlignTo - 1) / dwAlignTo) * dwAlignTo);
	}

	static DWORD RVA2Ptr(DWORD dwBaseAddress, DWORD dwRva)
	{
	if ((dwBaseAddress != 0) && dwRva)
	return (dwBaseAddress + dwRva);
	else
	return dwRva;
	}

	//----------------------------------------------------------------
	static PIMAGE_SECTION_HEADER RVA2Section(DWORD dwRVA)
	{
	int i;
	for(i = 0; i < wSecNum; i++) {
	if ( (dwRVA >= pSecHeader[i]->VirtualAddress)
		&& (dwRVA <= (pSecHeader[i]->VirtualAddress 
			+ pSecHeader[i]->SizeOfRawData)) ) {
		return ((PIMAGE_SECTION_HEADER)pSecHeader[i]);
	}
	}
	return NULL;
	}

	//----------------------------------------------------------------
	static PIMAGE_SECTION_HEADER Offset2Section(DWORD dwOffset)
	{
	int i;
	for(i = 0; i < wSecNum; i++) {
	if( (dwOffset>=pSecHeader[i]->PointerToRawData) 
		&& (dwOffset<(pSecHeader[i]->PointerToRawData +
		pSecHeader[i]->SizeOfRawData)))
	{
		return ((PIMAGE_SECTION_HEADER)pSecHeader[i]);
	}
	}
	return NULL;
	}

	//================================================================
	static DWORD RVA2Offset(DWORD dwRVA)
	{
	PIMAGE_SECTION_HEADER pSec;
	pSec = RVA2Section(dwRVA);//ImageRvaToSection(pimage_nt_headers,Base,dwRVA);
	if(pSec == NULL) {
	return 0;
	}
	return (dwRVA + (pSec->PointerToRawData) - (pSec->VirtualAddress));
	}
	//----------------------------------------------------------------
	static DWORD Offset2RVA(DWORD dwOffset)
	{
	PIMAGE_SECTION_HEADER pSec;
	pSec = Offset2Section(dwOffset);
	if(pSec == NULL) {
	return (0);
	}
	return(dwOffset + (pSec->VirtualAddress) - (pSec->PointerToRawData));
	}
	//将PE结构复制到内存的堆中
	BOOL CopyPEFileToMem(LPCSTR lpszFilename)
	{
	HANDLE  hFile;
	PBYTE   pMem;
	DWORD   dwBytesRead;
	int     i;
	DWORD   dwSecOff;

	PIMAGE_NT_HEADERS       pMemNtHeaders;   
	PIMAGE_SECTION_HEADER   pMemSecHeaders;

	hFile = CreateFile(//返回文件句柄
	lpszFilename,//文件名 这里是hello.exe可执行文件
	GENERIC_READ,//访问模式 读
	FILE_SHARE_READ,//共享模式
	NULL,//指向安全属性的指针
	OPEN_EXISTING,//如何创建
	FILE_ATTRIBUTE_NORMAL,//文件属性
	0);//用于复制文件句柄

	if (hFile == INVALID_HANDLE_VALUE) {//INVALID_HANDLE_VALUE 表示出错
	printf("[E]: Open file (%s) failed.
", lpszFilename);
	return FALSE;
	}
	dwFileSize = GetFileSize(hFile, 0);//判断文件长度 返回文件大小
	printf("[I]: Open file (%s) ok, 
	with size of 0x%08x.
", lpszFilename, dwFileSize);


	//pMem是hello.exe在堆中的首地址 也就是dos头地址
	pMem = (PBYTE)HeapAlloc(//在堆上分配内存 返回指向所分配内存块的首地址的指针
	hHeap,//main函数中有 是全局变量 要分配堆的句柄
	HEAP_ZERO_MEMORY,//将分配的内存全部清零
	dwFileSize);//要分配堆的字节数

	if(pMem == NULL) {//在堆上分配内存失败
	printf("[E]: HeapAlloc failed (with the size of 0x%08x).
", dwFileSize);
	CloseHandle(hFile);
	return FALSE;
	}

	ReadFile(//从文件指针指向的位置开始将数据读到一个文件中
	hFile,//文件句柄
	pMem,//用于保存读入数据的一个缓冲区 这里是堆中分配的内存首地址
	dwFileSize,//要读入的字节数
	&dwBytesRead,//指向实际读取字节数的指针
	NULL);    
	CloseHandle(hFile);//关闭内核对象


	//复制dos header
	//堆中的dos头pDosHeader
	//在堆上分配内存 返回指向所分配内存块的首地址的指针
	pDosHeader = (PIMAGE_DOS_HEADER)HeapAlloc(
	hHeap,//main函数中有 是全局变量 要分配堆的句柄
	HEAP_ZERO_MEMORY,//将分配的内存全部清零
	sizeof(IMAGE_DOS_HEADER));//要分配堆的字节数
	if(pDosHeader == NULL) {//在堆上分配内存失败
	printf("[E]: HeapAlloc failed for DOS_HEADER
");
	CloseHandle(hFile);
	return FALSE;
	}
	CopyMemory(//将内存中的数据从一个位置复制到另一个位置
	pDosHeader,//要复制内存块的目的地址
	pMem,//要复制内存块的源地址
	sizeof(IMAGE_DOS_HEADER));//要复制内存块的大小

	//复制DOS Stub
	//先计算dos stub的大小 dos头大小 
	//然后分配dos stub大小的内存空间
	dwDosStubSize = pDosHeader->e_lfanew - sizeof(IMAGE_DOS_HEADER);
	dwDosStubOffset = sizeof(IMAGE_DOS_HEADER);
	pDosStub = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, dwDosStubSize);
	if ((dwDosStubSize & 0x80000000) == 0x00000000)//dwDosStubSize 最高位是0
	{
	CopyMemory(pDosStub,
	(const void *)(pMem + dwDosStubOffset ), dwDosStubSize);
	}

	//复制NT header
	//先找到源堆中的NT头地址  pMemNtHeaders
	//分配NT头内存空间  返回目的NT头地址 pNtHeaders
	//复制
	pMemNtHeaders = (PIMAGE_NT_HEADERS)(pMem + pDosHeader->e_lfanew);
	//返回目的NT头地址
	pNtHeaders = (PIMAGE_NT_HEADERS)
	HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(IMAGE_NT_HEADERS));
	if(pNtHeaders == NULL) {
	printf("[E]: HeapAlloc failed for NT_HEADERS
");
	CloseHandle(hFile);
	return FALSE;
	}	
	//pMemNtHeaders 源地址
	CopyMemory(pNtHeaders, pMemNtHeaders, sizeof(IMAGE_NT_HEADERS));

	//NT头里面有一个OPT头 目的 pOptHeader
	pOptHeader = &(pNtHeaders->OptionalHeader);
	//NT头里面有一个FILE头 目的 pFileHeader
	pFileHeader = &(pNtHeaders->FileHeader);

	//复制 节表
	//寻找源节表地址  pMemSecHeaders源
	//通过NT头找到FIFE头 FIFE头中有节的数目 wSecNum
	//分配节表的内存空间 返回节表地址 pSecHeaders 目的
	//复制
	pMemSecHeaders = (PIMAGE_SECTION_HEADER) ((DWORD)
	pMemNtHeaders + sizeof(IMAGE_NT_HEADERS));
	wSecNum = pFileHeader->NumberOfSections;//文件节的数目
	pSecHeaders = (PIMAGE_SECTION_HEADER)
	HeapAlloc(hHeap, 
	HEAP_ZERO_MEMORY, wSecNum * sizeof(IMAGE_SECTION_HEADER));
	if(pSecHeaders == NULL) {
	printf("[E]: HeapAlloc failed for SEC_HEADERS
");
	CloseHandle(hFile);
	return FALSE;
	}//pMemSecHeaders 源
	CopyMemory(pSecHeaders,
	pMemSecHeaders, wSecNum * sizeof(IMAGE_SECTION_HEADER));

	for(i = 0; i < wSecNum; i++) {//pSecHeaders 目的
	pSecHeader[i] = (PIMAGE_SECTION_HEADER) //pSecHeader[i] 各个节表 目的
	  ((DWORD)pSecHeaders + i * sizeof(IMAGE_SECTION_HEADER));
	}

	//复制节
	//(目的节表 文件中的块偏移 + dos头地址 ) 复制到 dwSecOff
	//PEAlign()返回对齐后的文件大小 dwSecSize
	//分配堆中内存 返回内存首地址 pSecData
	//复制
	for(i = 0; i < wSecNum; i++) {//PointerToRawData 源节表 文件中节偏移
	dwSecOff = (DWORD)(pMem + pSecHeader[i]->PointerToRawData);
	//PEAlign参数1:源节表 文件中的块大小
	//PEAlign参数2:文件对齐值
	dwSecSize[i] = PEAlign(pSecHeader[i]->SizeOfRawData,
	pOptHeader->FileAlignment);        
	pSecData[i] = (PBYTE)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, dwSecSize[i]);
	if (pSecData[i] == NULL) {
		printf("[E]: HeapAlloc failed for the section of %d
", i);
		CloseHandle(hFile);
		return FALSE;
	}
	CopyMemory(pSecData[i], (PVOID)dwSecOff, dwSecSize[i]);
	}

	HeapFree(//释放堆内存
	hHeap,//堆句柄
	0,
	pMem);//被释放的内存块首地址  pMem 源
	printf("[I]: Load PE from file (%s) ok
", lpszFilename);

	return TRUE;
	}

	void OutputPEInMem()
	{
	int i;
	printf("**********************
");
	printf("Base Address: 0x%08x
", pDosHeader);    
	printf("e_lfanew: 0x%08x
", pDosHeader->e_lfanew);    
	printf("PE NT Headers Address: 0x%08x
", pNtHeaders);
	printf("NumberOfSections: 0x%08x
",wSecNum);
	//AddressOfEntryPoint 程序入口RVA地址
	printf("AddressOfEntryPoint: 0x%08x
", pOptHeader->AddressOfEntryPoint);
	//ImageBase 基址
	printf("ImageBase: 0x%08x
", pOptHeader->ImageBase);
	//SizeOfImage 映像大小
	printf("SizeOfImage: 0x%08x
", pOptHeader->SizeOfImage);
	printf("-------------------
");
	for(i = 0;i < wSecNum;i++)
	{//pSecHeader 目的节表
	printf("pSecHeaders:%s
",pSecHeader[i]->Name);
	printf("VirtualSize:0x%08x
",
	pSecHeader[i]->Misc.VirtualSize);//内存中节大小
	printf("VirtualAddress:0x%08x
",
	pSecHeader[i]->VirtualAddress);//内存中节RVA值
	printf("SizeOfRawData:0x%08x
",
	pSecHeader[i]->SizeOfRawData);//文件中的节大小
	printf("PointerToRawData:0x%08x
",
	pSecHeader[i]->PointerToRawData);//文件中的节偏移
	printf("Characteristics:0x%08x
",
	pSecHeader[i]->Characteristics);//节属性
	printf("-------------------
");
	}
	return;
	}

	int main()
	{
	LPCSTR lpszFileName = "hello.exe";
	//L:long指针 P:指针 C:常量 STR:字符串
	LPCSTR lpszInjFileName = "hello_inj0.exe";
	hHeap = GetProcessHeap();
	//GetProcessHeap返回调用进程的默认内存堆句柄
	if (! CopyPEFileToMem(lpszFileName)) {//复制PE结构进入堆
	return 1;
	}
	OutputPEInMem();//打印堆中的PE结构
	return 0;
	}


  • part2
  • 为”hello.exe”PE结构增加一个新的节,填入4个0xCC的值。具体步骤为:
    • 为PE结构添加一个新的节。
      请仔细阅读所附的AddNewSection()函数的代码,并理解如何为PE结构添加一个可执行的新节。
      请填充函数中的所缺失的两处代码。请理解并使用PEAlign函数来完成对齐操作。
    • 请编写函数SaveMemToPEFile()来将修改过的PE结构重新打包保存为可执行文件”hello_inj0.exe”。
      该函数的原型声明如下:
      BOOL SaveMemToPEFile(LPCSTR lpszFileName)
      请确保新文件”hello_inj0.exe”的正确性,并可以正常运行。
      然后请用Ollydbg来确认新节已正确插入到PE结构中。
========================if i have some wrong, please give me a message, thx.========================
原文地址:https://www.cnblogs.com/ailx10/p/5251629.html