本次學習內容為計算機的工作原理,主要從存儲計算機的工作模型,基本的匯編語言以及匯編代碼的執行過程三個方面來進行整理,重點分析匯編代碼的執行過程,以一個簡單的C語言程序反匯編實驗為例進行分析。
存儲程序的計算機工作模型
馮諾依曼體系結構

馮諾依曼體系結構的核心思想是存儲程序計算機,即將程序和數據都存放在計算機中,按存儲器的存儲程序首地址執行程序的第一條指令,是各種計算機體系結構都要遵從的一個“客觀規律”。
馮諾依曼體系主要特點有三個:
1.馮諾依曼體系將計算機分為:內存(程序內存,數據內存),CPU(運算器,控制器,寄存器),輸入設備和輸出設備。其中運算器,存儲器,控制器與輸入輸出設備五部分構成了計算機的硬件。
2.計算機內部采用二進制來表示指令和程序
3.將編寫好的程序和數據先存入存儲器中然后啟動計算機工作。
CPU的工作過程可以大體分為三步:取指令,分析指令與執行指令。其中,指令數據與地址的暫存都發生在寄存器中。匯編語言的主要作用對象就是寄存器。CPU的寄存器主要分為通用寄存器,控制寄存器和段寄存器三種。Intel處理器系列(X86)經歷了16位(8086,1978),32位(i386,1985)和64位(Pentium 4E,2004)三個關鍵階段。不同位數CPU的寄存器功能相差不大,下面詳細介紹X86-32的相關情況。
X86-32有8個通用寄存器:4個數據寄存器(EAX、EBX、ECX和EDX),2個變址寄存器(ESI和EDI)以及2個棧指針寄存器(ESP和EBP);6個段寄存器(ES、CS、SS、DS、FS和GS);2個控制寄存器:1個指令指針寄存器(EIP),1個標志寄存器(EFlags)。
基本的匯編語言
本文簡單介紹幾個匯編指令,詳細的匯編指令解釋可從以下鏈接查看https://blog.csdn.net/liuy88141/article/details/46391211?utm_source=blogxgwz0
MOVL
字符串傳送指令 MOVL
格式: MOVL OPRD1,OPRD2
功能: OPRD1<--OPRD2.
說明:
- 其中OPRD2為源串符號地址,OPRD1為目的串符號地址.
- 字節串操作: 若DF=0,則作加, 若DF=1,則作減.
- 對字串操作時: 若DF=0,則作加,若DF=1,則作減,.
- 在指令中不出現操作數時,字節串傳送格式為MOVSB、字串傳送格式為MOVSW.
- 本指令不影響標志位.
POPL
堆棧操作指令POPL
格式: POPL OPRD
功能:實現彈出操作的指令
說明:
- OPRD為32位(字)操作數,可以是寄存器或存儲器操作數.
- POPL指令的操作過程是: POLP OPRD:OPRD<--((SP)),(SP)<--(SP)+2
它與壓入操作相反,是先彈出棧頂的數頂,然后再修改指針SP的內容.
3.對狀態標志位沒有影響
PUSHL
堆棧操作指令 PUSHL
格式: PUSHL OPRD
功能: 實現壓入操作的指令
說明:
- OPRD為32位(字)操作數,可以是寄存器或存儲器操作數.
- PUSHL的操作過程是: (SP)<--(SP)-2,((sp))<--OPRD 即先修改堆棧指針SP(壓入時為自動減2),然后,將指定的操作數送入新的棧頂位置.
此處的((SP))<--OPRD,也可以理解為: [(SS)*16+(SP)]<--OPRD 或 [SS:SP]<--OPRD
3.對狀態標志位沒有影響
CALL
過程調用指令 CALL
格式: CALL OPRD
功能: 過程調用指令
說明:
- 其中OPRD為過程的目的地址.
- 過程調用可分為段內調用和段間調用兩種.尋址方式也可以分為直接尋址和間接尋址兩種.
- 本指令不影響標志位.
RET
返回指令 RET
格式: RET
功能: 當調用的過程結束后實現從過程返回至原調用程序的下一條指令,本指令不影響標志位.
說明:
由於在過程定義時,已指明其近(NEAR)或遠(FAR)的屬性,所以RET指令根據段內調用與段間調用,執行不同的操作
對段內調用: 返回時,由堆棧彈出一個字的返回地址的段內偏移量至IP.
對段外調用: 返回時,由堆棧彈出的第一個字為返回地址的段內偏移量,將其送入IP中,由堆棧彈出第二個字為返回地址的段基址,將其送入CS中.
匯編代碼的執行過程
在本部分我們以一個反匯編的小實驗為例進行分析。實驗環境為實驗樓提供的基於Web訪問方式的64位虛擬機環境。具體過程如下;
對main.c文件進行編譯

