首页
社区
课程
招聘
[原创]PE文件编辑工具
发表于: 2007-5-12 17:48 13116

[原创]PE文件编辑工具

2007-5-12 17:48
13116

//
//作者: 神杀中龙 microsoftxiao@163.com
//说明: 根据PE文件格式原理书写
//1. 在PE信息浏览器基础上改为增加区段工具 2007-5-12
// 参考的书籍来自看雪, 写出的东西也应回到看雪。 取之于雪,用之于雪。

#include <windows.h>
#include <iostream>
#include <sys/stat.h>
#include <ctime>
using namespace std;

#define SAFE_DELETE(p) { if((p)!=NULL) { delete (p); (p) = NULL; }}

bool InsertSection(BYTE *decBuf, struct _stat& st, char** argv);   // 插入区块
bool FillZeroArea(FILE* decFile, ULONG VirtualSize, ULONG VirtualAddress);                       // 填充全0区域
bool HelpInfo(char** argv);

struct _stat ST;
FILE* viewDecFile = NULL;
bool  g_bHaveSetVSizeaAddress = false;          // 是否设置插入段大小和地址

// Entry
int main(int argc, char** argv)
{
        HelpInfo(argv);
        srand(time(NULL));
        if(argc < 2) {
                printf("Welcome to use  欢迎使用 ^_^ 神杀中龙 microsoftxiao@163.com\n");
               
        } else {
                if(argv[2])
                {
                        printf("Have VSize and VAddress\n");
                        g_bHaveSetVSizeaAddress = true;
                } else {
                        printf("Not set VSize and VAddress(VSize默认大小4KB(0x1000)\n");
                        g_bHaveSetVSizeaAddress = false;
                }
                viewDecFile = fopen(argv[1], "rb");
                BYTE *buf = NULL;
               
        ::_stat(argv[1], &ST);
                buf = new BYTE[ST.st_size];
        fread(buf, 1, ST.st_size, viewDecFile);
                fclose(viewDecFile);
                InsertSection(buf,  ST, argv);
        
                SAFE_DELETE(buf);
        }
        return 0;
}

// 处理对齐
int GetMultiple4KB(int param_Any)
{
        int tmp4KBNum = param_Any / 0x1000;
        int tmpNeatNum = tmp4KBNum * 0x1000;
        return tmpNeatNum;
}

