20135203齊嶽 信息安全系統設計基礎第四周學習總結

20135203齊嶽 信息安全系統設計基礎第四周學習總結程序員

學習計時:共17小時數組

  • 讀書:10
  • 代碼:1
  • 做業:3
  • 博客:3

1、學習目標

  1. 理解逆向的概念以及
  2. 掌握X86彙編基礎,可以閱讀(反)彙編代碼
  3. 瞭解ISA(指令集體系結構)
  4. 理解函數調用棧幀的概念,並能用GDB進行調試安全

    2、學習任務

  5. 閱讀教材,完成課後練習(書中有參考答案)
    3.1-3.7中練習,重點:3.1,3.3,3.5,3.6,3.9,3.14,3.15,3.16,3.22,3.23,3.27,3.29,3.30,3.33,3.34
  6. 考覈:練習題把數據變換一下
  7. 實驗:須要動手的到實驗樓中練習一下
  8. 深化、實踐題目,額外加分less

    3、學習過程

3.1歷史觀點

X86 尋址方式經歷三代:

  1. DOS時代的平坦模式,不區分用戶空間和內核空間,很不安全
  2. 8086的分段模式
  3. IA32的帶保護模式的平坦模式

Linux使用平坦尋址方式,使程序員將整個存儲空間看作一個大的字節數組。函數

3.2程序編碼

ISA:

指令集體系結構,機器級程序的格式和行爲,它定義了處理器狀態、指令的格式以及每條指令對狀態的影響。oop

  • 程序計數器(一般稱爲PC,用%eip表示),指示將要執行的下一條指令在存儲器中的地址。學習

  • 整數寄存器文件:存儲地(對應於C語言的指針)或整數數據。編碼

  • 條件碼寄存器:保存着最近執行的算數或邏輯指令的狀態信息,用來實現控制或者數據流中的條件變化。翻譯

  • 浮點寄存器:用來存放浮點數據。設計

編譯過程:

  • C預處理器插入宏和頭文件:gcc -E xxx.c -o xxx.i

  • 編譯器產生源代碼的彙編代碼:gcc -S xxx.i -o xxx.s

  • 彙編器化成二進制目標代碼:gcc -c xxx.s -o xxx.o

  • 連接器生成最終可執行文件:gcc xxx. -o xxx

  • 用objdump -d xxx.o -o xxx.s 反彙編

創建函數調用棧幀的彙編代碼:

pushl   %ebp 將寄存器%ebp中的內容壓入程序棧
movl    %esp,%ebp  將%ebp中的內容放入寄存器%esp
......
popl    %ebp 寄存器%ebp中內容出棧
ret 返回結果

注意:

  1. 64位機器上想要獲得32代碼:gcc -m32 -S xxx.c

  2. MAC OS中沒有objdump, 有個基本等價的命令otool

  3. Ubuntu中 gcc -S code.c (不帶-O1) 產生的代碼更接近教材中代碼(刪除"."開頭的語句)

  4. 找到程序的字節表示:

    (gdb) x/17xb sum
  5. 二進制文件能夠用od命令查看,也能夠用gdb的x命令查看。有些輸出內容過多,咱們可使用 more或less命令結合管道查看,也可使用輸出重定向來查看

    od code.o | more
         od code.o > code.txt

格式註解

·開頭的行都是指導彙編器和連接器的命令,gcc -S 產生的彙編中能夠把 以‘.’開始的語句都刪除了再閱讀

Linux和Windows的彙編格式的區別:

  1. ATT格式(Linux下的彙編格式)&Intel格式(Windows的彙編格式):
  2. Intel代碼省略了指示大小的後綴。咱們看到指令mov而不是movl;
  3. Intel代碼省略了寄存器名字前的%,使用的是esp而不是%esp;
  4. Intel代碼用不一樣方式來描述存儲器中的位置:
    例如,是‘DWORD PTR [ebp+8]’而不是'8(%ebp)'

3.3數據格式

大多數GCC生成的彙編代碼指令都有一個字符後綴,代表操做數的大小。

(P111表格)

3.4訪問信息

寄存器

一個IA32中央處理單元(CPU)包含一組8個存儲32位值的寄存器。用來存儲整數數據和指針。

