1. 程式人生 > >push,你到底做了啥事情????

push,你到底做了啥事情????

事情要一件一件來說,就像吃飯要一口一口吃一樣。一共有兩件事情,這節就先講push的事情。

問一句,push, what the hell are u doing?

啟動bochs,開始push stack之旅。

擷取一部分原始碼:

protect:	
	;[7].進入到保護模式後,為了給予核心最大的訪問記憶體能力,ds段暫存器使用4G段描述符
	;初始化ds
	mov eax, 0x00000008 	
	mov ds, eax
	;初始化堆疊段
	mov eax, 0x00000018	
	mov ss, eax
	xor esp, esp	
	;8.將sector1的kernel程式碼拷貝到0x040000處的記憶體中來
	;1)由於目前不知道kernel的程式碼尺寸,先拷貝一個扇區看看
	;  根據約定,第一個扇區的前4個位元組應該就是kernel的總尺寸,可以根據這個尺寸知道後面還要載入多少扇區

	;@input: DI:SI 起始扇區號  DS:BX 寫入的記憶體地址		
	mov ebx, 0x40000 ;段內偏移	
	mov di, 0x0000
	mov si, 0x0001	
	call read_from_harddisk
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;@function read_from_harddisk
;@input: DI:SI 起始扇區號
;@input: DS:BX 寫入的記憶體地址
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;	
[bits 32]
read_from_harddisk:
	push eax
	push ebx
	push ecx
	push edx

目前我所知道的就只有:push eax, 就是把當前的esp值-4,然後把[ss:esp]的記憶體空間寫上eax內這4位元組的內容。

看上去挺簡單的,但是,事實上就是這麼簡單嗎????

首先先看下整個程式gdt表項,堆疊段位於0x03號選擇子,記憶體擴充套件方式是expand-down,所以棧底是高位,從0x7c00開始向下擴充套件

<bochs:5> info gdt
Global Descriptor Table (base=0x0000000000007e00, limit=39):
GDT[0x00]=??? descriptor hi=0x00000000, lo=0x00000000
GDT[0x01]=Data segment, base=0x00000000, limit=0xffffffff, Read/Write
GDT[0x02]=Code segment, base=0x00007c00, limit=0x00000200, Execute-Only, Non-Conforming, 32-bit
GDT[0x03]=Data segment, base=0x00007c00, limit=0xf1000fff, Read/Write, Expand-down
GDT[0x04]=Data segment, base=0x000b8000, limit=0x00008000, Read/Write
You can list individual entries with 'info gdt [NUM]' or groups with 'info gdt [NUM] [NUM]'
<bochs:6>
在保護模式下,下面程式碼主要就是初始化資料段以及堆疊段暫存器:
	;[7].進入到保護模式後,為了給予核心最大的訪問記憶體能力,ds段暫存器使用4G段描述符
	;初始化ds
	mov eax, 0x00000008 	
	mov ds, eax
	;初始化堆疊段
	mov eax, 0x00000018	
	mov ss, eax
	xor esp, esp