// 插入区块
bool InsertSection(BYTE *decBuf, struct _stat& st, char** argv)
{
        cout<<"计算区块各参数:";
        // 插入区块, 先获取OEP
        const int offDOSStub = 0x3C;
        long      off_elfanew = 0;   // 根据类型直接读出即可。
    char  szPEHead[5];           // PE00
        WORD  Machine = 0;           // CPU类型   
        DWORD AddressOfEntryPoint = 0; // 程序执行入口RVA
        DWORD ImageBase = 0;         // 程序默认装入基址
    WORD  SubSystem = 0;         // 子系统, 控制台或其他
    DWORD SizeOfImage = 0;       // 内存中整个PE映像大小
    WORD  SizeOfOptionalHeader = 0; // 可选映像头大小
    DWORD SectionTableHeaderValue = 0;  // 块表首值  PE(Base) + 0x12 + SizeOfOptionalHeader
    WORD NumberOfSections        = 0;  // 块(Section)个数
        DWORD SectionsLength          = 0;  // 块长度

       
   
        memcpy(&off_elfanew, &decBuf[offDOSStub], sizeof(long));
        //fwrite(&off_elfanew, 1, sizeof(off_elfanew), test);
        memcpy(szPEHead, &decBuf[off_elfanew], sizeof(szPEHead));
        //fwrite(szPEHead, 1, sizeof(szPEHead)-1, test);
        memcpy(&Machine, &decBuf[off_elfanew + 0x4], sizeof(WORD));
        if(Machine == 0x14C)
          printf("CPU Intel i386或以上系列: %X\n", Machine);
    memcpy(&AddressOfEntryPoint, &decBuf[off_elfanew + 0x28], sizeof(DWORD));
        printf("程序入口点: 0x0%X\n", AddressOfEntryPoint);
    memcpy(&ImageBase, &decBuf[off_elfanew + 0x34], sizeof(DWORD));
        printf("镜像基址: 0x0%X\n", ImageBase);
    memcpy(&SubSystem, &decBuf[off_elfanew + 0x5C], sizeof(WORD));
        switch(SubSystem)
        {
        case 0:
                printf("未知子系统\n");
                break;
        case 1:
        printf("不需要子系统: 0x0%X\n", SubSystem);
                break;
        case 2:
                printf("图形接口子系统(GUI): 0x0%X\n", SubSystem);
                break;
        case 3:
                printf("控制台子系统(Console or DOS or CUI): 0x0%X\n", SubSystem);
                break;
        case 5:
                printf("OS/2字符子系统: 0x0%X\n", SubSystem);
                break;
        case 7:
                printf("POSIX字符子系统: 0x0%X\n", SubSystem);
                break;
       
        default:
                printf("未知子系统\n");
                break;
        }
        memcpy(&SizeOfImage, &decBuf[off_elfanew + 0x50], sizeof(DWORD));
        printf("内存镜像大小: 0x%X\n", SizeOfImage);
    memcpy(&SizeOfOptionalHeader, &decBuf[off_elfanew + 0x14], sizeof(WORD));
        printf("可选映像头大小: %d字节(%X)\n", SizeOfOptionalHeader,SizeOfOptionalHeader);
    memcpy(&SectionTableHeaderValue, &decBuf[off_elfanew + 0x18 + SizeOfOptionalHeader], sizeof(DWORD));
        printf("块表首值: %X\n", SectionTableHeaderValue);
    memcpy(&NumberOfSections, &decBuf[off_elfanew + 0x06], sizeof(WORD));
        printf("块(Sections)个数: %X\n", NumberOfSections);
   
        IMAGE_SECTION_HEADER  tISH;   // 块表结构实例
        ZeroMemory(&tISH, sizeof(tISH));
        printf("块表项结构大小: %d字节\n", sizeof(tISH));
        int t_totalSectionLength = sizeof(tISH) * NumberOfSections;
    printf("总块表长度: %d字节(%X)\n", t_totalSectionLength, t_totalSectionLength);
    int t_InsertPosition = 0x18 + SizeOfOptionalHeader + t_totalSectionLength;
        printf("插块表项偏移位置: 0x0%X\n", t_InsertPosition);
   

        char szNewFileName[260];
        sprintf(szNewFileName, "__666%d_.exe",rand()%10000);
        FILE* test = fopen(szNewFileName, "wb");
        fwrite(decBuf, 1, ST.st_size, test);
       
        // 加区块
        if(test) {
          strncpy((char*)tISH.Name, ".pediy", sizeof(tISH.Name));
            
             // 这里要注意对齐, 取4KB的倍数, 否则可能出问题。
             // 我写了个小函数来处理对齐, GetMultiple4KB()
             // 这里只要对tISH.PointerToRawData做对齐就行了
             int tmpTrueAddress = GetMultiple4KB(st.st_size);
          tISH.Misc.VirtualSize = 0x3E000;          // 4KB - 这里根据需要调整, 一般默认4KB, 显示0x3E000是我粗心忘记改了, 4KB应该是0x1000
          tISH.SizeOfRawData = 0x3E000;             //
          tISH.VirtualAddress = st.st_size;          // 此处应该修改为内存映像的大小改地址应该为增加的一个空闲区  应该是先加区段,然后再改入口点 此地址作为最后的偏移量
          tISH.PointerToRawData = tmpTrueAddress;             // 行号表中行号的数目
          if(g_bHaveSetVSizeaAddress)
          {
                        tISH.Misc.VirtualSize = atoi(argv[2]);          // 4KB
                        tISH.SizeOfRawData = atoi(argv[2]);
          }
          tISH.PointerToRelocations = 0;         // 在OBJ文件中使用, 重定位的偏移
          tISH.PointerToLinenumbers = 0;         // 行号表的偏移(供调试用)
          tISH.NumberOfRelocations = 0;          // 在OBJ文件中使用,重定位项目数
          tISH.NumberOfLinenumbers = 0;
          tISH.Characteristics = 0xE0000020;     // 块属性, 表示包含执行代码、可读写并可执行。

          fseek(test, 0, SEEK_SET);
          
      fseek(test, off_elfanew + t_InsertPosition, SEEK_SET);
          
          fwrite(&tISH, 1, sizeof(tISH), test);   // 写入块结构
          fseek(test, off_elfanew + 0x06, SEEK_SET);
          NumberOfSections++;
          fwrite(&NumberOfSections, 1, sizeof(NumberOfSections), test);   // 修改块数目

          SizeOfImage += tISH.SizeOfRawData;     // 对齐后的大小?
          fseek(test, off_elfanew + 0x50, SEEK_SET);  // 定位
          fwrite(&SizeOfImage, 1, sizeof(SizeOfImage), test);

          FillZeroArea(test, tISH.Misc.VirtualSize, tISH.VirtualAddress);          //在指定地址后写零
        }
       
       
        fclose(test);
       
        remove(argv[1]);
        rename(szNewFileName, argv[1]);
        return 0;
}

