在任意代码空白区添加代码(C语言)

发布时间:2024年01月18日

1.添加过程?

图片
过程:FileBuffer->ImageBuffer->NewBuffer

?


?2.代码实现

/*														
添加任意代码到代码区														
1、定义变量(PE文件各部分指针以及文件各状态指针)														
2、判断传进来的缓冲区地址是否正确														
3、判断是否遵守PE格式														
4、获取DOS头、NT头、标准PE头、可选PE头、第一个节表信息														
5、判断空间是否足够写入代码														
6、将代码复制到空闲区														
7、修正代码														
8、ImageBuffer-->NewBuffer(CopyImageBufferToNewBuffer())													
9、NewBuffer-->File(MemeryToFile())	
10、释放内存(pFileBuffer,pImageBuffer,pNewBuffer)													
*/	
VOID TestAddCodeInCodeSec()
{
    LPVOID pFileBuffer = NULL;
    LPVOID pImageBuffer = NULL;
    LPVOID pNewBuffer =	NULL;
    PIMAGE_DOS_HEADER			pDosHeader		=	NULL;								
    PIMAGE_NT_HEADERS			pNTHeader		=	NULL;								
    PIMAGE_FILE_HEADER			pPEHeader		=	NULL;								
    PIMAGE_OPTIONAL_HEADER32	pOptionHeader	=	NULL;											
    PIMAGE_SECTION_HEADER		pSectionHeader	=	NULL;	
    PBYTE CodeBegin = NULL;
    BOOL isOK = FALSE;
    DWORD size = 0;
    
    //File-->FileBuffer
    ReadPEFile(FILEPATH_IN,&pFileBuffer);
    if (!pFileBuffer)	
    {
        printf("File-->FileBuffer错误!");
        return ;
    }
    
    //FileBuffer-->ImageBuffer
    CopyFileBufferToImageBuffer(pFileBuffer,&pImageBuffer);
    if (!pImageBuffer)
    {
        printf("FileBuffer-->ImageBuffer错误!");
        free(pFileBuffer);
        return ;
    }
    
    //获取DOS头
    pDosHeader	=	(PIMAGE_DOS_HEADER)pImageBuffer;
    //判断DOS头是否是可执行的文件
    if(*((PDWORD)((DWORD)pImageBuffer + pDosHeader->e_lfanew))	!=	IMAGE_NT_SIGNATURE)												
    {														
        printf("不是有效的PE标志\n");														
        return ;														
    }													
    //NT头地址														
    pNTHeader		=	(PIMAGE_NT_HEADERS)((DWORD)pImageBuffer	+ pDosHeader->e_lfanew);										
    //标准PE头地址														
    pPEHeader		=	(PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x04);											
    //可选PE头地址														
    pOptionHeader	=	(PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);												
    //第一个节表地址														
    pSectionHeader	=	(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
    
    //判断代码段空闲区是否足够存储ShellCode代码
    int tempDiffer = (pSectionHeader->SizeOfRawData) - (pSectionHeader->Misc.VirtualSize);
    if (tempDiffer < SHELLCODELENGTH)
    {
        printf("代码区空闲空间不足!");
        free(pFileBuffer);
        free(pImageBuffer);
        return ;
    }
    
    //将代码复制到空闲区
    CodeBegin = (PBYTE)((DWORD)pImageBuffer + pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize);
    memcpy(CodeBegin,ShellCode,SHELLCODELENGTH);
    
    //(E8/E9)修正的地址(X) = 真正要跳转的地址 - (E8/E9)这条指令的下一行地址
    DWORD CallAddr = MESSAGEBOXADDR 
        - (pOptionHeader->ImageBase + ((DWORD)(CodeBegin + 0x0D) - (DWORD)pImageBuffer));
    /*真正要跳转的地址:MESSAGEBOXADDR,((DWORD)(CodeBegin + 0x0D)-(DWORD)pImageBuffer)是在ImageBuffer中的偏移地址,
    E8这条指令的下一行地址:(pOptionHeader->ImageBase + ((DWORD)(CodeBegin + 0x0D)-(DWORD)pImageBuffer))*/
    *(PDWORD)(CodeBegin+0x09) = CallAddr;
    DWORD JmpAddr = (pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint)
        - (pOptionHeader->ImageBase + ((DWORD)CodeBegin + SHELLCODELENGTH - (DWORD)pImageBuffer));
    *(PDWORD)(CodeBegin+0x0E) = JmpAddr;

    //修改OEP
    pOptionHeader->AddressOfEntryPoint = (DWORD)CodeBegin - (DWORD)pImageBuffer;

    //ImageBuffer-->NewBuffer
    size = CopyImageBufferToNewBuffer(pImageBuffer,&pNewBuffer);
    if (size == 0 || !pNewBuffer)
    {
        printf("ImageBuffer-->FileBuffer错误!");
        free(pFileBuffer);
        free(pImageBuffer);
        return ;
    }
    
    //NewBuffer-->File
    isOK = MemeryToFile(pNewBuffer,size,FILEPATH_OUT);
    if (isOK)
    {
        printf("successfully!");
        return ;
    }

    //释放内存		
    free(pFileBuffer);	
    free(pImageBuffer);
    free(pNewBuffer);		
}										

3.完整运行代码?

#include "stdafx.h"
#include <stdlib.h>
#include <windows.h>

//读取文件的路径
#define FILEPATH_IN "C:\\WINDOWS\\system32\\notepad.exe"
//写入文件的路径
#define FILEPATH_OUT "C:\\notepad1.exe"
#define SHELLCODELENGTH 0x12
#define MESSAGEBOXADDR 0x77D507EA

BYTE ShellCode[] = 
{
    0x6A,00,0x6A,00,0x6A,00,0x6A,00,
    0xE8,00,00,00,00,
    0xE9,00,00,00,00,
};

/*												
File -> FileBuffer												
1、定义变量(文件指针,文件大小,缓冲区首地址)												
2、读取文件,并判断是否读取成功(if语句)												
3、读取文件大小(ftell()+fseek())												
4、分配缓冲区(malloc())												
5、初始化缓冲区(memset())												
6、将文件数据读取到缓冲区(fread())												
7、关闭文件,并返回实际读取的大小,输出缓冲区首地址(fclose(pFile)+fileSize+pFileBuffer)												
*/												
DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer)												
{	
    FILE    *pFile     = NULL;												
    DWORD   fileSize   = 0;        //文件大小												
    LPVOID  pTempFileBuffer = NULL;//缓冲区首地址												
    
    pFile = fopen(lpszFile, "rb");//打开文件												
    if(!pFile)												
    {												
        printf("无法打开EXE文件! ");												
        return 0;												
    }												
    
    //读取文件大小												
    fseek(pFile, 0, SEEK_END);	   //将指针从开始的位置移动到末尾											
    fileSize = ftell(pFile);	   //获取数据大小											
    fseek(pFile, 0, SEEK_SET);												
    
    //分配缓冲区(申请内存)												
    pTempFileBuffer = malloc(fileSize);												
    if(!pTempFileBuffer)												
    {												
        printf(" 分配空间失败! ");												
        fclose(pFile);												
        return 0;												
    }												
    
    //初始化缓冲区
    memset(pTempFileBuffer, 0 , fileSize);

    //将文件数据读取到缓冲区																								
    size_t n = fread(pTempFileBuffer, fileSize, 1, pFile);	//将数据读取到缓冲区中											
    if(!n)												
    {												
        printf(" 读取数据失败! ");												
        free(pTempFileBuffer);	//释放内存											
        fclose(pFile);	//关闭文件											
        return 0;												
    }
    *pFileBuffer = pTempFileBuffer;	
    
    //关闭文件																							
    pTempFileBuffer = NULL;												
    fclose(pFile);												
    return fileSize;												
}

