Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C ASLR随机化是否会因功能而异?_C_Assembly_X86_Aslr - Fatal编程技术网

C ASLR随机化是否会因功能而异?

C ASLR随机化是否会因功能而异?,c,assembly,x86,aslr,C,Assembly,X86,Aslr,我有以下代码片段: #include <inttypes.h> #include <stdio.h> uint64_t esp_func(void) { __asm__("movl %esp, %eax"); } int main() { uint32_t esp = 0; __asm__("\t movl %%esp,%0" : "=r"(esp)); printf("esp:

我有以下代码片段:

#include <inttypes.h>
#include <stdio.h>

uint64_t
esp_func(void)
{
  __asm__("movl %esp, %eax");
}

int
main()
{
  uint32_t esp = 0;

  __asm__("\t movl %%esp,%0" : "=r"(esp));

  printf("esp: 0x%08x\n", esp);
  printf("esp: 0x%08lx\n", esp_func());
  return 0;
}
esp_func
显示ASLR是活动的,具有28位的熵,这在我的现代Linux内核上是有意义的

没有意义的是第一个值:为什么它有很大的不同

我看了看装配,它看起来很奇怪

// From main
0x00001150      55             push rbp
0x00001151      4889e5         mov rbp, rsp
0x00001154      4883ec10       sub rsp, 0x10
0x00001158      c745fc000000.  mov dword [rbp-0x4], 0
0x0000115f      c745f8000000.  mov dword [rbp-0x8], 0
0x00001166      89e0           mov eax, esp            ; Move esp to eax
0x00001168      8945f8         mov dword [rbp-0x8], eax ; Assign eax to my variable `esp`
0x0000116b      8b75f8         mov esi, dword [rbp-0x8]
0x0000116e      488d3d8f0e00.  lea rdi, [0x00002004]
0x00001175      b000           mov al, 0
0x00001177      e8b4feffff     call sym.imp.printf     ; For whatever reason, the value in [rbp-0x8]
                                                       ; is assigned here. Why?


// From esp_func
0x00001140      55             push rbp
0x00001141      4889e5         mov rbp, rsp
0x00001144      89e0           mov eax, esp             ; Move esp to eax (same instruction as above)
0x00001146      488b45f8       mov rax, qword [rbp-0x8] ; This changes everything. What is this?
0x0000114a      5d             pop rbp
0x0000114b      c3             ret
0x0000114c      0f1f4000       nop dword [rax]

所以我的问题是,
[rbp-0x8]
中有什么,它是如何达到的,以及为什么这两个值不同?

不,堆栈ASLR在程序启动时发生一次。函数之间RSP的相对调整在编译时是固定的,只是为函数的局部变量留出空间的小常量。(C99可变长度数组和alloca对RSP进行运行时变量调整,但不是随机调整。)

您的程序包含未定义的行为,并且实际上没有打印RSP;取而代之的是前一个
printf
调用在寄存器中留下的一些堆栈地址(它似乎是堆栈地址,因此其高位确实随ASLR而变化)。它没有告诉您函数之间堆栈指针的差异,只是告诉您如何不使用GNUCinlineASM

第一个值是正确打印当前ESP,但这只是64位RSP的低32位


从非
无效
函数的末尾掉落是不安全的
,使用返回值是未定义的行为。任何使用返回值
esp_func()
的调用程序都必然会触发UB,因此编译器可以自由地在RAX中保留它想要的任何内容

如果您想写入
mov%rsp,%rax
/
ret
,则在纯asm中写入该函数,或将mov写入一个
“=r”(tmp)
局部变量。使用GNUCinlineASM修改RAX而不告诉编译器它不会改变任何东西;编译器仍然将其视为没有返回值的函数

MSVC内联asm不同:它显然支持使用
\u asm{mov eax,123}
或其他东西,然后从非void函数的末尾掉下来,MSVC甚至在内联时也会将其作为函数返回值。GNU C内联asm不需要这样愚蠢的攻击:如果您希望asm与C值交互,请使用带有输出约束的扩展asm,就像您在
main
中所做的那样。请记住,GNU C内联asm不是由编译器解析的,只是将模板字符串作为要组装的编译器asm输出的一部分发出


