簡體   English   中英

為什么在任意內存位置設置值不起作用?

[英]Why does setting a value at an arbitrary memory location not work?

我有這個代碼:

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

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

   *(volatile uint8_t*)0x12345678u = 1;
   int var = *(volatile uint8_t*)0x12345678;
   printf("%i", var);
   printf("%i", &var);

   return (EXIT_SUCCESS);
}

我想查看我之前指定的1和該 int 的地址。 但是在 bash 中由gcc編譯時,只會顯示沒有任何錯誤的“命令終止”。 有誰知道為什么會這樣?

PS:我是 C 的新手,所以只是在試驗。

你在做什么:

*(volatile uint8_t*)0x12345678u = 1;
int var = *(volatile uint8_t*)0x12345678;

完全錯誤。

您無法保證可以訪問像0x12345678這樣的任意地址,更不用說您的程序可寫了。 換句話說,您不能將值設置為任意地址並期望它起作用。 至少可以說這是未定義的行為,並且很可能由於操作系統阻止您接觸您不擁有的內存而使您的程序崩潰。

您在嘗試運行程序時得到的“命令終止”正是因為操作系統阻止您的程序訪問它不允許訪問的內存位置。 你的程序在它可以做任何事情之前就被殺死了。


如果你在 Linux 上,你可以使用mmap函數訪問它之前在(幾乎)任意地址請求內存頁(參見man mmap )。 這是一個實現您想要的示例程序:

#include <sys/mman.h>
#include <stdio.h>

#define WANTED_ADDRESS (void *)0x12345000
#define WANTED_OFFSET 0x678 // 0x12345000 + 0x678 = 0x12345678

int main(void) {
    // Request a memory page starting at 0x12345000 of 0x1000 (4096) bytes.
    unsigned char *mem = mmap(WANTED_ADDRESS, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

    // Check if the OS correctly granted your program the requested page.
    if (mem != WANTED_ADDRESS) {
        perror("mmap failed");
        return 1;
    }

    // Get a pointer inside that page.
    int *ptr = (int *)(mem + WANTED_OFFSET); // 0x12345678

    // Write to it.
    *ptr = 123;

    // Inspect the results.
    printf("Value  : %d\n", *ptr);
    printf("Address: %p\n", ptr);

    return 0;
}

操作系統和加載程序不會自動為您的程序提供所有可能的地址。 進程的虛擬地址空間是由程序加載器和進程內服務的各種操作按需構建的。 盡管每個地址在作為內存的潛在地址的意義上“存在”,但是當一個進程試圖訪問一個地址時會發生什么是由系統中的特殊數據結構控制的。 這些數據結構控制進程是否可以讀取、寫入或執行內存的各個部分,虛擬地址當前是否映射到物理內存,以及虛擬地址當前是否未映射到內存但會在需要時提供物理內存. 最初,進程的大部分地址空間都被標記為未使用(或者至少是隱式標記,因為地址空間的任何顯式記錄都不適用於它)。

到目前為止,在您嘗試執行的程序中,地址 0x12345678 尚未映射並標記為對您的進程可用,因此,當您的進程嘗試使用它時,系統檢測到錯誤並終止了您的進程。

(有些系統在加載程序時會隨機分配地址空間的布局,使攻擊者更難利用程序中的漏洞。因此,在程序的某些執行中可能會訪問 0x12345678,並且不是別人。)

引用自C11 標准 6.5.3.2p4

4 一元 * 運算符表示間接。 [...] 如果為指針分配了無效值,則一元 * 運算符的行為未定義。

您在(volatile uint8_t*)0x12345678u指針上使用*運算符。 這是一個有效的指針嗎? 它是無效的指針嗎? 什么是指針的“無效值”?

沒有檢查可以找出哪些特定的指針值有效,哪些無效。 它不是用 C 語言實現的。 隨機指針可能恰好是一個有效指針。 但最有可能的是,它是一個無效的指針。 在這種情況下 - 行為未定義。

取消引用無效指針是未定義的行為。 但是 - 在 C 范圍之外並進入操作系統 - 在 *unix 系統上嘗試訪問您不允許訪問的內存,應該在您的程序上發出信號 SIGSEGV 並終止您的程序。 很可能這就是發生的事情。 不允許您的程序訪問0x12345678值后面的內存位置,操作系統專門對此進行保護。

另請注意,該系統使用ASLR ,因此程序中的指針值確實在某種程度上是隨機的。 不是線性的,即。 *(char*)0x01不會訪問內存中的第一個字節。 操作系統(或更確切地說,由操作系統配置的底層硬件)使用所謂的虛擬內存將程序中的指針值轉換為 ram 中的物理位置。 相同的指針值可能恰好在您的程序第二次運行時有效。 但最有可能的是,因為指針可以有這么多的值,很可能它不是一個有效的指針。 您的操作系統會殺死您的程序,因為它檢測到無效的內存訪問。

暫無
暫無

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

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