鱼C论坛

 找回密码
 立即注册
查看: 597|回复: 1

[技术交流] PE头全程以调试过程来注释 完整代码

[复制链接]
发表于 2023-8-11 10:57:38 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
#include <iostream.h>
#include <stdio.h>
#include <windows.h>


void DOS_tou(){

        FILE *pFile = NULL;
        char * buffer;
        int nFileLength = 0;
        pFile = fopen("mfc001.exe", "r+b");
        cout<<"pFile=  "<<pFile<<endl;;
/*
        原型:        long ftell(FILE *stream); 因为 ftell() 返回long型
       
        功能:        返回当前文件位置。使用fseek函数后再调用函数ftell()就能非常容易地确定文件的当前位置。
               
        用法:        ftell(fp); 利用函数 ftell() 能方便地知道一个文件的长。如以下语句序列:
                        fseek(fp, 0L, SEEK_END);        首先将文件的当前位置移到文件的末尾,
                        len =ftell(fp);                                然后调用函数ftell()获得当前位置相对于文件首的位移,该位移值等于文件所含字节数                       
*/


        fseek(pFile,0,SEEK_END);                //定位到文件末尾
       

        nFileLength = ftell(pFile);                //函数 ftell() 用于得到文件指针当前位置相对于文件首的偏移字节数
        cout<<"nFileLength="<<nFileLength<<endl;
       
        rewind(pFile);                        //将读写位置指针重置到文件开头
       
        //rewind函数作用等同于 (void)fseek(stream, 0L, SEEK_SET); 用 法: void rewind(FILE *stream);
       
       

        int imageLength = nFileLength * sizeof(char)+1 ;               
        buffer = (char *)malloc(imageLength);                                        //申请nFileLength*(乘以) char个字节空间
        memset(buffer, 0, nFileLength * sizeof(char)+1);                //将已开辟内存空间 buffer 的首 nFileLength * sizeof(char)+ 1 个字节的值设为值0。
       
//        cout<<"===="<<pFile<<endl;

        fread(buffer, 1, imageLength, pFile);
/*
        buffer                                指向存储数据的缓冲区
        1                                        读取的基本单元字节大小,单位是字节,一般是 buffer 缓冲的单位大小
        imageLength                        要读取的数据块数目
        pFile                                指向文件对象的指针
        返回值:                        成功读取的元素个数


*/


        PIMAGE_DOS_HEADER ReadDosHeader = (PIMAGE_DOS_HEADER)buffer;
        cout<< "DOS_e_magic:\t"<<hex<< ReadDosHeader->e_magic<<endl;
        cout<<"DOS_e_lfanew:\t"<<hex<< ReadDosHeader->e_lfanew<<endl;

        if (buffer != NULL)          // 释放内存
        {
                free(buffer);
                buffer = NULL;
        }
        // 最后不要忘记关闭文件
        fclose(pFile);
}

//======================================上面这个dos_tou函数 可以删除 只是注释测试而已=================================================

char* Base_memset(FILE * pFile ){
        char * buffer;
        int nFileLength = 0;

        fseek(pFile,0,SEEK_END);
        nFileLength = ftell(pFile);
        rewind(pFile);

        int imageLength = nFileLength * sizeof(char)+1;
        buffer = (char *)malloc(imageLength);
        memset(buffer,0,nFileLength * sizeof(char)+1);

        fread(buffer,1,imageLength,pFile);
         
        return buffer;
}



PIMAGE_DOS_HEADER DOS_tou2(FILE * pFile){
       
        char * buffer = Base_memset(pFile);
       
        PIMAGE_DOS_HEADER ReadDosHeader = (PIMAGE_DOS_HEADER)buffer;
        cout<< "DOS_e_magic:\t"<<hex<< ReadDosHeader->e_magic<<endl;
        cout<<"DOS_e_lfanew:\t"<<hex<< ReadDosHeader->e_lfanew<<endl;
       

        return ReadDosHeader;
}

