WindowsPE(二)空白区添加代码新增,扩大,合并节

news2024/11/23 11:47:48

空白区添加代码

在 PE 中插入一段调用 MessageBox 的代码。

获取MessageBox地址,构造ShellCode代码

利 OD 定位出 MessageBoxA 函数的地址为 0x77D507EA 。
在这里插入图片描述
构造 shellcode :

unsigned char shellcode[] = {
        0x6A, 0x00,                     //  push 0
        0x6A, 0x00,                     //  push 0
        0x6A, 0x00,                     //  push 0
        0x6A, 0x00,                     //  push 0
        0xE8, 0x00, 0x00, 0x00, 0x00,   //  call MessageBox
        0xE9, 0x00, 0x00, 0x00, 0x00    //  jmp OEP
};

其中 shellcode 中的地址需要根据 shellcode 在 PE 中插入的位置来确定。

找到合适的空白区域

节表中的 SizeOfRawData 为 该节在 PE 文件中关于文件对齐后的大小,Misc.VirtualSize 为该节加载到内存后的真实大小。因此可以添加内容的空白区域大小为 SizeOfRawData - Misc.VirtualSize 。
遍历 PE 文件的节表,找到可以添加 shellcode 的节。

PIMAGE_SECTION_HEADER findFree(int size) {
    for (int i = 0; i < pe.pSectionHeaders.size(); i++) {
        PIMAGE_SECTION_HEADER &pSectionHeader = pe.pSectionHeaders[i];
        if (pSectionHeader->SizeOfRawData - pSectionHeader->Misc.VirtualSize >= size) {
            return pSectionHeader;
        }
    }
    return (PIMAGE_SECTION_HEADER) NULL;
}

修复 shellcode 中的地址

shellcode 中的 JMP 和 CALL 后面跟的地址 = 下一条指令的地址 - 目标地址 。

    DWORD ImageBase = pe.pNTHeader->OptionalHeader.ImageBase;
    DWORD OEP = ImageBase + pe.pNTHeader->OptionalHeader.AddressOfEntryPoint;
    DWORD rva = pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize;
    DWORD addr = ImageBase + rva;

    *(DWORD *) (shellcode + 9) = 0x77D507EA - (addr + 13);
    *(DWORD *) (shellcode + 14) = OEP - (addr + 18);

修改节的属性以及 OEP

将 OEP 指向 shellcode,并将 shellcode 所在节的属性修改为可执行。

    pe.pNTHeader->OptionalHeader.AddressOfEntryPoint = rva;
    DWORD foa = pSectionHeader->PointerToRawData + pSectionHeader->Misc.VirtualSize;

将修正后的 shellcode 写入 PE 文件并保存

    memcpy(pe.buf + foa, shellcode, sizeof(shellcode));
    pe.saveToFile("C:\\Documents and Settings\\Administrator\\桌面\\notepad1.exe");

运行结果

运行后生成了一个新的 PE 文件
在这里插入图片描述
运行 notepad1.exe,发现调用 MessageBox 的 shellcode 成功运行。
在这里插入图片描述
之后 PE 本身的代码也正常运行。
在这里插入图片描述

完整代码

// PE.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include<iostream>
#include<Windows.h>
#include<fstream>
#include <string>
#include <sstream>
#include<vector>


struct PE {
    char *buf;
    int size;
    PIMAGE_DOS_HEADER pDosHeader;
    PIMAGE_NT_HEADERS32 pNTHeader;
    std::vector<PIMAGE_SECTION_HEADER> pSectionHeaders;
    bool error;

    explicit PE(const std::string &path) : error(false), pDosHeader(NULL), pNTHeader(NULL) {
        std::ifstream fsRead(path.c_str(), std::ios::in | std::ios::binary);
        if (!fsRead.is_open()) {
            std::cerr << "[-] failed to open the PE file." << std::endl;
            error = true;
            return;
        }
        fsRead.seekg(0, std::ios::end);
        size = (int) fsRead.tellg();
        buf = new char[size];
        fsRead.seekg(0);
        fsRead.read(buf, size);

        pDosHeader = (PIMAGE_DOS_HEADER) buf;
        if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
            std::cerr << "[-] File is not a PE file." << std::endl;
            error = true;
            return;
        }

        pNTHeader = (PIMAGE_NT_HEADERS32) (buf + pDosHeader->e_lfanew);
        if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
            std::cerr << "[-] File is not a PE file." << std::endl;
            error = true;
            return;
        }
        pSectionHeaders.resize(pNTHeader->FileHeader.NumberOfSections);
        for (int i = 0, p = pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32); i < pSectionHeaders.size(); i++, p += sizeof(IMAGE_SECTION_HEADER)) {
            pSectionHeaders[i] = (PIMAGE_SECTION_HEADER) (buf + p);
        }
    }

    bool saveToFile(const std::string &path) {
        std::ofstream fsWrite(path.c_str(), std::ios::out | std::ios::binary);
        if (!fsWrite.is_open()) {
            std::cerr << "[-] failed to open the dump file." << std::endl;
            return false;
        }
        fsWrite.write(buf, size);
        fsWrite.close();
        return true;
    }

    bool isError() const {
        return error;
    }

    ~PE() {
        delete[]buf;
    }
};

