小编css*_*233的帖子

为什么x86-64 GCC函数序言分配的堆栈少于局部变量?

考虑以下简单程序:

int main(int argc, char **argv)
{
        char buffer[256];

        buffer[0] = 0x41;
        buffer[128] = 0x41;
        buffer[255] = 0x41;

        return 0;
}
Run Code Online (Sandbox Code Playgroud)

在x86-64机器上使用GCC 4.7.0编译.用GDB反汇编main()给出:

0x00000000004004cc <+0>:     push   rbp
0x00000000004004cd <+1>:     mov    rbp,rsp
0x00000000004004d0 <+4>:     sub    rsp,0x98
0x00000000004004d7 <+11>:    mov    DWORD PTR [rbp-0x104],edi
0x00000000004004dd <+17>:    mov    QWORD PTR [rbp-0x110],rsi
0x00000000004004e4 <+24>:    mov    BYTE PTR [rbp-0x100],0x41
0x00000000004004eb <+31>:    mov    BYTE PTR [rbp-0x80],0x41
0x00000000004004ef <+35>:    mov    BYTE PTR [rbp-0x1],0x41
0x00000000004004f3 <+39>:    mov    eax,0x0
0x00000000004004f8 <+44>:    leave  
0x00000000004004f9 <+45>:    ret    
Run Code Online (Sandbox Code Playgroud)

当缓冲区为256字节时,为什么sub rsp只有0x98 = 152d?当我将数据移入缓冲区[0]时,它似乎只是使用分配的堆栈帧之外的数据并使用rbp来引用,那么甚至sub rsp的点是什么,0x98? …

assembly stack gcc x86-64

11
推荐指数
1
解决办法
2516
查看次数

Segfault仅在GDB中出现

为什么以下程序在执行时不会崩溃,但在GDB中崩溃时会出现段错误?在32位x86(Athlon 64,如果它应该重要)上使用GCC 4.5.2编译.

#include <stdio.h>
#include <string.h>

int modify(void)
{
        __asm__("mov $0x41414141, %edx"); // Stray value.
        __asm__("mov $0xbffff2d4, %eax"); // Addr. of ret pointer for function().
        __asm__("mov %edx, (%eax)");
}

int function(void)
{
        modify();

        return 0;
}

int main(int argc, char **argv)
{
        function();

        return 0;
}
Run Code Online (Sandbox Code Playgroud)

mov $ 0xbffff2d4,%eax是使用GDB确定的,用于查找为"function"函数存储返回指针的地址.在不同的系统上,这可能会有所不同.ASLR因此而被禁用.

当我执行程序时,没有任何反应.dmesg也没有关于崩溃的报告.但是当我在GDB中执行相同的程序时:

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
=> 0x41414141:  Cannot access memory at address 0x41414141
Run Code Online (Sandbox Code Playgroud)

这是我期望在我正常执行程序时应该发生的事情.当其他程序崩溃时,我的确会像往常一样得到段错误,而且我可以轻松编写一个崩溃的小程序,其中有一个很好的段错误.但为什么这个特定的程序不会因为段错误而崩溃?

c linux x86 assembly segmentation-fault

5
推荐指数
1
解决办法
1718
查看次数

二进制环境变量和 setenv()

在 Linux 中使用二进制环境变量时,我发现了一些奇怪的行为,其中一些单个字节似乎是错误的。我仔细检查了它,似乎某些字节在给 setenv() 时总是会被错误地“转换”。看这个:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    char array[256];

    int i;
    for(i = 1; i < 256; i++) {
        array[i] = i;
    }

    setenv("badenv", array, 1);

    system("/bin/sh");

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我执行这个程序,然后当我做 echo $badenv > test; hexdump 测试我看到:

0000000 0101 0302 0504 0706 2008 0c0b 0e0d 100f
0000010 1211 1413 1615 1817 1a19 1c1b 1e1d 201f
0000020 2221 2423 2625 2827 2a29 2c2b 2e2d 302f
0000030 3231 3433 3635 3837 3a39 3c3b 3e3d …
Run Code Online (Sandbox Code Playgroud)

c linux environment-variables setenv

5
推荐指数
1
解决办法
2431
查看次数

堆栈驻留缓冲区溢出64位?

我正在研究一些与安全有关的事情,现在我正在玩我自己的堆栈.我正在做的事应该非常简单,我甚至都没有尝试执行堆栈,只是为了表明我可以控制64位系统上的指令指针.我已经关闭了所有我能够使用它的保护机制(NX-bit,ASLR,也用-fno-stack-protector -z execstack编译).我没有那么多64位汇编的经验,花了一些时间搜索和试验自己后,我想知道是否有人能够解决我遇到的问题.

