PE文件全解析
什麼是PE文件?
PE是Windows下的可執行文件的格式。這是微軟基於UNIX平台的COFF(Common Object File Format,通用文件格式)製成的。微軟原本的意思是提高程序的移植型。但是想法是好的,但是實際上只用於Windows系列的操作系統下。
PE文件是指32位的可執行文件,也稱PE32。注意:64位的可執行文件稱為PE+或PE32+,是PE32的一種擴展,不叫PE64。
PE文件的格式
使用notepad.exe為例。從DOS頭到(DOS header)到節區頭(Section header)是PE頭的部分,剩下的部分都叫PE體。文件中實用偏移offset,內存中實用VA(Virtual Address, 虛擬地址)來表示位置。文件載入到內存中時,情況就會發生變數(節區的大小,位置等等)。文件一般可分為代碼.text,數據.data,資源.rsrc,分別保存。不同的編譯工具節區的名稱大小,個數,內容都是不同的。採用不同的編譯選項所編譯出來的可執行文件也是不同的。
RV&RVA的轉換
· VA是指進程虛擬內存的絕對相對地址.
· RVA(相對虛擬地址),是指從某個基準位置(Image Base)開始的相對地址。
轉換關係如下:
RVA+Image Base = VA
PE頭內部信息大多以RVA的形式存在。因為PE文件載入到進程虛擬內存的特定位置,但是,這個位置可能已載入了其他的PE文件(DLL)。因此必須重定向到其他的空白位置。若PE頭信息使用的是VA,則無法正常訪問。所以,使用RVA來定位信息,即使發生了重定位,只要基準地址沒有發生變化,就可以正常訪問到指定的信息
PE頭
PE頭中郵許多許多結構體組成的。而且基本都是結構體中嵌套結構體,嵌套好多層。很複雜。要想學好逆向就必須了解每一個結構體中的內容,所代表的含義。
1.DOS頭
微軟在創建PE 文件格式時,人們正在廣泛使用DOS 文件,所以微軟為了考慮兼容性的問題,所以在PE 頭的最前邊還添加了一個 IMAGE_DOS_HEADER 結構體,用來擴展已有的DOS EXE。他的結構如下
Dos結構體的大小為40個位元組,這裡面有兩個重要的成員變數
· e_magic:DOS簽名,這個屬性的值基本都是4D5A=>ASCII值"MZ"
· e_lfanew:指示NT頭的偏移量(根據不同的文件擁有的值就是不一樣的)使用16進位編輯器打開windows自帶的筆記本。來查看他的結構體。
2.DOS存根
DOS頭後並不是PE頭,他後面跟的是DOS存根,這是個可選項,而且大小不固定,他的大小為NT頭的偏移量減去DOS頭的40個位元組,這一個區域中東西就是DOS存根。即使沒有DOS存根,程序也可以正常執行。這裡存的數據是由數據混合而成的。
可以再xp下實用debug查看這DOS存根中的代碼。在命令行中輸入:
debug C:Windowsnotepad.exe之後在按u,就會出現16位的彙編指令。
3.NT頭
緊接在後面的就是NT頭,這個頭的地址也就是剛剛在上面提到的e_lfanew的值,來看下這個文件的結構體
(所選區域為結構體內容)IMAGE_NT_HEADERS結構體的大小位F8,很大。
3.1 NT頭:文件頭
文件頭是表現的是IMAGE_FILE_HEADER結構體
typedef struct _IMAGE_FILE_HEADER { WORD Machine; //運行平台 WORD NumberOfSections; //塊(section)數目 DWORD TimeDateStamp; //時間日期標記 DWORD PointerToSymbolTable; //COFF符號指針,這是程序調試信息 DWORD NumberOfSymbols; //符號數 WORD SizeOfOptionalHeader; //可選部首長度,是IMAGE_OPTIONAL_HEADER的長度 WORD Characteristics; //文件屬性}
來看看幾個成員變數中所代表的意思是什麼首先是Machine,這個值表示運行程序需要的平台
可以看到這個值是014c,下面是一個列表,代表各種平台對應的值
第二個成員變數是NumberOfSections,表示節區數量,而且,當定義的節區與實際節區不同時,就會發生運行錯誤
可以看到是3個位元組。
第三個成員變數是TimeDateStamp,表示編輯器創建文件的時間。該成員的值不影響文件運行。
之後看第6個成員變數SizeOfOptionalHeader,IMAGE_NT_HEADERS結構體的最後一個成員為IMAGE_OPTIONAL_HEADER32的結構體。SizeOfOptionalHeader成員用來指出IMAGE_OPTIONAL_HEADER32結構體的長度。IMAGE_OPTIONAL_HEADER32是由c語言編寫的,所以,大小已經確定。WINDOWS的PE裝載器需要查看IMAGE_FILE_HEADER中的SizeOfOptionalHeader來確定IMAGE_OPTIONAL_HEADER32的大小。00E0的十進位為224,也就是OptionalHeader的大小。PE32+格式的文件中實用的是IMAGE_OPTIONAL_HEADER64結構體,而不是IMAGE_OPTIONAL_HEADER32結構體。2個結構體的尺寸是不用的,所以需要SizeOfOptionalHeader成員明確的指出結構體的大小來看他的值
第7個成員變數Characteristics表示文件屬性,他的每一個bit都代表了某種含義
Bit 0 :置1表示文件中沒有重定向信息。每個段都有它們自己的重定向信息。 這個標誌在可執行文件中沒有使用,在可執行文件中是用一個叫做基址重定向目錄表來表示重定向信息的,這將在下面介紹。
Bit 1 :置1表示該文件是可執行文件(也就是說不是一個目標文件或庫文件)。
Bit 2 :置1表示沒有行數信息;在可執行文件中沒有使用。
Bit 3 :置1表示沒有局部符號信息;在可執行文件中沒有使用。
Bit 4 :
Bit 7
Bit 8 :表示希望機器為32位機。這個值永遠為1。
Bit 9 :表示沒有調試信息,在可執行文件中沒有使用。
Bit 10:置1表示該程序不能運行於可移動介質中(如軟碟機或CD-ROM)。在這 種情況下,OS必須把文件拷貝到交換文件中執行。
Bit 11:置1表示程序不能在網上運行。在這種情況下,OS必須把文件拷貝到交換文件中執行。
Bit 12:置1表示文件是一個系統文件例如驅動程序。在可執行文件中沒有使用。
Bit 13:置1表示文件是一個動態鏈接庫(DLL)。
Bit 14:表示文件被設計成不能運行於多處理器系統中。
Bit 15:表示文件的位元組順序如果不是機器所期望的,那麼在讀出之前要進行 交換。在可執行文件中它們是不可信的(操作系統期望按正確的位元組順序執行程序)。
他的值如下
需要記住的兩個值為0002h(EXE文件)和2000h(DLL文件)
3.2 NT頭:可選頭
緊接著IMAGE_FILE_HEADER的結構體就是IMAGE_OPTIONAL_HEADER32,這個結構體是整個PE頭結構中最大的一個結構體。
打藍標的區域為可選頭的內容,找不到內同的可以計算一下。找到NT頭開頭的地方,一個DWORD類型的Signature,4個位元組,之後跟了一個20位元組大小的文件頭之後就是可選頭所開始的地址了。
接著來看看這個結構體的結構:
typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
繼續看每一個成員變數的值:
第一個成員變數:Magic 這個值代表的是 文件的格式
看看微軟官方的說法
IMAGE_OPTIONAL_HEADER32和IMAGE_OPTIONAL_HEADER64分別表示32位和64位的應用程序。這裡的值010B可以判斷出實用的是32位應用程序。看官方文檔還有一個值是0x107這個值代表的是系統的ROM文件。
緊接著的幾個成員變數都無關緊要。
直接看第7個值:AddressOfEntryPoint,這個成員變數保存著EP的RVA。也就是最先執行代碼起始地址。
第8個成員變數為:BaseOfCode,表示代碼段起始RVA先看他的值,是1000,剩下的等說到ImgaeBase再說。??
第9個成員變數為:BaseOfData,表示數據段的起始RVA,值位9000,跟上面一樣,等下再多說。??
計算得出最先執行的代碼起始地址,之後使用OD打開,看地址,就是剛剛計算得出的地址
接著來說上面的BaseOfCode,一個道理BaseofCode + ImageBase可以得到代碼段的起始地址。
同理BaseOfData + ImageBase可以得到段的起始地址
這是一些題外話,我們繼續來說_IMAGE_OPTIONAL_HEADER的成員變數
第11個成員變數SectionAlignment,表示節區在內存中的最下單位,這些節存儲著不同類型的數據。
第12個成員變數FileAlignment,他指定了節區在磁碟中的最小單位,對於同一個文件FileAlignment和SectionAlignment的值可能相同,也可能不同,但內存的節區大小或磁碟文件中的最小單位必定為SectionAlignment和FileAlignment。在網上看資料的時候說,SectionAlignment一定要大於或等於FileAlignment,這句話是不對的。。別問我為什麼,看圖
第20個成員變數SizeOflmage,這個值表示載入PE文件到內存中時,SizeOflmag指定了PE Image在內存中所佔的大小。一般來說,文件的大小與載入到內存中的大小是不同的(街區中定義了各位元組裝載的位置與所佔有內存的大小。後面說)
第21個成員變數SizeOfHeaders,這個變數用來指出整個PE頭的大小。這個值必須是FileAlingment的整數倍。第一節區所在位置和SizeOfHeaders距文件開始偏移的量相同。(不明白往上看,上面的那個PE文件載入到內存中的圖)
第23個成員變數Subsystem,這個值用來區分系統驅動文件(*.sys)與普通文件(*.exe,*.dll)。Subsystem成員可擁有的值。看錶格(官方給的太多了。)官網傳送門
第30個成員變數NumberOfRvaAndSizes,該成員變數記錄著DataDirectory(第31個成員變數)數組的個數。
第31個成員變數DataDirectory.
DataDirectory是由IMAGE_DATA_DIRECTORY結構體,
結構體類型:
typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size;} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
每一項都有被定義的成員變數
DataDirectory[0] = EXPORT DirectoryDataDirectory[1] = IMPORT DirectoryDataDirectory[2] = RESOURCE DirectoryDataDirectory[3] = EXCEPTION DirectoryDataDirectory[4] = SECURITY DirectoryDataDirectory[5] = BASERELOC DirectoryDataDirectory[6] = DEBUG DirectoryDataDirectory[7] = COPYRIGHT DirectoryDataDirectory[8] = GLOBALPTR DirectoryDataDirectory[9] = TLS DirectoryDataDirectory[A] = LOAD_CONFIG DirectoryDataDirectory[B] = BOUND_IMPORT DirectoryDataDirectory[C] = IAT DirectoryDataDirectory[D] = DELAY_IMPORT DirectoryDataDirectory[E] = COM_DESCRIPTOR DirectoryDataDirectory[F] = Reserved Directory
看第一個EXPORT Directory導出表
因為沒有導出表,所以正常,全部為0
第二個為導入表IMPORT Directory導入表
這裡就說這兩個,因為後面要用到
4 節區頭
節區頭中定義了各節區的屬性。PE文件中的code(代碼),data(數據),resource(資源)等都按照分類存儲在不同的節區中。把PE文件創建成多個節區結構的好處是,這樣可以保證安全性。起始完全可以把code和resource放在一個位元組中糾纏,但是容易引發安全問題,比如,向data段中寫數據,寫的數據如果超過緩衝區大小的時候,那麼下邊的code就被覆蓋了,應用程序崩潰。所以,PE文件格式的設計者就把相似的數據統一保存在一個被稱為節區地方,之後把需要的屬性記錄在節區頭中(內存大小啊,訪問許可權等等的)。也就是說各個節區分別設置了特性,許可權等。
接下來看存放節區頭的結構體IMAGE_SECTION_HEADER。每個結構體對應一個節區
typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; union { DWORD PhysicalAddress; DWORD VirtualSize; } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics;} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
·VirtualAddress,SizeOfRawData是沒有值的,他們的值都是由IMAGE_OPTIONAL_HEADER32中的SectionAlignment和FileAgnment
·VirtualSize,SizeOfRawData的值一般是不同的,程序一般在內存中跟在磁碟中的大小是不同的
因為是相同的結構體,所以只找一個作為演示
ps:其實看到他的那個NAME的值的時候我是蒙蔽的,不是說好的小端續排列么。(問了個大牛,他只對數有意義,對字元沒有這種說法的)
實用PEid來看下工具找到的值跟手動找到的一樣不一樣
完美,一毛一樣。
5. RVA to RAW
PE文件載入到內存時,每個節區都要能準確完成內存地址與文件轉移的映射。這種映射就叫 RVA to RAW,
方法如下:
· 查找RVA所在節區
· 實用簡單的公式計算文件偏移RAW
RAW - PointerToRawData = RVA - VirtualAddress RAW= RVA - VirtualAddress + PointerToRawData
演示一個計算:
RVA = 8888, FILE Offset=? 看上面的圖,首先查找RVA的值所在區域在第一節區`.text`,剛剛的`ImageBase`是1000000 計算就是: RAW = 8888(RVA)-1000(VitualAddress)+400(PointerToRawData) = 7C88
6. IAT
最最最噁心的部分IAT(import Address Table),導入地址表,IAT保存的內容與Windows操作系統核心的進程,內存,DLL結構等有關的,也就是說,IAT就是Windows的根基。在說簡單點,IAT就是一張表,記錄了程序正在使用那些庫中的那些函數
不過在學之前,我們得先看DLL文件,我不知到英文怎麼寫的,但是中文就叫動態鏈接庫。
DLL文件有兩種載入方式:
· 顯式載入:程序使用DLL文件的時候載入,使用完成釋放
· 隱式載入:程序在啟動的時候載入DLL文件,程序結束的時候釋放DLL
IAT提供的機制與隱式載入有關
看一個例子:
這是notepad.exe被導入od,調用CreateFileW()函數的代碼,這個函數位於kernel32.dll中,
之後那麼問題來了。為什麼不直接調用Call 7C8107F0?微軟在想什麼。。
Kernel32.dll版本在不同的操作系統肯定是不同的,對應的CreateFileW 函數也不同,為了兼容各種環境,編譯器準備了CreateFileW函數的實際地址,然後記下DWORD PTR DS:[xxxxxx]這樣的指令,執行文件時候,PE 裝載器將CreateFileW函數地址寫到這個位置。
而且,存在重定向的問題,如果兩個DLL文件有想用的ImageBase,那裝載的時候,一個裝上去了,另一個肯定不能繼續放在這個位置上了。
6.1 IMAGE_IMPORT_DESCRIPTOR
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 datetime 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 struct _IMAGE_IMPORT_BY_NAME { WORD Hint; BYTE Name[1];} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
對一般人來說,導入多少個庫,就會存在多少的這樣的結構體,這樣的結構體組成了數組,而且結構體是以NULL結束的。
INT元素的各個值就是上面的_IMAGE_IMPORT_BY_NAME結構體的指針
INT和IAT的大小應該是相同的
下面這張圖描述了一個exe程序載入dll的IMAGE_IMPORT_DESCRIPTOR(感覺這是最直觀的一張圖)
接著來看下完整的載入過程
1.讀取NAME 成員,獲取擴名稱字元串2.裝載相應庫: LoadLibrary("kernel32.dll")3.讀取OriginalFirstThunk成員,獲取INT 地址4.讀取INT 數組中的值,獲取相應的 IMAGE_IMPORT_BY_NAME地址,是RVA地址5.使用IMAGE_IMPORT_BY_NAME 的Hint 或者是name 項,獲取相應函數的起始位置 GetProcAddress("GetCurrentThreadId")6.讀取FistrThunk 成員,獲得IAT 地址。7.將上面獲得的函數地址輸入相應IAT 數組值。8.重複4-7 到INT 結束。
那麼現在問題來了。
OriginalFirstThunk 和 First Thunk 都指向的是函數,為什麼要這麼做?
OriginalFirstThunk 和 First Thunk 他們兩個兄弟都是類型為IMAGE_THUNK_DATA的數組,而這個IMAGE_THUNK_DATA又是一個指針大小聯合類型。
每一個IMAGE_THUNK_DATA的結構體沒有能導入一個名為IMAGE_IMPORT_BY_NAME的東西(就是上面那個讓你忽略的,一樣,先放著,等會再說這個結構體)
然後,數組最後一個內同0的IMAGE_THUNK_DATA作為結束標識符。趕緊補上結構體,看蒙蔽了估計都
typedef struct _IMAGE_THUNK_DATA32 { union { DWORD ForwarderString; // PBYTE DWORD Function; // PDWORD DWORD Ordinal; DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME } u1;} IMAGE_THUNK_DATA32;
對於IMAGE_THUNK_DATA,如果最高位為1,則表示函數以序號的方式從DLL中導出引用,低31位表示序號;如果最高位為0,則表示名字導出,此時32位表示一個RVA,這個RVA指向一個結構為IMAGE_IMPORT_BY_NAME的元素。
現在,再說我們一直提到的那個結構體IMAGE_IMPORT_BY_NAME
typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; BYTE Name[1];} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
Hint表示欄位也表示函數的序號,不過,這個值可有可無,有些編輯器一直把這個欄位設置成0
Name欄位定義了導入函數的名稱字元串,這是一個以0結尾的字元串
--x.重中之重
第一個數組(OriginalFirstThunk所指向)是單獨的一項,而且不能被改下,就是前面被稱為INT的傢伙。
第二個數組(FirstThunk所指向)事實上是由PE裝載器從重寫的
更詳細的說一下
PE裝載器首先搜索OriginalFirstThunk,找到之後載入程序迭代搜索數組中的每個指針,找到每個IMAGE_IMPORT_BY_NAME所指向的輸入函數入口地址,然後載入器用真正的入口函數代替FirstThunk數組中的入口,所以叫做輸入地址表,在偷一張圖
來打開notepad.exe看一下
這個值值究竟在哪裡了,他算PE文件的那個部分?他是PE體重的內容,但是想要知道他的位置信息,可以網上看,看IMAGE_OPTIONAL_HEADER32.DATADirectory[1].VirtualAddress中的值就能找到他的地址信息。
不相信手工找的也可以用工具來查看一下
後面的C8為他的大小,既然拿到了RVA地址,我們來計算一下(忘記公式的,網上看)
RAW = 7604 - 1000 + 400 = 6A046404 + C8 = 6acc 這也是結構體的結束
紅框框中的就是結構體第一個元素:
換算出地址之後,看一下他的NAME字元串的指針,他導入了函數所屬庫文件名稱
可以看到字元串的名稱為comdlg32.dll
之後我們來看OriginalFirstThunk這個東西。INT是一個包含導入函數信息的結構體數組。只有獲取了這些信息,才能準確請求相應函數的起始地址。等會再扯(EAT)
查看6D90
跟蹤第一個數組(7A7A),就能看到導入函數API的名稱
7A7A(RVA) - 1000 + 400 = 6E7A(RAW)
已經看到了PageSetupDlgW這個字元串,這是一個_IMAGE_IMPORT_BY_NAME的結構體,000F表示函數的序號,不知道有什麼用沒。後面的字元串以結尾,跟c語言一毛毛一樣
這時候來看IAT這個東西,RAW的值為6C4
用od打開看一下notepad.exe的IAT地址
可以看到這塊地址就是PageSetupDlgw方法的入口地址
7. EAT
Windows中庫是為了更方便調用其他程序調用耳機中包含的相關文件的函數的文件
EAT是一種核心機制,它使不同的應用程序可以調用庫文件中提供的函數,也就是說,只有通過EAT才能從相應庫中導出函數的起始地址。也就是說IMAGE_EXPORT_DIRECTORY中保存著導出信息,而且PE文件僅有一個用來說明庫EA的IMAGE_EXPORT_DIRECTORY結構體(有木有頭覺得比IAT友好許多,恩,肯定了,導入可以導入多個文件啊,兄弟)
使用Kernel32.dll這個文件來看看
RVA為0000262C,大小為00006D19.我們先計算出文件的偏移,雖然現在不用,但是等下會用
262C-1000+400 = 1A2C(RAW)
在看他的文件之前,先來分析下他的結構體
typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; // 未使用,總為0 DWORD TimeDateStamp; // 文件創建時間戳 WORD MajorVersion; // 未使用,總為0 WORD MinorVersion; // 未使用,總為0 DWORD Name; // 指向一個代表此 DLL名字的 ASCII字元串的 RVA DWORD Base; // 函數的起始序號 DWORD NumberOfFunctions; // 導出函數的總數 DWORD NumberOfNames; // 以名稱方式導出的函數的總數 DWORD AddressOfFunctions; // Export函數地址數組(數組元素的個數=NumberOfFunctions) DWORD AddressOfNames; // 指向輸出函數名字的RVA 函數名稱地址數組(數組元素的個數=NumberOfNames) DWORD AddressOfNameOrdinals; // 指向輸出函數序號的RVA Ordinal地址數組(數組元素個數=NumberOfNames)} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
附一張說明的圖片
載入器載入過程:
1. 根據名稱查找函數地址
(1)定位導出模塊的IMAGE_EXPORT_DESCRIPTOR(2)以NumberOfNames(已知的明明函數總數)為循環次數,循環比較AddressOfNames指向的地址項所對應的函數名字元串,如果沒找到匹配的名稱字元串,則找不到所對應的函數。(3)如果找到匹配的函數名字元串,則以此為索引號,取出AddressOfNameOrdinal所指向的序號。(4)取出的序號-nBase=函數地址索引號(5)以此為索引號,取出AddressOfFunctions指向的函數地址。
2. 根據序號查找函數地址
(1)定位導出模塊的IMAGE_EXPORT_DESCRIPTOR(2)序號-nBase=函數地址索引號(3)以此為索引號,取出AddressOfFunctions指向的函數地址。
最後,使用Kernel32.dll掌握下他的結構體
只看重要的值
先看函數名稱數組RAW=293C
可以發現,這一串都是有規律的字元串,不一定是我標記的地方,再往下也是,我們隨便找一個,比如說選擇數組中的第3個元素00004BBD,那他的RAW就是3FBD我們查看此處的值
就能查看到函數的名稱,接下來,就用這個函數查找該函數Ordinal的值
可以看到該區域由多個2位元組的Ordinal組成的數組(Ordinal數組中的個元素大小位2位元組),將2中求得的index(2)應用到Ordinal數組就能求得Ordinal(2).
AddressOfNameOrdinals[index] = ordinal(index=2,ordinal=2)
最後查看他的實際函數地址,進入到1A54地址處
找到了326F1
打開od導入文件進去,用它的ImageBase+326f1 你就可以在od中找到他了。我就不找了,寫的頭疼。。
如果你看完了,辛苦你了= =!
※官方渠道下載也不安全!運營商劫持流行軟體安裝包篡改為病毒
※組合利用Empire和Death Star:一鍵獲取域管理員許可權
※Elasticsearche殭屍網路:超過4000台伺服器遭到兩款POS 惡意軟體感染
※滲透挑戰賽:從SQL注入到管理員許可權
※通過DNS控制主機以及執行命令
TAG:嘶吼RoarTalk |