執行完成後,各個暫存器的值為:注意 esp為0x00000000
<bochs:18> reg
rax: 0x00000000_00000018 rcx: 0x00000000_00098000
rdx: 0x00000000_00000000 rbx: 0x00000000_0000000b
rsp: 0x00000000_00000000 rbp: 0x00000000_00000000
rsi: 0x00000000_000e7e20 rdi: 0x00000000_00000092
r8 : 0x00000000_00000000 r9 : 0x00000000_00000000
r10: 0x00000000_00000000 r11: 0x00000000_00000000
r12: 0x00000000_00000000 r13: 0x00000000_00000000
r14: 0x00000000_00000000 r15: 0x00000000_00000000
rip: 0x00000000_000000b3
eflags 0x00000046: id vip vif ac vm rf nt IOPL=0 of df if tf sf ZF af PF cf
之後呼叫函式後,有push動作,那麼會進行相關操作的記憶體區域肯定是<0x7c00,先dump一段記憶體看看,後面可以做一個push前後比較:
<bochs:20> xp /40bx 0x7bf0
[bochs]:
0x0000000000007bf0 <bogus+       0>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x0000000000007bf8 <bogus+       8>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x0000000000007c00 <bogus+      16>:    0xb8    0x00    0x00    0x8e    0xd8    0xb8    0x00    0x00
0x0000000000007c08 <bogus+      24>:    0xbb    0x00    0x00    0xb9    0x00    0x00    0xba    0x00
0x0000000000007c10 <bogus+      32>:    0x00    0xbf    0x00    0x00    0x8b    0x36    0xf7    0x7d
<bochs:21> s
Next at t=17825139
(0) [0x0000000000007d5f] 0010:000000000000015f (unk. ctxt): push eax                  ; 50
<bochs:22>
Next at t=17825140
(0) [0x0000000000007d60] 0010:0000000000000160 (unk. ctxt): push ebx                  ; 53
<bochs:23>
Next at t=17825141
(0) [0x0000000000007d61] 0010:0000000000000161 (unk. ctxt): push ecx                  ; 51
<bochs:24>
Next at t=17825142
(0) [0x0000000000007d62] 0010:0000000000000162 (unk. ctxt): push edx                  ; 52
<bochs:25>
Next at t=17825143
(0) [0x0000000000007d63] 0010:0000000000000163 (unk. ctxt): mov al, 0x01              ; b001
<bochs:26> xp /60bx 0x7be0
[bochs]:
0x0000000000007be0 <bogus+       0>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x0000000000007be8 <bogus+       8>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x0000000000007bf0 <bogus+       0>:    0x00    0x80    0x09    0x00    0x00    0x00    0x04    0x00
0x0000000000007bf8 <bogus+       8>:    0x18    0x00    0x00    0x00    0xc5    0x00    0x00    0x00
0x0000000000007c00 <bogus+      16>:    0xb8    0x00    0x00    0x8e    0xd8    0xb8    0x00    0x00
0x0000000000007c08 <bogus+      24>:    0xbb    0x00    0x00    0xb9    0x00    0x00    0xba    0x00
0x0000000000007c10 <bogus+      32>:    0x00    0xbf    0x00    0x00    0x8b    0x36    0xf7    0x7d
<bochs:27> reg
rax: 0x00000000_00000018 rcx: 0x00000000_00098000
rdx: 0x00000000_00000000 rbx: 0x00000000_00040000
rsp: 0x00000000_ffffffec rbp: 0x00000000_00000000
rsi: 0x00000000_000e0001 rdi: 0x00000000_00000000
r8 : 0x00000000_00000000 r9 : 0x00000000_00000000
r10: 0x00000000_00000000 r11: 0x00000000_00000000
r12: 0x00000000_00000000 r13: 0x00000000_00000000
r14: 0x00000000_00000000 r15: 0x00000000_00000000
rip: 0x00000000_00000163
eflags 0x00000046: id vip vif ac vm rf nt IOPL=0 of df if tf sf ZF af PF cf
<bochs:28>
執行完後,可以發現,記憶體變化如下:標顏色的這四段剛好對應上被push的 eax(0x00000018), ebx(0x00040000), ecx(0x00098000), edx(0x00000000)。可是,起始地址是從0x7c00開始的呀,從0x7bfc - 0x7bff之間還存在4個位元組的空位,這個是啥鳥?為啥空了4個位元組啊,在缺少系統的學習堆疊原理之前,這空著4個位元組讓人很匪夷所思。

沒有辦法的情況下,大膽的猜想是應該的,先看看這4個位元組是啥內容吧,具體的值是0x000000c5。


沒啥特殊。無奈之下,把編譯好的程式碼打印出來看看吧:

