事情要一件一件来说,就像吃饭要一口一口吃一样。一共有两件事情,这节就先讲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
<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>
没有办法的情况下,大胆的猜想是应该的,先看看这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, 这一句,它的实际地址会是啥呀?莫非也会翻转,翻转+翻转 = 正确了? 明天得继续研究下翻转的原理。基础不好,汗一个。。。什么都要从零学起。。。。。
今天就到此为止吧。困了,洗漱、睡觉。