首页
社区
课程
招聘
[求助]PE文件加区段
发表于: 2008-11-2 13:23 6617

[求助]PE文件加区段

2008-11-2 13:23
6617
#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;                                                                //_stat结构体可以得到文件的状态
FILE* viewDecFile = NULL;                                                //文件指针
bool  g_bHaveSetVSizeaAddress = false;          // 是否设置插入段大小和地址

// Entry
int main(int argc, char** argv)
{
        HelpInfo(argv);                                                                //显示帮助
        srand(time(NULL));                                                        //我一直没看懂这里是干什么的
        if(argc < 2)                                                                //参数个数若小于2个则显示返回
        {
                printf("Welcome to use  欢迎使用 ^_^ 神杀中龙 microsoftxiao@163.com\n");
        }
        else
        {
                if(argv[2])                                                                //是否指定加入段大小和地址
                {
                        printf("Have VSize and VAddress\n");
                    g_bHaveSetVSizeaAddress = true;
                }
                else                                                                        //若没指定则大小默认为4kB,地址为在最

后加
                {
                    printf("Not set VSize and VAddress(VSize默认大小4KB(0x1000)\n");
                        g_bHaveSetVSizeaAddress = false;
                }
               
                if(NULL==(viewDecFile = fopen(argv[1], "rb"))) //打开文件,rb表示以二进制打开只读
                {
                        printf("open file failed!");
                        return 0;
                }
            BYTE *buf = NULL;                                                                //定义缓冲区指针为空
  
        ::_stat(argv[1], &ST);                                                        //用_stat()获取文件状态存在ST中
                buf = new BYTE[ST.st_size];                                                //缓冲区大小取文件的大小
        fread(buf, 1, ST.st_size, viewDecFile);         //把文件读入缓冲区中,size为什么为1
                fclose(viewDecFile);                                                        //关闭原文件
            InsertSection(buf,  ST, argv);                                        //调用插入区段函数
        
                SAFE_DELETE(buf);                                                                //删除缓冲区,避免内存泄露
        }
        return 0;
}

// 插入区块
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));        //elfanew在dostub的0x3C处,读出拷入elfanew
        //fwrite(&off_elfanew, 1, sizeof(off_elfanew), test);
        memcpy(szPEHead, &decBuf[off_elfanew], sizeof(szPEHead));   //elfanew指向PE头signature处
        //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));  //先全0
        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));    //新区块的名字.pediy
                tISH.Misc.VirtualSize = 0x3E000;          // 4KB
                tISH.SizeOfRawData = 0x3E000;
                tISH.VirtualAddress = st.st_size;          // 改地址应该为增加的一个空闲区  应该是先加区段,然后再改入口点 此

地址作为最后的偏移量
                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;     // 块属性, 表示包含执行代码、可读写并可执行。

                fseek(test, 0, SEEK_SET);              //这一步不可少,因为你不知道当前文件指针指向哪里了,必须回到文件开头
   
                fseek(test, off_elfanew + t_InsertPosition, SEEK_SET);                //来到加块表处
   
                fwrite(&tISH, 1, sizeof(tISH), test);   // 写入块结构,为什么size又是1?????
                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;
}
我根据自己的理解对程序做了一些注释。
我有一些疑问,想各位大侠。
1.fread的定义为_CRTIMP size_t __cdecl fread(void *, size_t, size_t, FILE *);,为什么程序中第二个参数是1?
  fwrite的定义为_CRTIMP size_t __cdecl fwrite(const void *, size_t, size_t, FILE *);,为什么程序中的二个参数也是1?
2.fread读入的PE文件在内存中是按FileAlignment对齐的,还是按照SectionAlignment对齐的?
在FillZeroArea()函数中,VirtualSize是真实的大小,VirtualAddress却是节对齐后的地址。如果是文件对齐,那输入参数块大小应该是文件对齐粒度的整数倍,对不对?这个程序中没有处理输入参数不是文件对齐粒度整数倍时的情况。
用fread读入是不是跟用Ultraedit32打开一样的结构,那filemapping读入的是不是跟系统载入PE文件一样?
我真是太困惑了,还请大家指教。

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (2)
雪    币: 186
活跃值: (26)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
fread和fwrite的第二个参数表示读取的单位,如果该参数为2,则表示读取参数3*2个字节;是1,则表示读取参数3个字节。。
第二个问题不知所云,期待高手回答。
2008-11-2 21:23
0
雪    币: 213
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
第一个问题我查到了,还是多谢likedust。
期待大侠指教第二个问题,其实只要懂Pe结构并且实践过的人都知道。
我很久以前手动给记事本增加了一个区段,现在想用程序实现一下。
2008-11-2 22:26
0
游客
登录 | 注册 方可回帖
返回
//