unsigned char shellcode[] = {
        0x6A, 0x00,                     //  push 0
        0x6A, 0x00,                     //  push 0
        0x6A, 0x00,                     //  push 0
        0x6A, 0x00,                     //  push 0
        0xE8, 0x00, 0x00, 0x00, 0x00,   //  call MessageBox
        0xE9, 0x00, 0x00, 0x00, 0x00    //  jmp OEP
};

PE pe("C:\\Documents and Settings\\Administrator\\桌面\\notepad.exe");

PIMAGE_SECTION_HEADER findFree(int size) {
    for (int i = 0; i < pe.pSectionHeaders.size(); i++) {
        PIMAGE_SECTION_HEADER &pSectionHeader = pe.pSectionHeaders[i];
        if (pSectionHeader->SizeOfRawData - pSectionHeader->Misc.VirtualSize >= size) {
            return pSectionHeader;
        }
    }
    return (PIMAGE_SECTION_HEADER) NULL;
}

int main() {
    if (pe.isError()) {
        std::cerr << "[-] Failed to load PE." << std::endl;
        return 0;
    }

    PIMAGE_SECTION_HEADER pSectionHeader = findFree(sizeof(shellcode));
    if (pSectionHeader == NULL) {
        std::cerr << "[-] Filed to find free area." << std::endl;
        return 0;
    }

    DWORD ImageBase = pe.pNTHeader->OptionalHeader.ImageBase;
    DWORD OEP = ImageBase + pe.pNTHeader->OptionalHeader.AddressOfEntryPoint;
    DWORD rva = pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize;
    DWORD addr = ImageBase + rva;

    *(DWORD *) (shellcode + 9) = 0x77D507EA - (addr + 13);
    *(DWORD *) (shellcode + 14) = OEP - (addr + 18);

    pSectionHeader->Characteristics |= 0x60000020;
    pe.pNTHeader->OptionalHeader.AddressOfEntryPoint = rva;
    DWORD foa = pSectionHeader->PointerToRawData + pSectionHeader->Misc.VirtualSize;

    memcpy(pe.buf + foa, shellcode, sizeof(shellcode));
    pe.saveToFile("C:\\Documents and Settings\\Administrator\\桌面\\notepad1.exe");

    return 0;
}

新增节

在 PE 中新增一个 .new 节,长度为 0x1000 。

将 IMAGE_NT_HEADER 和节表整体上移

如图所示,在节表结束到一个节之间的空白区域可能存放有有用的数据。
在这里插入图片描述
因此为了尽量保证添加节表后不会破坏 PE 文件,需要将 IMAGE_NT_HEADER 和节表整体上移,将 DOS stub 覆盖。

    DWORD headSize = sizeof(IMAGE_NT_HEADERS32) + sizeof(IMAGE_SECTION_HEADER) * pe.pNTHeader->FileHeader.NumberOfSections;
    if (pe.pNTHeader->OptionalHeader.SizeOfHeaders - sizeof(IMAGE_DOS_HEADER) - headSize < sizeof(IMAGE_SECTION_HEADER) * 2) {
        std::cerr << "[-] Head space is not enough." << std::endl;
        return 0;
    }

    memcpy(pe.buf + sizeof(IMAGE_DOS_HEADER), pe.pNTHeader, headSize);
    DWORD offset = pe.pDosHeader->e_lfanew - sizeof(IMAGE_DOS_HEADER);
    pe.pDosHeader->e_lfanew = sizeof(IMAGE_DOS_HEADER);
    pe.pNTHeader = (PIMAGE_NT_HEADERS) ((char *) pe.pNTHeader - offset);
    for (int i = 0; i < pe.pSectionHeaders.size(); i++) {
        PIMAGE_SECTION_HEADER &pSectionHeader = pe.pSectionHeaders[i];
        pSectionHeader = (PIMAGE_SECTION_HEADER) ((char *) pSectionHeader - offset);
    }

在节表末尾添加一项,并将后一项填充 0

    PIMAGE_SECTION_HEADER pNewSection = (PIMAGE_SECTION_HEADER) (pe.buf + sizeof(IMAGE_DOS_HEADER) + headSize);
    memset(pNewSection, 0, sizeof(IMAGE_SECTION_HEADER));
    memcpy(pNewSection->Name, sectionName, sizeof(sectionName));
    PIMAGE_SECTION_HEADER pLastSection = pe.pSectionHeaders.back();
    DWORD fileAlignment = pe.pNTHeader->OptionalHeader.FileAlignment;
    DWORD sectionAlignment = pe.pNTHeader->OptionalHeader.SectionAlignment;
    pNewSection->Misc.VirtualSize = sectionSize;
    pNewSection->VirtualAddress = pLastSection->VirtualAddress + (pLastSection->Misc.VirtualSize + sectionAlignment - 1) / sectionAlignment * sectionAlignment;
    pNewSection->SizeOfRawData = (sectionSize + fileAlignment - 1) / fileAlignment * fileAlignment;
    pNewSection->PointerToRawData = pLastSection->PointerToRawData + pLastSection->SizeOfRawData;
    pNewSection->Characteristics = sectionCharacteristics;
    memset(pNewSection + 1, 0, sizeof(IMAGE_SECTION_HEADER));

