欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创][开源]Win32控制台解析PE文件

[原创][开源]Win32控制台解析PE文件

推荐下这个帖子 希望能帮助你更好理解
手查PE导出表:http://bbs.pediy.com/showthread.php?p=1403106#post1403106
手查PE重定向:http://bbs.pediy.com/showthread.php?t=206072
代码500行 除了必要的计算文件偏移必须要封装外  其它都在main函数内  我不是故意的...   真的...
部分效果截图:




---------------------------------
进入正题:
目前已解析的有:


  • 基本PE头信息
  • 区段
  • 目录

  • - 输出表
  •     - 输入表 
  •     - 资源  
  •     - 重定位

  • 位置计算器

  • 计算文件偏移(也算是最整个代码的核心吧):
    
    DWORD CalcOffset(DWORD Rva,PIMAGE_NT_HEADERS32  pNtH)
    
    {
    
      //PIMAGE_NT_HEADERS32 pnt=pNtH;
    
      PIMAGE_SECTION_HEADER pSecHTemp=IMAGE_FIRST_SECTION(pNtH);//区段头
    
      int index=0;
    
      
    
      while (!(Rva>=pSecHTemp->VirtualAddress&&
    
        Rva<pSecHTemp->VirtualAddress+pSecHTemp->SizeOfRawData))
    
      {
    
        //找完所有区段还没有找到
    
        if (index>pNtH->FileHeader.NumberOfSections)
    
        {      
    
          return Rva;
    
        }
    
        ++index;
    
        ++pSecHTemp;
    
      }
    
      v=Rva-pSecHTemp->VirtualAddress+pSecHTemp->PointerToRawData;;
    
      return v;
    
    }
    
    

    这部分代码依旧是计算文件偏移,重定位特需 和上面稍有些不同:
    
    DWORD RelCalcOffset(DWORD Rva,PIMAGE_NT_HEADERS32 pNtH,char *Tempbuf,PCHAR pName=NULL,PCHAR pData=NULL,int Flag=NULL)
    
    {
    
      //PIMAGE_NT_HEADERS32 pnt=pNtH;
    
      PIMAGE_SECTION_HEADER pSecHTemp=IMAGE_FIRST_SECTION(pNtH);//区段头
    
      int index=0;
    
      while (!(Rva>=pSecHTemp->VirtualAddress&&
    
        Rva<pSecHTemp->VirtualAddress+pSecHTemp->SizeOfRawData))
    
      {
    
        //找完所有区段还没有找到
    
        if (index>pNtH->FileHeader.NumberOfSections)
    
        {
    
          //  m_Section=L"部首";
    
          ////  DWORD a[5];
    
          //  _memccpy(&Address,pSecHTemp,6,24);
    
          //  //Address=(int);
    
          if (Flag==2)
    
          {
    
            return Rva-pNtH->OptionalHeader.ImageBase;
    
          }
    
          return Rva;
    
        }
    
        ++index;
    
        ++pSecHTemp;
    
      }
    
      //获取区段名
    
      if (pName!=NULL)
    
      {
    
        //return Rva-pNtH->OptionalHeader.ImageBase;
    
        memcpy(pName,pSecHTemp->Name,8);
    
      }
    
      v=Rva-pSecHTemp->VirtualAddress+pSecHTemp->PointerToRawData;;
    
      DWORD iiii=(long)Tempbuf+v;
    
      //获取数据
    
      if (pData!=NULL)
    
      {
    
        //if (Flag==NULL)
    
        //{
    
        //  return Rva-pNtH->OptionalHeader.ImageBase;
    
        //}
    
        if (Flag==2)
    
        {
    
          return Rva-pNtH->OptionalHeader.ImageBase;
    
        }
    
        //flag 为1时 
    
        memcpy(pData,PCHAR((long)Tempbuf+v),10);
    
      }
    
      return v;
    
    }
    
    

    打开并判断是否为PE文件:
    
      //TCHAR FileName[] = _T("C:\\Users\\Administrator\\Desktop\\PEText.exe");C:\Users\Administrator\Desktop\MFCLibrary1Dll.dll
    
       //  TCHAR FileName[] = _T("D:\\Program Files (x86)\\Tencent\\WeChat\\WeChat.exe");
    
      //TCHAR FileName[] = _T("D:\\Program Files\\Tencent\\QQ\\Bin\\QQScLauncher.exe");
    
      TCHAR FileName[] = _T("C:\\Users\\Administrator\\Desktop\\Inpaint.exe");
    
      //TCHAR FileName[] = _T("C:\\Users\\Administrator\\Desktop\\MFCLibrary1Dll.dll");?
    
      char* buf = nullptr;
    
      //得到文件句柄
    
      HANDLE hFile = CreateFile(
    
        FileName, GENERIC_READ|GENERIC_WRITE,
    
        0,
    
        NULL,
    
        OPEN_EXISTING,
    
        FILE_ATTRIBUTE_NORMAL,
    
        NULL);
    
      //得到文件大小
    
      DWORD dwFileSize = 
    
        GetFileSize(hFile, NULL);
    
      DWORD ReadSize = 0;
    
      buf = new char[dwFileSize];
    
      //将文件读取到内存
    
      ReadFile(hFile, buf, dwFileSize, &
    
        ReadSize, NULL);
    
      PIMAGE_DOS_HEADER pDosH=(PIMAGE_DOS_HEADER)buf;
    
      if(IMAGE_DOS_SIGNATURE!=pDosH->e_magic)
    
      {
    
        printf_s("不是DOS头");
    
        return 0;
    
      }
    
      printf_s("DOS头:0x%X\n",pDosH->e_magic);
    
      printf_s("EXE文件的偏移头:0x%X\n",pDosH->e_lfanew);
    
      //获取NT头
    
      PIMAGE_NT_HEADERS32 pNtH=(PIMAGE_NT_HEADERS32)(buf+pDosH->e_lfanew);  
    
      //PIMAGE_NT_HEADERS32 pNtH=(PIMAGE_NT_HEADERS32)buf;  
    
      if (pNtH->Signature!=IMAGE_NT_SIGNATURE)
    
      {
    
        printf_s("不是NT头");
    
        return 0;
    
      }
    
    

    基本PE头信息:
    
      printf_s("文件头:\n");
    
      printf_s("文件的运行平台:0x%X\n",pNtH->FileHeader.Machine);
    
      printf_s("区段的数量:0x%X\n",pNtH->FileHeader.NumberOfSections);
    
      printf_s("文件的创建
    
      printf_s("符号表偏移:0x%X\n",pNtH->FileHeader.PointerToSymbolTable);
    
      printf_s("符号个数:0x%X\n",pNtH->FileHeader.NumberOfSymbols);
    
      printf_s("扩展头大小:0x%X\n",pNtH->FileHeader.SizeOfOptionalHeader);
    
      printf_s("PE文件的一些属性:0x%X\n",pNtH->FileHeader.Characteristics);
    
      printf_s("---------------------------------------------------------\n");
    
      printf_s("扩展头:\n");
    
      printf_s("标志字:0x%X\n",pNtH->OptionalHeader.Magic);
    
      printf_s("所有代码区段总大小:0x%X\n",pNtH->OptionalHeader.SizeOfCode);
    
      printf_s("已初始化的数据总大小:0x%X\n",pNtH->OptionalHeader.SizeOfInitializedData);
    
      printf_s("未初始化的数据总大小:0x%X\n",pNtH->OptionalHeader.SizeOfUninitializedData);
    
      printf_s("入口点:0x%X\n",pNtH->OptionalHeader.AddressOfEntryPoint);
    
      printf_s("代码基址:0x%X\n",pNtH->OptionalHeader.BaseOfCode);
    
      printf_s("数据基址:0x%X\n",pNtH->OptionalHeader.BaseOfData);
    
      printf_s("镜像基址:0x%X\n",pNtH->OptionalHeader.ImageBase);
    
      printf_s("块对齐:0x%X\n",pNtH->OptionalHeader.SectionAlignment);
    
      printf_s("文件对齐:0x%X\n",pNtH->OptionalHeader.FileAlignment);
    
      printf_s("镜像大小:0x%X\n",pNtH->OptionalHeader.SizeOfImage);
    
      printf_s("部首大小:0x%X\n",pNtH->OptionalHeader.SizeOfHeaders);
    
      printf_s("校验和:0x%X\n",pNtH->OptionalHeader.CheckSum);
    
      printf_s("子系统:0x%X\n",pNtH->OptionalHeader.Subsystem);
    
      printf_s("DLL特征:0x%X\n",pNtH->OptionalHeader.DllCharacteristics);
    
      printf_s("栈可增长最大值:0x%X\n",pNtH->OptionalHeader.SizeOfStackReserve);
    
      printf_s("栈初始值:0x%X\n",pNtH->OptionalHeader.SizeOfHeapCommit);
    
      printf_s("堆可增长最大值:0x%X\n",pNtH->OptionalHeader.SizeOfHeapReserve);
    
      printf_s("堆初始值:0x%X\n",pNtH->OptionalHeader.SizeOfHeapCommit);
    
      printf_s("RVA数及大小:0x%X\n",pNtH->OptionalHeader.NumberOfRvaAndSizes);
    
    

    目录:
    
      printf_s("输出表  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[0].VirtualAddress,
    
                          pNtH->OptionalHeader.DataDirectory[0].Size);
    
      printf_s("输入表  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[1].VirtualAddress,
    
        pNtH->OptionalHeader.DataDirectory[1].Size);
    
      printf_s("资源  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[2].VirtualAddress,
    
        pNtH->OptionalHeader.DataDirectory[2].Size);
    
      printf_s("异常处理程序表  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[3].VirtualAddress,
    
        pNtH->OptionalHeader.DataDirectory[3].Size);
    
      printf_s("安全  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[4].VirtualAddress,
    
        pNtH->OptionalHeader.DataDirectory[4].Size);
    
      printf_s("重定位  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[5].VirtualAddress,
    
        pNtH->OptionalHeader.DataDirectory[5].Size);
    
      printf_s("版权  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[6].VirtualAddress,
    
        pNtH->OptionalHeader.DataDirectory[6].Size);
    
      
    
      printf_s("全局指针  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[7].VirtualAddress,
    
        pNtH->OptionalHeader.DataDirectory[7].Size);
    
      
    
      printf_s("线程局部存储初始化节(TLS)  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[8].VirtualAddress,
    
        pNtH->OptionalHeader.DataDirectory[8].Size);
    
      
    
      printf_s("载入配置  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[9].VirtualAddress,
    
        pNtH->OptionalHeader.DataDirectory[9].Size);
    
      
    
      printf_s("载入范围  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[10].VirtualAddress,
    
        pNtH->OptionalHeader.DataDirectory[10].Size);
    
      
    
      printf_s("导入地址表(IAT)  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[11].VirtualAddress,
    
        pNtH->OptionalHeader.DataDirectory[11].Size);
    
      
    
      printf_s("延迟输入  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[12].VirtualAddress,
    
        pNtH->OptionalHeader.DataDirectory[12].Size);
    
      
    
      printf_s("COM信息  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[13].VirtualAddress,
    
        pNtH->OptionalHeader.DataDirectory[13].Size);
    
      
    
      printf_s("保留  RVA:0x%X  SIZE:0x%X\n",pNtH->OptionalHeader.DataDirectory[14].VirtualAddress,
    
        pNtH->OptionalHeader.DataDirectory[14].Size);
    
    

    区段:
    
    PIMAGE_SECTION_HEADER pSecH=IMAGE_FIRST_SECTION(pNtH);
    
      int i=0;
    
      while (i<pNtH->FileHeader.NumberOfSections)
    
      {
    
        printf_s("名称 %s  ",pSecH->Name);
    
        printf_s("VOffset %08X  ",pSecH->VirtualAddress);
    
        printf_s("VSize %08X  ",pSecH->Misc.VirtualSize);
    
        printf_s("ROffset %08X  ",pSecH->PointerToRawData);
    
        printf_s("Rsize %08X  ",pSecH->SizeOfRawData);
    
        printf_s("标志 %08X  \n",pSecH->Characteristics);
    
        ++pSecH;
    
        i++;
    
      }
    
    

    输出表:
    
    PIMAGE_OPTIONAL_HEADER32 pOptH;//可选头
    
    //  PIMAGE_SECTION_HEADER pSecH;//区段头
    
      PIMAGE_DATA_DIRECTORY pDatD;//数据目录
    
      PIMAGE_EXPORT_DIRECTORY pExpD;// 导出表数据
    
      
    
      //获取可选头数据
    
      pOptH=&(pNtH->OptionalHeader);
    
      //获取数据目录
    
      pDatD=&(pOptH->DataDirectory[0]);
    
      //获取导出表数据
    
      pExpD=(PIMAGE_EXPORT_DIRECTORY)(buf+CalcOffset(pDatD->VirtualAddress,pNtH));
    
      //判断是否有导出函数
    
      if (pExpD->NumberOfFunctions==0)
    
      {
    
        printf_s("无导出函数");
    
      }
    
      //取出三个表的函数地址
    
      PDWORD pFunAddr=(PDWORD)(buf+CalcOffset(pExpD->AddressOfFunctions,pNtH));  //函数地址
    
      PDWORD pFunNameAddr=(PDWORD)(buf+CalcOffset(pExpD->AddressOfNames,pNtH));  //函数名地址
    
      PWORD pOrdinalAddr=(PWORD)(buf+CalcOffset(pExpD->AddressOfNameOrdinals,pNtH));//函数序号地址
    
      
    
      DWORD NumberOfFun=pExpD->NumberOfFunctions;
    
      DWORD NumberOfName=pExpD->AddressOfNames;  
    
      printf_s("函数偏移表地址:%08X\n",CalcOffset(pDatD->VirtualAddress,pNtH));
    
      printf_s("函数地址:%08X\n",pExpD->AddressOfFunctions);
    
      printf_s("函数名序号地址:%08X\n",pExpD->AddressOfNameOrdinals);
    
      printf_s("函数名称地址:%08X\n",pExpD->AddressOfNames);
    
      printf_s("基址:%08X\n",pExpD->Base);
    
      printf_s("特征值:%08X\n",pExpD->Characteristics);
    
      printf_s("名称:%08X\n",pExpD->Name);
    
    //  printf_s("名称:%08X",pExpD->);
    
      printf_s("函数数量:%08X\n",pExpD->NumberOfFunctions);
    
      printf_s("函数名数量:%08X\n",pExpD->NumberOfNames);
    
      for (DWORD i=0;i<NumberOfFun;i++)
    
      {
    
        
    
        //如果是无效函数 进行下一次
    
        if (!pFunAddr[i])
    
        {
    
          continue;
    
        }
    
        //此时为有效函数  在序号表查找是否有这个序号 用以判断是函数名导出函数序号导出
    
        DWORD j=0;
    
        for (;j<NumberOfName;j++)
    
        {
    
          if (i==pOrdinalAddr[j])
    
          {
    
            break;
    
          }
    
        }
    
        //size_t
    
        //找到了 这是一个函数名导出的函数
    
        if (j!=NumberOfName)
    
        {
    
        //  (PCHAR)(buf+CalcOffset(pFunNameAddr[j],pNtH));
    
          printf_s("序号:%d  ",pOrdinalAddr[j]+pExpD->Base);
    
          printf_s("RVA:%08X  ",pFunAddr[i]);
    
          printf_s("偏移:%08X  ",CalcOffset(pFunAddr[i],pNtH));
    
          printf_s("函数名:%s\n",(buf+CalcOffset(pFunNameAddr[j],pNtH)));
    
        }
    
        //没有找到  这是一个序号导出的函数 没有名字
    
        else
    
        {
    
          printf_s("%d",i+pExpD->Base);
    
          printf_s("%08X",pFunAddr[i]);
    
          if (strcmp((buf+CalcOffset(pFunNameAddr[i],pNtH)),"")==0)
    
          {
    
            printf_s("没有");
    
          }
    
          //else
    
          //{
    
          //  printf_s("%s\n",(buf+CalcOffset(pFunNameAddr[i],pNtH)));
    
          //}
    
          
    
        }
    
      }
    
    

    输入表:
    
    PIMAGE_IMPORT_DESCRIPTOR  pImpD;
    
      pDatD=&(pOptH->DataDirectory[1]);
    
      pImpD=(PIMAGE_IMPORT_DESCRIPTOR)(buf+CalcOffset(pDatD->VirtualAddress,pNtH));
    
      //第一层循环 每个导入的DLL依次解析
    
      while(pImpD->Name)
    
      {
    
        //DLL名称相关
    
        printf_s("DLL名称:%s  ",(buf+CalcOffset(pImpD->Name,pNtH)));
    
        printf_s("INT(输入名称表):%08X  ",pImpD->OriginalFirstThunk);
    
        printf_s("日期时间标志:%08X  ",pImpD->TimeDateStamp);
    
        printf_s("ForwarderChain:%08X  ",pImpD->ForwarderChain);
    
        printf_s("名称:%08X  ",pImpD->Name);
    
        printf_s("FirstThunk:%08X\n",pImpD->FirstThunk);
    
        //从获取的DLL导入函数地址表 IAT 计算偏移
    
        PIMAGE_THUNK_DATA32 pInt=(PIMAGE_THUNK_DATA32)(buf+CalcOffset(pImpD->FirstThunk,pNtH));
    
        
    
        //循环解析导入地址表IAT
    
        while(pInt->u1.Function)
    
        {
    
          
    
          DWORD ThunkOffest=CalcOffset(pImpD->OriginalFirstThunk,pNtH);
    
          //判断最高位是否为1 不为1按名称导入
    
          if (!IMAGE_SNAP_BY_ORDINAL32(pInt->u1.Ordinal))
    
          {
    
            
    
            //找到函数序号名地址 并将其取出  
    
            printf_s("ThunkRVA:%08X  ",pImpD->OriginalFirstThunk);
    
            printf_s("Thunk偏移:%08X  ",ThunkOffest);
    
            printf_s("Thunk值:%08X  ",pInt->u1.AddressOfData);
    
            PIMAGE_IMPORT_BY_NAME pFunName=(PIMAGE_IMPORT_BY_NAME)(buf+CalcOffset(pInt->u1.AddressOfData,pNtH));
    
            printf_s("提示:%04X  ",pFunName->Hint);
    
            printf_s("API名称:%s\n",pFunName->Name);
    
            //每次偏移四个字节
    
            pImpD->OriginalFirstThunk+=4;
    
            ThunkOffest+=4;
    
          }
    
          else
    
          {
    
            //找到函数序号名地址 并将其取出  
    
          //  PIMAGE_IMPORT_BY_NAME pFunName=(PIMAGE_IMPORT_BY_NAME)(buf+CalcOffset(pInt->u1.AddressOfData,pNtH));
    
            printf_s("ThunkRVA:%08X  ",pImpD->OriginalFirstThunk);
    
            printf_s("Thunk偏移:%08X  ",ThunkOffest);
    
            printf_s("Thunk值:%08X  ",pInt->u1.AddressOfData);
    
            printf_s("提示:-  ");
    
            printf_s("序号:%4xH  %4dD\n",pInt->u1.Ordinal&0x7fffffff,pInt->u1.Ordinal&0x7fffffff);
    
          }
    
          pInt++;
    
        }
    
        system("pause");
    
        pImpD++;
    
      }
    
    

    资源:
    
    //获取资源表
    
       pDatD=&(pOptH->DataDirectory[2]);
    
       PIMAGE_RESOURCE_DIRECTORY pResD=(PIMAGE_RESOURCE_DIRECTORY)(buf+CalcOffset(pDatD->VirtualAddress,pNtH));
    
       DWORD ReSize=pResD->NumberOfIdEntries+pResD->NumberOfNamedEntries;
    
       PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDE=(PIMAGE_RESOURCE_DIRECTORY_ENTRY)((long)pResD+sizeof(IMAGE_RESOURCE_DIRECTORY));
    
       
    
       char ResourceName[15][16]={"鼠标指针","位图","图标","菜单","对话框",
    
                    "字符串列表","字体目录","字体","快捷键","非格式化资源",
    
                    "消息列表","鼠标指针组","图标组","版本信息"};
    
       printf_s("第一层[名称条目]->:%04X   ",pResDE->NameIsString);  
    
       printf_s("第一层[ID条目]->:%04X   \n",ReSize);    
    
       for (DWORD FirstOrder=0;FirstOrder<ReSize;FirstOrder++)
    
       {
    
         //第一层 假如是字符串标识
    
         if (pResDE->NameIsString==1)
    
         {
    
           PIMAGE_RESOURCE_DIR_STRING_U pREsDStrU=(PIMAGE_RESOURCE_DIR_STRING_U)((long)pResD+pResDE->NameOffset);
    
           printf_s("资源类型名:%s",pREsDStrU->NameString);
    
         }
    
         //假如是一直类型 用序号作为标识
    
         else
    
         {
    
           switch (pResDE->Name)
    
           {
    
           case 0x1:
    
             printf_s("第一层:%s\n",ResourceName[0]);
    
             break;        
    
           case 0x2:        
    
             printf_s("第一层:%s\n",ResourceName[1]);
    
             break;        
    
           case 0x3:        
    
             printf_s("第一层:%s\n",ResourceName[2]);
    
             break;        
    
           case 0x4:        
    
             printf_s("第一层:%s\n",ResourceName[3]);
    
             break;        
    
           case 0x5:        
    
             printf_s("第一层:%s\n",ResourceName[4]);
    
             break;        
    
           case 0x6:        
    
             printf_s("第一层:%s\n",ResourceName[5]);
    
             break;        
    
           case 0x7:        
    
             printf_s("第一层:%s\n",ResourceName[6]);
    
             break;        
    
           case 0x8:        
    
             printf_s("第一层:%s\n",ResourceName[7]);
    
             break;        
    
           case 0x9:        
    
             printf_s("第一层:%s\n",ResourceName[8]);
    
             break;        
    
           case 0xA:        
    
             printf_s("第一层:%s\n",ResourceName[9]);
    
             break;        
    
           case 0xB:        
    
             printf_s("第一层:%s\n",ResourceName[10]);
    
             break;        
    
           case 0xC:        
    
             printf_s("第一层:%s\n",ResourceName[11]);
    
             break;        
    
           case 0xE:        
    
             printf_s("第一层:%s\n",ResourceName[12]);
    
             break;        
    
           case 0x10:        
    
             printf_s("第一层:%s\n",ResourceName[13]);
    
             break;        
    
           default:
    
             printf_s("第一层:%d\n",pResDE->Name);
    
             break;
    
           }
    
         }
    
         //找第二层 注意OFFEST 是相对于pResD也就是资源开始位置的偏移
    
         PIMAGE_RESOURCE_DIRECTORY pResD2=(PIMAGE_RESOURCE_DIRECTORY)((long)pResD+pResDE->OffsetToDirectory);
    
         DWORD ReSize2=pResD2->NumberOfIdEntries+pResD2->NumberOfNamedEntries;
    
         PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDE2=(PIMAGE_RESOURCE_DIRECTORY_ENTRY)((long)pResD2+sizeof(IMAGE_RESOURCE_DIRECTORY));
    
         printf_s("第一层[名称条目]->:%04X   ",pResD2->NumberOfNamedEntries);  
    
         printf_s("第一层[ID条目]->:%04X   \n",pResD2->NumberOfIdEntries);
    
         for (DWORD SecondOrder=0;SecondOrder<ReSize2;SecondOrder++)
    
         {
    
           if (pResDE2->DataIsDirectory==1)
    
           {
    
             //输出第二层资源的标识看是数字还是字符串
    
             if (pResDE2->NameIsString==1)
    
             {
    
               PIMAGE_RESOURCE_DIR_STRING_U pREsDStrU2=(PIMAGE_RESOURCE_DIR_STRING_U)((long)pResD+pResDE2->NameOffset);
    
               //输出资源类型名字
    
               printf("第二层->资源类型名:%ls   ",pREsDStrU2->NameString);
    
             }
    
             else
    
             {
    
               printf_s("第二层->资源类型名ID:%d   ",pResDE2->Id);        
    
             }
    
             //解析第三层 
    
             PIMAGE_RESOURCE_DIRECTORY pResD3=(PIMAGE_RESOURCE_DIRECTORY)((long)pResD+pResDE2->OffsetToDirectory);
    
             PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDE3=(PIMAGE_RESOURCE_DIRECTORY_ENTRY)((long)pResD3+sizeof(IMAGE_RESOURCE_DIRECTORY));
    
             PIMAGE_RESOURCE_DATA_ENTRY pResDataE=(PIMAGE_RESOURCE_DATA_ENTRY)((long)pResD+pResDE3->OffsetToData);
    
             
    
             printf_s("第三层->RVA:%08X   ",pResDataE->OffsetToData);
    
             printf_s("第三层->偏移:%08X   ",(CalcOffset(pResDataE->OffsetToData,pNtH)));
    
             printf_s("第三层->大小:%08X\n",pResDataE->Size);
    
           }
    
           else
    
           {
    
             break;
    
           }
    
           pResDE2++;
    
         }
    
         pResDE++;
    
       }
    
    

    重定位:
    
     pDatD=&(pOptH->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);//也就是5
    
       PIMAGE_BASE_RELOCATION pBasR=(PIMAGE_BASE_RELOCATION)((long)buf+CalcOffset(pDatD->VirtualAddress,pNtH));
    
       RELOCAREAINFO Temp={0};
    
       int j=0;
    
       while (pBasR->VirtualAddress)
    
       {
    
         printf_s("索引:%d  ",j);
    
         //区域的虚拟地址输出或获取到其他地方
    
         Temp.dwAreaRVA=pBasR->VirtualAddress;
    
         
    
         RelCalcOffset(Temp.dwAreaRVA,pNtH,buf,Temp.szSectionName,NULL);
    
         printf_s("%s   ",Temp.szSectionName);
    
         printf_s("%08X   ",pBasR->VirtualAddress);
    
         Temp.NumberOfReloc=(pBasR->SizeOfBlock-sizeof(IMAGE_BASE_RELOCATION))/2;
    
         printf_s("%02X  %d\n", Temp.NumberOfReloc, Temp.NumberOfReloc);
    
         DWORD dwCount=Temp.NumberOfReloc;
    
         //得到第三个成员的TYPEOFFEST起始位置 之后开始循环获取重定位信息
    
         PTYPEOFFSET pOffset=(PTYPEOFFSET)((long)pBasR+sizeof(IMAGE_BASE_RELOCATION));
    
         for (DWORD i=0;i<dwCount;i++)
    
         {
    
           printf_s("索引:%d  ",i);
    
           RELOCINFO RelocInfoTemp={0};
    
           //需要重定位的虚拟地址(RVA)
    
           RelocInfoTemp.dwRelocRVA=Temp.dwAreaRVA+pOffset->Offset;
    
           printf_s("RVA:%08X  ", RelocInfoTemp.dwRelocRVA);
    
           //根据相对虚拟地址算出的文件偏移
    
           RelocInfoTemp.dwOffset=RelCalcOffset(RelocInfoTemp.dwRelocRVA,pNtH,NULL,NULL);
    
            printf_s("偏移:%08X  ", RelocInfoTemp.dwOffset);
    
           //重定位方式
    
           RelocInfoTemp.bType=pOffset->Type;
    
           printf_s("类型:HIGHLOW (%d)  ",RelocInfoTemp.bType);
    
           //从算出的文件偏移取出的数据 这个数据就是需要重定位的虚拟地址(VA)
    
           RelocInfoTemp.dwRelocValue=*(PDWORD)((long)buf+RelocInfoTemp.dwOffset);
    
           printf_s("FAR地址:%08X  ",RelocInfoTemp.dwRelocValue);
    
           //RelocInfoTemp.bData
    
           DWORD VA= RelCalcOffset(RelocInfoTemp.dwRelocValue,pNtH,buf,NULL,(PCHAR)RelocInfoTemp.bData,2);
    
           RelCalcOffset(VA,pNtH,buf,NULL,(PCHAR)RelocInfoTemp.bData,1);
    
           for (int i=0;i<10;i++)
    
           {
    
             //判断是否在有效字符32-126区间内
    
             if (32<=(RelocInfoTemp.bData)[0]&&(RelocInfoTemp.bData)[0]<=126)
    
             {
    
               printf_s("%c",(RelocInfoTemp.bData)[i]);
    
             }
    
             else
    
             {
    
              printf_s("%02X ",(RelocInfoTemp.bData)[i]);
    
             }
    
           }
    
            printf_s("\n");
    
           pOffset++;
    
         }
    
        
    
         //讲一个区域重定位信息添加
    
        VecRelocAreaInfo.push_back(Temp);
    
        pBasR=(PIMAGE_BASE_RELOCATION)((long)pBasR+pBasR->SizeOfBlock);
    
        j++;
    
       }
    
    

    这个虽然不实用 但让你进一步理解PE文件结构及运作原理还是能够的
    周末有空我再发MFC版吧
    ---------------------
    源码:
    PE.rar

    上传的附件 1.png
    2.png
    3.png
    4.png
    PE.rar

    注意:上传附件及图片大小不得大于30M。

    ⚠️ 版权声明:
    本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
    未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

    0 0 0 举报
    复制成功