產生“.s”為擴展名的匯編代碼文件main.s

---->查看.s文件

---->刪除“.”開頭的鏈接輔助信息字符串

匯編指令執行過程分析
圖中最后得到的代碼主要分為三個部分:g函數,f函數以及main函數。
程序從main函數開始執行
pushl%ebp
esp的值首先減4,然后將ebp的值存儲在esp指示的位置,則棧中序號為1的位置存儲了ebp的當前值(0),ebp仍指向位置0,esp指向位置1;
movl %esp, %ebp
ebp與esp都指向位置1;
subl $4, %esp
esp指向位置2;
movl $12, (%esp)
棧的位置2存儲立即數12;
call f
首先將eip壓入棧中,eip此時的值是下一條將要執行的指令的位置,我們不妨以行號代表,即將行號23存入棧中位置3,此時esp指向位置3,ebp指向位置1;然后將函數f的起始位置賦給寄存器eip,即下一條指令為f的第一條指令;
pushl %ebp
棧中位置4存儲ebp的值(位置1),esp指向位置4;
movl %esp, %ebp
esp和ebp指向位置4;
subl $4, %esp
esp指向位置5;
movl 8(%ebp), %eax
將ebp之前兩個位置處的值給寄存器eax,即將位置2處存儲的值12給eax;
movl %eax, (%esp)
將eax中的值存儲到esp指向的位置,即棧中位置5存儲12;
call g
首先將eip壓入棧中,即將行號15存入棧中位置6,此時esp指向位置6,ebp指向位置4;然后將函數g的起始位置賦給寄存器eip,即下一條指令為g的第一條指令;
pushl %ebp
棧中位置7存儲ebp的值(位置4),esp指向位置7;
movl %esp, %ebp
esp和ebp都指向位置7;
movl 8(%ebp), %eax
將ebp之前兩個位置處的值給寄存器eax,即將位置5處存儲的值12給eax;
addl $6, %eax
eax中的值加上6,變為18;
popl %ebp
將棧頂數據彈出到ebp中,即將棧中位置7的值“位置4”賦給ebp,同時esp指向位置6;
ret
將棧頂數據彈出到eip中,即將棧中位置6的值“行15”賦給eip,同時esp指向位置5,下一條指令變為第15行處的指令;
leave
這條指令首先將ebp的值賦給esp,即esp和ebp都指向位置4,然后將棧頂元素彈出到ebp中,即將棧中位置4的值“位置1”賦給ebp,同時esp指向位置3;
ret
將棧頂數據彈出到eip中,即將棧中位置3的值“行23”賦給eip,同時esp指向位置2,下一條指令變為第23行處的指令;
addl $1, %eax
將寄存器eax中的值加1,變為19;
leave
先將ebp的值賦給esp,esp和ebp都指向位置1,然后將棧頂元素彈出到ebp中,即棧中位置4的值“位置0”賦給ebp,同時esp指向位置0;
ret
將棧頂數據彈出到eip中,即將棧中位置0的值賦給eip,同時esp指向位置-1,下一條指令變為程序開始前的指令。
遇到問題:在小實驗刪除“.”打頭字符串過程誤使用“g^.s*/d”指令
解決方法:使用了“g/./d’”后成功解決
心得體會:最近還沒有找到合適的學習節奏,每次作業都完成的匆匆忙忙,但是相比上一周的毫無計划性已經有所進步,爭取在兩周內能完全適應


