PE檔案基礎補註

來源:互聯網
上載者:User

PE檔案基礎補註

前言: 最近學習PE, 略有心得, 拿來和大家分享. 

感謝: 小蝦斑斑, 非安全  ,Bookworm對我的協助.

1.IMAGE_SECTION_HEADER小結:
  
1.1   獲得節表數 :NumberOfSections = NtHeader->FileHeader.NumberOfSections;

1.2   節表獲得方法
      
      方法1.因為NT頭之後就是節表,故,節表頭地址就是nt頭地址加上NT結構大小.
      SectionHeader=(PIMAGE_SECTION_HEADER)((UINT32)NtHeader+(UINT32)(sizeof(IMAGE_NT_HEADERS)));
  
      方法2.或者用ImageBase+SizeOfHeaders的辦法直接定位.
      SectionHeader=(PIMAGE_SECTION_HEADER)((UINT32)(NtHeader->OptionalHeader.ImageBase)+
                                          (UINT32)(NtHeader->OptionalHeader.SizeOfHeaders));
  
      方法3.既然節都是連在一起的,那麼,也就可以這樣: 
              SectionHeader= (PIMAGE_SECTION_HEADER) (NtHeader + 1),
  
      方法4.論壇裡面 hmimys 告訴的辦法:    
              SectionHeader=(PIMAGE_SECTION_HEADER)((UINT32)NtHeader+0x18+
                                                   (UINT32)(NtHeader->FileHeader.SizeOfOptionalHeader));
              到現在我還沒有弄懂為什麼 hmimys 說最好要用方法4而不用方法3.
    
    
2. IMAGE_IMPORT_DECSRITOR 小結:

2.1:獲得引入表結構起始地址:
     
     方法1:ImportDec = (PIMAGE_IMPORT_DESCRIPTOR)(NtHeader->OptionalHeader.DataDirectory[12].VirtualAddress);
            這個方法我覺得理論上是對的,但是我在啟動並執行時候總是得不到正確的地址.後來知道,似乎不能用'12',而要用IMAGE_DIRECTORY_ENTRY_IAT這個宏

     方法2:ImportDes = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)(NtHeader->OptionalHeader.DataDirectory)+
                                                    (DWORD)(sizeof(IMAGE_DATA_DIRECTORY)*12));
   
     方法3 : ImportDes = (PIMAGE_IMPORT_DESCRIPTOR)(NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress-
                Offset + (PBYTE)pMapping);

     注 : 前兩種方法都是從 IAT 中得出 IMAGE_IMPORT_DESCRIPTOR,而後面的那個是 非安全 大哥教的. 這裡有個疑問: 
          3種方法都可以得到 IMAGE_IMPORT_DESCRIPTOR 結構,都可以得到函數名, 區別在於前兩種方法枚舉的函數名不全. 
          難道說兩個結構都指向同一個結構PIMAGE_IMPORT_DESCRIPTOR?
 
2.2  IMAGE_IMPORT_DESCRIPTOR 結構既不是在Import Symbols中,也不是在IAT (IMAGE_IMPORT_ADDRESS_TABLE)中。它就是一個結構. 
     我原來說:"IMAGE_IMPORT_DESCRIPTOR 結構不是在Import Symbols中,是在IAT (IMAGE_IMPORT_ADDRESS_TABLE)中。" 有問題.
     就是因為這個錯誤的理解, 讓我走了好多死路.
     
     這個是Winnt.h中關於 IMAGE_SYNMBOL的結構資訊

     typedef struct _IMAGE_SYMBOL {
      union {
        BYTE    ShortName[8];
        struct {
            DWORD   Short;     // if 0, use LongName
            DWORD   Long;      // offset into string table
        } Name;
        PBYTE   LongName[2];
     } N;
     DWORD   Value;
     SHORT   SectionNumber;
     WORD    Type;
     BYTE    StorageClass;
     BYTE    NumberOfAuxSymbols;
     } IMAGE_SYMBOL;
     
     typedef IMAGE_SYMBOL UNALIGNED *PIMAGE_SYMBOL;

     而下面的是IAT: 

     typedef struct _IMAGE_IMPORT_BY_NAME {
      WORD    Hint;
      BYTE    Name[1];
     } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

     typedef struct _IMAGE_IMPORT_DESCRIPTOR {
      union {
          DWORD   Characteristics;            // 0 for terminating null import descriptor
          DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
      };
      DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date/time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

      DWORD   ForwarderChain;                 // -1 if no forwarders
      DWORD   Name;
      DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
     } IMAGE_IMPORT_DESCRIPTOR;
     typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

     _IMAGE_IMPORT_DESCRIPTOR 結構聯合中的OriginalFirstThunk , 就是到IMAGE_THUNK_DATA的RVA. 
     如果像下面這樣寫,也許更明白

     typedef struct _IMAGE_THUNK_DATA {
        union {
            PBYTE ForwarderString;
            PDWORD Function;
            DWORD Ordinal;
            PIMAGE_IMPORT_BY_NAME AddressOfData;
        } ;
     } IMAGE_THUNK_DATA,*PIMAGE_THUNK_DATA;

     typedef struct _IMAGE_IMPORT_DESCRIPTOR {
        union {
            DWORD Characteristics;
            PIMAGE_THUNK_DATA OriginalFirstThunk;
        } ;
        DWORD TimeDateStamp;
        DWORD ForwarderChain;
        DWORD Name;
        PIMAGE_THUNK_DATA FirstThunk;
     } IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;
     

