[原创]向PE文件中添加ShellCode
最烦打代码了,但是貌似代码是最高效的方式……
之前手动往PE文件中注入代码添加ShellCode,最近手动往PE文件中注入代码,貌似就是捆绑吧………………
1 ------------------------------------------正题分割线------------------------------------------
工具:一个想添加ShellCode的程序,VS2013
步骤为VS中的注释
-/******************************************************************************************/
//步骤分为五个 */
//1,将文件读取到缓冲区,用pFileBuffer接收吗,构造ShellCode(); */
//2,根据对齐的情况来,将接收的FileBuffer 手动转换为ImageBuffer; */
//3,向已经转换完成的ImageBuffer的代码空白区; */
//4,补充完ShellCode; */
//5,更改OEP; */
/*******************************************************************************************/
ps:貌似PE文件的类型在这两个头文件中
#include <atlbase.h>
#include <atlstr.h>
这里我用的程序是win自带的notepad;
首先将文件读入缓冲区;
<input type="checkbox">1
DWORDReadPEFile(INLPSTRlpszFile,OUTLPVOID*FileBuffer)
{
FILE*pFile=NULL;
DWORDfileSize=0;
//定义一个可以含有内存空间的类型
LPVOIDpFileBuffer=NULL;
//打开文件
fopen_s(&pFile,lpszFile,"rb");
if(!pFile)
{
printf("无法打开EXE文件!");
returnNULL;
}
else
{
printf("Successful!\n");
}
//读取文件大小
fseek(pFile,0,SEEK_END);
fileSize=ftell(pFile);
fseek(pFile,0,SEEK_SET);
//分配缓冲区
pFileBuffer=malloc(fileSize);
if(!pFileBuffer)
{
printf("分配空间失败!");
fclose(pFile);
returnNULL;
}
//将文件数据读取到缓冲区
size_tn=fread(pFileBuffer,fileSize,1,pFile);
if(!n)
{
printf("读取数据失败!");
free(pFileBuffer);
fclose(pFile);
returnNULL;
}
//关闭文件
*FileBuffer=pFileBuffer;
fclose(pFile);
pFileBuffer=NULL;
returnfileSize;
};
这个是单独的函数路径是在头文件中定义的,将文件读到内存中之后用FileBuffer接收。返回的文件的长度,注意的地方是要把在本函数中使用的空间最后变为NULL;这个时候的数据是按文件对齐排列在内存中的,需要把文件对齐拉伸为内存对齐。
第二个函数为FileBufferToImageBuffer;文件对齐转换为内存对齐,起始位置的地址从0转换为ImageBase;
DWORD FileBufferToImageBuffer(IN LPVOID pFileBuffer, OUT LPVOID* pImageBuffer){
//定义各个头的指针
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
LPVOID pTempImageBuffer = NULL;
//判断传入的缓冲区是否有效
if (pFileBuffer == NULL)
{
printf("缓冲区无效\n");
return 0;
}
//判断打开的文件是否是正确的文件
if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE){
printf("不是有效的MZ标记!\n");
return 0;
}
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
if (*((PWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE){
printf("不是有效的PE标记!\n");
return 0;
}
printf("第一个大小%x\n", pFileBuffer);
//NT头
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
//标准PE头
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
//可选PE头
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//节表
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pPEHeader->SizeOfOptionalHeader);
//申请新的空间、
pTempImageBuffer = malloc(pOptionalHeader->SizeOfImage);
printf("%x\n", pTempImageBuffer);
if (!pTempImageBuffer){
printf("申请空间失败!\n");
return 0;
}
//初始化新分配的空间
memset(pTempImageBuffer, 0, pOptionalHeader->SizeOfImage);
//将TempImageBuffer的内存空间填充0
memcpy(pTempImageBuffer,pDosHeader,pOptionalHeader->SizeOfHeaders);
PIMAGE_SECTION_HEADER ptempSectionHeader = pSectionHeader;
//拷贝节表信息? 内容?! 包括拉伸的过程 从文件中的转换到内存中的FileBuffer-》ImageBuffer
for (int i = 0; i < pPEHeader->NumberOfSections; i++, ptempSectionHeader++){//出错的环节
memcpy(
(void*)((DWORD)pTempImageBuffer + ptempSectionHeader->VirtualAddress),//拷贝信息存储的地址
(void*)((DWORD)pDosHeader + ptempSectionHeader->PointerToRawData),//拷贝开始的地址
ptempSectionHeader->SizeOfRawData);
}
*pImageBuffer = pTempImageBuffer;
pTempImageBuffer = NULL;
PIMAGE_DOS_HEADER PDosHeader = NULL;
return pOptionalHeader->SizeOfImage;
}
转换时需要先复制头信息,节表信息,PE文件头部没有对齐 只是起始位置改变。当复制节表表示的内容时需要每个节每个节填充;
这个地方需要注意RVA ROA FOA一些乱七八糟的概念;
FileBuffer转换为ImageBuffer之后就可以添加ShellCode了,这个地方用到一个概念,需要先把让OEP指向ShellCode然后再跳转回到原始OEP执行原始代码;代码如下
VOID TestAddCode(){
BYTE ShellCode[] = {
0x6A, 00, 0x6A, 00, 0x6A, 00, 0x6A, 00,
0xE8, 00, 00, 00, 00,
0xE9, 00, 00, 00, 00
};
LPVOID pFileBuffer;
LPVOID pImageBuffer;
LPVOID pNewBuffer;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PBYTE codeBegin = NULL;
BOOL isOK = FALSE;
DWORD size = 0;
//读取文件数据到缓冲区
ReadPEFile(INFILEPATH, &pFileBuffer);
if (!pFileBuffer){
printf("文件读取到缓冲区失败!\n");
free(pFileBuffer);
return;
}
//手动转换
FileBufferToImageBuffer(pFileBuffer, &pImageBuffer);
printf("");
if (!pImageBuffer){
printf("FileBuffer to ImageBuffer false!\n");
free(pFileBuffer);
free(pImageBuffer);
return;
}
//判断节表的空闲区是否可以注入代码
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
//标准PE头
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
//可选PE头
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//节表
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pPEHeader->SizeOfOptionalHeader);
if (((pSectionHeader->SizeOfRawData) - (pSectionHeader->Misc.VirtualSize)) < SHELLCODELENGTH){
printf("空闲去太小无法存放!\n");
free(pImageBuffer);
free(pFileBuffer);
return ;
}
codeBegin = (PBYTE)((DWORD)pImageBuffer + pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize
);
memcpy(codeBegin, ShellCode, SHELLCODELENGTH);
//修正E8
DWORD callAddr = (MESSAGEBOXADDR - (pOptionalHeader->ImageBase) + (((DWORD)codeBegin + 0xD - (DWORD
)pImageBuffer)));
*(PDWORD)(codeBegin + 9) = callAddr;
//修正E9
DWORD jmpAddr = ((pOptionalHeader->ImageBase + pOptionalHeader->AddressOfEntryPoint) - (pOptionalHeader
->ImageBase + ((DWORD)codeBegin + SHELLCODELENGTH)));
*(PDWORD)(codeBegin + 0xE) = jmpAddr;
//修正OEP
pOptionalHeader->AddressOfEntryPoint = (DWORD)codeBegin - (DWORD)pImageBuffer;
size = ImageBufferToNewBuffer(pImageBuffer, &pNewBuffer);
if (size == 0 || !pNewBuffer){
printf("imagebuffer->newBuffer false!\n");
free(pFileBuffer);
free(pImageBuffer);
free(pNewBuffer);
return;
}
PIMAGE_DOS_HEADER pDosHeader_new = NULL;
pDosHeader_new = (PIMAGE_DOS_HEADER)pNewBuffer;
printf("%s\n", pDosHeader_new);
isOK = SaveFile(pNewBuffer, size, OUTFILEPATH);
if (isOK){
printf("存盘成功! \n 文件存放在%s\n", OUTFILEPATH);
free(pFileBuffer);
free(pImageBuffer);
free(pNewBuffer);
}
}
每个函数都有独立的功能,貌似就是那六个字:高内聚,低耦合;
这里是将ShellCode存放在一个数组中然后直接将数组整体添加到应该添加的地方。
剩下的就是将已经添加过ShellCode的文件从内存对齐状态转换为文件对齐,然后保存文件,这个是之前从FileBuffer转换成ImageBuffer的逆过程;
就不添加代码了,太多了。
感谢滴水逆向的教程。
