天天看點

《老碼識途:從機器碼到架構的系統觀逆向修煉之路》- 第1章 - 總結本章學到了什麼學習感想學習筆記

本章學到了什麼

  1. 調試技巧:在VS中斷點調試,檢視反彙編代碼,step into進行步進調試,運作過程中檢視寄存器、記憶體位址、變量值變化等。
  2. 機器碼構造能力:使用C/C++中的直接在C代碼裡寫彙編語言的功能(_asm)。學會了常見的彙編指令,接觸了幾個帶有循環、跳轉的彙編語言代碼。
  3. 指針機制:對C/C++中的指針機制有了更深的了解。
  4. 函數調用機制:函數調用過程中棧的變化。函數調用約定的大緻了解。
  5. 數組模型
  6. 結構體模型
  7. 對齊:搞清楚了為什麼對齊這麼重要。
  8. switch分析:switch的彙編實作原理。
  9. 加載期重定位:二進制編譯和加載過程中的重定位機制究竟是怎麼回事。

學習感想

  1. “猜測 - 實證 - 建構”的學習方法。

在學習底層相關的知識時,總會遇到很多問題。那麼遇到問題,一定要追根究底,不能馬馬虎虎就過去了。不能“大概明白了”,而是要真的搞明白。這個具體的過程就是,在學習的過程中,提出問題,猜想這個問題的答案可能是什麼,根據自己的猜想去寫代碼或者調試求證。證明了之後,提煉出這個問題包含的知識點,再建構例子與驗證它。這個完整的過程搞明白了,對于一個知識點才算是真正地了解了。

反觀我自己之前的學習方法,看資料的時候,往往一帶而過,缺乏追根問底的精神,是以感覺一個東西好像搞明白了,其實根本就沒有。在平日學習中遇到的問題,很多時候也是上網搜一下知道怎麼回事就算了,卻沒有自己動手做一做,下一次一遇到的時候,還是不會。

  1. 體系化的學習可遇而不可求,要學會零散式地學習。

學習底層知識時,大部分知識點都是零散的,是以我們不應該好高骛遠,追求一蹴而就。在學習的過程中,我們會遇到很多實際問題,然後發現自己這也不會那也不會,感覺好像無從下手。我認為,應該就事論事,遇到問題,就去搜尋對應的解決方案,發現了一個問題解決一個問題,每次解決一個小的知識點,整個知識網絡就在解決這些小知識點的過程中慢慢建構起來了。打個比喻,我們的學習一開始全是漏洞,補都不補過來,但是不要失去信心,每次解決一個小問題,慢慢地零散的知識就結成網了,慢慢變得滴水不漏。