PIMAGE_NT_HEADERS PE_tou(FILE * pFile, PIMAGE_DOS_HEADER ReadDosHeader){
       
        char * buffer = Base_memset(pFile);
        PIMAGE_NT_HEADERS ReadNTHeader;
        ReadNTHeader = (PIMAGE_NT_HEADERS)(buffer + ReadDosHeader->e_lfanew);

        cout<<endl<<"==================PE_Signature标识================="<<endl;

        cout<<"PE_biaozhi标志:\t"<<hex<<ReadNTHeader->Signature<<endl;

        cout<<endl<<"==================PE_FileHeader标准头================="<<endl;

        cout<<"PE_Machine_CPU:\t"<<hex<<ReadNTHeader->FileHeader.Machine<<endl;
        cout<<"PE_NumberOfSections区段数:\t\t"<<hex<<ReadNTHeader->FileHeader.NumberOfSections<<endl;
        cout<<"PE_SizeOfOptionalHeader扩展头大小:\t"<<hex<<ReadNTHeader->FileHeader.SizeOfOptionalHeader<<endl;
        cout<<"PE_Characteristics 文件属性 :\t\t"<<hex<<ReadNTHeader->FileHeader.Characteristics<<endl;

        cout<<endl<<"==================PE_OptionalHeader扩展头================="<<endl;
       
        cout<<"PE_Magic 32/64:\t\t\t\t"<<hex<<ReadNTHeader->OptionalHeader.Magic<<endl;
        cout<<"PE_AddressOfEntryPoint入口:\t\t"<<hex<<ReadNTHeader->OptionalHeader.AddressOfEntryPoint<<endl;
        cout<<"PE_ImageBase 内存基址:\t\t\t"<<hex<<ReadNTHeader->OptionalHeader.ImageBase<<endl;

        cout<<"PE_SectionAlignment内齐大小:\t\t"<<hex<<ReadNTHeader->OptionalHeader.SectionAlignment<<endl;
        cout<<"PE_FileAlignment 文齐大小:\t\t"<<hex<<ReadNTHeader->OptionalHeader.FileAlignment<<endl;
        cout<<"PE_SizeOfImage 内总大小:\t\t"<<hex<<ReadNTHeader->OptionalHeader.SizeOfImage<<endl;
        cout<<"PE_SizeOfHeaders 文所头+节表大小:\t"<<hex<<ReadNTHeader->OptionalHeader.SizeOfHeaders<<endl;

        cout<<"PE_CheckSum 校验和:\t\t"<<hex<<ReadNTHeader->OptionalHeader.CheckSum<<endl;
        cout<<"PE_NumberOfRvaAndSizes 子结构体数组:\t"<<hex<<ReadNTHeader->OptionalHeader.NumberOfRvaAndSizes<<endl;


        return ReadNTHeader;
}




void qu_kuai_duan(FILE * pFile, PIMAGE_DOS_HEADER ReadDosHeader){
        cout<<endl<<"=======================遍历本程序中存在的区块段==========="<<endl;
               
        char * buffer = Base_memset(pFile);                       
        PIMAGE_NT_HEADERS ReadNTHeader;                //PE = MZ+F0                       
        ReadNTHeader = (PIMAGE_NT_HEADERS)(buffer + ReadDosHeader->e_lfanew);                        // 以上是定位PE头地址
       
        cout<<"88888888888:  "<<ReadDosHeader<<endl;                        //88888888888:  0x00440068

        PIMAGE_SECTION_HEADER ReadSectionHeader = IMAGE_FIRST_SECTION(ReadNTHeader);        //宏定义中IMAGE_FIRST_SECTION(ntheader)可以直接定位到区块段 PIMAGE_SECTION_HEADER
        PIMAGE_FILE_HEADER pFileHeader = &ReadNTHeader->FileHeader;                                                //为获取标准头中的区块段数量 NumberOfSections
       
       
        for (int i=0; i<pFileHeader->NumberOfSections;i++)
        {
                  
                cout<<"****************************"<<ReadSectionHeader[i].Name<<"***********************************"<<endl;
                cout<<"VirtualSize节内中实大小:"<<ReadSectionHeader[i].Misc.VirtualSize<<"\t";
                cout<<"VirtualAddress节区的内中起始地RVA:"<<ReadSectionHeader[i].VirtualAddress<<endl;
                cout<<"SizeOfRawData节文中大小:"<<ReadSectionHeader[i].SizeOfRawData<<"\t";
                cout<<"PointerToRawData节文中偏:"<<ReadSectionHeader[i].PointerToRawData<<endl;
                cout<<"Characteristics节属性:"<<ReadSectionHeader[i].Characteristics<<endl<<endl;

        }
        /*

        if (buffer != NULL)          // 释放内存

        {
                free(buffer);
                buffer = NULL;
        }
        // 最后不要忘记关闭文件
        fclose(pFile);
        */


}


DWORD find_pan_yi(DWORD dwRva, char* buffer){
        //dwRva                数据目录表中(的某个数据比如.rdata)的 VirtualAddress+xx32  
        //buffer        读取到的PE文件缓冲区= MZ_tou标识

        // 两个头
        PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;                                                // DOS tou
        PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);  // (pDos->e_lfanew)F0 + MZ_tou标识 = PE标识地址)

        //pSection本程序中区块表中第一个区表比如:[0]=.text, [1]=.rdata 的起始址

        //本程序中的区块段
        PIMAGE_SECTION_HEADER  pSection = IMAGE_FIRST_SECTION(pNt);                //宏定义中IMAGE_FIRST_SECTION(ntheader)可以直接定位到区块段 PIMAGE_SECTION_HEADER

        if (dwRva < pSection[0].VirtualAddress)                                                                //VirtualAddress节内中RVA 始地址,+ imageBase才是内中的真地
        {
                return dwRva;
        }

        for (int i =0; i<pNt->FileHeader.NumberOfSections;i++)                                //PE头中的标准头成员NumberOfSections区块段数量
        {               
                                                                                                                //VirtualAddress节内中RVA 始地址+ 节内中真实大小
                if (dwRva >= pSection[i].VirtualAddress && dwRva <= pSection[i].VirtualAddress+pSection[i].Misc.VirtualSize)
                {
                        //计算 wdRVA 减到 [i]区块段头还肾多远=偏yi
                        //PointerToRawData区块段在文件中的偏移
                        //结果是区块段数据始地到文件的偏移
                        return dwRva- pSection[i].VirtualAddress + pSection[i].PointerToRawData;
                }
        }
        return 0;
}


