好的,这将是一个很长的问题.我试图理解“缓冲区溢出”是如何工作的.我正在阅读aleph1的Smashing the stack for fun and profit,并且刚刚得到了以下代码的反汇编:
void function(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
}
void main() {
function(1,2,3);
}
使用GCC的-S标志的disameembly给了我:
.file "example1.c"
.text
.globl function
.type function, @function
function:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $48, %rsp
movl %edi, -36(%rbp)
movl %esi, -40(%rbp)
movl %edx, -44(%rbp)
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movq -8(%rbp), %rax
xorq %fs:40, %rax
je .L2
call __stack_chk_fail
.L2:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size function, .-function
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $3, %edx
movl $2, %esi
movl $1, %edi
call function
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
Aleph1的文章中没有.cfi指令,我猜他们当时没有使用过.我已经阅读了this question on SO,我得知它们被GCC用于异常处理.我也阅读了another question on SO,我得到了.LFB0,.LFE0,.LFE1和.LFB1是标签,但我有以下疑问:
>我得到了.cfi指令用于异常处理,但我不明白它们的含义.我一直在here,我看到一些定义,如:
.cfi_def_cfa register, offset
.cfi_def_cfa defines a rule for computing CFA as: take address from
register and add offset to it.
但是,如果你看一下我上面的反汇编,你找不到任何注册名称(比如EAX,EBX等),而是在那里找到一个数字(我一般都找到’6′)而且我不喜欢我不知道那应该是一个寄存器.特别是,任何人都能解释一下.cfi_def_cfa_offset 16,.cfi_offset 6,-16,.cfi_def_cfa_register 6和.cfi_def_cfa 7,8是什么意思?此外,CFA是什么意思?我问这个是因为主要是在书籍/论文中,程序序言如下:
pushl %ebp
movl %esp,%ebp
subl $20,%esp
但是,现在我认为现代计算机中的程序序列如下:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $48, %rsp
最初我认为使用CFI指令代替子助记符来设置偏移量但事实并非如此;尽管使用了CFI指令,仍然使用sub命令.
>我明白每个程序都有标签.但是,为什么程序中有多个嵌套标签?在我的情况下,main有.LFB1和.LFE2标签.多个标签需要什么?类似地,函数过程具有标签.LFB0,.L2和.LFE0
>这两个程序的最后3行似乎用于一些内务处理功能(告诉程序的大小,可能?)但我不确定它们是什么意思.任何人都可以解释他们的意思和用途是什么?
编辑:
(再添一个问题)
> CFI指令是否占用任何空间?因为在过程“函数”中,每个int参数占用4个字节而且它的数量是3,所以所有参数在内存中占用12个字节.接下来,第一个char数组占用8个字节(向上舍入为5个字节到8个字节),下一个char数组占用12个字节(向上舍入为10bytes到12bytes),因此整个char数组占用20个字节.总结这些全部,参数和局部变量只需要12 20 = 32个字节.
但是在过程“function”中,编译器减去48个字节来存储值.为什么?
解决方法:
根据你在逆向工程中的要求,我在这里把我的评论内容作为答案(我不知道这是否会继续,因为我看到一个激烈的竞争,在那里向下投票和向上投票)
Lindy Dancer回答了cfi和cfa的含义(呼叫帧信息)和(呼叫帧地址)
.L< NUM>表示根据Google中x64 GCC名称中各种花絮的标签,以下格式的所有标签都以.L开头,并以数字结尾.L1,.L2,.L …. infinity是标签
根据Google和一些早期的SO回答BF< num>表示Function-Begin和EF< num>表示功能结束
所以.LBF0,.LBF1. LBF …..无穷大和.LFE0,…….,.LFE ……无限
表示函数开始,函数在每个函数中结束,编译器可能需要这些函数来处理一些内部需求,所以你应该忘记它们,除非非常需要深入研究编译器内部
另一个标签.L2用于解决函数中的分支指令je
je .L2
此外,每个编译器都将对参数和本地的访问权限对齐并填充到某个边界
我不能确定,但对于GCC,x64默认对齐是16个字节
所以,如果您要求奇怪的预订,如
char foo [5]或
BYTE blah [10]
即使对于x86,索引5和10也不对齐
对于5 x86编译器将分配8个字节和10个16字节
像明智的x64 gcc可能会为每个请求分配16个字节
你实际上不应该担心为什么编译器会做它的功能
当你试图理解汇编逻辑时,只关注地址
如果编译器决定将x放在rbp / – X,那么它也会在该变量的范围或生命周期内在同一位置访问它