// 填充全零区
bool FillZeroArea(FILE* decFile, ULONG VirtualSize, ULONG VirtualAddress)
{
    fseek(decFile, VirtualAddress, SEEK_SET);
        BYTE *szBuf = NULL;
        szBuf = new BYTE[VirtualSize];
        strncpy((char*)szBuf, "Z", VirtualSize);
        fwrite(szBuf, 1, VirtualSize, decFile);
        BYTE tmp = 0;
        fseek(decFile, VirtualAddress, SEEK_SET);
        fwrite(&tmp, 1, sizeof(tmp), decFile);
        SAFE_DELETE(szBuf);

        return 0;
}

bool HelpInfo(char** argv)
{
        printf("帮助\n");
        printf("%s 目标PE文件(destination) 新加块大小(VirtualSize) 新加块偏移地址(VirtualAddress)\n", argv[0]);
        return 0;
}

现这里有个错误要修正, 方法见下面:
strncpy((char*)tISH.Name, ".pediy", sizeof(tISH.Name));
    tISH.Misc.VirtualSize = 0x1000;           // 4KB
    tISH.SizeOfRawData = 0x1000;
    tISH.VirtualAddress = SizeOfImage;           // 改地址应该为增加的一个空闲区   应该是先加区段,然后再改入口点 此地址作为最后的偏移量
    tISH.PointerToRawData = st.st_size;              // 行号表中行号的数目
    if(g_bHaveSetVSizeaAddress)
    {
    tISH.Misc.VirtualSize = atoi(argv[2]);           // 4KB
    tISH.SizeOfRawData = atoi(argv[2]);
    }
    tISH.PointerToRelocations = 0;          // 在OBJ文件中使用, 重定位的偏移
    tISH.PointerToLinenumbers = 0;          // 行号表的偏移(供调试用)
    tISH.NumberOfRelocations = 0;           // 在OBJ文件中使用,重定位项目数
    tISH.NumberOfLinenumbers = 0;
    tISH.Characteristics = 0xE0000020;      // 块属性, 表示包含执行代码、可读写并可

加区段工具, 在设置    VirtualAddress 实际上是该区段在内存中的偏移量, 所以, 这个地址只要是内存映像大小即可。

而文件偏移地址 PointerToRawData 则为文件的大小。

这也是为什么有的时候这两个值不一样的原因, 尽管内存映像的各区块大概 和磁盘文件是一一对应的。但是 实际上映射的地址 还是有偏移的。所以 有时当VirtualAddress 和 PointerToRawData相同时则可以执行, 有的时候相同时反而出错, 就是由于 装载地址不一致导致的, 也就是一致的地址,

那个地址值原来可能有数据了, 若再装载就等于要覆盖那个地址的数据。 既然在磁盘上区块是添加在文件尾, 那在内存中当然也就是内存映像尾恩。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (11)
雪    币: 47147
活跃值: (20460)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
建议有时间可以增加对64位PE32+的支持 ;)
2007-5-12 18:28
0
雪    币: 264
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
谢谢兄弟的软件,不错,,
2007-5-12 19:03
0
雪    币: 39
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
提个建议就是 C++里 new 的一个数组 最好也删除用 delete [],当然在这里是new的BYTE,delete时不会出问题:)。
2007-5-12 19:53
0
雪    币: 28
活跃值: (10)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
5
恩, 多谢楼上和看雪老大建议, 只是暂时太穷, 还只能在32位的系统上做程序。
2007-5-12 21:26
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
优秀啊!呵呵!
2007-6-26 17:21
0
雪    币: 241
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
谢谢楼主,支持一下。
2010-1-15 12:52
0
雪    币: 1
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
sadfsdfsdfsd
2010-1-15 13:34
0
雪    币: 324
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
很不错的工具,收藏,谢谢!
2010-1-15 14:35
0
雪    币: 227
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
很不错,值得学习啊!
2010-2-11 12:05
0
雪    币: 234
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
谢谢。
看不很懂。
收藏了。
2010-2-18 15:10
0
雪    币: 38
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
先收藏了,谢谢!
2010-3-12 19:09
0
游客
登录 | 注册 方可回帖
返回
//