20145210《信息安全系統設計基礎》第05周學習總結
教材內容總結
1.X86 尋址方式經歷三代:
•DOS時代的平坦模式,不區分用戶空間和內核空間,很不安全
•8086的分段模式
•IA32的帶保護模式的平坦模式
2.Intel與ATT格式的不同:
•Intel代碼省略了指示大小的后綴
•Intel代碼省略了寄存器名字前的“%”符號
•Intel代碼用不同的方式來描述存儲器中的位置。
•在帶有多個操作數的指令情況下,列出操作數的順序相反
3.64位機器上想要得到32代碼:gcc -m32 -S xxx.c
4.MAC OS中沒有objdump, 有個基本等價的命令otool
5.Ubuntu中 gcc -S code.c (不帶-O1) 產生的代碼更接近教材中代碼(刪除"."開頭的語句)
6.二進制文件可以用od 命令查看,也可以用gdb的x命令查看。
有些輸出內容過多,我們可以使用 more或less命令結合管道查看,也可以使用輸出重定向來查看:
od code.o | more
od code.o > code.txt
7.操作數的三種類型:
•立即數,也就是常數值。
•在ATT格式的匯編代碼中,立即數的書寫方式是“”后跟一個用標准C表示法表示的整數。任何一個能放進32 位字中的數值都可以用做立即數,不過匯編器在可能時會使用一個或兩個字節的編碼。
8.寄存器,表示某個寄存器的內容
9.存儲器,根據計算出來的地址(有效地址)訪問某個存儲器的位置。
10.有效地址的計算方式: Imm(Eb,Ei,s) = Imm + R[Eb] + R[Ei]*s
(注意:ATT格式中的方向:第一個是源操作數,第二個是目的操作數)
11.棧
•后進先出原則
•push壓棧,pop出棧
•棧頂:總是從這端插入和刪除元素
•棧頂元素的地址是最低的
•棧指針%esp保存着棧頂元素的地址
12.lean指令
•lean是加載有效地址指令
•指令形式實際上並沒有引用存儲器,它的第一個操作數其實是將有效地址寫入到目的操作數
•可以為后面的存儲器引用產生指針,簡潔的表述普通算數操作。
13.目的操作數必須是一個寄存器。
14.過程
過程調用:
•進入,為過程的局部變量分配空間
•將數據(以過程參數和返回值的形式)和控制從代碼的一部分傳遞到另一部分。
•退出,釋放這些空間。
15.轉移控制
call
•call指令和轉移指令相似,同樣分直接和間接,直接調用的目標是標號,間接調用的目標是*后面跟一個操作數指示符,和JMP一樣。
•call指令的效果是將返回地址入棧,並跳轉到被調用過程的起始處。返回地址是還在程序中緊跟在call后面的那條指令的地址。
ret
•ret指從棧中彈出地址,並跳轉到這個位置。
•在上學期的匯編語言學習中,call和ret常被用來進行子函數、子模塊的調用。
實驗樓代碼練習:
將C語言文件編譯成匯編文件並查看匯編文件內的內容:
刪除以.開頭的語句之后:
注釋棧幀情況:
反匯編代碼分析:
首先進入gdb調試環境,在虛擬機上輸入如下命令生成帶有調試信息的elf文件,然后進入gdb調試:
(gdb)layout asm
(gdb)b main
然后使用
(gdb)si
逐條指令執行並觀察寄存器變化:
分析結果如下:
對於main函數:
pushl %ebp
movl %esp,%ebp
把當前的ebp值入棧,再把ebp入棧后的esp中的值放入ebp,此時,esp和ebp都指向同一個內存地址,此時棧中的情況如圖:
pushl $8 //當前esp減4,然后把寬度為4的數值10放入esp當前指向的內存中
call SY //調用函數指令,把當前eip的值入棧,然后跳轉到SY函數的第一條指令開始執行
addl $4,%esp
此時棧中的情況如圖:
對於SY函數:
pushl %ebp
movl %esp,%ebp
//保存當前棧環境,為SY函數開辟新的棧空間
pushl 8(%ebp) //把當前ebp中的數值加8后作為內存地址,並把該內存地址指向的內存空間的數值“10”放入棧中
call YSY //調用函數指令,當前eip入棧,跳轉到YSY的第一條指令
addl $4,%esp
首先保存當前棧環境,為SY函數開辟新的棧空間
再把當前ebp中的數值加8后作為內存地址,並把該內存地址指向的內存空間的數值“8”放入棧中
再調用函數指令,當前eip入棧,跳轉到YSY的第一條指令
此時棧中情況如圖:
對於YSY函數:
pushl %ebp
movl %esp,%ebp
//保存當前棧環境,開辟新的棧空間
movl 8(%ebp),%eax //將ebp中數值加8后作為內存地址,並將該內存地址指向的內存空間的數值8放入eax寄存器中
addl $3,%eax //將把eax中的值加3再放回eax
popl %ebp //從棧中獲取舊的esp值,並放入ebp寄存器
ret //從esp所指內存處獲取值作為eip,然后跳轉到eip中存放的地址繼續執行
此時,函數YSY已經返回,其返回值存儲在eax寄存器中,返回值為11
此時棧中情況如圖:
返回到函數SY中:
addl $4,%esp //回收棧空間,棧空間收縮4個字節
leave //釋放SY函數使用的棧空間
ret //函數SY返回,程序回到main函數繼續執行
此時eax函數存放的是函數SY的返回值11,棧中情況如圖:
回到main函數繼續執行:
addl $4,%esp //棧收縮4個字節,回收棧空間
addl $1,%eax //將eax中的值加1后放回eax,執行后eax中的值為12
leave
ret
通過gdb單步調試及print /x $***可以查詢到相應寄存器的內容以及執行的指令
得到%ebp,%esp,eax寄存器的變化:
教材學習中遇到的問題
練習3.2:指令后綴主要根據什么來確定?參照了教材的例子以及練習題的答案,對於指令后綴的確定還是找不到標准