3. 地址轉換小結(RVAToOffset): 

   為什麼要地址轉換, 前人的文章說了很多,下面給出我的轉換方法:  
   
   3.1 函數,它能給出RVA返回此RVA所在的節,來自 Matt Pietrek的書: 

   PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD rva){
        unsigned i;
      PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION32(NtHeader);
      for ( i=0; i < NtHeader->FileHeader.NumberOfSections; i++,section++){
               if ( (rva >=section->VirtualAddress) && 
             (rva < (section->VirtualAddress + section->Misc.VirtualSize)))
            return section;
      }
         return 0; 
   }

   註: hnhuqiong 給的 ollydump300110 的源碼裡面也有類似函數,但是,
       很明顯的有漏洞,那就是若RVA不在任何一個Section那麼函數會返回最後
       一個Section, 而不是像這裡返回 0 .下面是原始串連
   http://bbs.pediy.com/showthread.php?threadid=26520

   3.2 RVAToOffset:

   我一直沒有注意的就是'Offset'這個詞. Offset其實還是一個位移,只不過是
   在檔案中, 要想得到目標檔案的IAT, 就要將這個值加上由 MapViewOfFile 返回
   的檔案基址指標.

   Offset的的獲得 : 
            pSection = GetEnclosingSectionHeader(NtHeader->OptionalHeader.DataDirectory                                       

                                           [IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress);
            Offset = (DWORD) (pSection->VirtualAddress - pSection->PointerToRawData);
  
   以獲得IMAGE_THUNK_DATA結構為例,給出用法: 

        ThunkData = (PIMAGE_THUNK_DATA)((DWORD)ImportDes->OriginalFirstThunk -
                                       Offset + (PBYTE)pMapping);
   呵呵, (DWORD)ImportDes->OriginalFirstThunk -Offset 得到的只是檔案中的位移, 
   注意加上由 MapViewOfFile 返回的pMapping. 如果你象我原來一樣,加上的是
   NtHeader->OptionalHeader.ImageBase , 那麼恭喜你, 訪問錯誤.            
  

4. 用VC 6.0 + API獲得IMAGE_IMPORT_BY_NAME結構的一點問題.

   在 VC 裡面,  在一個結構指標比如ThunkData後面加上'->'時, vc會自動的列出
   結構的成員供你選擇, 十分方便. 但是, 通過ThunkData繼續想獲得IMAGE_IMPORT_BY_NAME
   結構的時候, 你在ThunkData後面加'->'時, 出來的是一個'u1'. 此時不要疑惑,
   這個'u1'就是 IMAGE_THUNK_DATA 裡面的那個 union 的名稱, 所以你可以這樣得到
   IMAGE_IMPORT_BY_NAME結構: 

       ImportBN = (PIMAGE_IMPORT_BY_NAME)((DWORD)(ThunkData->u1.AddressOfData)-
                                Offset +(PBYTE)pMapping);

5.   Iczelion的PE教程關於匯入表的描述沒有講清楚,只是說用IMAGE_THUNK_DATA
      的每個數組元素和IMAGE_ORDINAL_FLAG32,比較可以推斷如果某個函數是由函數序數引出的,
      我就誤解成用ImportDes->OriginalFirstThunk或者ImportDes->FirstThunk 判斷。是不是錯的很遠?
      參考(【翻譯】“PE檔案格式”1.9版 完整譯文(附註釋))http://bbs.pediy.com/showthread.php?threadid=21932,
      我們應該用IMAGE_THUNK_DATA結構裡面的AddressOfData來判斷。下面的代碼可行:

      while(ThunkData->u1.AddressOfData!=NULL){
         ImportBN = (PIMAGE_IMPORT_BY_NAME)((DWORD)(ThunkData->u1.AddressOfData) - Offset +(PBYTE)pMapping); 
   //顯示匯入函數
   if(((DWORD)ThunkData->u1.AddressOfData & IMAGE_ORDINAL_FLAG32) == 0){
    AddText(hEdit,TEXT("%03d:  %s/r/n"),i++,ImportBN->Name);
   }
   else{
    AddText(hEdit,TEXT("%03d:  Ord by Hint/r/n"),i++);
   }
   ThunkData ++;  
      }//End of while

6      匯出表:
6.1    匯出表的結構,
   typedef struct _IMAGE_EXPORT_DIRECTORY {
      DWORD   Characteristics;
      DWORD   TimeDateStamp;
      WORD    MajorVersion;
      WORD    MinorVersion;
      DWORD   Name;
      DWORD   Base;
      DWORD   NumberOfFunctions;
      DWORD   NumberOfNames;
      DWORD   AddressOfFunctions;     // RVA from base of image
      DWORD   AddressOfNames;         // RVA from base of image
      DWORD   AddressOfNameOrdinals;  // RVA from base of image
   } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

6.2   AddressOfNames 和AddressOfNameOrdinals 是一一對應的,只不過一個用於名字,
      一個用於序號, 同一個函數的索引都相同。

6.3   NumberOfFunctions – NumberOfNames 應該就是由序號引出的函數數目了

6.4   對於由序號匯出的函數,不知道有沒有辦法能通過序數找到函數名。個人考慮似乎不可能這樣
      找函數名字,不然,微軟未公開的函數就都被我們通過函數序數枚舉出來了? :)

7:  把我的PE查看器修改了下, 原來的在處理用序號引出的函數時會出錯.:)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.