/*													
FileBuffer -> ImageBuffer													
将缓冲区的文件进行拉伸													
1、定义变量(PE文件各部分指针)													
2、判断传进来的缓冲区地址是否正确(pFileBuffer)													
3、判断是否遵守PE格式(IMAGE_DOS_SIGNATURE)													
4、获取DOS头、NT头、标准PE头、可选PE头、第一个节表信息的指针(pDosHeader,pNTHeader,pPEHeader,pOptionHeader,pSectionHeader)													
5、分配缓冲区(malloc())													
6、初始化缓冲区(memset())												
7、根据 SizeOfHeaders,先拷贝PE头(memcpy())													
8、根据节表,循环拷贝节(for循环)													
9、将拉伸后的数据首地址传出(*pImageBuffer)													
*/													
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer)													
{													
	PIMAGE_DOS_HEADER           pDosHeader       =      NULL;													
	PIMAGE_NT_HEADERS           pNTHeader        =      NULL;													
	PIMAGE_FILE_HEADER          pPEHeader        =      NULL;													
	PIMAGE_OPTIONAL_HEADER32    pOptionHeader    =      NULL;													
	PIMAGE_SECTION_HEADER       pSectionHeader   =      NULL;													
	LPVOID                      pTempImageBuffer =      NULL;													
	   												
	//判断传入的值是否有效													
	if(!pFileBuffer)													
	{													
	    printf("分配空间失败!");													
	    return 0;													
	}
    
    //判断是否为有效的MZ头	
    if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)													
    {													
        printf("不是有效的MZ头\n");													
        return 0;													
	}		
	    																								
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    //判断是否为有效的PE标志
    if(*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew))	!=	IMAGE_NT_SIGNATURE)												
    {														
        printf("不是有效的PE标志\n");														
        return 0;														
    }		
											  												
	//NT头地址													
	pNTHeader		=	(PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);										
	//标准PE头地址													
	pPEHeader		=	(PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 0x04);										
	//可选PE头地址													
	pOptionHeader	=	(PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);											
	//第一个节表地址													
	pSectionHeader	=	(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + (pPEHeader->SizeOfOptionalHeader));											
													
	//申请缓冲区大小													
	pTempImageBuffer = malloc(pOptionHeader->SizeOfImage);													
	if(!pTempImageBuffer)													
	{													
	    printf("分配空间失败");													
	    return 0;													
	}												
													
	//初始化缓冲区													
	memset(pTempImageBuffer,0,pOptionHeader->SizeOfImage);												
	    												
	//根据 SizeOfHeaders,先拷贝头													
	memcpy(pTempImageBuffer,pDosHeader,pOptionHeader->SizeOfHeaders);												
													
	//根据节表,循环拷贝节													
	PIMAGE_SECTION_HEADER	pTempSectionHeader	=	pSectionHeader;									
	for(int i=0;i<pPEHeader->NumberOfSections;i++)													
	{													
	    memcpy((void*)((DWORD)pTempImageBuffer + pTempSectionHeader->VirtualAddress),													
	           (void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData),													
	           pTempSectionHeader->SizeOfRawData);													
	    pTempSectionHeader++;													
	}													
													
	//返回数据													
	*pImageBuffer = pTempImageBuffer;													
	pTempImageBuffer = NULL;													
	return pOptionHeader->SizeOfImage;												
}													