更新 SizeOfImage 和 NumberOfSections 字段

注意:SizeOfHeaders 不需要更新,因为如果 SizeOfHeaders 需要更新那么节表没有足够的空间来新增节。

    assert(pe.pNTHeader->OptionalHeader.SizeOfImage == pNewSection->VirtualAddress);
    pe.pNTHeader->OptionalHeader.SizeOfImage += (pNewSection->Misc.VirtualSize + sectionAlignment - 1) / sectionAlignment * sectionAlignment;
    pe.pNTHeader->FileHeader.NumberOfSections++;

为新增的节分配空间并在其中添加代码

    assert(pe.size == pNewSection->PointerToRawData);
    pe.resize(pe.size + pNewSection->SizeOfRawData);

    PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER) (pe.buf + sizeof(IMAGE_DOS_HEADER) + headSize);
    DWORD ImageBase = pe.pNTHeader->OptionalHeader.ImageBase;
    DWORD OEP = ImageBase + pe.pNTHeader->OptionalHeader.AddressOfEntryPoint;
    DWORD rva = pSectionHeader->VirtualAddress;
    DWORD addr = ImageBase + rva;

    *(DWORD *) (shellcode + 9) = 0x77D507EA - (addr + 13);
    *(DWORD *) (shellcode + 14) = OEP - (addr + 18);

    pSectionHeader->Characteristics |= 0x60000020;
    pe.pNTHeader->OptionalHeader.AddressOfEntryPoint = rva;
    DWORD foa = pSectionHeader->PointerToRawData;

    memcpy(pe.buf + foa, shellcode, sizeof(shellcode));

    pe.saveToFile("C:\\Documents and Settings\\Administrator\\桌面\\notepad1.exe");

运行结果

新增了一个节,并且 OEP 也在改节。
在这里插入图片描述
运行生成的 PE 文件,添加的 shellcode 成功执行且原有的功能未受影响。
在这里插入图片描述

完整代码

// PE.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include<iostream>
#include<Windows.h>
#include<fstream>
#include<vector>
#include<assert.h>
#include<algorithm>

struct PE {
    char *buf;
    DWORD size;
    PIMAGE_DOS_HEADER pDosHeader;
    PIMAGE_NT_HEADERS32 pNTHeader;
    std::vector<PIMAGE_SECTION_HEADER> pSectionHeaders;
    bool error;

    explicit PE(const std::string &path) : error(false), pDosHeader(NULL), pNTHeader(NULL) {
        std::ifstream fsRead(path.c_str(), std::ios::in | std::ios::binary);
        if (!fsRead.is_open()) {
            std::cerr << "[-] failed to open the PE file." << std::endl;
            error = true;
            return;
        }
        fsRead.seekg(0, std::ios::end);
        size = (int) fsRead.tellg();
        buf = new char[size];
        fsRead.seekg(0);
        fsRead.read(buf, size);
        init();
    }

    void init() {
        pDosHeader = (PIMAGE_DOS_HEADER) buf;
        if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
            std::cerr << "[-] File is not a PE file." << std::endl;
            error = true;
            return;
        }

        pNTHeader = (PIMAGE_NT_HEADERS32) (buf + pDosHeader->e_lfanew);
        if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
            std::cerr << "[-] File is not a PE file." << std::endl;
            error = true;
            return;
        }
        pSectionHeaders.resize(pNTHeader->FileHeader.NumberOfSections);
        for (int i = 0, p = pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32); i < pSectionHeaders.size(); i++, p += sizeof(IMAGE_SECTION_HEADER)) {
            pSectionHeaders[i] = (PIMAGE_SECTION_HEADER) (buf + p);
        }
    }

    bool saveToFile(const std::string &path) {
        std::ofstream fsWrite(path.c_str(), std::ios::out | std::ios::binary);
        if (!fsWrite.is_open()) {
            std::cerr << "[-] failed to open the dump file." << std::endl;
            return false;
        }
        fsWrite.write(buf, size);
        fsWrite.close();
        return true;
    }

    void resize(DWORD _size) {
        char *tbuf = new char[_size];
        memset(tbuf, 0, _size);
        memcpy(tbuf, this->buf, min(_size, this->size));
        delete[]this->buf;
        this->buf = tbuf;
        this->size = _size;
        init();
    }

    bool isError() const {
        return error;
    }

    ~PE() {
        delete[]buf;
    }
};

const char sectionName[] = ".new";
const DWORD sectionSize = 0x1000;
const DWORD sectionCharacteristics = 0x60000020;

unsigned char shellcode[] = {
        0x6A, 0x00,                     //  push 0
        0x6A, 0x00,                     //  push 0
        0x6A, 0x00,                     //  push 0
        0x6A, 0x00,                     //  push 0
        0xE8, 0x00, 0x00, 0x00, 0x00,   //  call MessageBox
        0xE9, 0x00, 0x00, 0x00, 0x00    //  jmp OEP
};


