pe文件解析基础 | 宜武汇-ag真人国际厅网站

#include

#include

#include

#include

using namespace std;

 

class pefile {

private:

    handle hfile;                                   //文件句柄

    handle hprocess;                                //进程句柄

    dword processbaseaddr;                          //进程基址

    byte* filebuffer;                               //文件缓冲指针

    byte* imagebuffer;                              //映像缓冲指针

    dword filebuffersize;                           //文件缓冲大小

    dword imagebuffersize;                          //映像缓冲大小

 

    //filebuffer的各个指针

    pimage_dos_header pdosheader;                   //dos头

    pimage_nt_headers pntheader;                    //nt头

    pimage_file_header pfileheader;                 //标准pe头

    pimage_optional_header poptionalheader;         //扩展pe头

    pimage_data_directory pdatadirectory;           //数据目录表

    pimage_export_directory pexportdirectory;       //导出表

    pimage_base_relocation pbaserelocation;         //重定位表

    pimage_import_descriptor pimportdescriptor;     //导入表

    pimage_section_header psectionheader;           //节表

 

 

    //dos头关键成员

    word dossignature;          //dos签名

    long   ntoffset;            //nt头偏移

 

    //nt头关键成员

    dword pesignature;

 

    //标准pe头关键成员

    word machine;               //cpu型号

    dword numberofsections;     //节区数

    word sizeofoptionalheader;  //可选pe头大小

 

    //可选pe头关键成员

    dword addressofentrypoint;  //程序入口点ep

    dword imagebase;            //内存镜像基址

    dword filealignment;        //文件对齐大小

    dword sizeofimage;          //内存映像大小

    dword sizeofheaders;        //各种头的大小

 

 

    //初始化各个表头指针

    void initheaders() {

        pdosheader = (image_dos_header*)filebuffer;//dos头

        pntheader = (image_nt_headers*)(filebuffer pdosheader->e_lfanew);//nt头

        pfileheader = (image_file_header*)((dword)pntheader sizeof(dword));//标准pe头

        poptionalheader = (image_optional_header*)((dword)pfileheader sizeof(image_file_header));//可选pe头

        psectionheader = (image_section_header*)((dword)poptionalheader pfileheader->sizeofoptionalheader);//节表

        pdatadirectory = (pimage_data_directory)(poptionalheader->datadirectory);//数据目录表

        pbaserelocation = (pimage_base_relocation)(filebuffer rva2foa(pdatadirectory[image_directory_entry_basereloc].virtualaddress));//重定位表

        pimportdescriptor = (pimage_import_descriptor)(filebuffer rva2foa(pdatadirectory[image_directory_entry_import].virtualaddress));//导入表

        if (pdatadirectory[image_directory_entry_export].virtualaddress != 0)//如果存在导出表则获取导出表地址,否则置空

            pexportdirectory = (pimage_export_directory)(filebuffer rva2foa(pdatadirectory[image_directory_entry_export].virtualaddress));//导出表

        else pexportdirectory = null;

 

    }

 

    //初始化filebuffer关键成员

    void initkeymembers() {

 

        //dos头

        dossignature = pdosheader->e_magic;

        ntoffset = pdosheader->e_lfanew;

 

        //nt头

        pesignature = pntheader->signature;

 

        //标准pe头 20字节

        machine = pfileheader->machine;

        numberofsections = pfileheader->numberofsections;

        sizeofoptionalheader = pfileheader->sizeofoptionalheader;

 

        //可选头,根据32/64位有不同大小

        addressofentrypoint = poptionalheader->addressofentrypoint;

        imagebase = poptionalheader->imagebase;

        sectionalignment = poptionalheader->sectionalignment;

        filealignment = poptionalheader->filealignment;

        sizeofimage = poptionalheader->sizeofimage;

        sizeofheaders = poptionalheader->sizeofheaders;

    }

 

    //打印dos头

    void showdosheader() {

        printf("\n----------dosheader----------\n");

        printf("dossignature: %x\n", dossignature);

        printf("ntheaderoffset: %x\n", ntoffset);

        printf("\n----------dosheader----------\n");

    }

 

    //打印标准pe头

    void showfileheader() {

        printf("\n----------fileheader----------\n");

        printf("machine: %x\n", machine);

        printf("numberofsections: %x\n", numberofsections);

        printf("sizeofoptionalheader: %x\n", sizeofoptionalheader);

        printf("\n----------fileheader----------\n");

    }

 

    //打印可选pe头