/*														
ImageBuffer -> NewBuffer														
1、定义变量(PE文件各部分指针)														
2、判断传进来的缓冲区地址是否正确														
3、判断是否遵守PE格式														
4、获取DOS头、NT头、标准PE头、可选PE头、第一个节表信息														
5、申请缓冲区大小(malloc())														
6、初始化缓冲区(memset())														
7、根据 SizeOfHeaders(DOS头+NT头+节表的大小),先拷贝PE头														
8、根据节表,循环拷贝节														
9、将新的数据后的数据首地址传出,并返回大小(return FileSize)														
*/														
DWORD CopyImageBufferToNewBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer)														
{														
    PIMAGE_DOS_HEADER			pDosHeader		=	NULL;								
    PIMAGE_NT_HEADERS			pNTHeader		=	NULL;								
    PIMAGE_FILE_HEADER			pPEHeader		=	NULL;								
    PIMAGE_OPTIONAL_HEADER32	pOptionHeader	=	NULL;											
    PIMAGE_SECTION_HEADER		pSectionHeader	=	NULL;										
    LPVOID						pTempNewBuffer	=	NULL;						
    DWORD						FileSize		=	NULL;					
    
    //判断传进来的缓冲区地址是否正确														
    if(!pImageBuffer)														
    {														
        printf("分配空间失败!");														
        return 0;														
    }													
    
    //判断是否遵守PE格式														
    if(*((PWORD)pImageBuffer)	!=	IMAGE_DOS_SIGNATURE)												
    {														
        printf("不是有效的MZ头\n");														
        return 0;														
    }
    
    //获取DOS头
    pDosHeader	=	(PIMAGE_DOS_HEADER)pImageBuffer;
    
    //判断DOS头是否是可执行的文件
    if(*((PDWORD)((DWORD)pImageBuffer + pDosHeader->e_lfanew))	!=	IMAGE_NT_SIGNATURE)												
    {														
        printf("不是有效的PE标志\n");														
        return 0;														
    }													
    
    //NT头地址														
    pNTHeader		=	(PIMAGE_NT_HEADERS)((DWORD)pImageBuffer	+ pDosHeader->e_lfanew);										
    //标准PE头地址														
    pPEHeader		=	(PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x04);											
    //可选PE头地址														
    pOptionHeader	=	(PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);												
    //第一个节表地址														
    pSectionHeader	=	(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);												
    
    //申请缓冲区大小														
    FileSize = pOptionHeader->SizeOfImage;														
    pTempNewBuffer = malloc(FileSize);														
    if(!pTempNewBuffer)														
    {														
        printf("分配空间失败");														
        return 0;														
    }														
    
    //初始化缓冲区														
    memset(pTempNewBuffer,0,FileSize);													
    
    //根据节表,循环拷贝节														
    memcpy(pTempNewBuffer,pDosHeader,pOptionHeader->SizeOfHeaders);														
    PIMAGE_SECTION_HEADER	pTempSectionHeader	=	pSectionHeader;											
    for(int i=0;i<pPEHeader->NumberOfSections;i++)														
    {														
        memcpy((void*)((DWORD)pTempNewBuffer + pTempSectionHeader->PointerToRawData),														
            (void*)((DWORD)pImageBuffer + pTempSectionHeader->VirtualAddress),														
            pTempSectionHeader->SizeOfRawData);														
        pTempSectionHeader++;														
    }														
    
    *pNewBuffer = pTempNewBuffer;														
    pTempNewBuffer	=	NULL;												
    return FileSize;													
    
}														