void ImportTable(FILE * pFile, PIMAGE_DOS_HEADER ReadDosHeader){

        char * buffer = Base_memset(pFile);

        PIMAGE_NT_HEADERS ReadNTHeader =  PE_tou( pFile, ReadDosHeader);
                                                                                                                                                // 1 定位第二张表(导入表): PE扩展头中最后一个成员(16子结构)起始位置+ 导入表的宏(宏=1)  
        PIMAGE_DATA_DIRECTORY pImportDir = (PIMAGE_DATA_DIRECTORY)(ReadNTHeader->OptionalHeader.DataDirectory+IMAGE_DIRECTORY_ENTRY_IMPORT);
                                                                                                                                                //
        PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)((pImportDir->VirtualAddress) + buffer); //2 实际入口地址+读取缓存的偏移(进入导入表各种Dll地址)
       
        while(pImport->Name != NULL){
               
                char *szDllName = (char*)((pImport->Name)+buffer);                                //Name is RVA

                cout<<szDllName<<":"<<endl;
                cout<<"                OriginalFirstThunk指向INT表:\t"<<pImport->OriginalFirstThunk<<endl;                // 打印各个dll的 IMAGE_IMPORT_DESCRIPTOR 结构成员
                cout<<"                FirstThunk指向IAT表:        \t"<<pImport->FirstThunk<<endl<<endl;
               
        //        cout<<"-----------某dll在INT目录中的所有成员地址RVA:"<<endl;
                PIMAGE_THUNK_DATA pInt = (PIMAGE_THUNK_DATA)((pImport->OriginalFirstThunk)+buffer); // 3 从成员 OriginalFirstThunk 进入 IMAGE_THUNK_DATA (INT表)
                                                                                                                                                                                        // IMAGE_THUNK_DATA  INT表每个成员4个字节(都是个地址)
                DWORD index = 0;
                DWORD IprotOffset = 0;

                while(pInt->u1.Ordinal != 0){                                                                        // 如果IMAGE_THUNK_DATA (INT表某个成员) 是0那么就是结束了

                        IprotOffset = pImport->OriginalFirstThunk;                                        //以进入IMAGE_DATA_DIRECTORY中的 VirtualAddress        指向的的 OriginalFirstThunk 地址=INT                                               
                       
                        cout<<"ThunkRva: "<<pImport->OriginalFirstThunk+index<<"\t\t";

                        cout<<"ThunkOffset: "<<IprotOffset+index<<"\t";
                        index +=4;                                                                                                        // 以上面的做INT做偏移4个字节一个成员 先不取值 【以下先判断在取值=函数地址 】
               
                  
                        if((pInt->u1.Ordinal & 0x80000000) != 1){                                        // 如果是1:函数序号,否则是指向PIMAGE_IMPORT_BY_NAME的函数名称(首字母) [这里只取函数名]
                               
                                                                                                                        //4 进入 PIMAGE_IMPORT_BY_NAME                        //        返回实际入口地址 + 缓冲区的偏移读数据(否则读1)
                                PIMAGE_IMPORT_BY_NAME pName = (PIMAGE_IMPORT_BY_NAME)(find_pan_yi(reinterpret_cast<unsigned long>(pInt->u1.AddressOfData), buffer)+buffer);

                       
                                cout<<"ThunkValue函数内址:"<<pInt->u1.Function<<"\t";                //函数的内存地址

                                cout<<"函数序号:"<<pName->Hint<<"\t";                                                //函数的内存地址取值: 头2个字节就是函数序号接着一个字节是函数名称首字母                       
                               
                                cout<<"API: "<<pName->Name<<endl;

                                }

                        pInt++;
                }  
                       
                pImport++;
        }
       
}


int main(){
        FILE * pFile = NULL;
        pFile = fopen("mfc001.exe","r+b");
       

        PIMAGE_DOS_HEADER ReadDosHeader = DOS_tou2(pFile);
        PIMAGE_NT_HEADERS ReadNTHeader =  PE_tou( pFile, ReadDosHeader);


        qu_kuai_duan(pFile, ReadDosHeader);
       

        ImportTable(pFile, ReadDosHeader);

        return 0;
}


IERPOIK6F[RNE@W[Q~Q3@47.png
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2023-8-11 11:09:27 | 显示全部楼层
本帖最后由 maikehong 于 2023-8-11 15:24 编辑

我好辛苦啊,PE好了10天时间才这样这个毛效果 LoadPE 导入表数据 排序一样的一分不差。 给个点赞 评分送花,非常感谢!!!,我的偶像甲鱼大神也点个赞把!!!
2D1M9I~4QVK}_WU90[KG4(V.png
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-22 02:21

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表