主頁 > 企業開發 > 4.2 x64dbg 針對PE檔案的掃描

4.2 x64dbg 針對PE檔案的掃描

2023-07-07 09:16:25 企業開發

通過運用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定時器的整理

下一篇:返回列表

標籤雲
其他(162189) Python(38266) JavaScript(25527) Java(18291) C(15239) 區塊鏈(8275) C#(7972) AI(7469) 爪哇(7425) MySQL(7290) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5876) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4613) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2438) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) HtmlCss(1992) .NET技术(1986) 功能(1967) Web開發(1951) C++(1942) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1882) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 4.2 x64dbg 針對PE檔案的掃描

    通過運用`LyScript`插件并配合`pefile`模塊,即可實作對特定PE檔案的掃描功能,例如載入PE程式到記憶體,驗證PE啟用的保護方式,計算PE節區記憶體特征,檔案FOA與記憶體VA轉換等功能的實作,首先簡單介紹一下`pefile`模塊。pefile模塊是一個用于決議Windows可執行檔案(PE... ......

    uj5u.com 2023-07-07 09:16:25 more
  • 關于JS定時器的整理

    在JS中定時器有非常大的作用,例如: 執行延遲操作:使用setTimeout可以在一定的延遲后執行特定的代碼。這對于需要在一定時間后執行某些操作的情況非常有用,例如延遲顯示提示資訊、執行影片效果等。 定期重繪資料:使用setInterval可以定期執行某段代碼,例如定時從服務器獲取最新資料并更新頁面 ......

    uj5u.com 2023-07-07 08:48:51 more
  • uniapp如何給空包進行簽名操作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 首先安裝sdk https://www.oracle.com/java/technologies/downloads/ 正常下一步即可~安裝完畢后,進入在sdk根目錄執行cmd C:\Program Files\Java\jdk-18.0 ......

    uj5u.com 2023-07-07 08:48:33 more
  • Vue 先初始化父組件再初始化子組件的方法(自定義父子組件mounted

    > **寫在前面:** > > * 本篇內容內容主要講述了,在使用 `Konva` 進行開發程序中遇到的一些問題。(既然是組件加載順序,主要牽扯到的就是,父子組件的關系,父子組件的生命周期) > > * 眾所周知,`Vue`中父子組件生命周期的執行順序為: > > ```javascript > / ......

    uj5u.com 2023-07-07 08:48:21 more
  • 記ios的input框獲取焦點之后界面放大問題

    在移動端開發專案中,發現頁面在使用 iPhone 訪問的時候,點擊 input 和 textarea 等文本輸入框聚焦 focus() 時,頁面會整體放大,而且失去焦點之后頁面不能回傳原來的樣子。檢查了下功能上沒有什么大問題,但是頁面會整體放大,而且失去焦點之后頁面不能回傳原來的樣子。對于用戶體驗不 ......

    uj5u.com 2023-07-07 08:48:12 more
  • 編程實用工具推薦

    # 一、截圖神器 ## 1、Snipaste * Snipaste,一款簡單強大的截圖貼圖利器 * 下載地址:Snipaste ![](https://img2023.cnblogs.com/blog/3202319/202306/3202319-20230601170348434-18242786 ......

    uj5u.com 2023-07-07 08:47:31 more
  • NVM安裝與配置教程

    # 一、NVM簡介 在專案開發程序中,使用到vue框架技術,需要安裝node下載專案依賴,但經常會遇到node版本不匹配而導致無法正常下載,重新安裝node卻又很麻煩。為解決以上問題,nvm:一款node的版本管理工具,能夠管理node的安裝和使用,使用簡單,可下載指定node版本和切換使用不同版本 ......

    uj5u.com 2023-07-07 08:46:57 more
  • ElementUI的Dialog彈窗實作拖拽移動功能

    在專案中使用el-dialog中發現不能夠拖拽移動,因此網上找了相關資料,使用自定義指令實作拖拽功能。 1、創建自定義指令: 新建檔案directive/el-drag-dialog/index.js import drag from "./drag"; const install = functi ......

    uj5u.com 2023-07-07 08:46:40 more
  • 前端必須知道的手機除錯工具vConsole

    一個輕量、可拓展、針對手機網頁的前端開發者除錯面板。
    vConsole 是框架無關的,可以在 Vue、React 或其他任何框架中使用。
    現在 vConsole 是微信小程式的官方除錯工具。 ......

    uj5u.com 2023-07-07 08:46:30 more
  • vue使用html2canvas優化---節點過濾

    當你使用html2canvas對某個節點進行截圖時,專案小dom節點少那還沒什么性能問題,如果是個大專案,有成百上千個dom節點,那將是非常頭疼的事情(產品經理:小張啊,你這個截圖功能為什么需要這個長的時間,這讓客戶怎么用,重新改。小張:********...)。不多bb了,直接開始 html2ca ......

    uj5u.com 2023-07-07 08:46:23 more