5.PE——使用代码在任意节空白区添加shellcode

继上一篇手动添加shellcode,这篇以代码来实现,要思路的话还请去上一篇看,这篇为纯代码

main代码:

#pragma once
#include "FileUtil.h"
#include "ImageUtil.h"

int main()
{
    unsigned int size;
    char* buf = ReadFile("D:\\project\\cpp\\test.exe", &size);
    // 检查是否读取成功
    if (buf == nullptr)
    {
        printf("read file error\n");
        return -1;
    }
    // 检查是否是PE文件
    if (!IsPE(buf))
    {
        printf("not a PE file\n");
        return -1;
    }
    char* memoryBuf = ImageStretch(buf);
    delete[] buf;
    buf = nullptr;
    char shellCode[18] = { 0x6A, 0x00, 0x6A, 0x00, 0x6A ,0x00 ,0x6A ,0x00, 
        0xE8, 0x00, 0x00, 0x00, 0x00, 
        0xE9, 0x00, 0x00, 0x00, 0x00 };
    InjectShellCode(memoryBuf, shellCode, 18, 2);
    DWORD PhysicalSize = CalculateDiskSize(memoryBuf);
    char* PhysicalBuf = ImageCompress(memoryBuf, PhysicalSize);
    delete[] memoryBuf;
    memoryBuf = nullptr;
    WriteFile("D:\\project\\cpp\\test1.exe", PhysicalBuf, PhysicalSize);
    delete[] PhysicalBuf;
    PhysicalBuf = nullptr;
    return 0;
}

FileUtil.h

#pragma once
#include 

char* ReadFile(const char* path, unsigned int* size);

bool WriteFile(const char* path, char* buf, unsigned int size);

FileUtil.cpp

#include "FileUtil.h"

/*
 * 读取文件
 * @param path 文件路径
 * @param size 要返回的文件大小指针
 * @return 文件内容
 */
char* ReadFile(const char* path, unsigned int* size)
{
    FILE* file;
    errno_t err = fopen_s(&file, path, "rb");
    if (err != 0)
    {
        return nullptr;
    }
    // 将文件指针移动到文件末尾
    fseek(file, 0, SEEK_END);
    *size = ftell(file);
    if (*size == -1) {
        perror("Failed to get file length");
        fclose(file);
        return nullptr;
    }
    // 将文件指针移动到文件开头
    fseek(file, 0, SEEK_SET);
    char* buf = new char[*size];
    size_t bytesRead = fread(buf, 1, *size, file);
    if (bytesRead != *size)
    {
        perror("Failed to read file");
        fclose(file);
        return nullptr;
    }
    // 关闭文件
    fclose(file);
    file = nullptr;
    return buf;
}

/*
 * 写入文件
 * @param path 文件路径
 * @param buf 文件内容
 * @param size 文件大小
 * @return 是否写入成功
 */
bool WriteFile(const char* path, char* buf, unsigned int size)
{
    FILE* writeFile;
    errno_t err = fopen_s(&writeFile, path, "wb+");
    if (err != 0)
    {
        return false;
    }
    fwrite(buf, 1, size, writeFile);
    //delete[] buf;
    //buf = nullptr;
    // 关闭文件
    fclose(writeFile);
    writeFile = nullptr;
    return true;
}

ImageUtil.h

#pragma once
#include 
#include 
#include 
#include "StringUtil.h"

bool IsPE(char* buff);

char* ImageStretch(char* buff);

bool InjectShellCode(char* buff, char* shellcode, DWORD injectSize, DWORD sectionHeaderIndex);

DWORD CalculateDiskSize(char* buff);

char* ImageCompress(char* buff, DWORD size);

ImageUtil.cpp

#include "ImageUtil.h"

/*
 * 判断是否为PE文件
 */