int main() {
    PE pe("C:\\Documents and Settings\\Administrator\\桌面\\notepad.exe");

    if (pe.isError()) {
        std::cerr << "[-] Failed to load PE." << std::endl;
        return 0;
    }

    DWORD headSize = sizeof(IMAGE_NT_HEADERS32) + sizeof(IMAGE_SECTION_HEADER) * pe.pNTHeader->FileHeader.NumberOfSections;
    if (pe.pNTHeader->OptionalHeader.SizeOfHeaders - sizeof(IMAGE_DOS_HEADER) - headSize < sizeof(IMAGE_SECTION_HEADER) * 2) {
        std::cerr << "[-] Head space is not enough." << std::endl;
        return 0;
    }

    memcpy(pe.buf + sizeof(IMAGE_DOS_HEADER), pe.pNTHeader, headSize);
    DWORD offset = pe.pDosHeader->e_lfanew - sizeof(IMAGE_DOS_HEADER);
    pe.pDosHeader->e_lfanew = sizeof(IMAGE_DOS_HEADER);
    pe.pNTHeader = (PIMAGE_NT_HEADERS) ((char *) pe.pNTHeader - offset);
    for (int i = 0; i < pe.pSectionHeaders.size(); i++) {
        PIMAGE_SECTION_HEADER &pSectionHeader = pe.pSectionHeaders[i];
        pSectionHeader = (PIMAGE_SECTION_HEADER) ((char *) pSectionHeader - offset);
    }

    PIMAGE_SECTION_HEADER pNewSection = (PIMAGE_SECTION_HEADER) (pe.buf + sizeof(IMAGE_DOS_HEADER) + headSize);
    memset(pNewSection, 0, sizeof(IMAGE_SECTION_HEADER));
    memcpy(pNewSection->Name, sectionName, sizeof(sectionName));
    PIMAGE_SECTION_HEADER pLastSection = pe.pSectionHeaders.back();
    DWORD fileAlignment = pe.pNTHeader->OptionalHeader.FileAlignment;
    DWORD sectionAlignment = pe.pNTHeader->OptionalHeader.SectionAlignment;
    pNewSection->Misc.VirtualSize = sectionSize;
    pNewSection->VirtualAddress = pLastSection->VirtualAddress + (pLastSection->Misc.VirtualSize + sectionAlignment - 1) / sectionAlignment * sectionAlignment;
    pNewSection->SizeOfRawData = (sectionSize + fileAlignment - 1) / fileAlignment * fileAlignment;
    pNewSection->PointerToRawData = pLastSection->PointerToRawData + pLastSection->SizeOfRawData;
    pNewSection->Characteristics = sectionCharacteristics;
    memset(pNewSection + 1, 0, sizeof(IMAGE_SECTION_HEADER));

    assert(pe.pNTHeader->OptionalHeader.SizeOfImage == pNewSection->VirtualAddress);
    pe.pNTHeader->OptionalHeader.SizeOfImage += (pNewSection->Misc.VirtualSize + sectionAlignment - 1) / sectionAlignment * sectionAlignment;
    pe.pNTHeader->FileHeader.NumberOfSections++;


    assert(pe.size == pNewSection->PointerToRawData);
    pe.resize(pe.size + pNewSection->SizeOfRawData);

    PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER) (pe.buf + sizeof(IMAGE_DOS_HEADER) + headSize);
    DWORD ImageBase = pe.pNTHeader->OptionalHeader.ImageBase;
    DWORD OEP = ImageBase + pe.pNTHeader->OptionalHeader.AddressOfEntryPoint;
    DWORD rva = pSectionHeader->VirtualAddress;
    DWORD addr = ImageBase + rva;

    *(DWORD *) (shellcode + 9) = 0x77D507EA - (addr + 13);
    *(DWORD *) (shellcode + 14) = OEP - (addr + 18);

    pSectionHeader->Characteristics |= 0x60000020;
    pe.pNTHeader->OptionalHeader.AddressOfEntryPoint = rva;
    DWORD foa = pSectionHeader->PointerToRawData;

    memcpy(pe.buf + foa, shellcode, sizeof(shellcode));

    pe.saveToFile("C:\\Documents and Settings\\Administrator\\桌面\\notepad1.exe");
	
    return 0;
}

扩大节

修改节表最后一项的数据并为扩大的节分配空间

    PIMAGE_SECTION_HEADER pSectionHeader = pe.pSectionHeaders.back();
    DWORD fileAlignment = pe.pNTHeader->OptionalHeader.FileAlignment;
    DWORD sectionAlignment = pe.pNTHeader->OptionalHeader.SectionAlignment;
    pSectionHeader->Misc.VirtualSize += extendSize;
    pSectionHeader->SizeOfRawData = (pSectionHeader->Misc.VirtualSize + fileAlignment - 1) / fileAlignment * fileAlignment;
    pe.pNTHeader->OptionalHeader.SizeOfImage = (pe.pNTHeader->OptionalHeader.SizeOfImage + extendSize + sectionAlignment - 1) / sectionAlignment * sectionAlignment;
    pe.resize(pSectionHeader->PointerToRawData + pSectionHeader->SizeOfRawData);

