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


    空白区添加代码

    在 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
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    其中 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    修复 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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    修改节的属性以及 OEP

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

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

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

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

    运行结果

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

    完整代码

    // PE.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include
    #include
    #include
    #include 
    #include 
    #include
    
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122

    新增节

    在 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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在节表末尾添加一项,并将后一项填充 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));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    更新 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++;
    
    • 1
    • 2
    • 3

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

        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");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    运行结果

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

    完整代码

    // PE.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include
    #include
    #include
    #include
    #include
    #include
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161

    扩大节

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

        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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在扩大的节中写入 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");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    运行结果

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

    完整代码

    // PE.cpp : Defines the entry point for the console application.
    //
    #include "stdafx.h"
    #include
    #include
    #include
    #include
    #include
    #include
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131

    合并节

    合并节的时候有一个特别坑的点:虽然节表数量减少造成 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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    扩大 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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    运行结果

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

    完整代码

    // PE.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include
    #include
    #include
    #include
    #include
    #include
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
  • 相关阅读:
    Spring Cloud Alibaba —— 高可用流量控制组件
    2022-09-11 周工作记录
    【HDLBits 刷题 14】Verification Reading Simulations
    ansible下playbook安装httpd
    springboot+vue+elementUI 高校学生实习管理管理系统 #毕业设计
    关于入门深度学习mnist数据集前向计算的记录
    应急启动电源+充气一体式方案设计
    log4j2
    内核网络协议栈传输层协议框架
    【金万维】使用天联高级版登录用友U8+,进行凭证打印操作。
  • 原文地址:https://blog.csdn.net/qq_45323960/article/details/127938327