學習筆記

  1. 猜測 - 實證 - 建構
  2. 使用VS2008的反彙編、監視視窗、記憶體視窗、單步、斷點、全局變量指派的反彙編。
  3. C語言中的指針大小為4,隻存放了位址,那麼類型資訊有什麼用呢?類型資訊決定了在該位址處理資料的大小,即指派/讀取時寫/讀多少位元組。例如int *p,那麼對應彙編指令會使用dword,就是4位元組。
  4. 指針強制轉換的影響不是發生在轉換時(因為位址都是4位元組),而是在轉換後指派的時候,通路記憶體的位元組大小。要保證指針強制轉換是安全的,必須保證轉換後的指針指向的資料類型大小小于原資料類型大小。
  5. 對于一個補碼形式的負數求其正數值,就是求反加1。
  6. x86系列CPU的call指令尋址方式為:用與call指令相關的偏移量定位到跳轉的位址。

    偏移量計算:偏移量 = 跳轉到的位址 - call指令後一條指令的起始位址。

  7. call指令将傳回位址儲存在記憶體中,而且ESP寄存器指向了該記憶體。實際上這塊記憶體就是棧——ESP指向棧的棧頂。每次壓棧,棧頂位址變小,即ESP的值變小。

    實際上call指令相當于兩條指令的組合:

    push 傳回位址

    jmp 函數入口位址

  8. C語言的參數傳遞是從右往左壓棧傳遞參數。
  9. ESP的存在是為了指明棧頂的位置,那麼EBP存在是為了什麼呢?為了每一次調用函數時,能夠順利找到壓到棧中的參數位置。怎麼找?利用一個确定的基址加上偏移。這個基址就是EBP。為了確定EBP的正确,每個函數調用時都要有儲存和恢複EBP的過程。

    push ebp

    pop ebp

    在被調用的函數裡執行時,EBP用來作為基址擷取參數的值。

    在函數内配置設定局部變量的時候,要使用棧更低位址的空間,也就是繼續壓棧。

    是以,在使用EBP尋址的函數中,EBP+偏移量就是參數的位址(要回溯尋找),EBP-偏移量就是局部變量的位址。

  10. ret指令将棧頂儲存的位址值彈入寄存器EIP,即pop eip。

    編譯器必須保證執行ret時,ESP正好指向call指令壓棧儲存傳回位址的那段記憶體。

  11. 編譯器習慣上使用eax作為存儲傳回值的寄存器,被調用方在ret前設定eax,傳回後,調用方從eax擷取到該值。
  12. lea eax, [ebp + 10h] 即eax = ebp + 10h
  13. 平衡棧/清棧的兩種方式:①調用方清棧,call傳回後,執行add esp, x指令。②被調用方清棧:執行ret x指令。前者用空間代價換取了變參功能。
  14. 調用慣例calling convention

    (1)是寄存器還是棧傳遞參數

    (2)棧傳遞時,參數是從右往左還是從左往右壓棧

    (3)誰來清棧,是調用方還是被調用方

    例如,C語言的調用方式是棧傳參,參數從右往左壓棧,調用方清棧。

    _stdcall是微軟系統調用采用的慣例,除清棧是被調用方外,其他同C語言方式。

    _fastcall是寄存器傳遞。

  15. 函數指針

    函數指針包括入口位址和函數原型(函數參數表、傳回類型、調用慣例)兩方面的資訊。

    函數指針指派的原則是:隻能将與指針原型比對的函數的入口位址指派給它。

    大多數函數指針強制類型轉換都會出錯,是以不要進行函數指針的強制類型轉換。

    函數指針可用于虛函數調用。

  16. 基本資料(如單位元組、雙位元組、四位元組整數)存放處的位址必須能被自己資料類型的大小整除。

    對齊的規律:首先標明一個盒子,然後依序将字段往盒子中放,當盒子放不下後,又用下一個盒子存放,直至所有字段都存放完畢。

    盒子長度 = min{max{sizeof(成員變量)}, 對齊長度}

    字段放入盒子的可放置位置如下:

    離盒子頭部偏移位元組數 = n * sizeof(成員變量)(n=0, 1, 2, …)

    在程式設計的時候,遇到結構體,要注意是否有對齊問題。

  17. switch不能處理浮點數的原因是,它會将該數映射為數組的索引。

    實際上是一種跳轉位址表的方法,計算複雜度不因分支的增加而增加,在大部分情況下比if-else要快。

  18. 在CPU保護模式下,每個執行程序(程式的一個執行個體)都擁有自己獨立的線性位址空間,這種機制叫虛存系統。使用者态程式無法直接通路實體記憶體。

    每個程序都有自己獨立的0~4GB的線性位址空間。

    編譯的時候就知道全局變量位址。這是因為編譯器就能确定所有全局變量相對頭部的偏移量,隻要程式加載到編譯器希望加載的位址,則所有全局變量位址在編譯器都可以計算出。

    全局變量位址 = 程式頭部加載位址b + 全局變量相對程式頭部的偏移量a

    程式中要存儲這個希望加載的位址,稱為image base(基址)。

    如果不得不加載到一個不是希望加載的位址,那麼就要進行重定位(顯然這種情況經常出現)。如何確定修改成功呢?隻要所有變量與基址的相對位置的偏移量是确定的,那麼就沒問題了。這樣不需要去了解指令類型。

    relocAddr = actualBase + a

    *relocAddr = *relocAddr(重定位前) + actualBase - expectedAddr

    整個程式中有好多偏移量,是以程式中有一個表來存儲這些偏移量,稱為重定位表。

    Windows中的真實重定位是這樣的:為了節省重定位項的空間,不是用4位元組表示到程式頭部的偏移量,而是将需要重定位的部分劃分成一個個區(section)

    總偏移 = 區起始的偏移 + 2位元組表示的區中的偏移

  19. 動态連結庫中的重定位

    3個重要的API:LoadLibrary,将DLL從硬碟加載到記憶體;GetProcAddress,接收函數名作為輸入,傳回該函數入口位址。FreeLibrary,當獲得函數位址後,調用此API解除安裝已加載庫。

    在Win7之前,為了達到讓所有程式共享同一DLL代碼的目的,系統會将所有DLL加載到同一位址,就可以共享代碼段進而節省空間。但是系統DLL固定加載基址這個特性,會被Windows下的溢出攻擊利用。該個性如果不存在就幾乎可以消除Windows下的溢出攻擊利用。Windows 7中引入了dll随機加載的選項。(其實就是ASLR)

  20. 利用RTL學習彙編:

    (1)首先用彙編實作開發環境所帶的運作時庫(Run Time Library, RTL)中的函數,如C語言的RTL包括strlen、strcpy等。

    (2)然後分析系統庫實作的這些函數,因為它們調用頻繁,是以要求有很高的效率,基本都用彙編撰寫。

    (3)再做性能實驗,測試自己版本與系統版本的差異,并分析修改(可利用指令級分析工具Vtune)。

    (4)最後分析不同庫實作的異同和好壞,如VC、C++ Builder、Delphi、GCC。

  21. 還有一種融彙底層知識的方法:通過編寫攻防軟體,将作業系統、彙編、編譯原理、網絡等知識在極為幽微處(如位元組層次)貫通起來。

繼續閱讀