在扩大的节中写入 shellcode

    assert(extendSize >= sizeof(shellcode));
    DWORD ImageBase = pe.pNTHeader->OptionalHeader.ImageBase;
    DWORD OEP = ImageBase + pe.pNTHeader->OptionalHeader.AddressOfEntryPoint;
    DWORD rva = pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize - sizeof(shellcode);
    DWORD addr = ImageBase + rva;

    *(DWORD *) (shellcode + 9) = 0x77D507EA - (addr + 13);
    *(DWORD *) (shellcode + 14) = OEP - (addr + 18);

    pSectionHeader->Characteristics |= 0x60000020;
    pe.pNTHeader->OptionalHeader.AddressOfEntryPoint = rva;
    DWORD foa = pSectionHeader->PointerToRawData + pSectionHeader->Misc.VirtualSize - sizeof(shellcode);

    memcpy(pe.buf + foa, shellcode, sizeof(shellcode));

    pe.saveToFile("C:\\Documents and Settings\\Administrator\\桌面\\notepad1.exe");

运行结果

如下图所示,最后一个节被扩大了。
在这里插入图片描述
运行生成的 PE 文件,shellcode 被执行且原有功能未受影响。
在这里插入图片描述

完整代码

// PE.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include<iostream>
#include<Windows.h>
#include<fstream>
#include<vector>
#include<assert.h>
#include<algorithm>

struct PE {
    char *buf;
    DWORD size;
    PIMAGE_DOS_HEADER pDosHeader;
    PIMAGE_NT_HEADERS32 pNTHeader;
    std::vector<PIMAGE_SECTION_HEADER> pSectionHeaders;
    bool error;

    explicit PE(const std::string &path) : error(false), pDosHeader(NULL), pNTHeader(NULL) {
        std::ifstream fsRead(path.c_str(), std::ios::in | std::ios::binary);
        if (!fsRead.is_open()) {
            std::cerr << "[-] failed to open the PE file." << std::endl;
            error = true;
            return;
        }
        fsRead.seekg(0, std::ios::end);
        size = (int) fsRead.tellg();
        buf = new char[size];
        fsRead.seekg(0);
        fsRead.read(buf, size);
        init();
    }

    void init() {
        pDosHeader = (PIMAGE_DOS_HEADER) buf;
        if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
            std::cerr << "[-] File is not a PE file." << std::endl;
            error = true;
            return;
        }

        pNTHeader = (PIMAGE_NT_HEADERS32) (buf + pDosHeader->e_lfanew);
        if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
            std::cerr << "[-] File is not a PE file." << std::endl;
            error = true;
            return;
        }
        pSectionHeaders.resize(pNTHeader->FileHeader.NumberOfSections);
        for (int i = 0, p = pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32); i < pSectionHeaders.size(); i++, p += sizeof(IMAGE_SECTION_HEADER)) {
            pSectionHeaders[i] = (PIMAGE_SECTION_HEADER) (buf + p);
        }
    }

    bool saveToFile(const std::string &path) {
        std::ofstream fsWrite(path.c_str(), std::ios::out | std::ios::binary);
        if (!fsWrite.is_open()) {
            std::cerr << "[-] failed to open the dump file." << std::endl;
            return false;
        }
        fsWrite.write(buf, size);
        fsWrite.close();
        return true;
    }

    void resize(DWORD _size) {
        char *tbuf = new char[_size];
        memset(tbuf, 0, _size);
        memcpy(tbuf, this->buf, min(_size, this->size));
        delete[]this->buf;
        this->buf = tbuf;
        this->size = _size;
        init();
    }

    bool isError() const {
        return error;
    }

    ~PE() {
        delete[]buf;
    }
};

const DWORD extendSize = 0x3000;

unsigned char shellcode[] = {
        0x6A, 0x00,                     //  push 0
        0x6A, 0x00,                     //  push 0
        0x6A, 0x00,                     //  push 0
        0x6A, 0x00,                     //  push 0
        0xE8, 0x00, 0x00, 0x00, 0x00,   //  call MessageBox
        0xE9, 0x00, 0x00, 0x00, 0x00    //  jmp OEP
};

int main() {
    PE pe("C:\\Documents and Settings\\Administrator\\桌面\\notepad.exe");

    if (pe.isError()) {
        std::cerr << "[-] Failed to load PE." << std::endl;
        return 0;
    }

    PIMAGE_SECTION_HEADER pSectionHeader = pe.pSectionHeaders.back();
    DWORD fileAlignment = pe.pNTHeader->OptionalHeader.FileAlignment;
    DWORD sectionAlignment = pe.pNTHeader->OptionalHeader.SectionAlignment;
    pSectionHeader->Misc.VirtualSize += extendSize;
    pSectionHeader->SizeOfRawData = (pSectionHeader->Misc.VirtualSize + fileAlignment - 1) / fileAlignment * fileAlignment;
    pe.pNTHeader->OptionalHeader.SizeOfImage = (pe.pNTHeader->OptionalHeader.SizeOfImage + extendSize + sectionAlignment - 1) / sectionAlignment * sectionAlignment;
    pe.resize(pSectionHeader->PointerToRawData + pSectionHeader->SizeOfRawData);

    assert(extendSize >= sizeof(shellcode));

	pSectionHeader = pe.pSectionHeaders.back();
    DWORD ImageBase = pe.pNTHeader->OptionalHeader.ImageBase;
    DWORD OEP = ImageBase + pe.pNTHeader->OptionalHeader.AddressOfEntryPoint;
    DWORD rva = pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize - sizeof(shellcode);
    DWORD addr = ImageBase + rva;

    *(DWORD *) (shellcode + 9) = 0x77D507EA - (addr + 13);
    *(DWORD *) (shellcode + 14) = OEP - (addr + 18);

    pSectionHeader->Characteristics |= 0x60000020;
    pe.pNTHeader->OptionalHeader.AddressOfEntryPoint = rva;
    DWORD foa = pSectionHeader->PointerToRawData + pSectionHeader->Misc.VirtualSize - sizeof(shellcode);

    memcpy(pe.buf + foa, shellcode, sizeof(shellcode));

    pe.saveToFile("C:\\Documents and Settings\\Administrator\\桌面\\notepad1.exe");

    return 0;
}

