函数调用之栈结构

堆栈分析:
---------------------------------------------------------------------------------------------------------------------------------------------------
父函数
[stack]:BFB2951C dd 8048477h                             ; 调用sum后的返回地址,call指令添加的

[stack]:BFB29520 dd 0Ah                                  ;  ESP   main栈顶
[stack]:BFB29524 dd 14h                                  ; "__libc_start_main"
[stack]:BFB29528 dd offset initial
[stack]:BFB2952C dd offset off_D58FF4
[stack]:BFB29530 dd offset __libc_csu_init
[stack]:BFB29534 dd offset _start
[stack]:BFB29538 dd 14h
[stack]:BFB2953C dd 0Ah
[stack]:BFB29540 dd offset __libc_csu_init
[stack]:BFB29544 dd 0
[stack]:BFB29548 db 0C8h ;                             ;EBP


调用函数栈
+-------------------------+--------------------------------------父函数的EBP
|……      locals               |
+-------------------------+
|   arg3                        | arg1, arg2, arg3
+-------------------------+
|   arg2                        |
+-------------------------+
|   arg1                        |
+-------------------------+--------------------------------------- 父函数的ESP
|   返回地址                    | call指令添加,添加之后ESP向栈增长方向增长,该值在子函数调用ret后被弹出,并给EIP
+-------------------------+

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

子函数

call时, 参数被从右到左依次压入栈, 然后压入返回地址(ESP会向栈增长的方向增长)
然后进入被调用函数,一般新函数都会先执行下面两条汇编
push ebp                     ;保存父函数ebp(栈底)
mov     ebp, esp           ;设置新栈的栈起始地址为当前esp的地址
实际上push ebp,ebp被保存在了子函数栈中,之后执行 sub     esp, 10h类似的操作,开辟了16个字节的空间,但是保存的父函数的ebp占据了最开始的4个字节
[stack]:BFB29508 dd offset off_BFB29518                  ; 保存main函数的ebp,新栈的栈底
[stack]:BFB2950C dd 80482A4h
[stack]:BFB29510 dd 16h
[stack]:BFB29514 dd offset _GLOBAL_OFFSET_TABLE_
[stack]:BFB29518 off_BFB29518 dd offset asc_BFB29548


被调用函数栈
+-------------------------+--------------------------------------子函数的EBP
| 保存的父函数EBP          |   调用leave后被弹出
+-------------------------+
|   第一个local                | locals
+-------------------------+
|   第二个local                |
+-------------------------+
|   ……                          |
+-------------------------+--------------------------------------- 子函数的ESP


执行leave后,ebp变为父函数的ebp,移动到该位置,esp指向返回指令的地址处(非父函数的esp)
执行ret后,返回执行指令的地址从栈上被弹出给EIP,esp指向父函数的esp,移动到该位置(ret弹出靠近栈顶的一个地址给EIP)

!!!!!!!!!!!!!!!!!!!
Linux栈是从高地址向低地址增长的,也就是说EBP在ESP的上面,被传人的参数是在父函数的栈中,如下:
#argN
#argN-1
#.
#.
#.
#arg3
#arg2
#arg1
#返回地址
-----子函数栈
#保存父函数EBP
#.locals
这样如果在传递参数时,如果未校验参数长度,参数长度超过缓存区长度,参数就会向栈的栈的增长方向增长,
覆盖返回地址,就产生了漏洞。
如果参数长度恰好等于缓冲区长度,并向缓冲区执行字符串拷贝操作,这样多余的NULL字节就可能覆盖调用者的EBP的最低有效位
(LSB),导致漏洞(调用者EBP恰巧在该缓冲区之上,off-by-one,任意代码执行)。

猜你喜欢

转载自blog.csdn.net/ylcangel/article/details/87929264
今日推荐