最近,写了东西的时候用到了一个函数感觉很不错!MS就是好嘛!ImageRvaToVa就是把物理地址转换为虚拟地址的函数!可是最近到网络上找了好长时间也没发现有多少人用它!也许是我RP问题,我想不会吧!
   

写代码的时候,忽然要用到虚拟地址转换物理地址的函数,我顺手在VC里输入了ImageVaToRva,惯例嘛!怎么来的怎么回去!编译了好多遍就是没这个函数,不会吧!我觉得MS应该不会忘记写了的!怎么回的来,回不去了呢?找遍了"IMAGEHLP.H"这个文件也没找到,晕倒!开始发现MS真不是玩意,才想起来很多代码中有RvaToOffset或OffsetToRva自写的函数,真没想到MS真的没做,找了找发现非要拿区段信息才行,我看到很多朋友都说函数有问题,我也看了是乎有点问题,不相信!MS它也就拿区段干活!
拿OD看了一下!傻了!MS还真的是拿区段干活!在ImageRvaToVa中有ImageRvaToSection函数!
为了让我的ImageVaToRva重出江湖,就分析了一下:

//-------------------------------------------------------------------------------------------

76C67554 >  8BFF            mov     edi, edi
76C67556    55              push    ebp
76C67557    8BEC            mov     ebp, esp
76C67559    56              push    esi                        ;保存ESI
76C6755A    8B75 14         mov     esi, dword ptr [ebp+14]    ;将区段结构指针放入ESI
76C6755D    85F6            test    esi, esi                   ;如果ESI是否为0
76C6755F    57              push    edi                        ;保存EDI
76C67560    8B7D 10         mov     edi, dword ptr [ebp+10]    ;EDI=要转换值
76C67563    74 16           je      short 76C6757B             ;ESI为0,直接转换
76C67565    8B0E            mov     ecx, dword ptr [esi]       ;不为空,将结构地址放入ECX
76C67567    85C9            test    ecx, ecx                   ;ECX为空直接转换
76C67569    74 10           je      short 76C6757B
76C6756B    8B41 0C         mov     eax, dword ptr [ecx+C]   
76C6756E    3BF8            cmp     edi, eax
76C67570    72 09           jb      short 76C6757B
76C67572    8B51 10         mov     edx, dword ptr [ecx+10]
76C67575    03D0            add     edx, eax
76C67577    3BFA            cmp     edi, edx
76C67579    72 0E           jb      short 76C67589
76C6757B    57              push    edi                        ;要转换值
76C6757C    FF75 0C         push    dword ptr [ebp+C]          ;BASE基地址
76C6757F    FF75 08         push    dword ptr [ebp+8]          ;NT结构指针
76C67582    E8 8AFFFFFF     call    ImageRvaToSection
76C67587    8BC8            mov     ecx, eax
76C67589    85C9            test    ecx, ecx                      ;如果为0就跳出去
76C6758B    74 13           je      short 76C675A0
76C6758D    85F6            test    esi, esi
76C6758F    74 02           je      short 76C67593
76C67591    890E            mov     dword ptr [esi], ecx        ;保存区段地址
76C67593    8B41 14         mov     eax, dword ptr [ecx+14]     ;EAX=物理地址
76C67596    2B41 0C         sub     eax, dword ptr [ecx+C]      ;EAX=物理地址-虚拟地址
76C67599    0345 0C         add     eax, dword ptr [ebp+C]      ;EAX=EAX+基地址BASE
76C6759C    03C7            add     eax, edi                    ;EAX=EAX+要转换的值
76C6759E    EB 02           jmp     short 76C675A2
76C675A0    33C0            xor     eax, eax
76C675A2    5F              pop     edi
76C675A3    5E              pop     esi
76C675A4    5D              pop     ebp
76C675A5    C2 1000         retn    10


