<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    棧與棧幀的調試

    VSole2022-03-06 16:24:19

    棧,通常用于存儲局部變量、傳遞函數參數、保存函數返回地址等。

    棧內存在進程中的作用

    1、暫時保存函數內的局部變量

    2、調用函數時傳遞參數

    3、保存函數返回后的地址

    棧的特征

    一個進程中,棧頂指針(ESP)初始狀態指向棧底端、執行push命令將數據壓入棧時,棧頂指針就會上移到棧頂端。執行pop命令從棧中彈出數據時,若棧為空,則棧頂指針就會重新移動到棧底端。

    棧是由下往上擴展的,棧頂頂針在初始狀態下指向棧底,這就是棧的特征。

    棧操作示例

    1、od打開stack.exe文件,從中看到寄存器窗口中棧頂指針ESP的值為0012FFC4,棧窗口中最上面的地址(即棧頂地址)也為0012FFC4,右邊為0012FFC4對應的值。

    2、在代碼窗口按下F7(Step into),自己執行地址00401000處的PUSH 100指令。

    可以看到ESP的值減少了四個字節,變成了0012FFC0,并且棧窗口中的棧頂指定也指向了0012FFC0地址,且值也變成100。

    再次執行pop EAX,ESP的值增加4個字節,變為0012FFC4。OD狀態變成最開始的狀態。

    棧幀在程序中用于聲明局部變量、調用函數。理解棧幀主要用來掌握保存在其中的函數參數與局部變量。

    簡而言之,棧幀就是利用EBP(棧幀指針,注意不是ESP)寄存器訪問棧內局部變量、參數、函數返回地址等的手段。ESP寄存器承擔著棧頂指針的作用,而EBP寄存器則負責行駛棧幀指針的職能。

    在程序運行中,ESP寄存器的值隨時會變化,訪問棧中函數的局部變量、參數時,若以ESP的值為基準編寫程序會十分困難,并且也很難使CPU引用到準確的地址。

    所以,調用某函數時,先要把用作基準點(函數起始地址)的ESP值保存到EBP,并維持在函數內部。這樣,無論ESP的值如何變化,以EBP的值為基準能夠安全訪問到相關函數的局部變量、參數、返回地址。

    棧幀對應的匯編代碼:

    示例:stackframe.cpp源碼

    在main()函數的起始地址(40120)處下一個斷點,然后F9運行程序,程序運行到斷點處停止。

    在開始調試前,記錄一下棧的狀態。如下,此時ESP的值為0012FF7C,EBP的值為0012FFC0。

    注意,地址00401250保存在ESP(0012FF7C)中,它是main()函數執行完畢后要返回的地址。

    main()函數一開始運行就生成與其對應的函數。

    PUSH是一條壓棧指令,下面這一句的意思是將EBP的值壓入棧。main()函數中,EBP為棧幀指針,用來把EBP之前的值備份到棧中(main()函數執行完畢后,在返回之前,該值會再次恢復)。同時執行,這一條PUSH指令后,ESP的值變成12 FF78(0012FF7C-4)

    同時執行這一條PUSH指令后,ESP的值變成12 FF78(0012FF7C-4)。

    MOV是一條數據傳輸指令,這條語句的意思是將ESP的值傳送到EBP。也就是說,從這條指令開始,EBP就有了與現在ESP相同的值,并且直到main()函數執行完畢,EBP的值始終保持不變。

    換言之,可以通過EBP安全的訪問到存儲在棧中的函數參數與局部變量。執行完PUSH與MOV兩條指令,棧幀就生成了,即EBP被設置好了。

    在OD的棧窗口,右擊依次選擇Address-Relative to EBP。

    現在的棧內情況被轉換成相對于EBP的偏移后,能更直觀地觀察到棧內情況。如下,在棧窗口中可以看到EBP的位置。

    2、設置局部變量

    開始分析源文件中的變量聲明與賦值語句。下面的語句用于在棧中為局部變量(a,b)分配空間

    執行該指令之后,ESP由原來的12 FF78變成12 FF70。減去這8個字節,是為局部變量(a和b)開辟空間,以便將它們保存在棧中。由于局部變量a與b都是long型(長整型),它們分別占據4個字節,因此需要開辟8個字節的空間。

    為函數變量開辟好棧空間后,在main()函數內部,無論ESP的值如何變化,變量a與b的棧空間都不會收到損壞。由于EBP的值在函數內部是固定不變的,所以可以以它為基準來訪問函數的局部變量了。

    接下來看如下代碼,dword ptr ss:[ebp-0x4]可以理解為c語言中的指針。

    匯編語言與C語言的指針語句格式:

    提示:DWORD PTR SS:[EBP-4]語句中,SS是Stack Segment的縮寫,表示棧段。由于Windows中使用的段內存模型,使用時需要指出相關內存屬于哪一個區段。起始,32位的Windows OS中,SS、DS、ES的值皆為0,所以采用這種方式附上區段并沒有什么意義。

    因為EBP與ESP是指向棧的寄存器,所以添加上了SS寄存器。DWORD PTR SS:等字符串可以通過OD的相關選項來隱藏。

    再次分析一下剛剛的兩條MOV指令,它們的含義是把數據1與數據2分別保存在[EBP-4]與[EBP-8]中,即[EBP-4]代表局部變量a,[EBP-8]代表局部變量b。執行完兩條指令后,棧內情況如下圖,可以看到其對應的值分別為1,2。

    3、add()函數參數傳遞與調用

    代碼如下:

    請看下面五行匯編代碼,它描述了add()函數的整個過程。

    地址0040103C處為"CALL 40100" 命令,用于調用40100處的函數,而40100處的函數為add()函數。

    函數add()接收a、b兩個長整型參數,所以在調用add()之前需要把兩個參數壓入棧,地址401034—40103B之間的指令就是這個作用。這一過程中需要注意的是,參數入棧的順利與C語言源碼中的參數順序相反(這種現象被稱為函數參數的逆向存儲)。

    換言之,變量B首先入棧,接著變量a再入棧。執行完401034—40103B的指令后,棧內情況如下:

    執行CALL命令進入被調用函數之前,CPU會把函數的返回值壓入棧,用做函數執行完畢后的返回地址。

    從上圖可以看出調用add()函數之后,下一條命令的地址為401041。函數執行完畢后就會跳轉到這個地址。這個地址被稱為函數的返回地址。執行CALL命令后的棧窗口如下:

    接下來執行add()函數的匯編指令。指令如下:

    指令先把EBP值(main()函數的基址指針)保存到棧中,再把當前的ESP的值存儲到EBP當中。執行完,棧窗口中的情況如下:

    4、設置add()函數的局部變量(x,y)

    源代碼如下:

    聲明了兩個長整型的局部變量(x,y),并用兩個形式參數(a,b)分別為它們賦初始值。

    執行完下面的指令后,[EBP+8]與[EBP+C]分別執行參數a與b,而[EBP-8]與[EBP-4]分別指向add()函數的兩個局部變量x,y。棧窗口如下:

    5、ADD運算

    源碼:

    匯編指令:

    小知識:

    EAX是一種通用寄存器,在算術運算中存儲輸入輸出數據,為函數提供返回值。若向EAX輸入某個值,該值就會原封不動地返回。執行過程中,棧內狀態不變。

    6、刪除函數add()的棧幀以及函數執行完成返回

    把EBP值賦給ESP,與前文的MOV EBP,ESP指令對應。把存儲到EBP的值恢復到ESP中。

    提示:

    執行完MOV ESP,EBP之后,地址401003處的指令不在有效。POP EBP用于恢復函數add()開始執行時備份到棧中的EBP值,可以看到ESP的值為0012FF64,對應的值為401041是執行call 401000命令時cpu存儲到棧中的返回地址。

    EBP恢復為0012FF78,是當初main()函數的EBP值。

    執行下面的語句后,存儲在棧中的返回地址被調用。

    棧中地址如下:此時棧中的地址與返回到調用add()函數之前的狀態。

    7、從棧中刪除函數add()的參數

    執行如下命令,將ESP+8。

    ESP加8的原因可以從上一張圖看出,地址12FF68與12FF6C處存儲的是傳遞給函數add()的參數a與b。函數add()執行后,不需要參數a與b了,所以ESP+8將它們從棧中清理掉。

     

    提示:

    1、調用add()函數之前先使用push指令把參數a,b壓入棧;

    2、被調函數執行完畢后,函數的調用者(Caller)負責清理掉存儲在棧中的參數,這種方式稱為cdecl方式;

    反之,被調用者(Caller)負責清理保存在棧中的參數,這種方式被稱為stdcall方式。

    這些函數調用規則統稱為調用約定。

     

    8、調用printf()函數

    匯編指令如下:

    地址401044處的 EAX 寄存器中存儲著函數 add()的返回值,它是執行加法運算后的結果值3。地址40104A處的 CALL 401067命令中調用的是401067地址處的函數,它是一個 C 標準庫函數 printf ,所有 C 標準庫函數都由 Visual C ++編寫而成(其中包含著數量龐大的函數,在此不詳細介紹)。

    由于上面的 printf 函數有2個參數,大小為8個字節(32位寄存器+32位常量=64位=8字節),所以在40104F地址處使用 ADD 命令,將 ESP 加上8個字節,把函數的參數從棧中刪除。函數 printf()執行完畢并通過 ADD 命令刪除參數后。棧內情況如下:

    執行如下指令來設置返回值:

    常用于寄存器的初始化操作,執行下面兩條命令來對棧幀進行刪除以及函數終止。

    執行后棧內地址如下圖:

    執行完下面的命令,主函數執行完畢并返回,程序執行跳轉到返回地址(401250)。該地址執行visual C++的啟動函數區域。

    00401056 retn

    函數指針局部變量
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    mruby是一個Ruby語言的輕量級實現,mruby工作方式類似CPython,它可以將Ruby源碼編譯為字節碼,再在虛擬機中解釋運行。
    在所有函數調用發生時,向棧幀內壓入一個額外的隨機 DWORD,隨機數標注為“SecurityCookie”。在函數返回之前,系統將執行一個額外的安全驗證操作,被稱做 Security check。
    控制流劫持攻擊是當前較為主流的攻擊方式之一,包括ROP、JOP等等。
    前言藍屏死機指的是微軟Windows操作系統在無法從一個系統錯誤中恢復過來時所顯示的屏幕圖像。本文只探討由R3引起的BSoD,明眼人都知道研究R0的藍屏純屬脫褲子放屁。對于各安全廠商而言,這應該是需要特別注意的,如何連這都攔截不了,后面的防護也只是癡人說夢。
    本文對如何利用 DCOM 進行橫向移動的手法進行了總結,希望可以對大家的學習提供一些幫助。
    用od插件搜索一下,都開啟了SafeSEH保護。修改參數 ,再次調試查看。好的,現在可以看到,Handler已經被覆蓋為 C , 那么現在需要找到跳板地址來跳到shellcode。之后我又選擇了一個 pop ret指令的地址, 沒辦法,只能找到這個指令了,推算一下, 也就是 jmp [esp+4]。
    接著是read_password函數,與read_name函數基本一致。在看最后的password_checker()前,我們用正確密碼測試文件,顯示段錯誤。查看password_checker,login與admin的密碼比較后,來了個奇葩的有毒打印,接著前面的二級函數終于被調用了。我們需要知道報錯原因,gdb調試發現正好是二級指針調用出錯。利用思路經過上面的初步分析,我們知道程序在password_checker中調用一個二級指針失敗而段錯誤終止,考慮到canary的存在,srop不可能短期實現。即使我們無法利用這個二級指針getshell,它的存在也會讓程序終止。
    TP-LINK 型號為 TL-WR841N V10 的路由器設備上的漏洞被分配 ID CVE-2020-8423。該漏洞允許經過身份驗證的攻擊者通過向 wifi 網絡配置發送 GET 請求來遠程執行設備上的任意代碼。
    DCCP double free漏洞可能允許本地無特權用戶更改Linux內核內存,從而導致拒絕服務或升級權限以獲得對系統的管理訪問權限。DCCP是一種面向消息的傳輸層協議,它盡可能減少數據包報頭大小或終端節點處理的開銷,并提供不可靠數據包流的建立、維護和拆卸,以及該數據包流的擁塞控制。因此,如果您是高級Linux用戶,請自己應用補丁并重建內核。
    本文要實現的是對模塊加載的監控,并卸載掉目標模塊。實驗是在WIN 7 X86系統上進行,需要使用的文件是InjectDll.dll和TestDriver.sys。這兩個文件的內容非常簡單,InjectDll.dll在加載進進程中以后只會彈出加載的對話框,而TestDriver.sys只是在驅動加載和卸載的時候打印字符提醒。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类