在第一章中我們介紹了x64dbg
這款強大的偵錯軟體,通過該軟體逆向工程師們可以手動完成對特定程序的漏洞挖掘及脫殼等操作,雖然x64dbg
支援內建Script
指令碼執行模組,但指令碼引擎通常來說是不夠強大的,LyScript 外掛的出現填補了這方面的不足,該外掛的開發靈感來源於Immunity
偵錯程式中的ImmLib
庫,因Immunity
偵錯程式繼承自Ollydbg
導致該偵錯程式無法支援64位元應用的偵錯,同時該偵錯程式也長期沒有開發者進行維護,正是在這種情形之下LyScript
誕生。
LyScript是一款 x64dbg
自動化控制外掛,通過Python
控制x64dbg
的行為,實現遠端動態偵錯,解決了逆向工作者分析程式,反病毒人員脫殼,漏洞分析者尋找指令片段,原生指令碼不夠強大的問題,通過與Python
相結合利用Python
語法的靈活性以及其豐富的第三方庫,加速漏洞利用程式的開發,輔助漏洞挖掘以及惡意軟體分析。
LyScript外掛的安裝非常容易,讀者只需要在官方下載對應的外掛並放入到plugins/
根目錄下即可,當外掛載入成功,讀者可看到如下圖所示的輸出資訊,過程中可能會出現網路通訊授權框,請讀者允許並放行即可;
接著讀者還需要開啟命令列,並通過pip
命令安裝對應的外掛包,LyScript外掛一般而言分為兩個核心包LyScript
用於實現基於函數的通訊機制,而LyScriptTools
則是對函數的類版封裝,兩者建議全部安裝;
安裝此外掛讀者應該具備了Python
開發環境,版本必須大於3.6
才可以,當環境具備後,讀者可執行上述命令,依次安裝這幾個不同的版本。
Microsoft Windows [版本 10.0.19042.1826]
(c) Microsoft Corporation。保留所有權利。
C:\Users\admin> pip install LyScript32
C:\Users\admin> pip install LyScript64
C:\Users\admin> pip install LyScriptTools32
C:\Users\admin> pip install LyScriptTools64
LyScript 外掛提供了豐富的 API 封裝函數,包括暫存器、偵錯、模組、記憶體、堆疊、程序和執行緒等不同類別的函數。這些函數可以幫助開發者更方便地存取和修改程式的內部資訊,便於偵錯和破解,但需要開發者具備一定的程式設計和偵錯經驗,並遵守相應的規範和準則,以確保軟體的安全和穩定性。
注意:首先讀者需要啟動帶有外掛功能的
x64dbg
偵錯程式,並手動拖入一個任意被偵錯程序到偵錯程式中,這是使用外掛功能的必備條件,基於Python下的LyScript
模組無法獨立執行,這一點讀者需要格外注意。
根據外掛官方解釋,使用者在使用該外掛時首先需要通過dbg = MyDebug()
初始化一個偵錯控制類,當這個類被初始化結束後則可以通過dbg.connect()
函數連線到偵錯程式中,當連線被建立時則預設會建立一個持久對談直到Python指令碼結束才會被強制斷開,在連線期間讀者也可通過dbg.is_connect()
檢測通訊端是否存在,如下面這段程式碼則是一個最基本的實現方法。
>>> from LyScript32 import MyDebug
>>>
>>> dbg=MyDebug()
>>>
>>> connect_flag = dbg.connect()
>>> print("連線狀態: {}".format(connect_flag))
連線狀態: True
>>>
>>> ref = dbg.is_connect()
>>> print("是否在連線: ", ref)
是否在連線: True
>>>
>>> dbg.close()
True
在該程式碼範例中,使用者首先通過匯入 LyScript
外掛中的MyDebug
類,建立了一個名為dbg
的範例,並初始化該類。接著呼叫dbg.connect()
函數,建立連線。連線成功後,使用is_connect()
函數檢查通訊端是否存在,並將結果列印出來。最後,呼叫dbg.close()
函數以關閉偵錯程式連線。
暫存器是計算機中的一種高速儲存裝置,位於CPU內部。它們由一些元器件構成,通常是用於儲存和操作CPU指令和資料的硬體單元。不同的CPU架構可能會有不同的暫存器數量、位寬和功能。
暫存器是計算機架構中非常重要的一部分,因為他們能夠在CPU執行指令時快速地儲存和讀取資料,從而提高計算速度。暫存器的儲存速度比記憶體快得多,通常稱為「零快取器延遲」(zero cache delay),所以它們是程式優化的主要目標之一。
暫存器的功能多種多樣,包括儲存資料、指令地址、指令結果,以及用於計算、判斷等各種用途。常見的暫存器包括累加器、計數器、指標暫存器、標誌暫存器等。在程式中,暫存器經常用來儲存中間計算結果和臨時變數,通常比記憶體存取更快,從而提高程式執行效率。
在x64dbg中暫存器通常會展現在螢幕的右上角,以32位元為例,預設情況下暫存器可被分為,通用暫存器,標誌暫存器,DR系列特殊暫存器組,以及段選擇子等。
通用暫存器以及該暫存器的功能如下表格所示;
暫存器名 | 描述 |
---|---|
EAX | 累加器,用於存放一些計算結果或指標。 |
ECX | 計數器,主要用於迴圈計數等功能。 |
EDX | 資料暫存器,被用於存放一些指標或標誌資訊。 |
EBX | 基地址暫存器,主要用於存取記憶體中的資料。 |
ESP | 棧指標,用於指向當前的棧頂。 |
EBP | 基址指標,通常被用來指向當前的棧幀。 |
ESI | 源索引暫存器,通常用於字串操作。 |
EDI | 目標索引暫存器,也常常用於字串操作。 |
標誌暫存器以及該暫存器的功能如下表格所示;
標誌暫存器 | 含義 |
---|---|
CF | 進位標誌 (Carry Flag)。該標誌表示在執行無符號算術指令時是否發生了進位。 |
PF | 奇偶標誌 (Parity Flag)。該標誌表示指令執行後結果的低八位中1的個數是否為偶數。如果是偶數,標誌位被設定為1,否則為0。 |
AF | 奧半字進位標誌 (Auxiliary Carry Flag)。該標誌表示執行指令後低四位的進位情況。 |
ZF | 零標誌 (Zero Flag)。該標誌表示上一條指令執行後結果是否為零。如果結果為零,標誌位被設定為1,否則為0。 |
SF | 符號標誌 (Sign Flag)。該標誌表示結果是否為負數。如果結果為負數,標誌位被設定為1,否則為0。 |
TF | 偵錯標誌 (Trap Flag)。該標誌用於單步偵錯,當該標誌被設定為1時,CPU將在執行完每一條指令後暫停,這使得偵錯程式可以檢查這一指令對暫存器和記憶體的影響。 |
IF | 中斷允許標誌 (Interrupt Flag)。如果該標誌為1,表示CPU允許響應來自外部裝置的可遮蔽中斷請求。 |
DF | 方向標誌 (Direction Flag)。該標誌用於指示字串操作指令是否應該向前 (DF=0) 或向後 (DF=1) 方向進行操作。 |
OF | 溢位標誌 (Overflow Flag)。該標誌表示在執行有符號算術指令時是否發生了溢位。 |
在LyScript
外掛中,通常會使用get_register()
函數獲取特定通用暫存器的引數,與之對應的可以使用set_register()
函數實現設定,如下片段則是分別獲取與設定特定暫存器組的函數呼叫規範;
>>> eax = dbg.get_register("eax")
>>> hex(eax)
'0xa'
>>>
>>> eip = dbg.get_register("eip")
>>> hex(eip)
'0x76fdc3b8'
>>>
>>> flag = dbg.set_register("eax",100)
>>> flag
True
>>> hex(dbg.get_register("eax"))
'0x64'
同理,對於獲取與設定標誌暫存器,也有一對標準的通用函數get_flag_register()
函數用於獲取一個標誌,而與之對應的set_flag_register()
函數則用於設定一個標誌,需要注意的是在設定標誌時,第二個引數需傳入一個狀態[設定為真 True] / [設定為假 False]
而不是接受一個字元或整數;
>>> tf = dbg.get_flag_register("tf")
>>> tf
False
>>>
>>> zf = dbg.get_flag_register("zf")
>>> zf
True
>>>
>>> flag = dbg.set_flag_register("cf",True)
>>> flag
True
>>>
偵錯程式的核心功是對程式進行偵錯,而偵錯程式內部實現往往會呼叫作業系統提供的偵錯API,偵錯系列函數是這些API之一,用於幫助開發者在程式執行時得到更多的資訊,包括記憶體值、指令執行狀態、變數狀態等,以便更加全面和深入地瞭解程式碼的執行情況和錯誤。偵錯系列函數的主要作用如下:
總之,偵錯系列函數為偵錯程式提供了豐富的操作介面和偵錯工具,使得開發人員能夠更加深入和全面地瞭解程式的執行狀態,並藉助這些資訊更好地偵錯和定位程式的錯誤和漏洞。
首先介紹的是set_debug()
函數,該函數是偵錯程式中一個非常重要的函數,它被用於影響偵錯程式的狀態,常見的操作包括前進一次、後退一次、暫停偵錯、終止偵錯等。在偵錯過程中,開發人員會根據需要進行不同的偵錯操作,以理解程式執行的過程、獲得程式碼執行的狀態資訊以及找到程式碼中的錯誤。
該函數的可用的動作範圍包括:暫停(Pause)、執行(Run)、步入(StepIn)、步過(StepOut)、到結束(StepOver)、停止(Stop)以及等待(Wait)。通過呼叫set_debug()函數,並傳遞相應的引數,讀者可以方便地進行偵錯操作,並隨時獲取程式的執行狀態資訊。例如,將引數設定為「暫停」,則可以暫停程式的執行、檢視程式記憶體中的值以及檢查程式呼叫棧等資訊;將引數設定為「執行」則可以繼續程式的執行,直到遇到下一個斷點或者程式結束。
我們以步入(StepIn)、步過(StepOut)為例,讀者可使用如下命令執行一次動作;
>>> dbg.set_debug("StepIn")
True
>>> dbg.set_debug("StepOut")
True
>>>
此外判斷偵錯程式動作也是一種非常普遍的功能,外掛內提供了is_debugger() /is_running()/is_run_locked()
三個偵錯函數,函數is_debugger
可用於驗證當前偵錯程式是否處於偵錯狀態,函數is_running
則用於驗證是否在執行,函數is_run_locked
用於檢查偵錯程式是否被鎖定(暫停),這三個函數的呼叫規範與上方基本一致;
>>> dbg.is_debugger()
True
>>> dbg.is_running()
False
>>> dbg.is_run_locked()
True
斷點是偵錯程式中常用的工具之一,可以幫助開發人員暫停程式的執行並檢查程式中的錯誤。x64dbg中的斷點分為以下幾類:
軟體斷點(BP):軟體斷點是一種在程式執行期間暫停程式並引起中斷的程式碼指令,可用於修復軟體中的一些缺陷或偵錯程式。在x64dbg中,使用「F2」鍵可以在程式的程式碼段中設定軟體斷點,碰到指定斷點時會暫停程式並進入偵錯模式,以便對程式進行偵錯。
硬體斷點:硬體斷點是一種針對某個具體的地址,由CPU硬體支援的斷點,當程式執行到該地址時,CPU會中斷程式並通知偵錯程式進行偵錯。硬體斷點在偵錯程式中設定方式和軟體斷點相同,也是通過「F2」鍵來設定。在使用硬體斷點時,需要特殊注意硬體斷點的數量,因為硬體斷點數量通常非常有限。
記憶體斷點:記憶體斷點是一種根據條件變化暫停程式執行的斷點,它可以對記憶體地址進行監視,當記憶體中的指定值在程式執行時發生變化時觸發中斷。在x64dbg中,可以通過「右鍵選單」中的「記憶體瀏覽器」或「記憶體」視窗設定記憶體斷點。
LyScript外掛可以使用set_breakpoint()
函數設定軟體斷點,使用delete_breakpoint()
函數刪除一個軟體斷點,使用check_breakpoint()
函數可用於檢測斷點是否被命中,使用get_all_breakpoint()
可用於輸出當前所有斷點
>>> eip = dbg.get_register("eip")
>>> dbg.set_breakpoint(eip)
True
>>> dbg.check_breakpoint(eip)
True
>>> ref = dbg.get_all_breakpoint()
>>> ref
[{'addr': 4584081, 'enabled': 1, 'hitcount': 0, 'type': 1}, {'addr': 1996337564, 'enabled': 1, 'hitcount': 0, 'type': 1}, {'addr': 1996337612, 'enabled': 1, 'hitcount': 0, 'type': 1}, {'addr': 1996337643, 'enabled': 1, 'hitcount': 0, 'type': 1}]
同理,當讀者需要設定硬體斷點是可使用set_hardware_breakpoint()
設定,但需要注意硬體斷點在32位元系統中最多設定4個,如果需要取消斷點則可使用delete_hardware_breakpoint()
函數將斷點進行移除;
斷點型別可用範圍:[型別 0 = HardwareAccess / 1 = HardwareWrite / 2 = HardwareExecute]
>>> ref = dbg.set_hardware_breakpoint(eip,2)
>>> print(ref)
True
>>> ref = dbg.delete_hardware_breakpoint(eip)
>>> print(ref)
True
堆疊視窗是偵錯程式非常重要的一個功能視窗,可以幫助開發人員監視程式執行時的堆疊資訊。該視窗能夠顯示當前執行緒的呼叫棧、區域性變數(Local Variables)以及函數引數(Function Parameters)等重要資訊,並以圖形化的方式呈現出來,方便使用者進行檢視和偵錯。
在x64dbg的堆疊視窗中,對於每一個程式執行時的執行緒,都會顯示當前執行緒的呼叫棧資訊,最上面的棧幀表示當前正在執行的函數,下面棧幀則為呼叫該函數的函數。使用者可以通過向上和向下翻轉堆疊棧幀檢視程式函數呼叫的層級,便於查詢程式執行過程中的錯誤和問題。
此外,在當前呼叫棧幀中,使用者還可以檢視當前函數的引數和區域性變數等資訊,這些資訊通常是有助於偵錯程式問題的關鍵性資訊。使用者可以通過右鍵單擊堆疊視窗中的任何一幀來切換該幀的狀態,包括指標、引數、影象、字串、十六進位制等,以便更加準確地檢視當前棧的資訊。
綜上所述,堆疊視窗是x64dbg偵錯程式中非常重要的一個功能視窗,可以幫助使用者在程式執行過程中理解、偵錯和跟蹤程式執行的層次結構和變數資訊,解決程式碼的問題,提高開發效率。
堆疊系列函數包括了push_stack()
用於向目標堆疊中壓入一個數值,與之對應的pop_stack()
則用於在堆疊中彈出一個元素,peek_stack()
函數用於檢查堆疊內的引數,可設定偏移值,不設定則預設檢查第一個也就是棧頂。
>>> check = dbg.peek_stack()
>>> check
1996055601
>>>
>>> check = dbg.peek_stack(1)
>>> hex(check)
'-0x757bb72f'
>>>
>>> dbg.push_stack(10)
True
>>> dbg.push_stack(10)
True
>>> dbg.pop_stack()
True
>>> check = dbg.peek_stack(1)
>>> hex(check)
'0x76f96431'
>>>
反組合是偵錯程式中非常重要的核心功能之一,它可以將已編譯的二進位制程式碼轉換為對應的組合程式碼,幫助開發人員瞭解程式的執行流程和程式碼邏輯,並定位程式碼問題。在x64dbg偵錯程式中,反組合功能是其核心功能之一,具有以下特點:
反組合是x64dbg偵錯程式的核心功能之一,可以幫助開發人員進行程式碼分析和偵錯,定位程式碼問題,提高開發效率。通過x64dbg的反組合功能,開發人員可以有效地瞭解程式的程式碼邏輯、執行流程和指令序列,為偵錯和分析程式提供有力的支援。
當讀者需要使用外掛控制偵錯程式反組合時可以使用get_disasm_code()
該函數主要用於對特定記憶體地址進行反組合,需傳入兩個引數,並輸出一個字典型別的資料集合,如下案例我們反組合EIP位置處向下的30個行;
>>> eip = dbg.get_register("eip")
>>> disasm_dict = dbg.get_disasm_code(eip,30)
>>>
>>> for ds in disasm_dict:
... print("地址: {} 反組合: {}".format(hex(ds.get("addr")),ds.get("opcode")))
...
地址: 0x76fdb19c 反組合: ret
地址: 0x76fdb19d 反組合: mov ecx, dword ptr ds:[0x770537C0]
地址: 0x76fdb1a3 反組合: test cl, 0x3
地址: 0x76fdb1a6 反組合: je 0x76FDC66E
地址: 0x76fdb1ac 反組合: push eax
地址: 0x76fdb1ad 反組合: push 0x76F36CF0
記憶體讀寫功能,可以讓讀者在偵錯階段直接讀取和修改程式的記憶體內容,方便程式偵錯和分析。下面是對LyScript外掛記憶體讀寫功能的簡要概述:
記憶體讀取:通過LyScript外掛,開發人員可以通過程式碼讀取已經載入的程式程序的記憶體中特定地址的值。該外掛提供了幾個不同的記憶體讀取函數,包括ReadByte()、ReadWord()、ReadDword()、ReadQword()等。通過這些函數,開發人員可以直接讀取目標視窗中的指定記憶體地址,以獲取其值。
記憶體寫入:除了讀取程式記憶體的值,LyScript外掛還支援修改程式的記憶體值。記憶體寫入函數為WriteByte()、WriteWord()、WriteDword()、WriteQword()等幾個相關的函數。通過這些函數,開發人員可以直接修改目標視窗中的指定記憶體地址,以達到偵錯和分析程式的目的。
通過LyScript外掛的記憶體讀寫功能,讀者可以直接讀取和修改目標程式的記憶體資訊,方便偵錯和分析程式。同時,該外掛也提供了一些記憶體操作API,一些常用的記憶體操作功能,幫助安全從業者更加方便地操作記憶體資料。
當讀者需要讀取記憶體資料時可以使用read_memory_byte()
來實現,如下案例中通過get_register()
函數獲取到當前EIP的記憶體地址,並使用記憶體讀函數獲取該記憶體中的前十條資料集,輸出效果如下所示;
>>> eip = dbg.get_register("eip")
>>>
>>> for index in range(0,10):
... ref = dbg.read_memory_byte(eip+index)
... print(hex(ref))
...
0xc3
0x8b
0xd
0xc0
0x37
0x5
0x77
0xf6
0xc1
0x3
至此本章基本介紹就到此為止,本章介紹了在Windows
平臺中使用LyScript
外掛進行反組合和偵錯的相關內容。首先介紹了LyScript
外掛的基本功能和使用方法,如通過不同的命令進行反組合、檢視組合程式碼、設定斷點等。並結合具體案例,演示瞭如何在LyScript
中使用不同的命令進行程式反組合和偵錯。同時,還介紹了偵錯程式中常用的斷點型別及其功能,並以x64dbg
為例對堆疊視窗的功能進行詳細介紹。讀者如果需要了解更多關於LyScript
外掛的使用手冊可自行去官方查閱。