bool IsPE(char* buff)
{
    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)buff;
    // 判断开头是否为MZ
    if (IMAGE_DOS_SIGNATURE != dosHeader->e_magic)
    {
        return false;
    }
    
    // 64位
    /*if (IMAGE_NT_OPTIONAL_HDR64_MAGIC)
    {
        PIMAGE_NT_HEADERS64 ntHeader = (PIMAGE_NT_HEADERS64)((DWORD64)dosHeader + dosHeader->e_lfanew);
        return ntHeader->Signature == IMAGE_NT_SIGNATURE;
    }*/
    // 32位
    PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)(buff + dosHeader->e_lfanew);
    return ntHeader->Signature == IMAGE_NT_SIGNATURE;
}

/*
 * 拉伸
 * 
 * buff 未拉伸状态的文件数据
 * return 拉伸后的文件数据
 */
char* ImageStretch(char* buff)
{
    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)buff;
    PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)(buff + dosHeader->e_lfanew);
    // 申请内存
    char* memoryBuf = new char[ntHeader->OptionalHeader.SizeOfImage];
    memset(memoryBuf, 0, ntHeader->OptionalHeader.SizeOfImage);
    // 拷贝头
    memcpy(memoryBuf, buff, ntHeader->OptionalHeader.SizeOfHeaders);
    // 拷贝节表
    int optionalHeaderOffset = dosHeader->e_lfanew + sizeof(ntHeader->Signature) + sizeof(ntHeader->FileHeader) + ntHeader->FileHeader.SizeOfOptionalHeader;
    PIMAGE_SECTION_HEADER sectionHeader = (PIMAGE_SECTION_HEADER)(buff + optionalHeaderOffset);
    for (size_t i = 0; i < ntHeader->FileHeader.NumberOfSections; i++)
    {
        DWORD VirtualAddress = sectionHeader[i].VirtualAddress;
        char* section = buff + sectionHeader[i].PointerToRawData;
        memcpy(memoryBuf + VirtualAddress, section, sectionHeader[i].SizeOfRawData);
        int a = 0;
    }
    return memoryBuf;
}

/*
 * 注入shellcode
 * 
 * buff 拉伸状态的文件数据
 * shellcode shellcode数据
 * injectSize shellcode数据大小
 * sectionHeaderIndex 节表索引
 * return true 注入成功 false 注入失败
 */
bool InjectShellCode(char* buff, char* shellcode, DWORD injectSize, DWORD sectionHeaderIndex)
{
    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)buff;
    PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)(buff + dosHeader->e_lfanew);
    // 判断在节表是否超出
    if (sectionHeaderIndex >= ntHeader->FileHeader.NumberOfSections)
    {
        printf("[!] InjectShellCode: sectionHeaderIndex is out of range\n");
        return false;
    }
    // 判断这个节剩余空间是否足够注入该shellcode
    int optionalHeaderOffset = dosHeader->e_lfanew + sizeof(ntHeader->Signature) + sizeof(ntHeader->FileHeader) + ntHeader->FileHeader.SizeOfOptionalHeader;
    PIMAGE_SECTION_HEADER firstHeader = (PIMAGE_SECTION_HEADER)(buff + optionalHeaderOffset);
    PIMAGE_SECTION_HEADER sectionHeader = firstHeader + sectionHeaderIndex;
    if (sectionHeader->SizeOfRawData - sectionHeader->Misc.VirtualSize < injectSize)
    {
        printf("[!] InjectShellCode: sectionHeader[sectionHeaderIndex].SizeOfRawData < injectSize\n");
        return false;
    }
    // 计算要插入字节的位置
    DWORD injectOffset = sectionHeader->VirtualAddress + sectionHeader->Misc.VirtualSize;
    
    // 插入shellcode
    memcpy(buff + injectOffset, shellcode, injectSize);

    // 修复E8后面的字节 这里只以call MessageBoxA为例
    DWORD msgBoxAdr = 0x771E8380;
    DWORD E8Address = injectOffset + 8;
    // 计算公式 msgBoxAdr = ImageBase + E8Address + 5 + E8FixValue; => E8FixValue = msgBoxAdr - ImageBase - E8Address - 5;
    DWORD E8FixValue = msgBoxAdr - ntHeader->OptionalHeader.ImageBase - E8Address - 5;
    *(LPDWORD)(buff + E8Address + 1) = E8FixValue;
    // 修复E9后面的字节 ImageBase + AddressOfEntryPoint = ImageBase + E9Address + 5 + E9FixValue; => E9FixValue = AddressOfEntryPoint - E9Address - 5;
    DWORD E9Address = E8Address + 5;
    DWORD E9FixValue = ntHeader->OptionalHeader.AddressOfEntryPoint - E9Address - 5;
    *(LPDWORD)(buff + E9Address + 1) = E9FixValue;
    // 如果不是代码节,赋予权限
    char textName[] = ".text";
    for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++)
    {
        PIMAGE_SECTION_HEADER tempHeader = firstHeader + i;
        char* sectionName = (char*)tempHeader->Name;
        if (StringCmp(textName, sectionName))
        {
            if (sectionHeaderIndex != i)
            {
                sectionHeader->Characteristics |= tempHeader->Characteristics;
            }
        }
    }

    // 修复入口点
    ntHeader->OptionalHeader.AddressOfEntryPoint = injectOffset;
}