<bochs:20> u /20
00007cb3: (                    ): mov ebx, 0x00040000       ; bb00000400
00007cb8: (                    ): mov di, 0x0000            ; 66bf0000
00007cbc: (                    ): mov si, 0x0001            ; 66be0100
00007cc0: (                    ): call .+154                ; e89a000000
00007cc5: (                    ): mov eax, dword ptr ds:[ebx] ; 8b03
00007cc7: (                    ): mov ecx, 0x00000200       ; b900020000
00007ccc: (                    ): div eax, ecx              ; f7f1
00007cce: (                    ): mov ecx, eax              ; 89c1
00007cd0: (                    ): cmp eax, 0x00000000       ; 83f800
00007cd3: (                    ): jz .+29                   ; 741d
00007cd5: (                    ): cmp edx, 0x00000000       ; 83fa00
00007cd8: (                    ): jnz .+3                   ; 7503
00007cda: (                    ): sub ecx, 0x00000001       ; 83e901
00007cdd: (                    ): add ebx, 0x00000200       ; 81c300020000
00007ce3: (                    ): mov di, 0x0000            ; 66bf0000
00007ce7: (                    ): add si, 0x0001            ; 6683c601
00007ceb: (                    ): call .+111                ; e86f000000
00007cf0: (                    ): loop .-21                 ; e2eb
00007cf2: (                    ): mov eax, dword ptr ds:0x40004 ; a104000400
00007cf7: (                    ): mov ecx, dword ptr ds:0x40008 ; 8b0d08000400
發現有點意思,有一個地方和c5有一點點的相似,那就是下面的最後一行指令的地址:00007cc5,會不會是和這個c5是同樣的東西?如果你稍微與真實模式打過一些交道,相信你肯定會有這種直覺,那就是不自然的把段基址*10+c5做一個聯絡。這裡的c5應該是和程式碼段相關的。
00007cbc: (                    ): mov si, 0x0001            ; 66be0100
00007cc0: (                    ): call .+154                ; e89a000000
00007cc5: (                    ): mov eax, dword ptr ds:[ebx] ; 8b03
而且剛好,sreg一把,發現cs的段基址起始就是0x7c00,那麼顯而易見,c5就應該是eip的值吧~ 看看:注意cs段的base=0x7c00

是的,那麼c5其實就是等於程式碼段的段內偏移!

<bochs:27> sreg
es:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
        Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
cs:0x0010, dh=0x00409900, dl=0x7c000200, valid=1
        Code segment, base=0x00007c00, limit=0x00000200, Execute-Only, Non-Conforming, Accessed, 32-bit
ss:0x0018, dh=0x00cf9700, dl=0x7c001000, valid=1
        Data segment, base=0x00007c00, limit=0xf1000fff, Read/Write, Expand-down, Accessed
ds:0x0008, dh=0x00cf9300, dl=0x0000ffff, valid=1
        Data segment, base=0x00000000, limit=0xffffffff, Read/Write, Accessed
fs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
        Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
gs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
        Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
ldtr:0x0000, dh=0x00008200, dl=0x0000ffff, valid=1
tr:0x0000, dh=0x00008b00, dl=0x0000ffff, valid=1
gdtr:base=0x0000000000007e00, limit=0x27
idtr:base=0x0000000000000000, limit=0x3ff


原歸正狀:那麼從0x7bfc - 0x7bff之間還存在4個位元組的空位,而且剛好就是call完後return回來繼續執行的程式碼首地址,那麼push eax,你到底還幹了啥,這個答案就有了一個比較完整的解釋:

push eax幹了啥?

1.不用懷疑:肯定就是

sub esp, 4

mov ebx, esp

mov [ss:ebx], eax

2.經過上面驗證,在做1之前,他肯定還做了下面這鳥事情:

sub esp, 4

mov ebx, esp

mov [ss:ebx], 返回地址

然後才開始做上面的1.

目前還有一個疑問沒有想明白:

進入call之後,你會驚奇的發現,esp由之前的0x0變成了下面的:0xfffffffc。這個值不得了,當然你會說,這個不簡單嗎,就是數值翻轉了嘛,0-4成了負數,變成了0xfffffffc。

<bochs:30> reg
rax: 0x00000000_00000018 rcx: 0x00000000_00098000
rdx: 0x00000000_00000000 rbx: 0x00000000_00040000
rsp: 0x00000000_fffffffc rbp: 0x00000000_00000000
rsi: 0x00000000_000e0001 rdi: 0x00000000_00000000
r8 : 0x00000000_00000000 r9 : 0x00000000_00000000
r10: 0x00000000_00000000 r11: 0x00000000_00000000
r12: 0x00000000_00000000 r13: 0x00000000_00000000
r14: 0x00000000_00000000 r15: 0x00000000_00000000
rip: 0x00000000_0000015f
eflags 0x00000046: id vip vif ac vm rf nt IOPL=0 of df if tf sf ZF af PF cf
<bochs:31>

話是沒錯,我的疑問是,如果你後面再push一個ebx,那麼執行mov [ss:ebx], eax, 這一句,它的實際地址會是啥呀?莫非也會翻轉,翻轉+翻轉 = 正確了? 明天得繼續研究下翻轉的原理。基礎不好,汗一個。。。什麼都要從零學起。。。。。

今天就到此為止吧。困了,洗漱、睡覺。