    void showoptionalheader() {

        printf("\n----------optionalheader----------\n");

        printf("entrypoint: %x\n", addressofentrypoint);

        printf("imagebase: %x\n", imagebase);

        printf("sectionalignment: %x\n", sectionalignment);

        printf("filealignment: %x\n", filealignment);

        printf("sizeofimage; %x\n", sizeofimage);

        printf("sizeofheaders: %x\n", sizeofheaders);

        printf("\n----------optionalheader----------\n");

    }

 

    //打印nt头

    void showntheader() {

        printf("\n-----------ntheader----------\n");

        printf("pesignature: %x\n", pesignature);

        showfileheader();

        showoptionalheader();

        printf("\n-----------ntheader----------\n");

    }

 

    //打印节表

    void showsectionheaders() {

        printf("\n----------sectionheaders----------\n");

        for (dword i = 0; i < numberofsections; i ) {

            //逐个读取节表并打印

            printf("\n----------section%d----------\n", i);

            printf("name: %8s\n", psectionheader[i].name);

            printf("virtualsize: %x\n", psectionheader[i].misc.virtualsize);

            printf("virtualaddress: %x\n", psectionheader[i].virtualaddress);

            printf("sizeofrawdata: %x\n", psectionheader[i].sizeofrawdata);

            printf("pointertorawdata: %x\n", psectionheader[i].pointertorawdata);

            printf("characteristics: %x\n", psectionheader[i].characteristics);

            printf("\n----------section%d----------\n", i);

        }

        printf("\n----------sectionheaders----------\n");

    }

 

    //设置filebuffer

    void setfilebuffer(byte* newfilebuffer) {

        if (filebuffer)

            delete[] filebuffer;       //删除原始空间

        filebuffer = newfilebuffer;//指向新的空间

        init();                    //初始化

    }

 

    //设置imagebuffer

    void setimagebuffer(byte* newimagebuffer) {

        if (imagebuffer)

            delete[] imagebuffer;

        imagebuffer = newimagebuffer;

    }

 

    //将filebuffer拉伸成为imagebuffer

    void filebuffertoimagebuffer() {

        //1. 申请空间用于存储image

        imagebuffer = new byte[sizeofimage];

        imagebuffersize = sizeofimage;

        if (!imagebuffer)

        {

            printf("申请空间失败!\n");

            system("pause");

            return;

        }

        memset(imagebuffer, 0, sizeofimage);            //初始化内存空间,全部清零

        memcpy(imagebuffer, filebuffer, sizeofheaders); //直接复制各个表头

 

        //2. 拉伸filebuffer并写入imagebuffer

        for (dword i = 0; i < numberofsections; i ) {

            memcpy(imagebuffer psectionheader[i].virtualaddress, filebuffer psectionheader[i].pointertorawdata, psectionheader[i].sizeofrawdata);

            //起始地址是imagebase 节区起始地址rva sizeofdata是节区在文件中保存的数据

            //不使用virtualsize的原因是例如.textbss段 sizeofdata=0 virtualsize=10000

            //显然在文件中没有数据需要写入内存,只是在内存中占用那么多大小的空间而已

        }

    }

 

    //将imagebuffer压缩为filebuffer

    void imagebuffertofilebuffer() {

        //1. 申请空间用于存储imagebuffer压缩后的filebuffer

        dword newfilebuffersize = psectionheader[numberofsections - 1].pointertorawdata psectionheader[numberofsections - 1].sizeofrawdata;

        byte* newfilebuffer = new byte[newfilebuffersize];//最后一个节区的文件起始地址 文件大小即为pe文件大小

        memset(newfilebuffer, 0, newfilebuffersize);

 

        //2. 将imagebuffer的内容压缩并写入filebuffer

        for (dword i = 0; i < numberofsections; i ) //复制节区 

        {

            memcpy(newfilebuffer psectionheader[i].pointertorawdata, imagebuffer psectionheader[i].virtualaddress, psectionheader[i].sizeofrawdata);

            //节区文件偏移起始地址 节区内存偏移起始地址 节区文件大小

            //注意这里第三个参数不要使用virtualsize 否则可能会导致缓冲区溢出

            //(例如: .textbss段在文件中占用空间为0 但是内存中的大小为0x10000 所以这段没有必要写入文件中)

        }

        memcpy(newfilebuffer, imagebuffer, sizeofheaders); //复制各个表头

        setfilebuffer(newfilebuffer);                      //重新设置filebuffer

    }

 

    //获取进程基址