合并节

合并节的时候有一个特别坑的点:虽然节表数量减少造成 SizeOfHeaders 改变,但不能修改 SizeOfHeaders ,因为 PE 文件加载时有些结构的定位依赖 SizeOfHeaders 。

修改节表第一项的属性

由于所有节合并成一个,因此需要将节表第一项的大小属性修改成所有节合并后对应的属性,并且 Characteristics 需要是包含所有节对应属性。

    DWORD fileAlignment = pe.pNTHeader->OptionalHeader.FileAlignment;
    pe.pSectionHeaders[0]->Misc.VirtualSize = pe.pSectionHeaders.back()->VirtualAddress + pe.pSectionHeaders.back()->Misc.VirtualSize - pe.pSectionHeaders[0]->VirtualAddress;
    pe.pSectionHeaders[0]->SizeOfRawData = (pe.pSectionHeaders.back()->VirtualAddress + pe.pSectionHeaders.back()->SizeOfRawData - pe.pSectionHeaders[0]->VirtualAddress + fileAlignment - 1) / fileAlignment * fileAlignment;
    for (int i = 1; i < pe.pSectionHeaders.size(); i++) {
        pe.pSectionHeaders[0]->Characteristics |= pe.pSectionHeaders[i]->Characteristics;
    }

扩大 PE 文件大小并将所有节拉伸

    pe.resize(pe.pSectionHeaders[0]->PointerToRawData + pe.pSectionHeaders[0]->SizeOfRawData);
    //pe.pNTHeader->OptionalHeader.SizeOfHeaders = (pe.pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32) + sizeof(IMAGE_SECTION_HEADER) + fileAlignment - 1) / fileAlignment * fileAlignment;
    pe.pNTHeader->FileHeader.NumberOfSections = 1;
    for (int j = (int) pe.pSectionHeaders.size() - 1; j >= 1; j--) {
        PIMAGE_SECTION_HEADER pSectionHeader = pe.pSectionHeaders[j];
        char *buf = new char[pSectionHeader->SizeOfRawData];
        memcpy(buf, pe.buf + pSectionHeader->PointerToRawData, pSectionHeader->SizeOfRawData);
        memset(pe.buf + pSectionHeader->PointerToRawData, 0, pSectionHeader->SizeOfRawData);
        memcpy(pe.buf + pe.pSectionHeaders[0]->PointerToRawData + pSectionHeader->VirtualAddress - pe.pSectionHeaders[0]->VirtualAddress, buf, pSectionHeader->SizeOfRawData);
        delete[]buf;
    }

运行结果

PE 文件所有节合并在一起且 PE 文件可以正常运行。
在这里插入图片描述

完整代码

// PE.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include<iostream>
#include<Windows.h>
#include<fstream>
#include<vector>
#include<assert.h>
#include<algorithm>

struct PE {
    char *buf;
    DWORD size;
    PIMAGE_DOS_HEADER pDosHeader;
    PIMAGE_NT_HEADERS32 pNTHeader;
    std::vector<PIMAGE_SECTION_HEADER> pSectionHeaders;
    bool error;

    explicit PE(const std::string &path) : error(false), pDosHeader(NULL), pNTHeader(NULL) {
        std::ifstream fsRead(path.c_str(), std::ios::in | std::ios::binary);
        if (!fsRead.is_open()) {
            std::cerr << "[-] failed to open the PE file." << std::endl;
            error = true;
            return;
        }
        fsRead.seekg(0, std::ios::end);
        size = (int) fsRead.tellg();
        buf = new char[size];
        fsRead.seekg(0);
        fsRead.read(buf, size);
        init();
    }

    void init() {
        pDosHeader = (PIMAGE_DOS_HEADER) buf;
        if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
            std::cerr << "[-] File is not a PE file." << std::endl;
            error = true;
            return;
        }

        pNTHeader = (PIMAGE_NT_HEADERS32) (buf + pDosHeader->e_lfanew);
        if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
            std::cerr << "[-] File is not a PE file." << std::endl;
            error = true;
            return;
        }
        pSectionHeaders.resize(pNTHeader->FileHeader.NumberOfSections);
        for (int i = 0, p = pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32); i < pSectionHeaders.size(); i++, p += sizeof(IMAGE_SECTION_HEADER)) {
            pSectionHeaders[i] = (PIMAGE_SECTION_HEADER) (buf + p);
        }
    }

    bool saveToFile(const std::string &path) {
        std::ofstream fsWrite(path.c_str(), std::ios::out | std::ios::binary);
        if (!fsWrite.is_open()) {
            std::cerr << "[-] failed to open the dump file." << std::endl;
            return false;
        }
        fsWrite.write(buf, size);
        fsWrite.close();
        return true;
    }

    void resize(DWORD _size) {
        char *tbuf = new char[_size];
        memset(tbuf, 0, _size);
        memcpy(tbuf, this->buf, min(_size, this->size));
        delete[]this->buf;
        this->buf = tbuf;
        this->size = _size;
        init();
    }

    bool isError() const {
        return error;
    }

    ~PE() {
        delete[]buf;
    }
};

