PE文件分析——重定义表

  在PE文件的字节码中有一些写死的地址,如:

img

 使用这些内存地址需要在当前的模块下,如果该PE文件在其他模块下被加载,则其内存地址对应的内容并非当前的内容。

而这些地址又写死了,所以需要修复,为了方便修复,PE提供了一个重定位表,来存储这些写死的地址对应的信息,帮助修复。

  

img

 

 

重定义表位于可选PE头的数据目录表的第六个元素,也就是索引值为5的结构体

获得重定义表

和导出表导入表相同,得到PE头的数据目录表后访问具体的某个数组元素后,进入IMAGE_DATA_DIRECTORY结构体,然后访问VirtualAddress字段来获取地址,然后强转使用。

重定义表结构体

typedef struct _IMAGE_BASE_RELOCATION {
  DWORD   VirtualAddress;
  DWORD   SizeOfBlock;
// WORD   TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;

img

 

 

sizeofBlock存的同一颜色的块的大小,VitualAddress存放地址RVA

重定义表存放数据流程

先将数据地址的第一个拿来用,比如说第一个数据地址是0x40001,那么第一个表的VirtualAddress 就是0x40000,然后第一个块下面存放0x1,最大的块的内存为0x1000,超过了就要换下一个块了

VirtualAddress + 存放的偏移地址 = 需要修复的地址RVA

判断地址是否需要修复

在重定义表结构体下面,都是WORD类型的数据,两个字节两个字节的16位存储,取16位的前四位,如果等于3表示该地址需要修复。

也就是说存放的地址一共16位,前四位是拿来判断该地址是否可以修复,后12位才是来存放地址,真正的地址RVA = 后12位+VirtualAddress

 

代码解析重定义表

1 拿到可选PE头的对应的数据目录表的第六个重定义表的指向结构体

2 拿到指向结构体对应地址的第一个重定义表的第一个区块

3 利用区块自增来访问每一个区块

4 利用区块里面的访问数据来一个一个访问数据

 

void CPeUtil::test()
{
  //拿到数据目录表
  IMAGE_DATA_DIRECTORY directory = pOptionalHeader->DataDirectory[5];
  //拿到第一个重定位表
  PIMAGE_BASE_RELOCATION RelocationTable = PIMAGE_BASE_RELOCATION(RvaToFoa(directory.VirtualAddress) + FileBuff);

  //开始遍历重定位表
  while (1)
  {
      if (RelocationTable->VirtualAddress == 0)
      {
          break;
      }
      //拿到重定义表的数据的大小
      DWORD SizeNumber = (RelocationTable->SizeOfBlock - 8) / 2;
      //开始遍历重定义表块的内容
      //先拿到第一个数据的地址
      WORD* Offset = (WORD*)RelocationTable + 4;
      for (int i = 0; i < SizeNumber; i++)
      {
          if (*Offset & 0x3000 == 0x3000)
          {
              DWORD Rva = RelocationTable->VirtualAddress + *Offset&0x0FFF;
              printf("Rva = %x\n",Rva);
          }
          Offset++;
      }
      RelocationTable = RelocationTable + RelocationTable->SizeOfBlock;
  }
}