    dword getprocessbaseaddress(handle hprocess) {

        hmodule hmods[1024];

        dword cbneeded;

        dword baseaddress = 0;

 

        if (enumprocessmodules(hprocess, hmods, sizeof(hmods), &cbneeded)) {

            for (unsigned int i = 0; i < (cbneeded / sizeof(hmodule)); i ) {

                tchar szmodname[max_path];

                if (getmodulefilenameex(hprocess, hmods[i], szmodname,

                    sizeof(szmodname) / sizeof(tchar))) {

                    moduleinfo moduleinfo;

                    if (getmoduleinformation(hprocess, hmods[i], &moduleinfo, sizeof(moduleinfo))) {

                        baseaddress = (uintptr_t)moduleinfo.lpbaseofdll;

                        break; // we found the first module's base address

                    }

                }

            }

        }

        return baseaddress;

    }

 

public:

    //创建进程并获取进程基址

    bool createprocesswrapper(lpctstr applicationname, lptstr commandline) {

        startupinfo startupinfo;

        zeromemory(&startupinfo, sizeof(startupinfo));

        startupinfo.cb = sizeof(startupinfo);

 

        bool success = createprocess(

            applicationname,

            commandline,

            null, null, false, 0, null, null,

            &startupinfo,

            &processinfo

        );

        if (success) {

            hprocess = processinfo.hprocess;

            processbaseaddr = getprocessbaseaddress(hprocess);

        }

        return success;

    }

 

    //将filebuffer写入文件

    bool filebufferwritetofile(const wchar* filename) {

        //创建文件 注意这里第三个参数不能用generic_all 推测是由于可执行权限导致出错 仅读写没有问题

        //create_always 无论文件是否存在都会写入

        handle hfile = createfile(filename, generic_read | generic_write, 0, null, create_always, file_attribute_normal, null);

        if (hfile == invalid_handle_value)

            return false;

        return writefile(hfile, filebuffer, filebuffersize, null, null); // 写入文件

    }

 

    //将imagebuffer写入文件

    bool imagebufferwritetofile(const wchar* filename) {

        handle hfile = createfile(filename, generic_read | generic_write, 0, null, create_always, file_attribute_normal, null);

        if (hfile == invalid_handle_value)

            return false;

        return writefile(hfile, imagebuffer, sizeofimage, null, null);

    }

 

    //扩大filebuffer大小 exsize为文件对齐后的额外空间大小

    void expandfilebuffer(dword exsize) {

        byte* newbuffer = new byte[filebuffersize exsize];

        memset(newbuffer filebuffersize, 0, exsize);//额外空间清零

        memcpy(newbuffer, filebuffer, filebuffersize);//复制原始数据

        filebuffersize = exsize;//调整大小

        setfilebuffer(newbuffer);

    }

 

    //扩大imgaebuffer大小

    void expandimagebuffer(dword exsize) {

        byte* newbuffer = new byte[imagebuffersize exsize];

        memset(newbuffer imagebuffersize, 0, exsize);

        memcpy(newbuffer, imagebuffer, imagebuffersize);

        imagebuffersize = exsize;

        setimagebuffer(newbuffer);

    }

 

    pefile(lpcwchar filename) {

        hfile = createfile(filename, generic_read, file_share_read, null, open_existing, file_attribute_normal, null);  //打开文件

        if (!hfile) {

            printf("openfilefailure!\n");

        }

 

        filebuffersize = getfilesize(hfile, null);   //获取文件大小

        filebuffer = new byte[filebuffersize];     //分配内存空间用于存储文件

 

        if (!filebuffer) {

            printf("allocfilebuffermemoryfailure!\n");

        }

 

        if (!readfile(hfile, filebuffer, filebuffersize, null, null)) //读取文件并存储到内存中

        {

            delete[] filebuffer;

            printf("readfilefailure!\n");

        }

 

        closehandle(hfile);//读取完后关闭文件

 

        initheaders();

        initkeymembers();

        filebuffertoimagebuffer();//创建imagebuffer

 

        hprocess = null;

        processbaseaddr = 0;

    }

 

    //初始化表头指针和关键变量

    void init() {

        initheaders();

        initkeymembers();

        //initimageheaders();

    }

 

    //打印pe文件信息

    void showpefile() {

        showdosheader();

        showntheader();

        showsectionheaders();

        printdirectory();

        printexportdirectory();

        printrelocationtable();

        printimporttable();

    }

 

    //打印数据目录表

