浅谈代码段加密原理(防止静态分析)

前言

在软件安全里,有一种保护手段叫加密,一般情况下都是为代码段加密,使原本的代码无法被静态分析,只能动态调试。

涉及到的知识有:PE文件结构,代码重定位,shellcode。

代码加密时可用各种算法组合起来使用,只要保证解密时用逆推的方法还原成源代码即可,下面例子当中用的是最简单的异或加密

由于博主也是第一次接触这个代码段加密,有写的不对或者不够的地方,还麻烦大佬指正。感谢!!

思路

  1. 获取代码段内容,进行加密后再覆盖回去

  2. 把重定位去掉,目的使重定位发生的时机在解密之后,否则解密的数据是已经被重定位过的

  3. 新增一个节,在这个节中编写解密步骤,并且要自己实现代码节的重定位,理由同2。并将OEP改写为当前节的解密位置。

主要步骤

步骤

  1. 通过读取内容,找到代码段位置(如果没被修改过,用oep所在的节作为代码节来判断比较科学,而不是单单靠.text),保存代码段的偏移和长度,后续解密需要,然后进行异或加密。

  2. 通过可选PE头的数据目录成员,找到保存重定位表的数据 (直接声明一个IMAGE_SECTION_HEADER来存放),再分别申请2个内存来存放属于代码节的重定位数据和非代码节的数据,并记录其长度。

  3. 删除重定位相关的数据,删除节表,文件内容,各头里面的相关数据

  4. 获取到当前节表的末尾,创建一个新的节,以当前文件的映射大小(SizeOfImage)为节内存起点,以当前文件大小为节文件起点。节属性为可读可学可执行已被初始化((0x20000000 | 0x40000000 | 0x80000000 | 0x40) = E0000040)

  5. 根据2保存的代码节的重定位数据长度+4(在该节找到一个4字节的位置(可紧接着重定位数据末尾)作为标记位,存放4个0x00进去,以便后面shellcode用来获取目标基址与当前基址的差值,以便重定位时使用))扩充内存,并把增加了标志位的重定位数据赋值进去。

  6. 把原oep保存下来,并把新增节的RVA+代码段的重定位数据长度+4(标志位)作为新的OEP。

  7. 编写shellcode:先通过E8,00,00,00,00方法来获取当前新增节所在的地址,再通过节地址获得当前进程的基址(1.当前指令地址-指令偏移;2.根据重定位后的数据),然后通过偏移找到代码段进行解密(注意是内存偏移而不是文件偏移),解密后,获取代码节的重定位数据(该节开始数据就是重定位数据,长度为需要重定位的代码节数据长度+4),进行重定位,重定位完成后跳转到原OEP进行文件正常运行流程(此处要自己进行代码段的重定位是因为代码段的数据已经自己加密过了,如果不自己实现解密后再重定位,那么解密出来的数据是加密后且重定位的数据,无法正常运行)

  8. 完善新增节后的文件数据(文件大小、文件映射大小、文件代码大小(SizeOfCode,这个节将作为解密使用,所以也是代码类)、节表数量等),并将原先存放文件内容的空间进行扩容realloc,扩容大小为原大小+这个节的文件大小。(扩容后可能返回一个新的地址,原地址被收回,所以要使用各种头的数据的时候,要重新获取)

注意新增节的长度为代码段重定位数据长度+4 +5(e8 00 00 00 00)+shellcode长度,且注意内存对齐和文件对齐。

  1. 完善完新节后,用同样的方法,修复原重定位节,并增加5.中的标志位的位置到重定位表数据中(重定位块为4字节块RVA+4字节块长度+2字节块中偏移(0x3XXX开头有效)组成,但重定位块为4字节对齐,所以最小的重定位块为12字节),根据2.中所求得的非代码段的重定位的数据长度,再+12+8(8字节0,用来结尾)的长度进行非代码节的重定位数据的扩容,并根据上一个节的大小,完善该节的相关数据。记得文件大小和内存大小的对齐。并更新可选PE头当中的相关数据。

  2. 将8.的非代码段的重定位数据,拼接到文件内容的末尾。拼接前记得先对文件内容大小进行扩容。

