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, 这一句,它的实际地址会是啥呀?莫非也会翻转,翻转+翻转 = 正确了? 明天得继续研究下翻转的原理。基础不好,汗一个。。。什么都要从零学起。。。。。


今天就到此为止吧。困了,洗漱、睡觉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值