2013-06-02 82 views
1
#include <stdio.h> 
#include <stdlib.h> 

int main() 
{ 
    char *name; 
    char *command; 
    name=(char *)malloc(10); 
    command=(char *)malloc(128); 
    printf("address of name is : %d\n",name); 
    printf("address of command is:%d\n",command); 
    printf("Distance between addresses is :%d\n",command-name); 
    printf("Enter your name:"); 
    gets(name); 
    printf("Hello %s\n",name); 
    system(command); 
} 

分配恆定內存量(緩衝區大小)和兩個地址(相鄰內存塊)之間的距離有什麼區別?在這個例子中,名稱和命令之間的區別是16字節,名稱的緩衝區大小是10字節。哪一個會觸發緩衝區溢出?緩衝區大小和兩個地址之間的距離有什麼區別?

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

// typedef size_t u_long; 

int main(){ 
    u_long distance; 
    char *buf1= (char *)malloc(16); 
    char *buf2= (char *)malloc(16); 
    distance= (u_long)buf2 - (u_long)buf1; 
    printf("buf1 = %p\nbuf2 = %p\ndistance = 0x%x bytes\n", 
      buf1, buf2, distance); 
    memset(buf2, 'A', 15); buf2[15]='\0'; 
    printf("before overflow buf2 = %s\n", buf2); 
    memset(buf1, 'B', (8+distance)); 
    printf("after overflow buf2 = %s\n", buf2); 
    return 0; 
} 

回答

0

因爲namecommand是由單獨的呼叫分配給malloc(),他們的地址之間的差異,嚴格來說,毫無意義。指針運算僅適用於相同陣列或相同分配塊中的地址。當然,減去兩個指針會給你一些某種類型的東西,你不能用它來做任何事情。沒有理由相信malloc()將使用相鄰的內存塊,或者即使稍後的分配將具有比較早的分配更高的地址。然而,它可以自由分配,但它看起來合適。

您分配了10個字節,所以您可以使用10個字節。而已。

+0

那麼,要觸發緩衝區溢出,你必須知道兩個相鄰地址之間的距離。例如,如果我們輸入1234567891234567/bin/sh,我們將獲得一個命令shell,因此第16個字符後面的所有內容都將替換命令的內容並將被執行。 – c0ldhand

+0

但是,在上面的第二個例子中,我們沒有使用距離,我們使用分配的緩衝區大小來觸發16字節的緩衝區溢出。 – c0ldhand

+0

那麼示例1和示例2有什麼區別? – c0ldhand

0

當我跑了一個非常溫和的編輯後的版本在Mac OS X 10.8.3第二個程序與GCC 4.7.1,與0x%x改爲0x%zXu_long註釋掉typedef的,輸出是:

buf1 = 0x7fe8d0c000e0 
buf2 = 0x7fe8d0c03a00 
distance = 0x3920 bytes 
before overflow buf2 = AAAAAAAAAAAAAAA 
Abort trap: 6 

這兩個分配並不是很接近,這是完全允許的,但(坦率地說)不是我期望看到的。寫入約14 KiB肯定會引發未定義的行爲。

您無法對可移植代碼中的內存分配位置做出假設。如果您希望調整到某個特定版本的malloc(),則可以這樣做,但這會限制您的便攜性。儘管如此,在像Mac這樣的64位系統中,當您分別分配兩個1字節的分配時,它們通常分開16個字節。添加該代碼在你中間:

char *buffer[4]; 
    for (int i = 0; i < 4; i++) 
    { 
     buffer[i] = (char *)malloc(1); 
     printf("buffer[%i] = %p\n", i, buffer[i]); 
    } 

給輸出:

buf1 = 0x7fe821c000e0 
buf2 = 0x7fe821c03a00 
distance = 0x3920 bytes 
buffer[0] = 0x7fe821c03a10 
buffer[1] = 0x7fe821c03a20 
buffer[2] = 0x7fe821c03a30 
buffer[3] = 0x7fe821c03a40 
before overflow buf2 = AAAAAAAAAAAAAAA 
Abort trap: 6 

要求在每次循環16個字節時,我也得到了相同的輸出。那很有意思;這意味着沒有與這些存儲塊直接相鄰的控制信息(在這些分配之間沒有用於控制信息的空間 - 它可以存儲在buf1buf2之間的一些空間中)。在其他64位系統上,我將最小分配視爲32字節,其中一些空間用於控制信息。也就是說,來自malloc()的1個字節的連續請求的地址將產生相距32個字節的地址,但是對於32個字節的連續請求將產生相距64個字節的地址。

在32位Linux機器,具有1個字節和8個字節的請求,我得到的分配:

buffer[0] = 0x804a008 
buffer[1] = 0x804a018 
buffer[2] = 0x804a028 
buffer[3] = 0x804a038 

隨着對16個字節的請求,我得到:

buffer[0] = 0x804a008 
buffer[1] = 0x804a020 
buffer[2] = 0x804a038 
buffer[3] = 0x804a050 

作爲您可以看到,在第一種情況下,分配間隔爲16個字節;在第二個24字節中。額外的8個字節是控制開銷。

當你請求的空間N個字節和malloc()回報你一個指針P,你有明確的權限訪問的地址範圍內的數據:

(char *)P + 0 .. (char *)P + N - 1 (inclusive) 

此範圍之外的任何訪問導致不確定的行爲。未定義的行爲可能包括出現工作。


在第一個程序,你執行system(command)沒有這種command點初始化數據。這不太可能導致幸福。

+0

我感謝您的詳細解釋。我明白現在的差異。非常感謝。 – c0ldhand