欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]向PE文件中添加ShellCode

[原创]向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的逆过程;

就不添加代码了,太多了。



感谢滴水逆向的教程。

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功