    void printdirectory() {

        pimage_data_directory pdirectory = poptionalheader->datadirectory;

        printf("\n**********数据目录表**********\n");

        for (dword i = 0; i < poptionalheader->numberofrvaandsizes; i ) {

            switch (i) {

            case image_directory_entry_export:

                printf("\n==========导出表==========\n");

                break;

            case image_directory_entry_import:

                printf("\n==========导入表==========\n");

                break;

            case image_directory_entry_resource:

                printf("\n==========资源目录==========\n");

                break;

            case image_directory_entry_exception:

                printf("\n==========异常目录==========\n");

                break;

            case image_directory_entry_security:

                printf("\n==========安全目录=========\n");

                break;

            case image_directory_entry_basereloc:

                printf("\n==========重定位基本表==========\n");

                break;

            case image_directory_entry_debug:

                printf("\n==========调试目录==========\n");

                break;

            case image_directory_entry_architecture:

                printf("\n==========描述字串==========\n");

                break;

            case image_directory_entry_globalptr:

                printf("\n==========机器值==========\n");

                break;

            case image_directory_entry_tls:

                printf("\n==========tls目录==========\n");

                break;

            case image_directory_entry_load_config:

                printf("\n==========载入配置目录==========\n");

                break;

            case image_directory_entry_bound_import:

                printf("\n==========绑定输入表==========\n");

                break;

            case image_directory_entry_iat:

                printf("\n==========导入地址表==========\n");

                break;

            case image_directory_entry_delay_import:

                printf("\n==========延迟导入表==========\n");

                break;

            case image_directory_entry_com_descriptor:

                printf("\n==========com信息==========\n");

                break;

                printf("\n==========保留表==========\n");

                break;

            }

            printf("virtualaddress=%x\nsize=%x\nfoa=%x\n", pdirectory[i].virtualaddress, pdirectory[i].size, rva2foa(pdirectory[i].virtualaddress));

 

        }

        printf("\n**********数据目录表打印完毕**********\n\n");

 

 

    }

 

    //通过函数名获取导出函数地址

    dword getfuncaddrbyname(const char* funcname) {

        word* pexportfuncoridinalstable = (word*)(rva2foa(pexportdirectory->addressofnameordinals) filebuffer);//导出函数序号表

        dword* pexportfuncaddresstable = (dword*)(rva2foa(pexportdirectory->addressoffunctions) filebuffer);//导出函数地址表

        dword* pexportfuncnamestable = (dword*)(rva2foa(pexportdirectory->addressofnames) filebuffer);//导出函数名称表

 

        //1. 通过导出函数名称表得到序号表下标

        for (dword i = 0; i < pexportdirectory->numberofnames; i ) {

            //注意导出函数名称表表项是字符串指针 该指针值为rva

            if (strcmp(funcname, (char*)(rva2foa(pexportfuncnamestable[i]) filebuffer)) == 0)

            {

                pos = i;

                break;

            }

        }

        if (pos == -1)//查找失败

            return 0;

 

        //2. 通过序号表得到序号

        oridinalnum = pexportfuncoridinalstable[pos];

 

        //3. 得到函数地址

        return pexportfuncaddresstable[oridinalnum];

    }

 

    //通过函数序号获取导出函数地址

    dword getfuncaddrbyoridinals(word oridinalnum) {

        dword* pexportfuncaddresstable = (dword*)(rva2foa(pexportdirectory->addressoffunctions) filebuffer);//导出函数地址表

        return pexportfuncaddresstable[oridinalnum - pexportdirectory->base];//减去base值作为索引直接查找函数地址

    }

 

    //根据导出函数序号返回导出函数名

    pchar getfuncnamebyoridinals(word oridinalnum) {

        word* pexportfuncoridinalstable = (word*)(rva2foa(pexportdirectory->addressofnameordinals) filebuffer);//导出函数序号表

        dword* pexportfuncnamestable = (dword*)(rva2foa(pexportdirectory->addressofnames) filebuffer);//导出函数名称表

        for (dword i = 0; i < pexportdirectory->numberofnames; i )

        {

            if (pexportfuncoridinalstable[i] == oridinalnum)//实际存储的序号=函数序号-base

                return (pchar)(rva2foa(pexportfuncnamestable[i]) filebuffer);

        }

        return null;//没有找到说明是无名函数

    }

 

    //打印导出表详细信息