我有一个程序(下面的源代码),它只是将一个字符串复制到一个没有边界检查的堆栈驻留缓冲区.但是,当我用一系列0x41覆盖时,我希望看到RIP设置为0x4141414141414141,而我发现我的RBP设置为此值.我确实遇到了分段错误,但是在执行RET指令时RIP不会更新为此(非法)值,即使RSP设置为合法值也是如此.我甚至在GDB中验证了在RET指令之前有可读存储器在RSP处包含一系列0x41.

我的印象是LEAVE指令:

MOV(E)SP,(E)BP

POP(E)BP

但是在64位上,"LEAVEQ"指令似乎(类似):

MOV RBP,QWORD PTR [RSP]

我认为这只是通过在执行该指令之前和之后观察所有寄存器的内容来实现的.LEAVEQ似乎只是RET指令的上下文相关名称(GDB的反汇编程序给它),因为它仍然只是一个0xC9.

RET指令似乎与RBP寄存器有关,可能是解除引用它?我认为RET确实(类似):

MOV RIP,QWORD PTR [RSP]

但是就像我提到的那样,似乎取消引用RBP,我认为这样做是因为当没有其他寄存器似乎包含非法值时,我得到了分段错误.

该计划的源代码:

#include <stdio.h>
#include <string.h>

int vuln_function(int argc,char *argv[])
{
    char buffer[512];

    for(int i = 0; i < 512; i++) {
        buffer[i] = 0x42;
    }

    printf("The buffer is at %p\n",buffer);

    if(argc > 1) {
        strcpy(buffer,argv[1]);
    }

    return 0;
}    

int main(int argc,char *argv[])
{
    vuln_function(argc,argv);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

for循环只是用0x42填充缓冲区的合法部分,这使得在溢出之前很容易在调试器中看到它.

调试会话的摘录如下:

(gdb) disas vulnerable
Dump of assembler code for function vulnerable:
   0x000000000040056c …
Run Code Online (Sandbox Code Playgroud)

linux stack-overflow 64-bit callstack gdb

4
推荐指数
1
解决办法
5070
查看次数

如何以改变文件部分数据长度的方式修改ELF文件?

我正在尝试修改自己的ELF文件的可执行内容,以查看是否可行。我编写了一个程序,该程序读取和解析ELF文件,搜索应更新的代码,对其进行更改,然后在更新节标题中的sh_size字段后将其写回。

但是,这不起作用。如果我只是简单地交换一些字节和其他字节,它就可以工作。但是,如果更改大小,它将失败。我知道有些sh_offsets彼此紧邻。但是,当我减小可执行代码的大小时,这无关紧要。

当然,我的程序中可能存在一个错误(或多个),但是我已经很努力地进行了研究。

除了寻求调试程序方面的帮助外,我只是想知道,除了sh_size字段之外,还有什么要更新才能使它起作用的(减小大小时)?除了该字段之外,还有什么会使长度更改失败的事情吗?

编辑:

看来安迪·罗斯(Andy Ross)完全正确。即使在这个非常简单的程序中,我也遇到了__libc_start_main中的一些间接寻址,我不能轻易修改这些间接寻址来更新它将达到的偏移量。

不过,我很好奇,什么是仍然设法尽可能解决这个问题的最佳方法?我知道我无法在所有情况下都解决此问题,但是对于某些简单程序,应该可以更新使其运行所需的内容?我应该尝试编写自己的虚拟机还是尝试开发一个“调试器”以将每个可疑问题指令替换为INT 3?有任何想法吗?

linux elf

3
推荐指数
1
解决办法
3140
查看次数

GDB如何知道函数的长度?

GDB采用什么方法来确定函数的长度?我注意到在从main()中删除了两个字节后,GDB认为该函数仍然是原始长度,所以我假设它使用了一些调试信息?

特别是,main()的结尾最初是:

0x00000000004005a1 <+133>:   mov    edi,0x4006ac
0x00000000004005a6 <+138>:   call   0x4003a0 <puts@plt>
0x00000000004005ab <+143>:   mov    eax,0x0
0x00000000004005b0 <+148>:   leave  
0x00000000004005b1 <+149>:   ret    
Run Code Online (Sandbox Code Playgroud)

然后我删除了两个字节(在程序清单的早期):

0x000000000040059f <+131>:   mov    edi,0x4006ac
0x00000000004005a4 <+136>:   call   0x40039e
0x00000000004005a9 <+141>:   mov    eax,0x0
0x00000000004005ae <+146>:   leave  
0x00000000004005af <+147>:   ret    
0x00000000004005b0 <+148>:   nop
0x00000000004005b1 <+149>:   nop
Run Code Online (Sandbox Code Playgroud)

即GDB认为整体长度仍然相同.我想知道GDB是如何做到这一点的.

该文件是以下类型:a.out:ELF 64位LSB可执行文件,x86-64,版本1(SYSV),动态链接(使用共享库),未剥离仅使用"gcc"创建,没有参数.

linux gdb elf

1
推荐指数
1
解决办法
487
查看次数