ImageRvaToSection如下:
//------------------------------------------------------------------------------------
76C67511 >  8BFF            mov     edi, edi
76C67513    55              push    ebp
76C67514    8BEC            mov     ebp, esp
76C67516    8B4D 08         mov     ecx, dword ptr [ebp+8]      ;NT结构地址
76C67519    0FB741 14       movzx   eax, word ptr [ecx+14]      ;EAX=SizeOfOptionalHeader
76C6751D    8D4408 18       lea     eax, dword ptr [eax+ecx+18] ;EAX=区段开始地址
76C67521    0FB749 06       movzx   ecx, word ptr [ecx+6]       ;获得区段总数为6
76C67525    56              push    esi
76C67526    33F6            xor     esi, esi
76C67528    85C9            test    ecx, ecx                     
76C6752A    57              push    edi
76C6752B    76 1A           jbe     short 76C67547               ;如果区段数小于等于0,那么就跳出去
76C6752D    8B50 0C         mov     edx, dword ptr [eax+C]       ;EDX=区段虚拟地址
76C67530    3955 10         cmp     dword ptr [ebp+10], edx      ;与RVA比较如果大于就跳
76C67533    72 0A           jb      short 76C6753F
76C67535    8B78 10         mov     edi, dword ptr [eax+10]      ;不大于,将物理区段大小放入EDI
76C67538    03FA            add     edi, edx                     ;EDI=物理区段大小+虚拟区段地址
76C6753A    397D 10         cmp     dword ptr [ebp+10], edi      ;EDI是否大于RVA值,如果大于就跳出去
76C6753D    72 0A           jb      short 76C67549
76C6753F    83C0 28         add     eax, 28                      ;不大于计算下一区段
76C67542    46              inc     esi
76C67543    3BF1            cmp     esi, ecx
76C67545  ^ 72 E6           jb      short 76C6752D
76C67547    33C0            xor     eax, eax
76C67549    5F              pop     edi
76C6754A    5E              pop     esi
76C6754B    5D              pop     ebp
76C6754C    C2 0C00         retn    0C

//---------------------------------------------------------------------------
大致分析了,如上,我自己效仿了个函数,把虚拟地址转换为物理地址的:

LPVOID CPNExeInfo::ImageVaToRva(PIMAGE_NT_HEADERS _nt,LPVOID Base,ULONG va)
{

  DWORD dwVa   =va;
  PIMAGE_SECTION_HEADER begin=(PIMAGE_SECTION_HEADER)((DWORD)_nt+_nt->FileHeader.SizeOfOptionalHeader+0x18);

  for(int i=0;i<_nt->FileHeader.NumberOfSections;i++,begin++)
  {
    DWORD   dwSectionVa  =begin->PointerToRawData+(DWORD)Base;
    DWORD   dwSectionSize=begin->SizeOfRawData;

    if(dwVa>=dwSectionVa&&dwVa<(dwSectionSize+dwSectionVa))
    {
      dwVa=dwVa-(DWORD)Base;
      dwVa=dwVa+begin->VirtualAddress;
      dwVa=dwVa-begin->PointerToRawData;
      return (LPVOID)dwVa;
    }else if(dwVa<dwSectionVa) 
    {
      //如果计算值大于前一区段的最大值,而后一区段地址大于计算值
      //则假设在前一区段:
      dwVa=dwVa-(DWORD)Base;
      begin--;
      dwVa=dwVa+begin->VirtualAddress;
      dwVa=dwVa-begin->PointerToRawData;
    }
  }
  return NULL;
}

有什么不对的地方大家给意见一下!来给大家解解闷!...........

  • 标 题:答复
  • 作 者:loveboom
  • 时 间:2007-11-21 23:29

叫法容易引起误会,RVA为相对虚拟地址,VA为虚拟地址,
如果是RVA2VA,直接RVA + ImgBase就行了。
看意思应该是Rva2FilePos吧(FilePos即为文件偏移)。
ms提供这个函数有bug的,以前测试某些壳时出现过(不记得是FSG还是什么)。

  • 标 题:答复
  • 作 者:shoooo
  • 时 间:2007-11-22 09:08

区段是可以乱序的
可以不是从小到大排列

  • 标 题:答复
  • 作 者:StarsunYzL
  • 时 间:2007-11-22 09:09

虚拟地址转换物理地址,叫法好像有点不妥吧,咋看还以为是内存管理里提到的物理地址……