2012-09-27 40 views
7

Tôi đã xem xét các khái niệm cơ bản về lỗ hổng tràn bộ đệm và cố gắng hiểu cách ngăn xếp hoạt động. Đối với điều đó tôi muốn viết một chương trình đơn giản thay đổi địa chỉ của địa chỉ trả về thành một số giá trị. Ai có thể giúp tôi với việc tìm ra kích thước của con trỏ cơ sở để có được bù đắp từ đối số đầu tiên?Sửa đổi địa chỉ trả về trên ngăn xếp

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; 
} 

Mã lắp ráp được tạo ra trông như sau:

.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 

Phần liên quan của phân khúc khung foo sẽ trông như thế này:

[char ret] [con trỏ cơ sở] [địa chỉ trả lại ]

Tôi có vị trí đầu tiên chỉ có kích thước 1 byte. Có phải chỉ thêm 1 byte vào con trỏ cơ sở hoặc kích thước của từ như được đề cập trong http://insecure.org/stf/smashstack.html? Và làm cách nào để tôi biết kích thước của con trỏ cơ sở?

Trả lời

0

Dường như bạn đang sử dụng kiến ​​trúc 64 bit, vì thanh ghi RBP và RSP dài 64 bit. Nếu bạn khai báo ptrchar*, bạn sẽ phải tăng 8 lần để di chuyển qua ngăn xếp. Thay vào đó bạn có thể khai báo nó là uint64_t *. Kiểu dữ liệu này thường có sẵn trong <stdint.h>.

Tuy nhiên định nghĩa khung ngăn xếp khác nhau tùy thuộc vào kiến ​​trúc đích và thậm chí trên hành vi trình biên dịch và tối ưu hóa. Tuy nhiên, nếu bạn đang thử nghiệm thì cũng tốt thôi.

1

Bạn sẽ không thể thực hiện điều này trong vanilla C, bạn không có quyền kiểm soát cách trình biên dịch đưa ra khung ngăn xếp.

Trong x86-64, địa chỉ trả về phải là %rbp + 8. Bạn có thể sử dụng một số lắp ráp nội tuyến để nhận được rằng (cú pháp gcc):

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

Tương tự cho việc thiết lập nó.

Thậm chí đó là một chút sơ sài, vì bạn không biết liệu trình biên dịch sẽ thiết lập %rbp hay không. YMMV.

+1

+1 Biên dịch lại luôn có thể di chuyển các biến. Chỉ để kiểm tra OP, trong trường hợp cụ thể này địa chỉ trả về là '& ret + 17', điều này không có khả năng thay đổi miễn là các biến cục bộ trong hàm đó không thay đổi. – ughoavgfhw

+0

offset của 17 tác phẩm. bạn có thể giải thích làm thế nào bạn xác định nó? – fliX

+0

@fliX Trong assembly, lệnh 'leaq -9 (% rbp),% rax' đang lấy địa chỉ trên ngăn xếp. Vì nó được sắp xếp theo byte và chỉ tính toán địa chỉ, đó phải là vị trí 'ret'. Lấy 9 và thêm 8 cho con trỏ cơ sở 64 bit trước đó, bạn nhận được 17. – ughoavgfhw

1

Con trỏ của bạn rất có thể chỉ là con trỏ, vì vậy nó có kích thước sizeof (int *). Nhưng cũng có một giá trị khác ở giữa biến số ret và con trỏ cơ sở của bạn. Tôi sẽ giả định giá trị của một thanh ghi (eax?). Điều này sẽ dẫn đến một cái gì đó như sau, nếu bạn muốn có một vòng lặp vô tận:

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

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

Mục tiêu lợi nhuận mà được sửa đổi giả sử nó có kích thước của một con trỏ (có thể khác nhau cho bộ giảng dạy khác). Bằng cách giảm nó, mục tiêu trả về được đặt thành một điểm trước điểm gọi foo.

Các vấn đề liên quan