/*
 * 计算压缩的磁盘大小
 * buff 拉伸状态的文件数据
 * return 磁盘文件大小
 */
DWORD CalculateDiskSize(char* buff) {
    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)buff;
    PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(buff + dosHeader->e_lfanew);
    // 获取节表指针
    int optionalHeaderOffset = dosHeader->e_lfanew + sizeof(ntHeaders->Signature) + sizeof(ntHeaders->FileHeader) + ntHeaders->FileHeader.SizeOfOptionalHeader;
    PIMAGE_SECTION_HEADER sectionHeader = (PIMAGE_SECTION_HEADER)(buff + optionalHeaderOffset);
    int index = ntHeaders->FileHeader.NumberOfSections - 1;
    // 获取最后一个节表的指针和数据大小
    DWORD PointerToRawData = sectionHeader[index].PointerToRawData;
    DWORD SizeOfRawData = sectionHeader[index].SizeOfRawData;
    return PointerToRawData + SizeOfRawData;
}

/*
 * 压缩
 * buff 拉伸状态的文件数据
 * size 磁盘文件大小
 * return 压缩状态的文件数据
 */
char* ImageCompress(char* buff, DWORD size)
{
    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)buff;
    PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)(buff + dosHeader->e_lfanew);

    // 申请内存
    char* PhysicalBuf = new char[size];
    memset(PhysicalBuf, 0, size);
    // 拷贝头
    memcpy(PhysicalBuf, buff, ntHeader->OptionalHeader.SizeOfHeaders);
    // 拷贝节表
    int optionalHeaderOffset = dosHeader->e_lfanew + sizeof(ntHeader->Signature) + sizeof(ntHeader->FileHeader) + ntHeader->FileHeader.SizeOfOptionalHeader;
    PIMAGE_SECTION_HEADER sectionHeader = (PIMAGE_SECTION_HEADER)(buff + optionalHeaderOffset);
    for (size_t i = 0; i < ntHeader->FileHeader.NumberOfSections; i++)
    {
        DWORD PointerToRawData = sectionHeader[i].PointerToRawData;
        memcpy(PhysicalBuf + PointerToRawData, buff + sectionHeader[i].VirtualAddress, sectionHeader[i].SizeOfRawData);
    }
    return PhysicalBuf;
}

StringUtil.h

#pragma once

int StringSize(const char* str);

bool StringCmp(char* str1, char* str2);

StringUtil.cpp

#include "StringUtil.h"

int StringSize(const char* str)
{
	int len = 0;
    while (*str)
    {
        len++;
        str++;
    }
    return len;
}

bool StringCmp(char* str1, char* str2)
{
	int len1 = StringSize(str1);
    int len2 = StringSize(str2);
	if (len1 != len2) return false;
    int i = 0;
    while (str1[i] == str2[i])
    {
        i++;
    }
    return len1 == (i-1);
}

你可能感兴趣的:(PE文件结构,windows)