    void printexportdirectory() {

        //不存在导出表

        if (!pexportdirectory)

        {

            printf("**********不存在导出表**********\n");

            return;

        }

        printf("\n==========导出表==========\n");

        printf("name: %x (%s)\n", pexportdirectory->name, (char*)(filebuffer rva2foa(pexportdirectory->name)));

        printf("base: %x\n", pexportdirectory->base);

        printf("numberoffunctions: \t%x\n", pexportdirectory->numberoffunctions);

        printf("numberofnames: \t\t%x\n", pexportdirectory->numberofnames);

        printf("addressoffunctions: \trva=%x\tfoa=%x\n", pexportdirectory->addressoffunctions, rva2foa(pexportdirectory->addressoffunctions));

        printf("addressofnames: \trva=%x\tfoa=%x\n", pexportdirectory->addressofnames, rva2foa(pexportdirectory->addressofnames));

        printf("addressofnameordinals: \trva=%x\tfoa=%x\n", pexportdirectory->addressofnameordinals, rva2foa(pexportdirectory->addressofnameordinals));

 

        word* pexportfuncoridinalstable = (word*)(rva2foa(pexportdirectory->addressofnameordinals) filebuffer);//导出函数序号表

        dword* pexportfuncaddresstable = (dword*)(rva2foa(pexportdirectory->addressoffunctions) filebuffer);//导出函数地址表

        dword* pexportfuncnamestable = (dword*)(rva2foa(pexportdirectory->addressofnames) filebuffer);//导出函数名称表

 

        printf("\noridinal\t     rva\t     foa\tfunctionname\n");

 

        for (dword i = 0; i < pexportdirectory->numberoffunctions; i ) {

            if (pexportfuncaddresstable[i] == 0)//地址为零则跳过

                continue;

            pchar funcname = null;

            //由于导出函数序号表仅保存有名函数序号,所以有序号必定有名称,否则无名称

            //函数序号=函数地址表下标 base

            printf("x\tx\tx\t", i pexportdirectory->base, pexportfuncaddresstable[i], rva2foa(pexportfuncaddresstable[i]));

            //是否存在函数名要单独判断 存储序号=函数序号-base,故传递i即可

            if (funcname = getfuncnamebyoridinals(i))

                printf("%s\n", funcname);

            else

                printf("noname\n");

        }

        printf("\n==========导出表结束==========\n");

    }

 

    //打印重定位表的某个块

    void printrelocationblock(pimage_base_relocation prelocationblock) {

        pword pblock = (pword)((dword)prelocationblock 8);//注意每个表项占2字节 但是高4位用来判断是否需要修改

        dword pageoffset = prelocationblock->virtualaddress;//每个块的虚拟地址即为页面起始地址

 

        printf("序号\t属性\t     rva\t     foa\t指向rva\n");

        for (dword i = 0; i < (prelocationblock->sizeofblock - 8) / 2; i ) {

            //每块高四位用作属性判断,低12位才是页内偏移值 还要注意与运算优先级低于 不用括号会导致出错

            //指向的rva即需要矫正的地址

            printf("x\t%4x\tx\tx\tx\n", i, pblock[i] >> 12, (pblock[i] & 0x0fff) pageoffset, rva2foa((pblock[i] & 0x0fff) pageoffset), *(dword*)(filebuffer rva2foa((pblock[i] & 0x0fff) pageoffset)) & 0x00ffffff);

        }

    }

 

    //打印重定位表

    void printrelocationtable() {

        pimage_base_relocation prelocationtable = pbaserelocation;

        printf("\n==========重定位表==========\n");

        printf("序号\t    区段\t     rva\t     foa\t项目数\n");

 

        //表块全为0时结束

        dword count = 0;

        while (prelocationtable->virtualaddress || prelocationtable->sizeofblock) {

            //项目数=(sizeofblock-8)/2

            printf("m\t%8s\tx\tx\tx\n", count , getsectionnamebyrva(prelocationtable->virtualaddress), prelocationtable->virtualaddress, rva2foa(prelocationtable->virtualaddress), (prelocationtable->sizeofblock - 8) / 2);

            prelocationtable = (pimage_base_relocation)((dword)prelocationtable prelocationtable->sizeofblock);//注意这里应该将指针值强转后 块大小指向下一个块

        }

 

        prelocationtable = pbaserelocation;

        count = 0;

        while (prelocationtable->virtualaddress || prelocationtable->sizeofblock) {

            printf("\n==========block%d==========\n", count );

            printrelocationblock(prelocationtable);//打印第i个块

            prelocationtable = (pimage_base_relocation)((dword)prelocationtable prelocationtable->sizeofblock);

        }

 

        printf("\n==========重定位表结束==========\n");

    }

 