%eax    %ax (%ah %al)  通用寄存器
%ecx    %cx (%ch %cl)  通用寄存器
%edx    %dx  (%dh %dl)   通用寄存器
%ebx    %bx  (%bh %bl)   通用寄存器
%esi    %si             用來操縱數組
%edi    %di             用來操縱數組
%esp    %sp             操縱棧幀
%ebp    %bp             操縱棧幀

注意:

對於32位的eax,16位的ax,8位的ah,al都是獨立的,咱們經過下面例子說明:

假定當前是32位x86機器,eax寄存器的值爲0x8226,執行完addw $0x8266
,%ax指令後eax的值是多少? 
解析:0x8226+0x826=0x1044c, ax是16位寄存器,出現溢出,最高位的1會
丟掉,剩下0x44c,不要覺得eax是32位的不會發生溢出.

尋址方式

根據操做數的不一樣類型,尋址方式可分爲如下三種:

  • 當即數尋址方式:操做數爲常數值,寫做$後加一個整數。

  • 寄存器尋址方式:操做數爲某個寄存器中的內容

  • 存儲器尋址方式:根據計算出來的地址訪問某個存儲器的位置

尋址模式:一個當即數偏移Imm,一個基址寄存器Eb,一個變址寄存器Ei,一個比例因子s(必須爲1,2,4,8)有效地址計算爲:Imm(Eb,Ei,s) = Imm + R[Eb] + R[Ei]*s

數據傳送指令

  • MOV至關於C語言的賦值'='

    mov     S,D     S中的字節傳送到D中

注意:

  1. ATT格式中的方向;
  2. 不能從內存地址直接MOV到另外一個內存地址,要用寄存器中轉一下。
  3. 區分MOV,MOVS(符號擴展),MOVZ(零擴展)

push和pop:

pushl   S       R[%esp] ← R[%esp]-4
                    M[R[%esp]] ← S

    popl    D       D ← M[R[%esp]]
                    R[%esp] ← R[%esp]+4

注意:

  1. 棧頂元素的地址是全部棧中元素地址中最低的,後進先出;
  2. 指針就是地址;局部變量保存在寄存器中。

3.5算術和邏輯操做

加載有效地址

leal,從存儲器讀數據到寄存器,而從存儲器引用的過程其實是將有效地址寫入到目的操做數。目的操做數必須是一個寄存器。

一元操做和二元操做

  • 一元操做:只有一個操做數,既是源又是目的,能夠是一個寄存器或者存儲器。

  • 二元操做:第二個操做數既是源又是目的,兩個操做數不能同時是存儲器。

移位

先給出位移量,而後是位移的數值,可進行算數和邏輯右移。移位操做移位量能夠是當即數或%cl中的數。

3.6控制

條件碼:

描述最近的算數或者邏輯操做的屬性,能夠檢測這些寄存器來執行條件分支指令。

CF:進位標誌,最近操做使高位產生進位,用來檢測無符號操做數的溢出
    ZF:零標誌,最近操做得出的結果爲0
    SF:符號標誌,最近操做獲得的結果爲負數
    OF:溢出標誌,最近操做致使一個補碼溢出-正溢出或負溢出。

注意:

  1. leal不改變條件碼寄存器
  2. CMP與SUB的區別:CMP也是根據兩個操做數之差設置條件碼,但只設置條件碼而不更新目標寄存器
  3. 有條件跳轉的條件看狀態寄存器(教材上叫條件碼寄存器)

訪問條件碼的讀取方式:

  1. 根據條件碼的某個組合,將一個字節設置成0或1;
  2. 跳轉到程序某個其餘的部分
  3. 有條件的傳送數據。

SET指令根據t=a-b的結果設置條件碼

跳轉指令及其編碼

控制中最核心的是跳轉語句:

  • 有條件跳轉(實現if,switch,while,for)

  • 無條件跳轉jmp(實現goto)

當執行PC相關的尋址時,程序計數器的值是跳轉指令後面那條指令的地址,而不是跳轉指令自己的地址。

翻譯條件分支

將條件和表達式從C語言翻譯成機器代碼,最經常使用的方式是結合有條件和無條件跳轉。

C語言中if-else語句的通用形式:

if(test-expr)
    then-statement
