判斷應用程式是32位還是64位
1.首先介紹PE結構
Windows系統下的可執行檔案,是基於Microsoft設計的一種新的檔案結構,此結構被稱之為PE結構。PE的意思是Portable Executable(可移植的執行體),所有Win32執行體都是用PE檔案格式,其中包括SYS、DLL、EXE、COM、OCX等。(不管是學習逆向、破解還是安全,瞭解PE檔案格式都是非常必要的。)
PE檔案的第一個部分是IMAGE_DOS_HEADER,大小為64B,這裡面有兩個重要的資料成員。第一個為e_magic,這個必須為MZ,即0x5A4D。當然,0x5A4D這是典型的小端格式(Little Endian);另一個重要的資料成員是最後一個成員e_lfanew,這個成員的值為IMAGE_NT_HEADERS的偏移。在IMAGE_DOS_HEADER和IMAGE_NT_HEADERS之間一個DOS Stub,這段程式用於在DOS環境中顯示一個字串,“This program cannot be run in DOS mode”,現在DOS早已滅絕,這段資料由編譯器生成,可以用0填充或者刪除(刪除的話要移動很多資料)。
IMAGE_DOS_HEADER的定義如下:
IMAGE_DOS_HEADER STRUCT {//(注:最左邊是檔案頭的偏移量。) +0h WORD e_magic //Magic DOS signature MZ(4Dh 5Ah) DOS可執行檔案標記 +2h WORD e_cblp //Bytes on last page of file +4h WORD e_cp //Pages in file +6h WORD e_crlc //Relocations +8h WORD e_cparhdr //Size of header in paragraphs+0ah WORD e_minalloc //Minimun extra paragraphs needs +0ch WORD e_maxalloc //Maximun extra paragraphs needs +0eh WORD e_ss //intial(relative)SS value DOS程式碼的初始化堆疊SS +10h WORD e_sp //intial SP value DOS程式碼的初始化堆疊指標SP +12h WORD e_csum //Checksum +14h WORD e_ip //intial IP value DOS程式碼的初始化指令入口[指標IP]+16h WORD e_cs //intial(relative)CS value DOS程式碼的初始堆疊入口 +18h WORD e_lfarlc //File Address of relocation table +1ah WORD e_ovno //Overlay number +1ch WORD e_res[4] //Reserved words +24h WORD e_oemid //OEM identifier(for e_oeminfo) +26h WORD e_oeminfo //OEM information;e_oemid specific +29h WORD e_res2[10] // Reserved words +3ch DWORD e_lfanew //Offset to start of PE header 指向PE檔案頭 } IMAGE_DOS_HEADER ENDS
用C32Asm檢視一個EXE程式的結構:
PE Header 是PE相關結構NT映像頭(IMAGE_NT_HEADER)的簡稱,裡邊包含著許多PE裝載器用到的重要欄位。IMAGE_NT_HEADERS 結構的定義:
IMAGE_NT_HEADERS STRUCT
{
+0h DWORD Signature;
+4h IMAGE_FILE_HEADER FileHeader;
+18h IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS ENDS
Signature 欄位:在一個有效的 PE 檔案裡,Signature 欄位被設定為00004550h,ASCII 碼字元是“PE00”。標誌這 PE 檔案頭的開始。“PE00” 字串是 PE 檔案頭的開始,DOS 頭部的 e_lfanew 欄位正是指向這裡,如上圖所示。
IMAGE_FILE_HEADER 結構定義:
typedef struct _IMAGE_FILE_HEADER
{
+04h // 執行平臺
+06h WORD NumberOfSections; // 檔案的區塊數目
+08h DWORD TimeDateStamp; // 檔案建立日期和時間
+0Ch DWORD PointerToSymbolTable;// 指向符號表(主要用於除錯)
+10h DWORD NumberOfSymbols; // 符號表中符號個數(同上)
+14h WORD SizeOfOptionalHeader; // IMAGE_OPTIONAL_HEADER32 結構大小
+16h WORD Characteristics; // 檔案屬性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
(1)Machine:可執行檔案的目標CPU型別。
IMAGE_FILE_MACHINE_I386 0x014c x86
IMAGE_FILE_MACHINE_IA64 0x0200 Intel Itanium
IMAGE_FILE_MACHINE_AMD64 0x8664 x64
(2)NumberOfSection: 區塊的數目。(注:區塊表是緊跟在 IMAGE_NT_HEADERS 後邊的)
(3)TimeDataStamp: 表明檔案是何時被建立的。
這個值是自1970年1月1日以來用格林威治時間(GMT)計算的秒數,這個值是比檔案系統(FILESYSTEM)的日期時間更加精確的指示器。
提示:VC的話可以用_ctime 函式或者 gmtime 函式。
(4)PointerToSymbolTable: COFF 符號表的檔案偏移位置,現在基本沒用了。
(5)NumberOfSymbols: 如果有COFF 符號表,它代表其中的符號數目,COFF符號是一個大小固定的結構,如果想找到COFF 符號表的結束位置,則需要這個變數。
(6)SizeOfOptionalHeader: 緊跟著IMAGE_FILE_HEADER 後邊的資料結構(IMAGE_OPTIONAL_HEADER)的大小。(對於32位PE檔案,這個值通常是00E0h;對於64位PE32+檔案,這個值是00F0h )。
(7)Characteristics: 檔案屬性,有選擇的通過幾個值可以運算得到。( 這些標誌的有效值是定義於 winnt.h 內的 IMAGE_FILE_** 的值,具體含義見下表。普通的EXE檔案這個欄位的值一般是 0100h,DLL檔案這個欄位的值一般是 210Eh。)
2.然後我們通過讀取PE檔案的執行平臺欄位判斷是32位還是64位:(這裡我寫的VC++6.0是Win32控制檯應用程式)
#include <stdio.h> #include <windows.h> int CrnGetImageFileMachine(char* lpFileName); int main() { int n = CrnGetImageFileMachine("C:\\Users\\Administrator\\Desktop\\VS版 ADT控制卡程式 x64\\adt8948.dll");//需要檢測的可執行檔案 if (n == 0x014C) printf("x86\n");//32位 else if (n == 0x0200) printf("IA64\n");//純64位 else if (n == 0x8664) printf("x64\n");//64位 else printf("未知\n"); return 1; } int CrnGetImageFileMachine(char* lpFileName) { IMAGE_DOS_HEADER idh; FILE *f = fopen(lpFileName, "rb"); fread(&idh, sizeof(idh), 1, f);
IMAGE_FILE_HEADER ifh; fseek(f, idh.e_lfanew + 4, SEEK_SET); fread(&ifh, sizeof(ifh), 1, f); fclose(f); return ifh.Machine; }如何判斷一個執行的程式是64位的還是32位的