代码

#include <Windows.h>
#include <iostream>
#include <stdio.h>
#include <tlhelp32.h>
#include <io.h>
#include <string.h>
#include "Asmjit\asmjit.h"

using namespace std;
using namespace asmjit;
using namespace asmjit::x86;

char *g_pFileSrc = NULL;
int g_iTextVirtualAddr = 0; //代码节在内存中的地址
int g_iTextVirtualLen = 0;     //代码节在内存中的长度


int main()
{
    char szFileName[] = { "C:\Users\Admin\Desktop\弹窗.exe" };
    HANDLE hFile = CreateFileA(szFileName,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,   //获取文件内容
        FILE_ATTRIBUTE_ARCHIVE,
        NULL);

    if (hFile == INVALID_HANDLE_VALUE )
    {
        printf("文件打开失败
");
        return 1;
    }

    //获取文件大小
    DWORD dwFileSize = GetFileSize(hFile, NULL);

    //申请空间将exe读取到内存中
    g_pFileSrc = new char[dwFileSize];
    if (NULL == g_pFileSrc)
    {
        printf("空间申请失败
");
        return false;
    }
    ReadFile(hFile, g_pFileSrc, dwFileSize, NULL, NULL);
    CloseHandle(hFile);

    PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)g_pFileSrc;
    PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
    DWORD dwSectionCount = pNtHead->FileHeader.NumberOfSections;//节表数量
    //通过可选PE头的地址+可选PE头的长度,得到节表位置
    PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)(&pNtHead->OptionalHeader) + pNtHead->FileHeader.SizeOfOptionalHeader);



    /////////////////////////////////////
    /////////////加密代码节//////////////
    /////////////////////////////////////
    for (int i = 0; i < dwSectionCount; i++)
    {
        //通过OEP的地址所在的节确定代码节位置
        if (pNtHead->OptionalHeader.AddressOfEntryPoint >= pSection->VirtualAddress
            && pNtHead->OptionalHeader.AddressOfEntryPoint < (pSection->VirtualAddress + pSection->Misc.VirtualSize))
            //if (!strcmp((char*)pSection->Name,".text"))  //找到代码节
        {
            g_iTextVirtualAddr = pSection->VirtualAddress;  //后续解密需要
            g_iTextVirtualLen = pSection->Misc.VirtualSize;
            cout << pSection->Name;
            for (int iCount = 0; iCount < pSection->Misc.VirtualSize; iCount++)
            {
                *(char*)(g_pFileSrc + pSection->PointerToRawData + iCount) ^= 0x35;
            }
            pSection->Characteristics |= 0x80000000;  //让代码节可写,以便后续解密
            break;
        }
        pSection++;
    }



    /////////////////////////////////////
    ///////////保存重定位表信息//////////
    /////////////////////////////////////
    DWORD dwRelocRVA = pNtHead->OptionalHeader.DataDirectory[5].VirtualAddress; //得到重定位表的RVA
    pNtHead->OptionalHeader.DataDirectory[5].VirtualAddress = 0;
    DWORD dwRelocVirtualSize = 0;//重定位表的长度
    pNtHead->OptionalHeader.DataDirectory[5].Size = 0;
    DWORD dwRelocFileAddr = 0;//重定位表的文件位置
    DWORD dwRelocFileLen = 0;//重定位表的文件长度
    char szRelocName[8] = { 0 }; //重定位表名字(可能被改名混淆,做记录)
    DWORD dwRelocTab = 0;        //重定位节属性


    int iTextRelocSize = 0;  //代码节块的大小
    int iNotTextRelocSize = 0;//非代码节块的大小
    char *szNotTextRelocSrc = NULL;//重定位表非代码段数据
    char *szTextRelocSrc = NULL;    //重定位表代码段数据

    DWORD dwReloctOffset = 0; //当前遍历的偏移




    /////////////////////////////////////
    ////////拷贝并删除重定位表数据///////
    /////////////////////////////////////
    pSection = (PIMAGE_SECTION_HEADER)((DWORD)(&pNtHead->OptionalHeader) + pNtHead->FileHeader.SizeOfOptionalHeader);//重新找到表头
    for (int i = 0; i < dwSectionCount; i++)
    {
        if (pSection->VirtualAddress == dwRelocRVA) //找到重定位节表,保留重定位表相关数据到内存中,随后删除重定位表
        {
            //保存重定位表数据
            dwRelocVirtualSize = pSection->Misc.VirtualSize;
            dwRelocFileAddr = pSection->PointerToRawData;
            dwRelocFileLen = pSection->SizeOfRawData;
            dwRelocTab = pSection->Characteristics;
            strcpy(szRelocName, (char *)pSection->Name);

            PIMAGE_BASE_RELOCATION pBaseRelocation = (PIMAGE_BASE_RELOCATION)(g_pFileSrc + dwRelocFileAddr);//重定位表当前块
            while ((pBaseRelocation->VirtualAddress != 0) && (pBaseRelocation->SizeOfBlock != 0))  //遍历到重定位表末尾为止
            {
                //如果当前重定位范围在代码段内  则记录起来
                if (pBaseRelocation->VirtualAddress >= g_iTextVirtualAddr & pBaseRelocation->VirtualAddress < g_iTextVirtualAddr + g_iTextVirtualLen)
                {
                    while (pBaseRelocation->VirtualAddress < g_iTextVirtualAddr + g_iTextVirtualLen) //由于代码段是连续的,一直遍历到非代码段
                    {
                        iTextRelocSize += pBaseRelocation->SizeOfBlock; //记录长度
                        pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pBaseRelocation + pBaseRelocation->SizeOfBlock);
                    }
                    szTextRelocSrc = (char *)realloc(szTextRelocSrc, iTextRelocSize);
                    //从末尾-重定位长度=重定位数据  再+遍历的偏移  得到需要复制的数据的地址  复制长度为所以代码节范围的重定位数据长度
                    memcpy(szTextRelocSrc, g_pFileSrc + dwFileSize - dwRelocFileLen + dwReloctOffset, iTextRelocSize);
                    dwReloctOffset += iTextRelocSize;//复制完之后  更新偏移(由于这个这段数据连续的  全部复制完再更新)
                }
                else
                {
                    szNotTextRelocSrc = (char*)realloc(szNotTextRelocSrc, iNotTextRelocSize + pBaseRelocation->SizeOfBlock); //将原本的存放重定位的空间扩容
                    /*非代码块的复制源起点=重定位表位置(起始位置+末尾-重定位长度)+偏移
                    目标位置为刚申请出来的空间的空闲起点(起点+已赋值的长度)*/
                    memcpy(szNotTextRelocSrc + iNotTextRelocSize ,
                        g_pFileSrc + dwFileSize - dwRelocFileLen + dwReloctOffset,
                        pBaseRelocation->SizeOfBlock);
                    dwReloctOffset += pBaseRelocation->SizeOfBlock;//此时文件偏移增加
                    iNotTextRelocSize += pBaseRelocation->SizeOfBlock;//记录非代码段的长度
                    pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pBaseRelocation + pBaseRelocation->SizeOfBlock);
                }

            }
            /*此时,原重定位的数据已经分成了非代码段(szNotTextRelocSrc)和代码段(szTextRelocSrc)两部分
            还原时,还原非代码段的数据即可*/

            //文件内容+文件长度 = 取到文件末尾  再-当前重定位表的长度,取到重定位表的起始位置
            memset(g_pFileSrc + dwFileSize - dwRelocFileLen, 0, dwRelocFileLen);//把重定位表数据用0覆盖
            memset(pSection, 0, sizeof(IMAGE_SECTION_HEADER));
            pNtHead->FileHeader.NumberOfSections--;
            //调整文件内存大小
            int iAlignmentSize = dwRelocVirtualSize % pNtHead->OptionalHeader.SectionAlignment;
            if (!iAlignmentSize)
            {
                pNtHead->OptionalHeader.SizeOfImage -= dwRelocVirtualSize;        //调整文件内存大小
            }
            else
            {
                pNtHead->OptionalHeader.SizeOfImage =
                    pNtHead->OptionalHeader.SizeOfImage
                    - (dwRelocVirtualSize - iAlignmentSize + pNtHead->OptionalHeader.SectionAlignment);
            }
            dwFileSize -= dwRelocFileLen;
            break;
        }
        pSection++;
    }



    //找到最后一个节表,得到新增节的文件起点和映射到内存中的起点
    pSection = (PIMAGE_SECTION_HEADER)((DWORD)(&pNtHead->OptionalHeader) + pNtHead->FileHeader.SizeOfOptionalHeader);
    pSection += (pNtHead->FileHeader.NumberOfSections - 1);
    //文件起点   //文件长度原本就已经对齐  无需再对齐
    int iNewSectionFileAddr = pSection->PointerToRawData + pSection->SizeOfRawData;

    //文件映射起点
    int iNewSectionFileMapAddr = 0;
    int iMemoryAlignment = pSection->Misc.VirtualSize % pNtHead->OptionalHeader.SectionAlignment;
    if (!iMemoryAlignment)
    {
        iNewSectionFileMapAddr = pSection->VirtualAddress + pSection->Misc.VirtualSize;
    }
    else
    {
        iNewSectionFileMapAddr = pSection->VirtualAddress
            + pSection->Misc.VirtualSize
            + (pNtHead->OptionalHeader.SectionAlignment - iMemoryAlignment);
    }


    /////////////////////////////////////
    ////////////开始新增节///////////////
    /////////////////////////////////////
    pSection++;
    /*记录当前属性 方便调试*/
    memset(pSection, 0, sizeof(IMAGE_SECTION_HEADER));    //初始化当前节属性
    memcpy(pSection->Name, ".kd", 3);                        //节名字
    pSection->VirtualAddress = iNewSectionFileMapAddr;                //节的内存起始地址
    pSection->PointerToRawData = iNewSectionFileAddr;                //节的文件起始地址

    pSection->Characteristics = (0x20000000 | 0x40000000 | 0x80000000 | 0x40); //节属性 可读可写可执行已被初始化
    pNtHead->FileHeader.NumberOfSections += 1;            //节表数量+1


    //作为标志位  后面获取内存基址使用
    int iTabOffset = iTextRelocSize;            //标志位偏移
    int iTabRva = pSection->VirtualAddress;//标志位所在的RVA起点
    int iTextAlignment = iTextRelocSize + 4 ; //站位后的代码节重定位数据长度

    szTextRelocSrc = (char*)realloc(szTextRelocSrc,iTextAlignment);
    memset(szTextRelocSrc + iTextRelocSize, 0, 4);//重定位标记
    //g_pFileSrc = (char*)realloc(g_pFileSrc, dwFileSize + iTextAlignment);
    //memcpy(g_pFileSrc + dwFileSize , szTextRelocSrc, iTextAlignment);//添加代码节的重定位数据
    //dwFileSize += iTextAlignment;

    int iOldOEP = pNtHead->OptionalHeader.AddressOfEntryPoint;//原来的oep
    //将OEP改为新增节偏移   (原始的内存长度(映射末尾)-当前映射基址)
    pNtHead->OptionalHeader.AddressOfEntryPoint = pSection->VirtualAddress + iTextAlignment;
    int iNewOEP = pNtHead->OptionalHeader.AddressOfEntryPoint;
    /////////////////////////////////////
    ////////////编写shellcode///////////
    /////////////////////////////////////
    JitRuntime        _x86RunTimeObject;
    X86Assembler    a(&_x86RunTimeObject); //重定位


    // 动态编译过程
    // 也就是组织shellcode的过程
    //得到实际加载基址:1.利用重定位 (还可以直接利用shellcode自定位获得)
    int ioldIB = pNtHead->OptionalHeader.ImageBase;//目标基址
    extern int g_iTextVirtualAddr;//解密需要
    extern int g_iTextVirtualLen;

    //获取进程基址
    char szGetOEP[] = { 0xE8,0x00,0x00,0x00,0x00 }; //同理jit.call(0x00000000);
    a.pop(eax);
    a.sub(eax, 5);//此时得到oep所在的地址 oep距离基址隔着
    a.mov(ebx, eax);//备份一个OEP所在进程地址
    a.sub(eax, iNewOEP);//OEP所在的基址-偏移  得到基址
    a.mov(ebp, eax);    //保存进程基址到ebp

    a.sub(ebx, iTextAlignment - iTabOffset);//得到标志块的位置
    a.mov(edx, dword_ptr(ebx));//得到重定位后该地址的值  该值为目标基址与实际基址的差值(由于以前是0,重定位后的值=0-目标+实际)


    //解密代码段
    Label begin = a.newLabel();
    Label over = a.newLabel();
    //此时ebp为实际加载基址
    a.mov(esi, ebp);
    a.add(esi, g_iTextVirtualAddr);//esi为代码段的起点
    a.mov(edi, 0);//edi为循环偏移
    a.bind(begin);
    a.cmp(edi, g_iTextVirtualLen);
    a.jnl(over);
    a.mov(ecx, esi);
    a.add(ecx, edi); //开始解密
    a.movsx(al, byte_ptr(ecx));
    a.xor_(al, 0x35);
    a.mov(byte_ptr(ecx), al);
    a.inc(edi);
    a.jmp(begin);
    a.bind(over);//解密完毕


    //代码节重定位
    /*基址为代码节的重定位数据、iTextRelocSize代码节的长度、ebp程序基址、edi遍历偏移*/
    Label TextBegin = a.newLabel(); //代码节遍历起点
    Label TextOver = a.newLabel();  //代码节遍历终点
    Label LumpBegin =a.newLabel(); //代码节的块遍历起点
    Label LumpOver = a.newLabel();  //代码节的块的遍历终点

    /*重定位数据:基址+iTextbAddr、数据长度:iTextRelocSize
    edi程序基址、esi重定位数据基址(基址+iTextbAddr)*/
    int iTextOffset = pSection->VirtualAddress; //重定位所在的偏移


    a.push(ebp);
    a.mov(ebp, esp);
    a.sub(esp, 16);//-16块RVA -12块长度 -8块循环下标 -4块内偏移(0x3XXX) ebp即程序基址
    a.mov(dword_ptr(ebp, -4), edx);//把差值保留在edx中

    a.cmp(dword_ptr(ebp), ioldIB);
    a.je(TextOver);//当前进程基址=预计基址 则无需重定位
    a.mov(edi, 0); //edi遍历偏移
    a.bind(TextBegin);//节循环起点
    a.cmp(edi, iTextRelocSize);//遍历下标大于等于重定位长度  则遍历完成
    a.jnl(TextOver);
    a.mov(esi, dword_ptr(ebp));//得到进程基址
    a.add(esi, iTextOffset);//esi为重定位数据的基址
    a.mov(eax, dword_ptr(esi, edi));
    a.cmp(eax, 0);  //如果为0 则遍历完毕
    a.je(TextOver);
    a.mov(dword_ptr(ebp, -16), eax);//块RVA
    a.mov(edx, 4);
    a.add(edx, edi);
    a.mov(eax, dword_ptr(esi, edx));
    a.mov(dword_ptr(ebp, -12), eax);//块长度
    a.mov(dword_ptr(ebp,-8), 8); //块内循环偏移   从第8位开始 4RVA+4SIZE

    a.bind(LumpBegin);//节循环起点
    a.mov(ebx, dword_ptr(ebp, -8)); //ebx块内偏移
    a.cmp(ebx, dword_ptr(ebp, -12));
    a.jnl(LumpOver);
    a.mov(ecx, esi);//重定位数据基址
    a.add(ecx, edi);//当前遍历偏移
    a.movzx(eax, word_ptr(ecx, ebx));
    a.add(dword_ptr(ebp, -8), 2);//块内偏移+2
    a.cmp(eax, 0); //为0 则遍历完毕当前块
    a.je(LumpOver);
    a.xor_(eax, 0x3000);
    a.add(eax, dword_ptr(ebp, -16));//+块RVA
    a.add(eax, dword_ptr(ebp));//得到重定位地址
    a.mov(edx, dword_ptr(eax));//获得里面的数据
    a.add(edx, dword_ptr(ebp, -4));
    /*a.sub(edx, ioldIB);
    a.add(edx, dword_ptr(ebp));*/
    a.mov(dword_ptr(eax), edx);//重定位成功
    a.jmp(LumpBegin);
    a.bind(LumpOver);//节循环终点
    a.add(edi, dword_ptr(ebp, -12));
    a.jmp(TextBegin);
    a.bind(TextOver);//节循环终点

    a.add(esp, 16);//-16
    a.pop(ebp);
    //回到原OEP  此时ebp为程序基址
    a.mov(ebx, ebp);
    a.add(ebx, iOldOEP);//当前进程基址+原OEP
    a.jmp(ebx);

    // 生成机器码
    // 也叫生成shellcode
    // 使用一个函数指针指向
    PVOID pCode = a.make();
    int iJitSize = a.getCodeSize() + sizeof(szGetOEP);
    iTextAlignment += iJitSize;

    if (!(iTextAlignment % pNtHead->OptionalHeader.FileAlignment))  //节文件长度   根据代码段重定位数据长度
    {
        pSection->SizeOfRawData = iTextAlignment;
    }
    else
    {
        pSection->SizeOfRawData = iTextAlignment
            + pNtHead->OptionalHeader.FileAlignment
            - (iTextAlignment % pNtHead->OptionalHeader.FileAlignment);
    }
    if (!(iTextAlignment % pNtHead->OptionalHeader.SectionAlignment))  //节内存长度
    {
        pSection->Misc.VirtualSize = iTextAlignment;
    }
    else
    {
        pSection->Misc.VirtualSize = iTextAlignment
            + pNtHead->OptionalHeader.SectionAlignment
            - (iTextAlignment % pNtHead->OptionalHeader.SectionAlignment);
    }
    pNtHead->OptionalHeader.SizeOfCode += pSection->SizeOfRawData;        //新增节的内容同样为代码
    pNtHead->OptionalHeader.SizeOfImage += pSection->Misc.VirtualSize;

    int iNewSecionFileSize = pSection->SizeOfRawData;
    g_pFileSrc = (char*)realloc(g_pFileSrc, dwFileSize + iNewSecionFileSize);
    memcpy(g_pFileSrc + dwFileSize, szTextRelocSrc, iTextRelocSize + 4);//添加代码节的重定位数据
    memcpy(g_pFileSrc + dwFileSize + iTextRelocSize + 4, szGetOEP, sizeof(szGetOEP));//压入求基址指令
    memcpy(g_pFileSrc + dwFileSize + iTextRelocSize + 4 + sizeof(szGetOEP), pCode, a.getCodeSize()); //压入shellcode
    dwFileSize += iNewSecionFileSize;



    /////////////////////////////////////
    //////////修复重定位表//////////////
    /////////////////////////////////////
    pDosHead = (PIMAGE_DOS_HEADER)g_pFileSrc;
    pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
    dwSectionCount = pNtHead->FileHeader.NumberOfSections;//节表数量
    //通过可选PE头的地址+可选PE头的长度,得到节表位置
    pSection = (PIMAGE_SECTION_HEADER)((DWORD)(&pNtHead->OptionalHeader) + pNtHead->FileHeader.SizeOfOptionalHeader);
    pSection += dwSectionCount;  //pSection的首地址+(节表数量*40)
    memset(pSection, 0, sizeof(IMAGE_SECTION_HEADER));            //初始化

    //重定位表属性
    strcpy((char*)pSection->Name, szRelocName);                    //节名
    /*如果原有的重定位表的虚拟空间大小比实际存放数据的空间大10字节(用于新增新的重定位块),则无需扩大,否则则要按节对齐尺寸扩大
    前12字节为数据,后40为一个重定位块的长度 (以全为0的重定位块为结尾)  */
    pSection->PointerToRawData = dwFileSize;
    int iRelocSize = iNotTextRelocSize + 20;
    if (!(iRelocSize % pNtHead->OptionalHeader.FileAlignment))
    {
        pSection->SizeOfRawData = iRelocSize;
    }
    else
    {
        pSection->SizeOfRawData = iRelocSize
            + pNtHead->OptionalHeader.FileAlignment
            - (iRelocSize % pNtHead->OptionalHeader.FileAlignment);
    }
    int iAddrSize = pSection->SizeOfRawData; //这个节的文件长度
    pSection->VirtualAddress = pNtHead->OptionalHeader.SizeOfImage;//节RVA起始位置
    if (!(iRelocSize % pNtHead->OptionalHeader.SectionAlignment))
    {
        pSection->Misc.VirtualSize = iRelocSize;
    }
    else
    {
        pSection->Misc.VirtualSize = iRelocSize
            + pNtHead->OptionalHeader.SectionAlignment
            - (iRelocSize % pNtHead->OptionalHeader.SectionAlignment);
    }
    pSection->Characteristics = dwRelocTab | 0x80000000;
    pNtHead->OptionalHeader.DataDirectory[5].VirtualAddress = pSection->VirtualAddress;
    pNtHead->OptionalHeader.DataDirectory[5].Size = iNotTextRelocSize + 12 + 8;
    pNtHead->FileHeader.NumberOfSections++;
    pNtHead->OptionalHeader.SizeOfImage += pSection->Misc.VirtualSize;


    //重定位的块以4字节对齐
    char szAddReloc[12] = { 0 };
    int iTabLen = sizeof(szAddReloc);
    int iTabAddr = iTabRva + iTabOffset;
    iTabRva = iTabAddr & 0xfff000;
    iTabOffset = (iTabAddr & 0xfff) | 0x3000;
    memcpy(szAddReloc, &iTabRva, 12);//RVA
    memcpy(szAddReloc + 4, &iTabLen, 4);//size
    memcpy(szAddReloc + 8, &iTabOffset, 4);//偏移  位于第8个字节后面

    szNotTextRelocSrc = (char *)realloc(szNotTextRelocSrc, iAddrSize);//增加重定位数据
    memcpy(szNotTextRelocSrc + iNotTextRelocSize, szAddReloc, sizeof(szAddReloc));//把新的重定位块数据加到原重定位块数据上
    memset(szNotTextRelocSrc + iNotTextRelocSize + sizeof(szAddReloc), 0, 8);

    g_pFileSrc = (char*)realloc(g_pFileSrc, dwFileSize + iAddrSize);//把空间大小增加重定位节所占文件大小

    memcpy(g_pFileSrc + dwFileSize, szNotTextRelocSrc, iAddrSize);
    dwFileSize += iAddrSize;//更新文件大小


    HANDLE hNewFile = CreateFileA("C:\Users\Admin\Desktop\弹窗测试.exe",
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        CREATE_ALWAYS,   //创建并覆盖上一个文件
        FILE_ATTRIBUTE_ARCHIVE,
        NULL);

    if (hNewFile == INVALID_HANDLE_VALUE)
    {
        printf("文件保存失败
");
        return 1;
    }
    int iError = GetLastError();
    LPDWORD iNum = NULL;
    WriteFile(hNewFile, g_pFileSrc, dwFileSize, iNum, NULL); //写入文件
    CloseHandle(hNewFile);

    MessageBoxA(0, "by:阿怪
          2020.8.25", "加密成功", 0);
    system("pause");
}

运行效果

shellcode的例子各位可以在网上搜索或者自己想办法用汇编转换成机器码输入。

转自:https://www.cnblogs.com/aaaguai/p/13599696.html

--欢迎大家来交流反馈。
原文地址:https://www.cnblogs.com/hhyl/p/13603590.html