else
    else-statement

彙編結構:

t=test-expr;
    if!(t)
        goto false;
    then-statement
    goto done;
false:
    else-statement
done:

循環

do-while循環

C語言中do-while語句的通用形式:

do
    body-statement
    while(test-expr);

彙編結構:

loop:
    body-statement
    t=test-expr;
    if(t)
        goto loop;

while循環

C語言中while語句的通用形式:

while(test-expr)
    body-statement

彙編結構:

t=test-expr;
    if(!t)
        goto done;
loop:
    body-statement
    t=test-expr;    
    if(t)
        goto loop;
done:

for循環

C語言中for語句的通用形式:

for(init-expr;test-expr;update-expr)
    body-statement

彙編結構:

init-expr
    t=test-expr;
    if(!t)
        goto done;
loop:
    body-statement
    update-expr;
    t=test-expr;
    if(t)
        goto loop;
    done:

switch語句

根據一個整數索引值進行多重分支,執行switch語句的關鍵步驟是經過跳轉表來訪問代碼位置,使結構更加高效。

3.7過程

數據傳遞、局部變量的分配和釋放經過操縱程序棧來實現。

棧幀結構

  • 爲單個過程分配的棧叫作棧幀,寄存器%ebp爲幀指針,而寄存器指針%esp爲棧指針,程序執行時棧指針移動,大多數信息的訪問都是相對於幀指針。

  • 棧向低地址方向增加,而棧指針%esp指向棧頂元素。

轉移控制

call:目標是指明被調用過程起始的指令地址,效果是將返回地址入棧,並跳轉到被調用過程的起始處。

ret:從棧中彈出地址,並跳轉到這個位置。

函數返回值存在%eax中

寄存器使用慣例

程序寄存器是惟一能被全部過程共享的資源,調用者保存寄存器 和 被調用者保存寄存器是分開的,對於哪個寄存器保存函數調用過程當中的返回值要有統一的約定。

3.11關於棧幀的gdb命令

  • backtrace/bt:打印當前的函數調用棧的全部信息。後面加n或-n表示打印棧頂上n層(或者下n層)的棧信息。

  • frame n:n爲棧中的層編號,從0開始,相似C語言中數組的下標。移動到n指定的棧幀中去,並打印選中的棧的信息。若是沒有n,則打印當前幀的信息。

  • up n:表示向棧的頂移動n層。

  • down n:表示向棧底移動n層。

(此處爲百度+本身翻譯,若有不許請指正)

4、實驗練習

C語言代碼:

int q(int x)
{
    return x + 2;
}

int y(int x)
{
    return q(x);
}

int main(void)
{
     return y(7) + 9;
}

使用gcc –S –o main.s main.c -m32命令編譯成彙編代碼以下:

q:
    pushl   %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %eax
    addl    $2, %eax
    popl    %ebp
    ret
y:
    pushl   %ebp
    movl    %esp, %ebp
    pushl   8(%ebp)
    call    q
    addl    $4, %esp
    leave
    ret
main:
    pushl   %ebp
    movl    %esp, %ebp
    pushl   $7
    call    y
    addl    $4, %esp
    addl    $9, %eax
    leave

5、遇到的問題

  • 3.16

    testl %eax,%eax
    jle .L3

如何能判斷條件爲a<=0?(對test語句理解不到位)

  • 3.23

    leal    (%eax,%eax),%edx
      movl    %ebx,%eax

(%eax中存放着val,%ebx中存放x。)

這兩行代碼如何實現val<<1的效果。

  • 3.30

    call next
      next:
          popl %eax

    call指令的效果是將 返回地址入棧,也就是call後面指令的地址,即popl %eax,而%eax的值又被設置爲popl指令的地址,整個過程是順序執行的而並無發生跳轉,因此無需ret彈出。這種理解正確嗎?
    爲何它是實現將程序計數器 放到整數寄存器中的惟一方式?

  • 對於跳轉指令和循環語句的彙編代碼閱讀仍是不熟練,感受書上的例題沒有徹底吃透,在分析棧幀結構時順序執行的代碼基本能夠搞清楚,涉及到call和ret相關的語句時就感受比較困難,還須要深刻理解。

相關文章
相關標籤/搜索