    //打印int表

    void printint(pimage_import_descriptor pimporttable) {

        printf("\n==========int==========\n");

        printf("thunkrva\tthunkfoa\tthunkval\tfuncname\n\n");

        pimage_thunk_data32 pthunkdata = (pimage_thunk_data32)(rva2foa(pimporttable->originalfirstthunk) filebuffer);

        while (pthunkdata->u1.ordinal) {

            //最高位为1时表示按序号导入,低31位作为序号值

            printf("x\tx\tx\t", foa2rva((dword)pthunkdata - (dword)filebuffer), (dword)pthunkdata - (dword)filebuffer, pthunkdata->u1.ordinal);

            if (pthunkdata->u1.ordinal & 0x80000000) {

                printf("x\n", pthunkdata->u1.ordinal & 0x7fffffff);

            }

            //最高位为0时表示按函数名称导入,值作为指向image_import_by_name结构体地址的rva

            else

            {

                pimage_import_by_name pimportname = (pimage_import_by_name)(rva2foa(pthunkdata->u1.addressofdata) filebuffer);

                printf("%s\n", pimportname->name);

            }

            pthunkdata ;

        }

    }

    //打印iat表

    void printiat(pimage_import_descriptor pimporttable) {

        printf("\n==========iat==========\n");

        pdword pthunkdata = (pdword)(rva2foa(pimporttable->firstthunk) filebuffer);

        printf(" funcrva\t funcfoa\tfuncaddr\n");

            while (*pthunkdata) {

                printf("x\tx\tx\n", *pthunkdata,rva2foa(*pthunkdata), *pthunkdata imagebase);

                pthunkdata ;

            }

    }

    //打印导入表

    void printimporttable() {

        pimage_import_descriptor pimporttable = pimportdescriptor;

        printf("\n**********导入表**********\n");

        printf("dllname\t\t\t int rva\ttimestamp\tiat rva\n");

        while (pimporttable->originalfirstthunk) {

            printf("%-24sx\tx\tx\n", (rva2foa(pimporttable->name) filebuffer),pimporttable->originalfirstthunk,pimporttable->timedatestamp,pimporttable->firstthunk);

            pimporttable ;

        }

 

        pimporttable = pimportdescriptor;

        while (pimporttable->originalfirstthunk) {

 

            printf("\n==========dllname:%s==========\n", rva2foa(pimporttable->name) filebuffer);

            printint(pimporttable);

            printiat(pimporttable);

 

            pimporttable ;

        }

        printf("\n**********导入表**********\n");

    }

 

    //通过rva判断所属区段名

    pchar getsectionnamebyrva(dword rva) {

        for (dword i = 0; i < numberofsections; i ) {

            if (rva >= psectionheader[i].virtualaddress && rva < psectionheader[i].virtualaddress alignsize(psectionheader[i].misc.virtualsize, 0x1000))//成功找到所属节区

                return (pchar)psectionheader[i].name;

        }

    }

 

    //rva转foa

    dword rva2foa(dword rva) {

        dword foa = 0;

        //1. 判断rva属于哪个节区 节区内存起始地址<=rva<=节区内存起始地址 节区大小 内存大小需要对齐 注意右边界应该是开区间

        //2. foa=rva-virtualaddress pointertorawdata

        for (dword i = 0; i < numberofsections; i ) {

            if (rva >= psectionheader[i].virtualaddress && rva < psectionheader[i].virtualaddress alignsize(psectionheader[i].misc.virtualsize, 0x1000))//成功找到所属节区

            {

                foa = rva - psectionheader[i].virtualaddress psectionheader[i].pointertorawdata;

                break;

            }

        }

        return foa;

    }

 

    //foa转rva

    dword foa2rva(dword foa) {

        dword rva = 0;

        //1. 判断foa属于哪个节区 节区文件起始地址<=foa<=节区文件起始地址 节区大小 文件大小默认是对齐值

        //2. rva=foa-pointertorawdata virtualaddress

        for (dword i = 0; i < numberofsections; i ) {

            if (foa >= psectionheader[i].pointertorawdata && foa < psectionheader[i].pointertorawdata psectionheader[i].sizeofrawdata) {

                rva = foa - psectionheader[i].pointertorawdata psectionheader[i].virtualaddress;

                break;

            }

        }

        return rva;

 

    }

 

    //输入原始大小和对齐值返回对齐后的大小

