2012-09-27 106 views
7

我查看了緩衝區溢出漏洞的基礎知識,並試圖瞭解堆棧如何工作。爲此,我想編寫一個簡單的程序,將返回地址的地址更改爲某個值。任何人都可以幫助我計算出基指針的大小以獲得第一個參數的偏移量嗎?修改堆棧上的返回地址

void foo(void) 
{ 
    char ret; 
    char *ptr; 

    ptr = &ret; //add some offset value here 
    *ptr = 0x00; 
} 

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

    return 1; 
} 

生成的彙編程序代碼如下所示:

.file "test.c" 
    .text 
    .globl foo 
    .type foo, @function 
foo: 
.LFB0: 
    .cfi_startproc 
    pushq %rbp 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    movq %rsp, %rbp 
    .cfi_def_cfa_register 6 
    leaq -9(%rbp), %rax 
    movq %rax, -8(%rbp) 
    movq -8(%rbp), %rax 
    movb $0, (%rax) 
    popq %rbp 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE0: 
    .size foo, .-foo 
    .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 
    subq $16, %rsp 
    movl %edi, -4(%rbp) 
    movq %rsi, -16(%rbp) 
    call foo 
    movl $1, %eax 
    leave 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE1: 
    .size main, .-main 
    .ident "GCC: (GNU) 4.7.1 20120721 (prerelease)" 
    .section .note.GNU-stack,"",@progbits 

foo的幀段的相關部分應該是這樣的:

[炭RET] [基指針] [返回地址]

我有第一個只有1個字節大小的位置。如http://insecure.org/stf/smashstack.html中所提到的,它是否僅比基址指針或單字的大小還要多1個字節?我怎樣才能知道基指針的大小?

回答

0

看來你使用的是64位架構,因爲RBP和RSP寄存器是64位長的。如果您將ptr聲明爲char*,則必須增加8次才能移動堆棧。 相反,您可以聲明它爲uint64_t *。該數據類型通常在<stdint.h>中可用。

但是,堆棧幀定義因目標架構甚至編譯器行爲和優化而異。但是,如果你正在試驗,那很好。

1

你不能在香草C中做到這一點,你不能控制編譯器如何佈局堆棧幀。

在x86-64中,返回地址應該在%rbp + 8。你可以使用一些內聯彙編來獲得(gcc語法):

uint64_t returnaddr; 
asm("mov 8(%%rbp),%0" : "=r"(returnaddr) : :); 

類似的設置它。

即使這有點簡單,因爲您不知道編譯器是否要設置%rbp或不。因人而異。

+1

+1重新編譯總是可以移動變量。只是在OP的測試中,在這種特殊情況下,返回地址是'&ret + 17',只要該函數中的局部變量沒有改變,該地址不太可能改變。 – ughoavgfhw

+0

抵消了17件作品。你能解釋一下你是如何確定的嗎? – fliX

+0

@fliX在彙編中,指令'leaq -9(%rbp),%rax'正在堆棧中獲取一個地址。由於它是字節對齊的,並且唯一的地址計算必須在'ret'的地方。以9爲前一個64位基址指針加8,就得到了17。 – ughoavgfhw

1

你的basepointer很可能只是一個指針,所以它的大小sizeof(int *)。 但是在變量ret和基址指針之間還有另一個值。 我會假設它的值(eax?)。這將導致類似下面的,如果你想要一個死循環:

void foo(void) 
{ 
    char ret; 
    char *ptr; 

    ptr = (char*)(&ret) + (sizeof(ret) + 2*sizeof(int*)) ; 
    *(int*)ptr -= 0x0c; 
} 

被修改,假定它有一個指針的大小返回目標(可以爲其他指令集有所不同)。通過遞減它,返回目標設置爲調用點foo之前的一個點。