/*														
存盘														
1、定义变量(文件指针)														
2、打开文件(fopen())														
3、判断文件指针是否有效														
4、文件写入(fwrite())														
5、释放空间,存盘成功返回值TRUE																																									
*/			
BOOL MemeryToFile(IN LPVOID pMemBuffer,size_t size,LPSTR lpszFile)
{
    //定义文件指针
    FILE *fp = NULL;

    //打开文件,文件使用方式为读写  
    fp = fopen(lpszFile, "wb+");

    //判断指针是否有效
    if (fp == NULL)
    {
        return FALSE;
    }

    //写入:将lpszFile(起始路径)中的文件写到pMemBuffer(目标路径)中去
    fwrite(pMemBuffer, size, 1, fp);
    //关闭文件
    fclose(fp);
    fp = NULL;
    return TRUE;
}															

/*														
添加任意代码到代码区														
1、定义变量(PE文件各部分指针以及文件各状态指针)														
2、判断传进来的缓冲区地址是否正确														
3、判断是否遵守PE格式														
4、获取DOS头、NT头、标准PE头、可选PE头、第一个节表信息														
5、判断空间是否足够写入代码														
6、将代码复制到空闲区														
7、修正代码														
8、ImageBuffer-->NewBuffer(CopyImageBufferToNewBuffer())													
9、NewBuffer-->File(MemeryToFile())	
10、释放内存(pFileBuffer,pImageBuffer,pNewBuffer)													
*/	
VOID TestAddCodeInCodeSec()
{
    LPVOID pFileBuffer = NULL;
    LPVOID pImageBuffer = NULL;
    LPVOID pNewBuffer =	NULL;
    PIMAGE_DOS_HEADER			pDosHeader		=	NULL;								
    PIMAGE_NT_HEADERS			pNTHeader		=	NULL;								
    PIMAGE_FILE_HEADER			pPEHeader		=	NULL;								
    PIMAGE_OPTIONAL_HEADER32	pOptionHeader	=	NULL;											
    PIMAGE_SECTION_HEADER		pSectionHeader	=	NULL;	
    PBYTE CodeBegin = NULL;
    BOOL isOK = FALSE;
    DWORD size = 0;
    
    //File-->FileBuffer
    ReadPEFile(FILEPATH_IN,&pFileBuffer);
    if (!pFileBuffer)	
    {
        printf("File-->FileBuffer错误!");
        return ;
    }
    
    //FileBuffer-->ImageBuffer
    CopyFileBufferToImageBuffer(pFileBuffer,&pImageBuffer);
    if (!pImageBuffer)
    {
        printf("FileBuffer-->ImageBuffer错误!");
        free(pFileBuffer);
        return ;
    }
    
    //获取DOS头
    pDosHeader	=	(PIMAGE_DOS_HEADER)pImageBuffer;
    //判断DOS头是否是可执行的文件
    if(*((PDWORD)((DWORD)pImageBuffer + pDosHeader->e_lfanew))	!=	IMAGE_NT_SIGNATURE)												
    {														
        printf("不是有效的PE标志\n");														
        return ;														
    }													
    //NT头地址														
    pNTHeader		=	(PIMAGE_NT_HEADERS)((DWORD)pImageBuffer	+ pDosHeader->e_lfanew);										
    //标准PE头地址														
    pPEHeader		=	(PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x04);											
    //可选PE头地址														
    pOptionHeader	=	(PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);												
    //第一个节表地址														
    pSectionHeader	=	(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
    
    //判断代码段空闲区是否足够存储ShellCode代码
    int tempDiffer = (pSectionHeader->SizeOfRawData) - (pSectionHeader->Misc.VirtualSize);
    if (tempDiffer < SHELLCODELENGTH)
    {
        printf("代码区空闲空间不足!");
        free(pFileBuffer);
        free(pImageBuffer);
        return ;
    }
    
    //将代码复制到空闲区
    CodeBegin = (PBYTE)((DWORD)pImageBuffer + pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize);
    memcpy(CodeBegin,ShellCode,SHELLCODELENGTH);
    
    //(E8/E9)修正的地址(X) = 真正要跳转的地址 - (E8/E9)这条指令的下一行地址
    DWORD CallAddr = MESSAGEBOXADDR 
        - (pOptionHeader->ImageBase + ((DWORD)(CodeBegin + 0x0D) - (DWORD)pImageBuffer));
    /*真正要跳转的地址:MESSAGEBOXADDR,((DWORD)(CodeBegin + 0x0D)-(DWORD)pImageBuffer)是在ImageBuffer中的偏移地址,
    E8这条指令的下一行地址:(pOptionHeader->ImageBase + ((DWORD)(CodeBegin + 0x0D)-(DWORD)pImageBuffer))*/
    *(PDWORD)(CodeBegin+0x09) = CallAddr;
    DWORD JmpAddr = (pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint)
        - (pOptionHeader->ImageBase + ((DWORD)CodeBegin + SHELLCODELENGTH - (DWORD)pImageBuffer));
    *(PDWORD)(CodeBegin+0x0E) = JmpAddr;

    //修改OEP
    pOptionHeader->AddressOfEntryPoint = (DWORD)CodeBegin - (DWORD)pImageBuffer;

    //ImageBuffer-->NewBuffer
    size = CopyImageBufferToNewBuffer(pImageBuffer,&pNewBuffer);
    if (size == 0 || !pNewBuffer)
    {
        printf("ImageBuffer-->FileBuffer错误!");
        free(pFileBuffer);
        free(pImageBuffer);
        return ;
    }
    
    //NewBuffer-->File
    isOK = MemeryToFile(pNewBuffer,size,FILEPATH_OUT);
    if (isOK)
    {
        printf("successfully!");
        return ;
    }

    //释放内存		
    free(pFileBuffer);	
    free(pImageBuffer);
    free(pNewBuffer);		
}										

int main()
{
    TestAddCodeInCodeSec();
    return 0;
}

4.添加的代码区?

?

图片
添加成功的代码

文章来源:https://blog.csdn.net/qq_53613951/article/details/135657539
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。