小甲鱼 OllyDbg 教程系列 (二) :从一个简单的实例来了解PE文件
?
小甲魚視頻講解:
https://www.bilibili.com/video/av6889190?p=6
https://www.bilibili.com/video/av6889190?p=7
從一個簡單的實例來了解PE文件:https://www.freebuf.com/articles/system/86596.html
https://blog.csdn.net/billvsme/article/details/38340937
RegisterMe 下載地址:https://pan.baidu.com/s/11rtj1oVEX4wnXDw8D9PJqw? ? 提取碼:l209?
?
?
打開這個RegisterMe.exe程序,會出現如下煩人的消息框:
?
?
?
1. 引入
?
目標:去掉煩人的消息框
工具:Ollydbg
?
1. 載入程序,由圖可以看出調用了兩個MessageBox,當程序執行到這里的時候分別會有上述消息框彈出。?
2. 觀察 cmp eax, 0×0
這里判斷 eax 是否等于0
je(如果相等就跳轉 – ZF=1??
由于 eax 等于 40000
所以這一個跳轉永遠不會成立,所以一定會執行這個煩人的MessageBox。
那么我們就可以想辦法使它跳轉。
?
3. 我們嘗試改變ZF標記值(je跳轉根據ZF標記判斷)
如圖,此時 ZF 標記為 0,我們 雙擊 這個0,使其標記為 1
再觀察:
跳轉實現了,我們成功跳過了消息窗口!!
不過這樣每次要改變ZF標記很麻煩,我們可不可以讓它直接跳轉不進行判斷呢?所以我們可以雙擊編輯je short 00401024為jmp short 00401024
成功跳過了MessageBox!!
我們想使用一種更加完美的方法來跳過這個消息框。。。。
假如我們把程序入口設置成00401024不就直接跳過了MessageBox了嗎?在這之前,我需要解釋一些PE的知識(請耐心地閱讀,這才是本文所要講的重點,而不是如何破解這個程序!!)?
下面標有紅色的代表重點,如果你時間緊迫,可以只看有下劃線的文字
?
?
?
深入的必經之路:
?
PE(Portable Executable)文件簡介
PE(Portable Executable)文件是Windows操作系統下使用的可執行文件格式。它是微軟在UNIX平臺的COFF(通用對象文件格式)基礎上制作而成。最初設計用來提高程序在不同操作系統上的移植性,但實際上這種文件格式僅用在Windows系列操作系統下。
PE文件是指32位可執行文件,也稱為PE32。64位的可執行文件稱為PE+或PE32+,是PE(PE32)的一種擴展形式(請注意不是PE64)。
PE文件結構一般如上圖所示。
當一個PE文件被執行時,PE裝載器首先檢查DOS header里的PE header的偏移量。如果找到,則直接跳轉到PE header的位置。
當PE裝載器跳轉到PE header后,第二步要做的就是檢查PE header是否有效。如果該PE header有效,就跳轉到PE header的尾部。
緊跟PE header尾部的是節表。PE裝載器執行完第二步后開始讀取節表中的節段信息,并采用文件映射(在執行一個PE文件的時候,Windows并不在一開始就將整個文件讀入內存,而是采用與內存映射的機制,也就是說,Windows裝載器在裝載的時候僅僅建立好虛擬地址和PE文件之間的映射關系,只有真正執行到某個內存頁中的指令或者訪問某一頁中的數據時,這個頁面才會被從磁盤提交到物理內存,這種機制使文件裝入的速度和文件大小沒有太大的關系)的方法將這些節段映射到內存,同時附上節表里指定節段的讀寫屬性。
PE文件映射入內存后,PE裝載器將繼續處理PE文件中類似 import table (輸入表)的邏輯部分
這四個步驟便是PE文件的執行順序,具體細節讀者可以參考相關文檔。
(以上四個步驟摘自《黑客破解精通》)?
下面用我們要破解程序進行簡單說明:
首先用 WinHex 打開破解程序。上圖是程序的起始部分,也是PE文件的頭部分。文件運行需要的所有信息就儲存在這個PE頭文件中。所以,學習PE文件格式就是學習PE頭中的結構體。
也可以使用?UltraEdit 、010edit?打開(?推薦使用 010edit?打開,這是一個專門查看 16進制的工具,最好用的16進制查看工具,沒有之一?? )
使用 010edit?打開文件:
?
事情根本沒有這么簡單:
上圖描述了文件加載到內存的情形,包含了許多內容,我們逐一學習。
文件中使用偏移(offset),內存中使用VA(Virtual Address,虛擬地址)來表示位置。
VA指進程虛擬內存的絕對地址,RVA(Relative Virtual Address,相對虛擬地址)是指從某基準位置(ImageBase)開始的相對地址。VA與RVA滿足下面的換算關系:
RVA + ImageBase = VA
PE頭內部信息大多是RVA形式存在。原因在于(主要是DLL)加載到進程虛擬內存的特定位置時,該位置可能已經加載了其他的PE文件(DLL)。此時必須通過重定向(Relocation)將其加載到其他空白的位置,若PE頭信息使用的是VA,則無法正常訪問。因此使用RVA來重定向信息,即使發生了重定向,只要相對于基準位置的相對位置沒有變化,就能正常訪問到指定信息,不會出現任何問題。
當PE文件被執行時,PE裝載器會為進程分配4CG的虛擬地址空間,然后把程序所占用的磁盤空間作為虛擬內存映射到這個4GB的虛擬地址空間中。一般情況下,會映射到虛擬地址空間中的0X400000的位置。
?
?
PE頭
?
DOS頭
typedef struct _IMAGE_DOS_HEADER { // DOS的.EXE頭部USHORT e_magic; // DOS簽名“MZ-->Mark Zbikowski(設計了DOS的工程師)”USHORT e_cblp; // 文件最后頁的字節數USHORT e_cp; // 文件頁數USHORT e_crlc; // 重定義元素個數USHORT e_cparhdr; // 頭部尺寸,以段落為單位USHORT e_minalloc; // 所需的最小附加段USHORT e_maxalloc; // 所需的最大附加段USHORT e_ss; // 初始的SS值(相對偏移量)USHORT e_sp; // 初始的SP值USHORT e_csum; // 校驗和USHORT e_ip; // 初始的IP值USHORT e_cs; // 初始的CS值(相對偏移量)USHORT e_lfarlc; // 重分配表文件地址USHORT e_ovno; // 覆蓋號USHORT e_res[4]; // 保留字USHORT e_oemid; // OEM標識符(相對e_oeminfo)USHORT e_oeminfo; // OEM信息USHORT e_res2[10]; // 保留字LONG e_lfanew; // 指示NT頭的偏移(根據不同文件擁有可變值) } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;其中比較重要的有e_magic和e_lfanew,由圖可知
e_magic的值為4D5A,e_lfanew的值為000000C0(注意不是C0000000,詳見我的上一篇文章)
WORD占2個字節,LONG占4個字節,剛好是30個WORD和1個LONG,從00000000到0000003F?
?
DOS存根:
?
即使沒有DOS存根,文件也能正常執行
?
?
NT頭(PE最重要的頭)
其定義如下:
typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; Signature:類似于DOS頭中的e_magic,其高16位是0,低16是0x4550,用字符表示是'PE‘(00004550)。 IMAGE_FILE_HEADER:IMAGE_FILE_HEADER是PE文件頭,定義如下:typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;其中有4個重要的成員(若設置不正確,將會導致文件無法正常運行)
#1.Machine
每個CPU擁有唯一的Machine碼,兼容32位Intel X86芯片的Machine碼為14C(如圖)。以下是定義在winnt.h文件中的Machine碼:
#define IMAGE_FILE_MACHINE_UNKNOWN 0 #define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386. #define IMAGE_FILE_MACHINE_R3000 0x0162 // MIPS little-endian, 0x160 big-endian #define IMAGE_FILE_MACHINE_R4000 0x0166 // MIPS little-endian #define IMAGE_FILE_MACHINE_R10000 0x0168 // MIPS little-endian #define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // MIPS little-endian WCE v2 #define IMAGE_FILE_MACHINE_ALPHA 0x0184 // Alpha_AXP #define IMAGE_FILE_MACHINE_SH3 0x01a2 // SH3 little-endian #define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 #define IMAGE_FILE_MACHINE_SH3E 0x01a4 // SH3E little-endian #define IMAGE_FILE_MACHINE_SH4 0x01a6 // SH4 little-endian #define IMAGE_FILE_MACHINE_SH5 0x01a8 // SH5 #define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian #define IMAGE_FILE_MACHINE_THUMB 0x01c2 #define IMAGE_FILE_MACHINE_AM33 0x01d3 #define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC Little-Endian #define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 #define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel 64 #define IMAGE_FILE_MACHINE_MIPS16 0x0266 // MIPS #define IMAGE_FILE_MACHINE_ALPHA64 0x0284 // ALPHA64 #define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 // MIPS #define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 // MIPS #define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64 #define IMAGE_FILE_MACHINE_TRICORE 0x0520 // Infineon #define IMAGE_FILE_MACHINE_CEF 0x0CEF #define IMAGE_FILE_MACHINE_EBC 0x0EBC // EFI Byte Code #define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8) #define IMAGE_FILE_MACHINE_M32R 0x9041 // M32R little-endian #define IMAGE_FILE_MACHINE_CEE 0xC0EE#2.NumberOfEsctions
PE文件把代碼,數據,資源等依據屬性分類到各節中儲存。
NumberOfEsctions指文件中存在的節段(又稱節區)數量,也就是節表中的項數。該值一定要大于0,且當定義的節段數與實際不符時,將發生運行錯誤。
#3.SizeOfOptionalHeader
IMAGE_NT_HEADERS結構最后一個成員IMAGE_OPTIONAL_HEADER32。
SizeOfOptionalHeader用來指出IMAGE_OPTIONAL_HEADER32結構體的長度。PE裝載器需要查看SizeOfOptionalHeader的值,從而識別IMAGE_OPTIONAL_HEADER32結構體的大小。
PE32+格式文件中使用的是IMAGE_OPTIONAL_HEADER64結構體,這兩個結構體尺寸是不相同的,所以需要在SizeOfOptionalHeader中指明大小。
#4.Characteristics
該段用于標識文件的屬性,文件是否是可運行的狀態,是否為DLL文件等信息。
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file. #define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no unresolved externel references). #define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line nunbers stripped from file. #define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Local symbols stripped from file. #define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 // Agressively trim working set #define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 // App can handle >2gb addresses #define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed. #define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine. #define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // Debugging info stripped from file in .DBG file #define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 // If Image is on removable media, copy and run from the swap file. #define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 // If Image is on Net, copy and run from the swap file. #define IMAGE_FILE_SYSTEM 0x1000 // System File. #define IMAGE_FILE_DLL 0x2000 // File is a DLL. #define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 // File should only be run on a UP machine #define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed.為方便理解,上述程序的NT頭內容如下:
(成員功能概述)
NumberOfSections:該PE文件中有多少個節段,也就是節表中的項數。 TimeDateStamp:PE文件的創建時間,一般有連接器填寫。 PointerToSymbolTable:COFF文件符號表在文件中的偏移。 NumberOfSymbols:符號表的數量。 SizeOfOptionalHeader:緊隨其后的可選頭的大小。 Characteristics:可執行文件的屬性。IMAGE_OPTIONAL_HEADER32:
其定義如下:?
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_HEADER32, *PIMAGE_OPTIONAL_HEADER32;我們需要關注下列成員,這些事運行程序必需的,設置錯誤將導致程序無法正常運行。
#1.Magic
為IMAGE_OPTIONAL_HEADER32時,magic碼為10B,為IMAGE_OPTIONAL_HEADER64時,magic碼為20B
#2.AddressOfEntryPoint
AddressOfEntryPoint持有EP的RVA值。該值指出程序最先執行的代碼起始地址,相當重要。
#3.ImageBase
一般來說,使用開發工具(VB/VC++/Delphi)創建好EXE文件后,其ImageBase值為00400000,DLL文件的ImageBase值為10000000(當然也可以指定其他值)。
執行PE文件時,PE裝載器先創建進程,再將文件載入內存,然后把EIP寄存器的值設置為ImageBase+AddressOfEntryPoint
#4.SectionAlignment,FileAlignment
PE文件的Body部分被劃分成若干節段,這些節段儲存著不同類別的數據。FileAlignment指定了節段在磁盤文件中的最小單位,而SectionAlignment則指定了節區在內存中的最小單位(SectionAlignment必須大于或者等于FileAlignment)
#5.SizeOfImage
當PE文件加載到內存時,SizeOfImage指定了PE Image在虛擬內存中所占用的空間大小,一般文件大小與加載到內存中的大小是不同的(節段頭中定義了各節裝載的位置與占有內存的大小,后面會講到)
#6.SizeOfHeader
SizeOfHeader用來指出整個PE頭大小。該值必須是FileAlignment的整數倍。第一節段所在位置與SizeOfHeader距文件開始偏移的量相同。
#7.Subsystem
Subsystem值用來區分系統驅動文件(*.sys)與普通可執行文件(*.exe,*.dll)。
Subsystem成員可擁有值如下:
#define IMAGE_SUBSYSTEM_UNKNOWN 0 // Unknown subsystem. #define IMAGE_SUBSYSTEM_NATIVE 1 // Image doesn't require a subsystem. 系統驅動 #define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 // Image runs in the Windows GUI subsystem. 窗口應用程序 #define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 // Image runs in the Windows character subsystem. 控制臺應用程序 #define IMAGE_SUBSYSTEM_OS2_CUI 5 // image runs in the OS/2 character subsystem. #define IMAGE_SUBSYSTEM_POSIX_CUI 7 // image runs in the Posix character subsystem. #define IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8 // image is a native Win9x driver. #define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 // Image runs in the Windows CE subsystem. #define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 // #define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 // #define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 // #define IMAGE_SUBSYSTEM_EFI_ROM 13 #define IMAGE_SUBSYSTEM_XBOX 14 #define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16#8.DataDirectory
數據目錄,定義如下:
· typedef struct _IMAGE_DATA_DIRECTORY { · DWORD VirtualAddress; · DWORD Size; · } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;可以看出,有地址(VirtualAddress)有大小(Size),數組定義的一定是一個區域,數組每項都有被定義的值,不同項對應不同數據結構,比如導入表,導出表等,定義如下:
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory // IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage) #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP #define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers #define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor各位重點關注標紅的IMPORT和EXPORT,它們是PE頭中的非常重要的部分,其它部分不怎么重要,大致了解下即可。?
#9.NumberOfRvaAndSizes
NumberOfRvaAndSizes用來指定DataDirectory的數組個數,雖然結構體定義中明確指出了數組個數為16,但也有可能不是16,PE裝載器需要通過這個值來識別。
各成員代表的值和偏移量就不一一寫出了,累死咯。。。?
(成員功能概述)
Magic:表示可選頭的類型。 MajorLinkerVersion和MinorLinkerVersion:鏈接器的版本號。 SizeOfCode:代碼段的長度,如果有多個代碼段,則是代碼段長度的總和。 SizeOfInitializedData:初始化的數據長度。 SizeOfUninitializedData:未初始化的數據長度。 AddressOfEntryPoint:程序入口的RVA,對于exe這個地址可以理解為WinMain的RVA。對于DLL,這個地址可以理解為DllMain的RVA,如果是驅動程序,可以理解為DriverEntry的RVA。當然,實際上入口點并非是WinMain,DllMain和DriverEntry,在這些函數之前還有一系列初始化要完成,當然,這些不是本文的重點。 BaseOfCode:代碼段起始地址的RVA。 BaseOfData:數據段起始地址的RVA。 ImageBase:映象(加載到內存中的PE文件)的基地址,這個基地址是建議,對于DLL來說,如果無法加載到這個地址,系統會自動為其選擇地址。 SectionAlignment:節對齊,PE中的節被加載到內存時會按照這個域指定的值來對齊,比如這個值是0x1000,那么每個節的起始地址的低12位都為0。 FileAlignment:節在文件中按此值對齊,SectionAlignment必須大于或等于FileAlignment。 MajorOperatingSystemVersion、MinorOperatingSystemVersion:所需操作系統的版本號,隨著操作系統版本越來越多,這個好像不是那么重要了。 MajorImageVersion、MinorImageVersion:映象的版本號,這個是開發者自己指定的,由連接器填寫。 MajorSubsystemVersion、MinorSubsystemVersion:所需子系統版本號。 Win32VersionValue:保留,必須為0。 SizeOfImage:映象的大小,PE文件加載到內存中空間是連續的,這個值指定占用虛擬空間的大小。 SizeOfHeaders:所有文件頭(包括節表)的大小,這個值是以FileAlignment對齊的。 CheckSum:映象文件的校驗和。 Subsystem:運行該PE文件所需的子系統。DllCharacteristics:DLL的文件屬性,只對DLL文件有效,可以是下面定義中某些的組合:
SizeOfStackReserve:運行時為每個線程棧保留內存的大小。 SizeOfStackCommit:運行時每個線程棧初始占用內存大小。 SizeOfHeapReserve:運行時為進程堆保留內存大小。 SizeOfHeapCommit:運行時進程堆初始占用內存大小。 LoaderFlags:保留,必須為0。 NumberOfRvaAndSizes:數據目錄的項數,即下面這個數組的項數。 DataDirectory:數據目錄,這是一個數組。?
節段(區)頭:
PE文件有不同的節段:code(代碼),data(數據),resource(資源),這樣設計避免了很多安全問題,比如向data寫數據,由于某原因導致溢出,其下的code就會被覆蓋,程序就會崩潰。
code/data/resource都有不同的權限,如下:
節段頭是由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與PointerToRawData不帶有任何值,分別由(定義在IMAGE_OPTIONAL_HEADER32中的)SectionAlignment和FileAlignment確定。
上述程序有4個節段。
?
?
?
如何運用我們上面學習到的PE知識?
?
了解了PE知識,繼續我們的破解工作!!
我們在 數據窗口(dump)右鍵 —>? 轉到( 或者直接快捷鍵?Ctrl +G)跳轉到起始位置(400000)。
注意是在數據窗口?!!!!!
跳轉到 400000H?位置 (?PE?文件的?基址 位置 )
?
點擊右上方 l(小寫L)、e、m、t、w?的 m 查看內存情況。
?
雙擊該處進入,看到 DOS HEADER 已經載入了,直接向下翻,查找DOS頭的 e_lfanew 成員:
查看到偏移量是000000C0,記住我們載入內存時,基準位置(ImageBase)是400000,相對虛擬地址(RVA)是:
VA(虛擬地址) = ImageBase(PE基址) + RVA(相對虛擬地址)所以此時PE頭位置是 004000C0 ( 00400000 + 000000C0?),我們向下翻到該處(或者?Ctrl + G,輸入 004000C0)。
找最重要的的 AddressOfEntryPoint (?程序入口字段?)。
找到 AddressOfEntryPoint=0×1000,我么需要讓它跳轉到 401024,為啥是 0040 1024?這個地址,通過?Ollydbg 調試,這個地址就是注冊地址,注冊完之后,跳出注冊成功窗口。
修改?AddressOfEntryPoint?的值,雙擊 004000E8(00 10 00 00 –>00001000)的值進行修改,修改為24 10 00 00 (00001024):
然后 右鍵 ->?保存到可執行文件 (?在數據區域,右鍵 ->??):
保存完成后,用OD載入剛保存的RegisterMe1.0,發現入口已經變成了我們修改的00401024,成果跳過了第一個煩人的消息框!
我們再向下執行:
執行到call Register.00401052時彈出了“我們需要注冊的信息”
我們再向下執行:
再第二個MessageBox處又彈出了消息框,我們這次采用NOP(No operation)填充:
填充完成后,我們保存為可執行文件RegisterMe2.0。雙擊執行,再也沒有可惡地消息框咯!
?
?
?
方法 2:
使用?jmp?直接跳轉:
?
方法 3:
使用 nop指令(?空白指令,什么都不做?) 填充并覆蓋?注冊?部分的匯編代碼:
填充后效果:
?
方法4:
?
小結:
?
?
?
總結
以上是生活随笔為你收集整理的小甲鱼 OllyDbg 教程系列 (二) :从一个简单的实例来了解PE文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hack.Chat 在浏览器里快速建立简
- 下一篇: 简明Python教程学习笔记_4_数据结