    dword alignsize(dword origsize, dword alignval) {

        //通过对对齐值取模判断是否对齐,如果对齐则返回原值,否则返回对齐后的值

        return origsize % alignval ? (origsize / alignval 1) * alignval : origsize;

    }

 

    //计算call/jmp指令的偏移值 目的地址-(当前指令地址 5)

    dword offsetofcallandjmp(dword desaddr, dword selfaddr) {

    }

 

    //创建新的节区 返回新节区指针

    pimage_section_header createnewsection(const char* newsectionname, dword newsectionsize) {

        //1. 检查节表空闲区是否足够保存新的节表 80字节

        //空白空间起始地址=nt头偏移 nt头大小 所有节表大小

        dword blankmemaddr = (ntoffset sizeof(image_nt_headers)) numberofsections * sizeof(image_section_header);

        dword blankmemsize = sizeofheaders - blankmemaddr;//空白空间大小=sizeofheaders-各个表头大小-所有节表大小

        if (blankmemsize < sizeof(image_section_header) * 2)

            return null;

 

        //2. 申请新的空间

        expandfilebuffer(newsectionsize);

        pimage_section_header pnewsectionheader = (pimage_section_header)(filebuffer blankmemaddr);//指向新增的节表

 

        //3. 复制.text段的节表信息

        for (dword i = 0; i < numberofsections; i ) {

            if (!strcmp((char*)psectionheader[i].name, ".text"))

            {

                memcpy(pnewsectionheader, (lpvoid)&psectionheader[i], sizeof(image_section_header));

                break;

            }

        }

 

        //4. 修正pe文件信息

        //标准pe头

        pfileheader->numberofsections = numberofsections;         //numberofsections 1

 

        //节区头

        memcpy(pnewsectionheader->name, newsectionname, strlen(newsectionname));//name

        pnewsectionheader->misc.virtualsize = newsectionsize;               //virtualsize

 

        //注意这里必须先修改virtualaddress

        //virtualaddress 各段间是紧邻着的 所以可以根据上个段的末尾来确定新段的起始地址 上个段的起始地址 上个段的大小对于0x1000向上取整即可

        pnewsectionheader->virtualaddress = alignsize(psectionheader[numberofsections - 2].virtualaddress psectionheader[numberofsections - 2].sizeofrawdata, 0x1000);

        pnewsectionheader->sizeofrawdata = newsectionsize;//sizeofrawdata

        //pointertorawdata 文件偏移=上个段的文件起始地址 段在文件中的大小

        pnewsectionheader->pointertorawdata = psectionheader[numberofsections - 2].pointertorawdata psectionheader[numberofsections - 2].sizeofrawdata;

        pnewsectionheader->characteristics |= 0x20000000;           //characteristics 可执行权限

 

        //可选头

        poptionalheader->sizeofimage = sizeofimage = sizeofimage alignsize(newsectionsize, 0x1000);//可选pe头 sizeofimage 必须是内存对齐的整数倍 直接添加一页大小

 

        return pnewsectionheader;

    }

 

    //通过创建新节区的方式注入代码

    bool injectcodebycreatenewsection() {

        //1. 创建新的节区

        pimage_section_header pnewsectionheader = createnewsection(".inject", 0x1000);

 

        //修正可选头

        dword oep = addressofentrypoint; //保存oep

        poptionalheader->dllcharacteristics &= 0xffffffbf;//取消aslr随机基址 随机基址的值是0x40 所以和(0xffffffff-0x40)进行与运算即可

        poptionalheader->addressofentrypoint = addressofentrypoint = pnewsectionheader->virtualaddress;//修改ep 注意ep=rva 不用加基址

 

        //2. 将代码写入新的节区

        byte injectcode[18] = {         //偏移  指令

            0x6a,0x00,                  //0     push 0

            0x6a,0x00,                  //0     push 0

            0x6a,0x00,                  //0     push 0

            0x6a,0x00,                  //0     push 0

            0xe8,0x00,0x00,0x00,0x00,   //8     call messagebox messagebox=0x763c0e50 这个地址会随着系统启动而变化

            0xe9,0x00,0x00,0x00,0x00    //13    jmp oep

 

        };

        dword messageboxaddr = 0x76260e50;

        //矫正call和jmp地址

        *(dword*)&injectcode[9] = offsetofcallandjmp(messageboxaddr, imagebase pnewsectionheader->virtualaddress 8);

        *(dword*)&injectcode[14] = offsetofcallandjmp(oep, pnewsectionheader->virtualaddress 13);//跳转回oep正常执行程序    

        memcpy(filebuffer pnewsectionheader->pointertorawdata, injectcode, sizeof(injectcode));//将代码写入新的内存空间           

 

        //3. 保存文件

        return filebufferwritetofile(l"injectcodebycreatenewsection1.exe");

    }

 

