好的,这将是一个很长的问题。我试图理解"缓冲区溢出"作品。我正在阅读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_def_cfa register,offset
.cfi_def_cfa将计算CFA的规则定义为:从地址获取地址 注册并添加偏移量。
但是,如果你看看我上面的反汇编你找不到任何注册名称(比如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指令而不是sub
助记符来设置偏移量,但事实并非如此;尽管使用了CFI指令,sub
命令仍在使用。
我知道每个程序都有标签。但是,为什么程序中有多个嵌套标签?在我的情况下,main有.LFB1和.LFE2标签。多个标签需要什么?类似地,function
过程具有标签.LFB0,.L2和.LFE0
这两个程序的最后3行似乎用于一些内务处理功能(告诉程序的大小,可能?)但我不确定它们是什么意思。任何人都可以解释它们的含义以及它们的用途是什么?
编辑:
(再增加一个问题)
CFI指令是否会占用任何空间?因为在过程" function"中,每个int参数占用4个字节并且它的数量是3,所以所有参数在内存中占用12个字节。接下来,第一个char
数组占用8个字节(向上舍入为5个字节到8个字节),下一个char
数组占用12个字节(向上舍入为10个字节到12个字节),因此整个char
数组占用20个字节字节。总结这些所有参数和局部变量只需要12 + 20 = 32个字节。
但是在过程" function"中,编译器减去48个字节来存储值。为什么呢?
答案 0 :(得分:9)
CFI代表呼叫帧信息。这是编译器描述函数中发生的事情的方式。调试器可以使用它来呈现调用堆栈,链接器可以合成异常表,进行堆栈深度分析以及其他类似的事情。
实际上,它描述了存储处理器寄存器等资源以及返回地址的位置。
CFA代表调用帧地址,表示调用函数的堆栈指针位置的地址。这需要获取有关堆栈中下一帧的信息。
答案 1 :(得分:2)
根据您在逆向工程中的请求,我将我的评论内容作为答案放在这里(我不知道这是否会继续,因为我看到一个激烈的竞争,在那里向下投票和向上投票)< / p>
Lindy Dancer回答了cfi and cfa means
(call frame information
)和(c all frame address
)
.L<num>
表示Google中x64 GCC名称中各种花絮的标签。以下格式的所有标签均以.L
开头,以a numeral
结尾,因此.L1 , .L2 , .L....infinity
为标签< / p>
根据Google和一些早期的SO
个答案BF<num>
表示功能开始,EF<num>
表示FUNCTION-END
所以.LBF0 , .LBF1 . LBF.....infinity
和.LFE0 ,......., .LFE....infinity
表示函数开始,函数在每个函数中结束,编译器可能需要这些函数来处理一些内部需求,所以除非有非常需要深入研究编译器内部,否则你应该忘记它们
存在另一个标签.L2
以解决函数中的分支指令je
je .L2
每个编译器也会对参数和本地的访问权限进行对齐并填充到某个边界
我无法确定,但对于GCC,x64默认对齐是16字节 所以,如果您要求奇怪的预订,如
char foo [ 5 ]或
BYTE blah [ 10 ]
即使对于5 and 10
x86
也未对齐
for 5 x86 compiler will assign
8字节s and for 10 16 bytes
与您的每个请求一样明智x64 gcc might assign 16 bytes
你实际上不应该担心为什么编译器能做它所做的事情
当你试图理解汇编逻辑时,只关注地址
如果编译器决定will put x at rbp +/- X
它将also access it at the same location
超出该变量的范围或生命期
答案 2 :(得分:1)
48是跳过参数和本地人。 5字节数组在8字节边界上对齐,10字节在16字节边界上对齐。参数每个需要8个字节,因此参数的3 * 8加上本地的8 + 16给出了24 + 24或48.您可以通过询问每个事件的地址来在gdb中看到它。