int main() {
    PE pe("C:\\Documents and Settings\\Administrator\\桌面\\notepad.exe");

    if (pe.isError()) {
        std::cerr << "[-] Failed to load PE." << std::endl;
        return 0;
    }

    assert(pe.pNTHeader->FileHeader.NumberOfSections > 1);

    DWORD fileAlignment = pe.pNTHeader->OptionalHeader.FileAlignment;
    pe.pSectionHeaders[0]->Misc.VirtualSize = pe.pSectionHeaders.back()->VirtualAddress + pe.pSectionHeaders.back()->Misc.VirtualSize - pe.pSectionHeaders[0]->VirtualAddress;
    pe.pSectionHeaders[0]->SizeOfRawData = (pe.pSectionHeaders.back()->VirtualAddress + pe.pSectionHeaders.back()->SizeOfRawData - pe.pSectionHeaders[0]->VirtualAddress + fileAlignment - 1) / fileAlignment * fileAlignment;
    for (int i = 1; i < pe.pSectionHeaders.size(); i++) {
        pe.pSectionHeaders[0]->Characteristics |= pe.pSectionHeaders[i]->Characteristics;
    }

    pe.resize(pe.pSectionHeaders[0]->PointerToRawData + pe.pSectionHeaders[0]->SizeOfRawData);
    //pe.pNTHeader->OptionalHeader.SizeOfHeaders = (pe.pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32) + sizeof(IMAGE_SECTION_HEADER) + fileAlignment - 1) / fileAlignment * fileAlignment;
    pe.pNTHeader->FileHeader.NumberOfSections = 1;
    for (int j = (int) pe.pSectionHeaders.size() - 1; j >= 1; j--) {
        PIMAGE_SECTION_HEADER pSectionHeader = pe.pSectionHeaders[j];
        char *buf = new char[pSectionHeader->SizeOfRawData];
        memcpy(buf, pe.buf + pSectionHeader->PointerToRawData, pSectionHeader->SizeOfRawData);
        memset(pe.buf + pSectionHeader->PointerToRawData, 0, pSectionHeader->SizeOfRawData);
        memcpy(pe.buf + pe.pSectionHeaders[0]->PointerToRawData + pSectionHeader->VirtualAddress - pe.pSectionHeaders[0]->VirtualAddress, buf, pSectionHeader->SizeOfRawData);
        delete[]buf;
    }

    memset(pe.pSectionHeaders[1], 0, sizeof(IMAGE_SECTION_HEADER));

    pe.saveToFile("C:\\Documents and Settings\\Administrator\\桌面\\notepad1.exe");

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/20442.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ORB-SLAM2 ---- Initializer::ReconstructF函数

目录 1.函数作用 2.函数解析 2.1 调用函数解析 2.2 Initializer::ReconstructF函数总体思路 2.2.1 代码 2.2.2 总体思路解析 2.2.3 根据基础矩阵和相机的内参数矩阵计算本质矩阵 2.2.4 从本质矩阵求解两个R解和两个t解&#xff0c;共四组解 2.2.5 分别验证求解的4种…

准备面试题【面试】

前言 写作于 2022-11-13 19:27:08 发布于 2022-11-20 16:34:44 准备 程序员囧辉 我要进大厂 面试阿里&#xff0c;HashMap 这一篇就够了 Java 基础高频面试题&#xff08;2022年最新版&#xff09; 问遍了身边的面试官朋友&#xff0c;我整理出这份 Java 集合高频面试题…

【mysql】mysql 数据备份与恢复使用详解

一、前言 对一个运行中的线上系统来说&#xff0c;定期对数据库进行备份是非常重要的&#xff0c;备份不仅可以确保数据的局部完整性&#xff0c;一定程度上也为数据安全性提供了保障&#xff0c;设想如果某种极端的场景下&#xff0c;比如磁盘损坏导致某个时间段数据丢失&…

什么是Spring,Spring的核心和设计思想你了解吗?

目录 1.初识Spring 1.1 什么是容器 1.2 什么是IoC 2.什么是IoC容器. 2.1 什么是DI 哈喽呀,你好呀,欢迎呀,快来看一下这篇宝藏博客吧~~~ 1.初识Spring Srping指的是Spring Framework(Spring 框架).我们经常会听见框架二字,其中java中最最主流的框架当属Spring.Spring是一…

SAP S4 FI后台详细配置教程- PART4 (科目及税费相关配置篇)

目录 1、总帐科目 1.1编辑科目表清单 1.2 科目表分配给公司代码 1.3 定义科目组 1.4 定义留存收益科目 2、销售/购置税 2.1 维护销售/购置税务代码税率 2.2 配置销项/销项税会计科目 大家好本篇是&#xff1a;SAP S4 FI后台详细配置教程- PART4 &#xff08;科目及税…

Fiddler的安装和使用

文章目录1、Fiddler的安装2、Fiddler的使用3、抓包工具的原理1、Fiddler的安装 官网链接&#xff1a;https://www.telerik.com/fiddler 进入官网首页 页面跳转后 2、Fiddler的使用 下载好后直接安装&#xff0c;安装后打开&#xff0c;它就会自动抓取HTTP包&#xff0c;在左…

[附源码]SSM计算机毕业设计-东湖社区志愿者管理平台JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

力扣113题引发的关于DFS和回溯的一点思考

最近刚学回溯和DFS&#xff0c;刷力扣遇到一道题&#xff08;113题&#xff09;&#xff0c;如下&#xff1a; 我们不细究回溯和DFS的区别联系。关于这道题的2种写法&#xff0c;我把第一种称为回溯。 class Solution {List<List<Integer>> res new LinkedList&l…

29.Nacos的简介与安装(springcloud)

1.Nacos 简介官网&#xff1a; https://nacos.io/zh-cn/Nacos 致力于发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集&#xff0c;帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。 Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是…

【Android Studio Gradle】发布aar到私有Artifactory仓库

1. 前言 在【Android Studio Gradle】使用Artifactory构建本地仓库中介绍了如何利用工具配置一个maven私有库&#xff0c;那么在开发library的时候为了方便难免会用到需要将该库发布到这个仓库的功能。经过测试和配置&#xff0c;确实在Artifactory仓库中也可以通过gradlew命令…

【MySQL基础】为什么大部分人选择使用MySQL数据库?

目录 一、为什么大部分人选择使用MySQL数据库&#xff1f; 二、MySQL简介 1.MySQL介绍 2.MySQL的特点 3. MySQL的版本 从用户的角度&#xff0c;针对不同的用户 从单纯的版本数字区分 &#x1f49f; 创作不易&#xff0c;不妨点赞&#x1f49a;评论❤️收藏&#x1f49…

我把皮小浪の的 蓝色妖姬系列做进了java窗口

— &#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏 unity实战入门 ⭐效果图如下 ⭐⭐涉及的相关类的包含关系图 ⭐# 视频入口&#xff1a;请点击 文章目录一…

比 O(nlog(n)) 做得更好——2.改变问题以及排序和填充数组

改变问题&#xff0c;以及对键进行排序和填充数组。 目录 【第1篇】比 O(nlog(n)) 做得更好——1.创造合适的条件 长按关注《Python学研大本营》&#xff0c;加入读者群&#xff0c;分享更多精彩 扫码关注《Python学研大本营》&#xff0c;加入读者群&#xff0c;分享更多精彩 …

【Redis-04】Redis两种持久化方式

Redis是基于内存的数据结构服务器&#xff0c;保存了大量的键值对数据&#xff0c;所以持久化到磁盘是非常必要的&#xff0c;Redis提供了两种持久化的方式&#xff0c;分别是RDB和AOF。下面我们看下这两种持久化方式的具体实现原理。 1.RDB持久化 首先&#xff0c;RDB持久化方…

【Spring(二)】java对象属性的配置(Bean的配置)

有关Spring的所有文章都收录于我的专栏&#xff1a;&#x1f449;Spring&#x1f448; 目录 一、前言 二、通过Setter方法配置Bean 三、通过构造器配置Bean   1. 通过属性名配置   2. 通过索引配置   3. 通过属性类型配置 四、通过p命名空间配置bean 五、引用/注入其他bean…

openEuler快速入门-openEuler系统安装openGauss数据库安装

文章目录前言一、安装openEuler系统安装二、运行虚拟机&#xff0c;配置三、安装openGauss数据库总结前言 openEuler&#xff1a;openEuler 是一款开源操作系统。当前 openEuler 内核源于 Linux&#xff0c;支持鲲鹏及其它多种处理器&#xff0c;能够充分释放计算芯片的潜能&a…

[附源码]java毕业设计网络身份认证技术及方法

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

_001_Zotero入门

Zetoro大纲一、安装Zotero二、收集题录2.1 浏览器插件2.2 通过标识符添加2.3 拖拽文献2.4 从剪切板导入2.5 批量导入2.6 手动添加&#xff08;不建议使用&#xff09;2.7 方法总结三、管理题录3.1 移动分类3.2 查重3.3 关联3.4 标签3.5 笔记3.6 RSS订阅3.7 快捷键3.8 总结四、在…

Verilog 时序逻辑 UDP

时序逻辑 UDP 与组合逻辑 UDP 在定义形式和行为功能上均有不同&#xff0c;主要区别如下&#xff1a; 1、时序逻辑 UDP 的输出端必须声明为 reg 型。2、时序逻辑 UDP 可以用 initial 语句初始化。3、状态表格式也稍有不同&#xff1a; ... : <current_state> : &l…

RabbitMQ初步到精通-第七章-RabbitMQ之延迟队列

目录 第七章-RabbitMQ之延迟队列 1. 延迟队列概念 2. 应用场景 3. 架构模式 3.1 队列TTL实现 3.2 消息TTL实现 3.3 插件实现 4. 代码验证 5. 总结 第七章-RabbitMQ之延迟队列 1. 延迟队列概念 延迟-意即 非实时&#xff0c;之前我们讨论大部分的案例都是生产者将消息发…