    //扩大节区

    bool expandsection(dword exsize) {

        //扩大节区大小是针对imagebuffer而言的,所以我们添加的大小要进行内存对齐

 

 

        //1. 申请一块新空间

        expandfilebuffer(exsize);       //注意这个节表指针要在申请新空间之后

        pimage_section_header plastsectionheader = &psectionheader[numberofsections - 1];//只能扩大最后一个节区

 

        //2. 调整sizeofimage

        //如果virtualsize exsize超过了alignsize(virtualsize,0x1000) 那么需要调整,否则不需要改变

        //例如vs=0x500 ex=0x400 显然,原始vs内存对齐也会占0x1000 扩展后没有超过0x1000

        //取文件大小和内存大小的最大值

 

        //先计算扩展后的内存对齐值和扩展前的内存对齐值之间的差值

        dword aligneximage = alignsize(plastsectionheader->misc.virtualsize exsize, 0x1000) -

            alignsize(max(plastsectionheader->misc.virtualsize, plastsectionheader->sizeofrawdata), 0x1000);//内存对齐后的值

        if (aligneximage > 0)//如果差值>0说明需要扩展映像 否则内存对齐的空白区足够存储扩展区

            poptionalheader->sizeofimage = sizeofimage = sizeofimage aligneximage;

 

        //3. 修改文件大小和内存大小 注意要在修改sizeofimage后再更新这两个值

        plastsectionheader->sizeofrawdata = alignsize(exsize, 0x200);//文件大小必须是文件对齐整数倍

        plastsectionheader->misc.virtualsize = exsize;//由于是内存对齐前的大小,所以直接加上文件对齐后的大小即可

 

        //4. 保存文件

        return filebufferwritetofile(l"expandsectionfile.exe");

    }

 

    //合并所有节区为1个

    bool combinesection() {

        //1. 直接修改imagebuffer

        pimage_dos_header pdosheaderofimage = (pimage_dos_header)imagebuffer;

        pimage_nt_headers pntheadersofimage = (pimage_nt_headers)(imagebuffer pdosheader->e_lfanew);

        pimage_file_header pfileheaderofimage = (pimage_file_header)(&pntheadersofimage->fileheader);

        pimage_optional_header poptionalheaderofimage = (pimage_optional_header)(&pntheadersofimage->optionalheader);

        pimage_section_header psectionheaderofimage = (pimage_section_header)((dword)poptionalheaderofimage pfileheaderofimage->sizeofoptionalheader);

 

        //复制节区属性

        for (dword i = 1; i < numberofsections; i ) {

            psectionheaderofimage[0].characteristics |= psectionheaderofimage[i].characteristics;

        }

        //调整节表

        psectionheaderofimage[0].pointertorawdata = psectionheaderofimage[0].virtualaddress;//文件偏移改为内存偏移

        psectionheaderofimage[0].misc.virtualsize = psectionheaderofimage[0].sizeofrawdata = sizeofimage - psectionheaderofimage[0].virtualaddress;//新的节区大小为所有节区内存大小之和

        poptionalheaderofimage->sizeofheaders = alignsize(sizeofheaders - (numberofsections - 1) * sizeof(image_section_header), 0x200);//调整头大小

        //删除其他节表

        memset(&psectionheaderofimage[1], 0, sizeof(image_section_header) * (numberofsections - 1));

        pfileheaderofimage->numberofsections = 1;

        return imagebufferwritetofile(l"combinesection1.exe");

    }

 

    ~pefile() {

        if (filebuffer)          //释放空间

            delete[] filebuffer;

        if (imagebuffer)

            delete[] imagebuffer;

        if (hprocess)

            closehandle(hprocess);

    }

int main() {

    //pefile pefile = pefile(l"c:\\users\\admin\\desktop\\dailyexercise.exe");

    pefile pefile = pefile(l"c:\\users\\admin\\desktop\\dlltest.dll");

    pefile.showpefile();

    return 0;

原文链接:https://bbs.kanxue.com/thread-278377.htm

网络摘文,本文作者:15h,如若转载,请注明出处:https://www.15cov.cn/2023/08/27/pe文件解析基础/

发表评论

邮箱地址不会被公开。 必填项已用*标注

网站地图