通過運用LyScript
插件并配合pefile
模塊,即可實作對特定PE檔案的掃描功能,例如載入PE程式到記憶體,驗證PE啟用的保護方式,計算PE節區記憶體特征,檔案FOA與記憶體VA轉換等功能的實作,首先簡單介紹一下pefile
模塊,
pefile模塊是一個用于決議Windows可執行檔案(PE檔案)的Python模塊,它可以從PE檔案中提取出檔案頭、節表、匯入表、匯出表、資源表等資訊,也可以修改PE檔案的一些屬性,可以用于分析針對Windows平臺的惡意軟體、撰寫自己的PE檔案修改工具等場景,
使用pefile模塊可以快速方便地定位到PE檔案的一些關鍵資訊,例如程式入口點、程式頭、代碼的開始和結束位置等,在基于PE檔案進行逆向分析和開發中非常有用,在Python中使用pefile模塊也非常簡單,通過匯入模塊和加載PE檔案后就可以輕松獲取和修改PE檔案的各種屬性了,
4.2.1 獲取PE結構記憶體節表
在讀者使用LyScript
掃描行程PE結構之前,請讀者自行執行pip install pefile
將pefile模塊安裝到系統中,接著我們開始實作第一個功能,將PE可執行檔案中的記憶體資料通過PEfile模塊打開并讀入記憶體,實作PE引數決議,
此功能的核心實作原理,通過get_local_base()
得到text
節的基址,然后再通過get_base_from_address()
函式得到text節得到程式的首地址,通過read_memory_byte
依次讀入資料到記憶體中,最后使用pefile.PE
決議為PE結構,其功能如下所示:
- 1.使用
MyDebug
模塊創建并初始化dbg
物件,連接除錯環境, - 2.呼叫
dbg.get_local_base()
獲取除錯程式的基地址,將其賦值給local_base
變數, - 3.呼叫
dbg.get_base_from_address(local_base)
將除錯器地址轉換為程式入口地址,將轉換后的地址賦值給base
變數, - 4.使用回圈遍歷方式讀取除錯程式首地址處的
4096
位元組資料并存盤到byte_array
位元組陣列中, - 5.使用
pefile
模塊創建一個PE檔案物件oPE
,并將byte_array
作為資料源傳入, - 6.最后使用
dump_dict()
方法從PE檔案物件中提取出可選頭資訊并列印輸出timedate
變數,
具體的實作細節可以總結為如下代碼形式;
from LyScript32 import MyDebug
import pefile
if __name__ == "__main__":
# 初始化
dbg = MyDebug()
dbg.connect()
# 得到text節基地址
local_base = dbg.get_local_base()
# 根據text節得到程式首地址
base = dbg.get_base_from_address(local_base)
byte_array = bytearray()
for index in range(0,4096):
read_byte = dbg.read_memory_byte(base + index)
byte_array.append(read_byte)
oPE = pefile.PE(data = https://www.cnblogs.com/LyShark/p/byte_array)
timedate = oPE.OPTIONAL_HEADER.dump_dict()
print(timedate)
保存并運行這段代碼,讀者可以看到如下所示的輸出效果;
總體上,這段代碼的作用是利用除錯器將除錯程式的首地址處的4096位元組讀入記憶體,然后使用pefile模塊將其決議為PE檔案,最后輸出PE檔案的可選頭資訊,該代碼可以用于在除錯程序中對除錯程式的PE檔案進行逆向分析和研究,
接著我們繼續向下決議,通常讀者可通過oPE.sections
獲取到當前行程的完整節資料,如下通過LyScirpt
模塊配合PEFile
模塊決議記憶體鏡像中的section
節表屬性,其完整代碼如下所示;
from LyScript32 import MyDebug
import pefile
if __name__ == "__main__":
# 初始化
dbg = MyDebug()
dbg.connect()
# 得到text節基地址
local_base = dbg.get_local_base()
# 根據text節得到程式首地址
base = dbg.get_base_from_address(local_base)
byte_array = bytearray()
for index in range(0,8192):
read_byte = dbg.read_memory_byte(base + index)
byte_array.append(read_byte)
oPE = pefile.PE(data = https://www.cnblogs.com/LyShark/p/byte_array)
for section in oPE.sections:
print("%10s %10x %10x %10x"
%(section.Name.decode("utf-8"), section.VirtualAddress,
section.Misc_VirtualSize, section.SizeOfRawData))
dbg.close()
讀者可自行運行這段代碼片段,則會看到當前被加載行程中記憶體節表的完整輸出,這段代碼輸出效果如下圖所示;
4.2.2 計算節表記憶體Hash散列值
接著我們繼續再進一步,實作計算PE節表Hash散列值,Hash函式的計算常用于病毒木馬特征值的標記,通過對特定檔案進行散列值生成,即可得知該檔案的版本,從而實作快速鎖定源檔案的目的,
什么是Hash散列值
哈希散列值通常被用作數字簽名、資料完整性驗證、訊息認證等等領域,它可以根據資料的內容計算出一個固定長度的值(通常是16到64位元組),并且在資料被篡改的情況下會生成不同的散列值,因此可以用來在不傳輸原資料的情況下驗證資料的完整性,
例如,我們可以使用MD5哈希函式對一個檔案進行哈希計算,得到一個128位的哈希散列值,將其與原始檔案共同存盤在另一個不同的地方,當我們需要驗證此檔案是否被篡改時,只需要重新對檔案進行哈希計算,得到一個新的散列值,并將其與原來存盤的散列值進行比對,如果兩個值相同,就可以確定檔案未被篡改,
什么是Hash散列函式
哈希散列函式,也叫哈希函式,是一種將任意長度的訊息映射到固定長度的散列值的函式,它通常是通過執行一系列演算法將輸入資料轉換為一個固定大小的二進制資料而實作的,
哈希散列函式是密碼學中的重要工具之一,它具有不可逆性、單向性(難以從散列值反推源資料)、抗碰撞性(不同的源資料計算出來的散列值相等的概率很小)等特性,廣泛應用于資料加密、身份認證、數字簽名等領域,
常見的哈希散列函式有MD5、SHA-1、SHA-2、SHA-3等,其中SHA-2是應用最廣泛的哈希函式之一,在許多加密協議和安全標準中被廣泛使用,雖然哈希函式具有不可逆性,但是由于計算能力的不斷提高,一些強大的計算能力可以被用來破解哈希函式,因此選擇合適的哈希函式也非常重要,
我們以MD5
以及CRC32
為例,如果讀者需要計算程式中每個節的散列值,則需通過dbg.get_section()
函式動態獲取到所有程式中的節,并取出addr,name,size
三個欄位,通過封裝的md5()
以及crc32
等函式完成計算并輸出,這段代碼的核心實作流程如下所示;
import binascii
import hashlib
from LyScript32 import MyDebug
def crc32(data):
return "0x{:X}".format(binascii.crc32(data) & 0xffffffff)
def md5(data):
md5 = hashlib.md5(data)
return md5.hexdigest()
if __name__ == "__main__":
dbg = MyDebug()
dbg.connect()
# 回圈節
section = dbg.get_section()
for index in section:
# 定義位元組陣列
mem_byte = bytearray()
address = index.get("addr")
section_name = index.get("name")
section_size = index.get("size")
# 讀出節內的所有資料
for item in range(0,int(section_size)):
mem_byte.append( dbg.read_memory_byte(address + item))
# 開始計算特征碼
md5_sum = md5(mem_byte)
crc32_sum = crc32(mem_byte)
print("[*] 節名: {:10s} | 節長度: {:10d} | MD5特征: {} | CRC32特征: {}"
.format(section_name,section_size,md5_sum,crc32_sum))
dbg.close()
運行后等待片刻,讀者應該可以看到如下圖所示的輸出結果,圖中每一個節的散列值都被計算出來;
4.2.3 驗證PE啟用的保護模式
PE檔案的保護模式包括了、隨機基址(Address space layout randomization,ASLR)、資料不可執行(Data Execution Prevention,DEP)、強制完整性(Forced Integrity,FCI)這四種安全保護機制,它們的主要作用是防止惡意軟體攻擊,并提高系統的安全性和可靠性,
1.隨機基址(Address space layout randomization,ASLR)
隨機基址是一種Windows作業系統中的記憶體防護機制,它可以使惡意軟體難以通過記憶體地址預測來攻擊應用程式,在隨機基址機制下,作業系統會隨機改變應用程式的地址空間布局,使得每次運行時程式在記憶體中加載的地址不同,從而防止攻擊者憑借對程式記憶體地址的猜測或破解來攻擊程式,
隨機基址的驗證方式是定位到PE結構的pe.OPTIONAL_HEADER.DllCharacteristics
并對PE檔案的OPTIONAL_HEADER
中的DllCharacteristics
屬性進行取值位運算操作并以此作為判斷的依據:
- 首先,使用
hex()
將pe.OPTIONAL_HEADER.DllCharacteristics
轉換為16進制字串, - 然后,將該16進制字串和
0x40
進行按位與運算, - 最后,將得到的結果與
0x40
進行比較,
根據微軟的檔案,pe.OPTIONAL_HEADER.DllCharacteristics
是一個32位的掩碼屬性,用于描述PE檔案的一些特性,其中DllCharacteristics
的第7位(從0開始)表示該檔案是否啟用了ASLR(Address Space Layout Randomization)
特性,如果啟用,則對應值為0x40,因此,上述代碼的作用是判斷該PE檔案是否啟用了ASLR
特性,如果結果為真,則說明該檔案啟用了ASLR
;否則,說明該檔案未啟用ASLR,
資料不可執行(Data Execution Prevention,DEP)
資料不可執行是一種Windows作業系統中的記憶體防護機制,它可以防止惡意軟體針對系統記憶體中的資料進行攻擊,在DEP機制下,作業系統會將記憶體分為可執行和不可執行兩部分,其中不可執行部分主要用于存放程式資料,而可執行部分用于存放代碼,這樣當程式試圖執行不可執行的資料時,作業系統會立即終止程式,從而防止攻擊者通過操縱程式資料來攻擊系統,
同樣使用位運算子&,對PE檔案的OPTIONAL_HEADER
中的DllCharacteristics
屬性進行了取值并進行了位運算操作,該代碼的具體意義為:
- 首先,使用hex()將
pe.OPTIONAL_HEADER.DllCharacteristics
轉換為16進制字串, - 然后,將該16進制字串和
0x100
進行按位與運算, - 最后,將得到的結果與
0x100
進行比較,
根據微軟的檔案,pe.OPTIONAL_HEADER.DllCharacteristics
是一個32位的掩碼屬性,用于描述PE檔案的一些特性,其中,DllCharacteristics
的第8位(從0開始)表示該檔案是否啟用了NX特性(No eXecute)
,如果啟用,則對應值為0x100
,NX特性是一種記憶體保護機制,可以防止惡意代碼通過將資料區域當作代碼區域來執行代碼,提高了系統的安全性,因此,上述代碼的作用是判斷該PE檔案是否啟用了NX特性,如果結果為真,則說明該檔案啟用了NX特性;否則,說明該檔案未啟用NX特性,
強制完整性(Forced Integrity,FCI)
強制完整性是一種Windows作業系統中的強制措施,它可以防止惡意軟體通過DLL注入來攻擊系統,在FCI機制下,作業系統會通過數字簽名和其他校驗措施對系統DLL和其他關鍵檔案進行驗證,確保這些檔案沒有被修改或替換,如果檢測到檔案已被修改或替換,作業系統將會拒絕這些檔案并終止相關行程,這樣可以保護系統的完整性和安全性,
同樣使用位運算子&,對PE檔案的OPTIONAL_HEADER
中的DllCharacteristics
屬性進行了取值并進行了位運算操作,該代碼的具體意義為:
- 首先,使用hex()將
pe.OPTIONAL_HEADER.DllCharacteristics
轉換為16進制字串, - 然后,將該16進制字串和0x80進行按位與運算,
- 最后,將得到的結果與0x80進行比較,
根據微軟的檔案,pe.OPTIONAL_HEADER.DllCharacteristics
是一個32位的掩碼屬性,用于描述PE檔案的一些特性,其中,DllCharacteristics
的第7位(從0開始)表示該檔案是否啟用了動態基址特性(Dynamic Base)
,如果啟用,則對應值為0x40
,動態基址特性與ASLR(Address Space Layout Randomization)功能是緊密相關的,當啟用ASLR時,動態基址特性也會被自動啟用,因此,上述代碼的作用是判斷該PE檔案是否啟用了動態基址特性,如果結果為真,則說明該檔案啟用了動態基址特性;否則,說明該檔案未啟用動態基址特性,
根據如上描述,要想實作檢查行程內所有模塊的保護方式,則首先要通過dbg.get_all_module()
獲取到行程的所有模塊資訊,當模塊資訊被讀入后,通過dbg.read_memory_byte()
獲取到該記憶體的機器碼,并通過pefile.PE(data=https://www.cnblogs.com/LyShark/p/byte_array)
裝載到記憶體,通過對不同數值與與運算即可判定是否開啟了保護,
- 隨機基址 hex(pe.OPTIONAL_HEADER.DllCharacteristics) & 0x40 == 0x40
- 資料不可執行 hex(pe.OPTIONAL_HEADER.DllCharacteristics) & 0x100 == 0x100
- 強制完整性 hex(pe.OPTIONAL_HEADER.DllCharacteristics) & 0x80 == 0x80
那么根據如上描述,這段核心代碼可以總結為如下案例;
from LyScript32 import MyDebug
import pefile
if __name__ == "__main__":
# 初始化
dbg = MyDebug()
dbg.connect()
# 得到所有加載過的模塊
module_list = dbg.get_all_module()
print("-" * 100)
print("模塊名 \t\t\t 基址隨機化 \t\t DEP保護 \t\t 強制完整性 \t\t SEH例外保護 \t\t")
print("-" * 100)
for module_index in module_list:
print("{:15}\t\t".format(module_index.get("name")),end="")
# 依次讀入程式所載入的模塊
byte_array = bytearray()
for index in range(0, 4096):
read_byte = dbg.read_memory_byte(module_index.get("base") + index)
byte_array.append(read_byte)
oPE = pefile.PE(data=https://www.cnblogs.com/LyShark/p/byte_array)
# 隨機基址 => hex(pe.OPTIONAL_HEADER.DllCharacteristics) & 0x40 == 0x40
if ((oPE.OPTIONAL_HEADER.DllCharacteristics & 64) == 64):
print("True\t\t\t",end="")
else:
print("False\t\t\t",end="")
# 資料不可執行 DEP => hex(pe.OPTIONAL_HEADER.DllCharacteristics) & 0x100 == 0x100
if ((oPE.OPTIONAL_HEADER.DllCharacteristics & 256) == 256):
print("True\t\t\t",end="")
else:
print("False\t\t\t",end="")
# 強制完整性=> hex(pe.OPTIONAL_HEADER.DllCharacteristics) & 0x80 == 0x80
if ((oPE.OPTIONAL_HEADER.DllCharacteristics & 128) == 128):
print("True\t\t\t",end="")
else:
print("False\t\t\t",end="")
if ((oPE.OPTIONAL_HEADER.DllCharacteristics & 1024) == 1024):
print("True\t\t\t",end="")
else:
print("False\t\t\t",end="")
print()
dbg.close()
讀者可以運行這段案例,即可看到如下圖所示的輸出效果;
4.2.4 PE結構的FOA/VA/RAV轉換
在PE檔案結構中,VA、RVA和FOA都是用來描述記憶體中資料的位置和在檔案中的偏移量,具體含義如下:
- VA(Virtual Address):虛擬地址,也叫映像地址,是指在記憶體中的地址,VA通常是程式運行時要訪問的地址,由作業系統進行地址轉換映射到物理地址上,
- RVA(Relative Virtual Address):相對虛擬地址,是指當前位置相對于所在塞段的起始地址的偏移量,RVA通常是用于描述PE檔案中的各個段的相對位置,它不像VA一樣是用于真正運行程式的,而是在加載 PE 檔案時進行重定位所使用的,
- FOA(File Offset Address):檔案偏移量,是指在檔案中的偏移量,也就是從檔案起始位置到資料的偏移量,FOA通常是用于描述PE檔案中的各個段和頭資訊在檔案中的位置,可以用來定位和修改檔案中的資料,
需要注意的是,這三種地址是不同的,其值也不同,VA和RVA通常是在Windows作業系統中使用;FOA通常是在PE檔案處理時使用,在PE檔案加載時,Windows作業系統會將RVA轉換為VA,將程式的段加載到記憶體中,并根據需要對其進行重定位(如果代碼中包含有絕對地址的話),然后將控制權交給程式的入口點,程式進入執行狀態,
首先實作VA轉為FOA
的案例,在這段核心代碼中,通過dbg.get_base_from_address(dbg.get_local_base())
獲取到記憶體中的程式基地址,并與VA地址相減得到記憶體中的RVA地址,并呼叫PEfile庫中的get_offset_from_rva
完成轉換,
def get_offset_from_va(pe_ptr, va_address):
# 得到記憶體中的程式基地址
memory_image_base = dbg.get_base_from_address(dbg.get_local_base())
# 與VA地址相減得到記憶體中的RVA地址
memory_local_rva = va_address - memory_image_base
# 根據RVA得到檔案內的FOA偏移地址
foa = pe_ptr.get_offset_from_rva(memory_local_rva)
return foa
其次是將FOA檔案偏移轉為VA
虛擬地址,此類代碼與上方代碼基本一致,通過pe_ptr.get_rva_from_offset
先得到RVA相對偏移,然后在通過dbg.get_base_from_address(dbg.get_local_base())
得到記憶體中程式基地址,然后計算VA地址,最后直接計算得到VA地址,
def get_va_from_foa(pe_ptr, foa_address):
# 先得到RVA相對偏移
rva = pe_ptr.get_rva_from_offset(foa_address)
# 得到記憶體中程式基地址,然后計算VA地址
memory_image_base = dbg.get_base_from_address(dbg.get_local_base())
va = memory_image_base + rva
return va
最后一種則是將FOA檔案偏移地址轉為RVA
相對地址,此類代碼中通過列舉所有節中的引數,并以此動態計算出實際的RVA地址回傳,
# 傳入一個FOA檔案地址轉為RVA地址
def get_rva_from_foa(pe_ptr, foa_address):
sections = [s for s in pe_ptr.sections if s.contains_offset(foa_address)]
if sections:
section = sections[0]
return (foa_address - section.PointerToRawData) + section.VirtualAddress
else:
return 0
最終將三段代碼整合在一起,即可構成一個互相轉換的案例,至于PE結構的決議問題,詳細度過PE結構篇的你不需要我做太多的解釋了,
from LyScript32 import MyDebug
import pefile
# 傳入一個VA值獲取到FOA檔案地址
def get_offset_from_va(pe_ptr, va_address):
# 得到記憶體中的程式基地址
memory_image_base = dbg.get_base_from_address(dbg.get_local_base())
# 與VA地址相減得到記憶體中的RVA地址
memory_local_rva = va_address - memory_image_base
# 根據RVA得到檔案內的FOA偏移地址
foa = pe_ptr.get_offset_from_rva(memory_local_rva)
return foa
# 傳入一個FOA檔案地址得到VA虛擬地址
def get_va_from_foa(pe_ptr, foa_address):
# 先得到RVA相對偏移
rva = pe_ptr.get_rva_from_offset(foa_address)
# 得到記憶體中程式基地址,然后計算VA地址
memory_image_base = dbg.get_base_from_address(dbg.get_local_base())
va = memory_image_base + rva
return va
# 傳入一個FOA檔案地址轉為RVA地址
def get_rva_from_foa(pe_ptr, foa_address):
sections = [s for s in pe_ptr.sections if s.contains_offset(foa_address)]
if sections:
section = sections[0]
return (foa_address - section.PointerToRawData) + section.VirtualAddress
else:
return 0
if __name__ == "__main__":
dbg = MyDebug()
dbg.connect()
# 載入檔案PE
pe = pefile.PE(name=dbg.get_local_module_path())
# 讀取檔案中的地址
rva = pe.OPTIONAL_HEADER.AddressOfEntryPoint
va = pe.OPTIONAL_HEADER.ImageBase + pe.OPTIONAL_HEADER.AddressOfEntryPoint
foa = pe.get_offset_from_rva(pe.OPTIONAL_HEADER.AddressOfEntryPoint)
print("檔案VA地址: {} 檔案FOA地址: {} 從檔案獲取RVA地址: {}".format(hex(va), foa, hex(rva)))
# 將VA虛擬地址轉為FOA檔案偏移
eip = dbg.get_register("eip")
foa = get_offset_from_va(pe, eip)
print("虛擬地址: 0x{:x} 對應檔案偏移: {}".format(eip, foa))
# 將FOA檔案偏移轉為VA虛擬地址
va = get_va_from_foa(pe, foa)
print("檔案地址: {} 對應虛擬地址: 0x{:x}".format(foa, va))
# 將FOA檔案偏移地址轉為RVA相對地址
rva = get_rva_from_foa(pe, foa)
print("檔案地址: {} 對應的RVA相對地址: 0x{:x}".format(foa, rva))
dbg.close()
如上代碼中所傳遞地址必須保證是正確的,否則會報錯,讀者應根據自己的需求選擇對應的函式來執行,代碼運行后輸出效果如下圖所示;
4.2.5 PE結構檢索SafeSEH記憶體地址
SafeSEH(Safe Structured Exception Handling)是Windows作業系統提供的一種安全保護機制,用于防止惡意軟體利用緩沖區溢位漏洞來攻擊應用程式,
當應用程式使用結構化例外處理機制(SEH)時,其例外處理鏈表(ExceptionHandler鏈表)可以被攻擊者用來執行代碼注入攻擊,這是由于例外處理鏈表本質上是一個指標陣列,如果應用程式使用了未經驗證的指標指向例外處理函式,則攻擊者可以構造惡意的例外處理模塊來覆寫原有的處理程式,從而迫使程式執行攻擊者注入的代碼,這種攻擊技術被稱為SEH注入(SEH Overwrite),
為了解決這個問題,SafeSEH機制被引入到Windows作業系統中,其主要思想是在應用程式的匯入表中加入了一個SafeSEH表,用于存盤由編譯器生成的SEH處理函式的地址串列,在程式執行時,Windows作業系統將檢查程式SEH鏈表中的指標是否存在SafeSEH表中,如果該指標不存在于SafeSEH表中,則Windows作業系統將終止應用程式的執行,SafeSEH機制可以提高系統的安全性和可靠性,防止惡意軟體利用緩沖區溢位和SEH注入等漏洞來攻擊應用程式,
SafeSEH的檢索問題,讀者可依據如下步驟依次實作;
- 1.初始化除錯器dbg,并執行dbg.connect()連接到正在運行的行程,
- 2.memory_image_base = dbg.get_base_from_address(dbg.get_local_base()):獲取程式記憶體鏡像的基地址,
- 3.peoffset = dbg.read_memory_dword(memory_image_base + int(0x3c)):通過PE頭的偏移地址得到PE頭的基地址,
- 4.flags = dbg.read_memory_word(pebase + int(0x5e)):讀取PE頭中的DllCharacteristics屬性的值,使用了read_memory_word函式,該值用于判斷是否開啟了SafeSEH保護,
- 5.如果flags值的第10位(即0x400)為1,則說明該程式已開啟了SafeSEH保護,并輸出保護狀態為“NoHandler”;否則不列印狀態資訊,
- 6.numberofentries = dbg.read_memory_dword(pebase + int(0x74)):讀取PE頭中的NumberOfRvaAndSizes屬性的值,該值用來描述資料目錄表中的專案個數,
- 7.如果numberofentries值大于10,則說明該PE檔案結構例外,退出程式,
- 8.sectionaddress和sectionsize用于指定程式頭表中第10個PE節的地址和大小,如果第10個節大小不為0且為0x40或與第一個DWORD和第二個DWORD的值相等,則說明該程式在節表中找到了SafeSEH記錄,
- 9.sehlistaddress和sehlistsize指定
SafeSEH
記錄串列的地址和大小,如果sehlistaddress
不為0且sehlistsize
不為0,則說明該程式啟用了SafeSEH
保護,并輸出保護狀態, - 10.如果condition等于False,則說明PE結構不符合要求或未啟用SafeSEH保護,如果data小于0x48,則說明該DLL或EXE程式無法被識別,如果sehlistaddress和sehlistsize不同時等于零,則列印SafeSEH保護中的長度,
查找SafeSEH
記憶體地址,讀入PE檔案到記憶體,驗證該程式的SEH保護是否開啟,如果開啟則嘗試輸出SEH記憶體地址,其實作代碼可總結為如下案例;
from LyScript32 import MyDebug
import struct
LOG_HANDLERS = True
if __name__ == "__main__":
dbg = MyDebug()
dbg.connect()
# 得到PE頭部基地址
memory_image_base = dbg.get_base_from_address(dbg.get_local_base())
peoffset = dbg.read_memory_dword(memory_image_base + int(0x3c))
pebase = memory_image_base + peoffset
flags = dbg.read_memory_word(pebase + int(0x5e))
if(flags & int(0x400)) != 0:
print("SafeSEH | NoHandler")
numberofentries = dbg.read_memory_dword(pebase + int(0x74))
if numberofentries > 10:
# 讀取 pebase+int(0x78)+8*10 | pebase+int(0x78)+8*10+4 讀取八位元組,分成兩部分讀取
sectionaddress, sectionsize = [dbg.read_memory_dword(pebase+int(0x78)+8*10),
dbg.read_memory_dword(pebase+int(0x78)+8*10 + 4)
]
sectionaddress += memory_image_base
data = https://www.cnblogs.com/LyShark/p/dbg.read_memory_dword(sectionaddress)
condition = (sectionsize != 0) and ((sectionsize == int(0x40)) or (sectionsize == data))
if condition == False:
print("[-] SafeSEH 無保護")
if data < int(0x48):
print("[-] 無法識別的DLL/EXE程式")
sehlistaddress, sehlistsize = [dbg.read_memory_dword(sectionaddress+int(0x40)),
dbg.read_memory_dword(sectionaddress+int(0x40) + 4)
]
if sehlistaddress != 0 and sehlistsize != 0:
print("[+] SafeSEH 保護中 | 長度: {}".format(sehlistsize))
if LOG_HANDLERS == True:
for i in range(sehlistsize):
sehaddress = dbg.read_memory_dword(sehlistaddress + 4 * i)
sehaddress += memory_image_base
print("SEHAddress = {}".format(hex(sehaddress)))
dbg.close()
運行這段代碼,則可輸出當前行程內所有啟用SafeSEH
的記憶體地址空間,如下圖所示;
原文地址
https://www.lyshark.com/post/558b1012.html
文章作者:lyshark (王瑞)文章出處:https://www.cnblogs.com/LyShark/p/17533827.html
本博客所有文章除特別宣告外,均采用 BY-NC-SA 許可協議,轉載請注明出處!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/556764.html
標籤:訊息安全
上一篇:關于JS定時器的整理
下一篇:返回列表