我不知道为什么clang会从堆栈中重新加载一个返回值,但这只是clang内部的一个工件,以及它是如何在禁用优化的情况下生成代码的。但由于未定义的行为,它被允许这样做。它是一个非void函数,因此需要有一个返回值。最简单的方法是只发出一个
ret
,这是一些编译器在启用优化的情况下所做的,但由于过程间优化,即使这样也不能解决问题

在C中,使用未返回值的函数的返回值实际上是未定义的行为。这适用于C级;就编译器而言,使用内联asm修改寄存器而不告诉编译器它不会改变任何事情。因此,您的程序作为一个整体包含UB,因为它将结果传递给
printf
。这就是为什么编译器可以这样编译:您的代码已经被破坏了。实际上,它只是从堆栈内存中返回一些垃圾

TL:DR:这不是将
mov%rsp,%rax
/
ret
作为函数的asm定义发出的有效方法

(C++增强了它的UB,从一端开始,但是在C中只要调用方不使用返回值,它是合法的。如果您用C++编译了与优化相同的源代码,G+甚至在您的内联ASM模板之后甚至不会发出<代码> ReT < /Cult>指令。这可能是为了支持C的默认-<代码> int <代码>如果声明的函数没有返回类型,则返回类型。)


这也是为什么从注释中修改的版本(printf格式字符串已修复)、使用启用优化()编译的版本会打印出“令人惊讶的”不同的“RSP”值:第二个版本根本不使用RSP

#include <inttypes.h>
#include <stdio.h>

uint64_t __attribute__((noinline)) rsp_func(void)
{
  __asm__("movq %rsp, %rax");
}  // UB if return value used

int main()
{
  uint64_t rsp = 0;

  __asm__("\t movq %%rsp,%0" : "=r"(rsp));

  printf("rsp: 0x%08lx\n", rsp);
  printf("rsp: 0x%08lx\n", rsp_func());   // UB here
  return 0;
}
GNU C基本asm(无约束)对任何东西都没有用处(除了
\uuuu属性(裸)函数体)。


当UB在编译时可见时,不要假设编译器会执行您所期望的操作。(当UB在编译时不可见时,编译器必须生成适用于某些调用者或被调用者的代码,这样您就可以获得您所期望的asm。但是,编译时可见UB意味着所有赌注都没有了。)不,堆栈ASLR在程序启动时发生一次。函数之间RSP的相对调整在编译时是固定的,只是为函数的局部变量留出空间的小常量。(C99可变长度数组和
alloca
对RSP进行运行时变量调整,但不是随机的。)

您的程序包含未定义的行为,并且实际上没有打印RSP;相反,前一次
printf
调用在寄存器中留下了一些堆栈地址(它似乎是堆栈地址,因此其高位确实随ASLR而变化)。它没有告诉您函数之间的堆栈指针差异,只是告诉您如何不使用GNU C内联asm

第一个值是正确打印当前ESP,但这只是64位RSP的低32位


从非
void
函数的末尾掉下来是不安全的
,使用返回值是未定义的行为。任何使用
esp_func()
返回值的调用程序都必然会触发UB,因此编译器可以在RAX中自由地保留它想要的任何内容

如果要编写
mov%rsp,%rax
/
ret
,请编写该函数
#include <inttypes.h>
#include <stdio.h>

uint64_t __attribute__((noinline)) rsp_func(void)
{
  __asm__("movq %rsp, %rax");
}  // UB if return value used

int main()
{
  uint64_t rsp = 0;

  __asm__("\t movq %%rsp,%0" : "=r"(rsp));

  printf("rsp: 0x%08lx\n", rsp);
  printf("rsp: 0x%08lx\n", rsp_func());   // UB here
  return 0;
}
Compiler stderr
<source>:7:1: warning: non-void function does not return a value [-Wreturn-type]
}
^
1 warning generated.
Program returned: 0
Program stdout

rsp: 0x7fff5c472f30
rsp: 0x7f4b811b7170
# from the Godbolt link
rsp_func:                               # @rsp_func
        mov     rax, rsp
        ret
main:                                   # @main
        push    rax
        mov     rsi, rsp
        mov     edi, offset .L.str
        xor     eax, eax
        call    printf
        call    rsp_func               # return value ignored because of UB.
        mov     edi, offset .L.str
        xor     eax, eax
        call    printf                 # printf("0x%08lx\n", garbage in RSI left from last printf)
        xor     eax, eax
        pop     rcx
        ret
.L.str:
        .asciz  "rsp: 0x%08lx\n"