簡體   English   中英

使用void(*)(void)指針調用void函數(int),並使用參數准備堆棧

[英]Calling a void function(int) with a void(*)(void) pointer and prepare stack yourself with arguments

我有一個void(*)(void)函數指針,該指針實際上是一個void f(int) 關鍵是,我目前不知道它是什么樣的函數,所以我不能簡單地將其void(*)(int)void(*)(int) 我以為我可以簡單地使用函數的參數准備堆棧。

#include <stdio.h>

void func(int x) {
    printf("%p : %d\n", &x, x);
}

int main(int argc, char* argv[]) {
    int* ptr = &argc - 8;
    *ptr = -1;
    printf("%p : %d\n", ptr, *ptr);

    void (*f)(void) = (void(*)(void)) &func;
    f();

    return 0;
}

返回:

0x7ffd72ec204c : -1
0x7ffd72ec204c : 0

我希望它返回-1兩次。 但是,似乎gcc在調用函數時添加了一些代碼來清除堆棧,這就是為什么func()改為顯示0原因。

是否有某種編譯器標志可用於禁用該標志?

我正在使用gcc (Debian 4.9.2-10) 4.9.2 ,但是如果我可以在任何編譯器中使用它,那就足夠了! 我的一致是Linux 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt20-1+deb8u3 (2016-01-17) x86_64 GNU/Linux

我已閱讀過有關MUST_PASS_IN_STACK -可以幫上忙嗎? 如果可以,該如何使用?

我知道這不是最佳做法。 這只是一個理論練習。

您試圖做的是不確定的行為。 在標准C中,不可能在運行時構造具有任意參數類型的函數調用。 您也無法假設變量最終會出現在堆棧上的布局(如果有的話)。 您甚至不能假設函數參數在堆棧上傳遞。

如果您想做這樣的事情,可以考慮研究像libffi這樣的庫,它通過為每個平台和每個操作系統實現不同的解決方案來實現類似的目的

您可以插入特定於平台的匯編語言代碼以完成所需的操作。 請注意,它不可移植。

讓我們看一下程序的稍微簡化版本的兩個版本:

版本1(簡潔代碼):

#include <stdio.h>

void func(int x) {
    printf("%p : %d\n", &x, x);
}

int main() {
   void (*f)(int) = &func;
   f(-2);
   return 0;
}

第2版​​(駭客程式碼):

#include <stdio.h>

void func(int x) {
    printf("%p : %d\n", &x, x);
}

int main() {
   void (*f)(void) = (void (*)(void))&func;
   return 0;
}

您可以使用gcc -S生成兩個版本的匯編代碼。

我的環境中版本1的匯編代碼:

    .file   "soc.c"
    .section .rdata,"dr"
.LC0:
    .ascii "%p : %d\12\0"
    .text
    .globl  func
    .def    func;   .scl    2;  .type   32; .endef
    .seh_proc   func
func:
    pushq   %rbp
    .seh_pushreg    %rbp
    movq    %rsp, %rbp
    .seh_setframe   %rbp, 0
    subq    $32, %rsp
    .seh_stackalloc 32
    .seh_endprologue
    movl    %ecx, 16(%rbp)
    movl    16(%rbp), %eax
    movl    %eax, %r8d
    leaq    16(%rbp), %rdx
    leaq    .LC0(%rip), %rcx
    call    printf
    nop
    addq    $32, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .def    __main; .scl    2;  .type   32; .endef
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    pushq   %rbp
    .seh_pushreg    %rbp
    movq    %rsp, %rbp
    .seh_setframe   %rbp, 0
    subq    $48, %rsp
    .seh_stackalloc 48
    .seh_endprologue
    call    __main
    leaq    func(%rip), %rax
    movq    %rax, -8(%rbp)
    movq    -8(%rbp), %rax
    movl    $-2, %ecx
    call    *%rax
    movl    $0, %eax
    addq    $48, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .ident  "GCC: (GNU) 4.9.3"
    .def    printf; .scl    2;  .type   32; .endef

我的環境中版本2的匯編代碼:

    .file   "soc.c"
    .section .rdata,"dr"
.LC0:
    .ascii "%p : %d\12\0"
    .text
    .globl  func
    .def    func;   .scl    2;  .type   32; .endef
    .seh_proc   func
func:
    pushq   %rbp
    .seh_pushreg    %rbp
    movq    %rsp, %rbp
    .seh_setframe   %rbp, 0
    subq    $32, %rsp
    .seh_stackalloc 32
    .seh_endprologue
    movl    %ecx, 16(%rbp)
    movl    16(%rbp), %eax
    movl    %eax, %r8d
    leaq    16(%rbp), %rdx
    leaq    .LC0(%rip), %rcx
    call    printf
    nop
    addq    $32, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .def    __main; .scl    2;  .type   32; .endef
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    pushq   %rbp
    .seh_pushreg    %rbp
    movq    %rsp, %rbp
    .seh_setframe   %rbp, 0
    subq    $48, %rsp
    .seh_stackalloc 48
    .seh_endprologue
    call    __main
    leaq    func(%rip), %rax
    movq    %rax, -8(%rbp)
    movq    -8(%rbp), %rax
    call    *%rax
    movl    $0, %eax
    addq    $48, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .ident  "GCC: (GNU) 4.9.3"
    .def    printf; .scl    2;  .type   32; .endef

兩個版本的匯編代碼之間的唯一區別是第44行。

   movl    $-2, %ecx

如果將相同的匯編代碼注入到程序的第二個版本中,則:

#include <stdio.h>

void func(int x) {
    printf("%p : %d\n", &x, x);
}

int main() {
   void (*f)(void) = (void (*)(void))&func;
   __asm__("movl    $-2, %ecx");
   f();
   return 0;
}

編譯器生成預期的匯編代碼。 當我運行上述程序時,我得到:

0x22cae0 : -2

與您在該程序的第一個版本中看到的輸出相同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM