printf("%s", s1); printf("%s", s2);因此,在我們的大學作業中,我們被要求在不觸及變數的情況下更改兩個序列函式的輸出。目的是讓我們在基于 Linux 的系統上使用行程的記憶體布局的理論知識。
預期的輸出是第一次printf呼叫輸出 s1 和 s2 按空格分隔的輸出,并且第二次呼叫的輸出保持原始應用程式的預期。和 的值和大小s1是s2已知的。
我最初的想法是malloc(0)減去等于字串長度的偏移量( 1 表示塊大小值),然后將其轉換為char *. 由于 2 個字串值非常小(絕對小于 4KiB,即頁面大小),我希望只有一個區域分配給堆,因此它是線性的。
但是從看起來我得到的值為零,這意味著我正在查看未初始化的記憶體,或者與我希望定位的字串不同的東西。
以下是有問題的代碼:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void heap_attack() {
// alternatively it can have signature of
// void heap_attack(void * v);
// but the task is assumed to be solvable with the original signature
}
int main(int argc, char const *argv[])
{
char * s1 = malloc(9);
char * s2 = malloc(9);
if (s1 == NULL || s2 == NULL) return EXIT_FAILURE;
strcpy(s1, "s0000000");
strcpy(s2, "s1111111");
heap_attack();
printf("student 1: %s\n", s1);
printf("student 2: %s\n", s2);
free(s1);
free(s2);
return 0;
}
我的實作heap_attack開始如下:
void heap_attack() {
char * heap_end = malloc(0) - 1; // 1 for the size fragment preceding writable space
char * s1 = heap_end - 9;
printf("%s", s1); // here i expected to see the s1111111 string printed to stdout
}
uj5u.com熱心網友回復:
假設您正在使用 glibc(最常見的設定)在 GNU/Linux 上作業,那么您可以做出一些假設來幫助您解決問題。
- 正如您所說,兩個分配的塊都將駐留在同一個新初始化的(線性)堆中,該堆通常跨越多個頁面(幾個 KB)。僅供參考:Linux x86 上的頁面大小是 4K,而不是 4M。
- 在堆初始化之后,連續分配(中間
malloc()沒有任何呼叫free())將分配連續的記憶體塊,因此第一個分配的字串將在第二個之前。 - 您可以通過查看源代碼了解 glibc 分配器使用的結構(選擇正確的版本,運行
/lib/x86_64-linux-gnu/libc.so.6將列印版本)。您還可以查看我的另一個答案,我在其中簡要解釋了 a 的內部布局malloc_chunk。 - 通過查看源代碼或通過測驗,您可以注意到 malloc 的塊實際上在大小上四舍五入到
2 * size_t.
假設 1 和 2(我們可以在這個特定環境中再次做出)保證:
s2 > s1(即s2是在記憶體之后s1)- 兩個字串之間應該正好有
(s2 - s1 - strlen(s2) - 1位元組,除非改變,否則這個值不會改變strlen(s2)。 - 下一次分配
malloc(x)將在 之后s2,并且始終處于與 相同的固定偏移量s2,您可以輕松計算一次然后使用(假設s2保持相同的長度)。
上面的假設 3 將幫助您計算出所需計算的塊的實際大小。對于malloc(9)相應的塊(包括標題)將是 32 個位元組(標題為 16 資料假設為 16 sizeof(size_t) == 8)。此外malloc_usable_size(),將為您提供不包括標題的確切大小。
用空格填充這些位元組將完成您想要的。但是,這樣做會破壞 的塊標頭s1,并且以后任何嘗試釋放(損壞的)塊的嘗試都極有可能導致崩潰。你可以完全避免free()在你的情況下,因為你并不真的需要它。無論如何,作業系統都會在程式終止時回收記憶體。
你的一個可能的實作heap_attack()是:
void heap_attack(void) {
char *top_chunk = malloc(1);
char *top_chunk_header = top_chunk - 2 * sizeof(size_t);
char *s2 = top_chunk_header - 16; // assuming strlen(s2) <= 15
char *s1 = s2 - 2 * sizeof(size_t) - 16 // assuming strlen(s1) <= 15;
// Fill memory between s1 8 and s2 with spaces
}
uj5u.com熱心網友回復:
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define STRING_DISTANCE (0x20)
#define ARBITRARY_STACK_OFFSET (20)
// The strategy is as follows: We can tell the difference from the contigous heap blocks (0x20) in advance (at least for the same machine)
// So given s1 - we would like to simply overwrite everything between with spaces - that way when it prints s1, it will print spaces until s2 and its null terminator
// The only challenge is to find s1, the way we do that here is by simply traveling up the stack and finding our two strings, assuming we know their offsets.
void heap_attack()
{
size_t a = 0;
void * s1 = 0;
for (uintptr_t * stack_ptr = &a; a < ARBITRARY_STACK_OFFSET; a = 1) // Travel up the stack, from a variable in our frame, to the function calling us.
{
if (stack_ptr[a] - (uintptr_t)s1 == STRING_DISTANCE)
{
printf("stack offset - %lu\ns1 - %p\n", a, (void *)stack_ptr[a]);
break;
}
s1 = stack_ptr[a];
}
for (char * x = (char *)s1 strlen(s1); x < s1 STRING_DISTANCE; x = 1)
{
*x = ' ';
}
}
int main()
{
char * s1 = malloc(9);
char * s2 = malloc(9);
printf("%p - %p\n", s1, s2);
if (s1 == NULL || s2 == NULL) return EXIT_FAILURE;
strcpy(s1, "s0000000");
strcpy(s2, "s1111111");
heap_attack();
printf("student 1: %s\n", s1);
printf("student 2: %s\n", s2);
// I disabled the frees because I corrupted the blocks, corrupting the blocks is neccessary so it would print spaces between them
// If we don't want to corrupt the blocks, another option would be to also override the format string, but that's outside the scope of this challenge imo
// free(s1);
// free(s2);
return 0;
}
如果我們選擇堆解決方案:
void heap_attack()
{
char * s1 = (char *)malloc(9) - STRING_DISTANCE * 2;
for (char * x = (char *)s1 strlen(s1); x < s1 STRING_DISTANCE; x = 1)
{
*x = ' ';
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/477681.html
上一篇:當執行緒在已解鎖的互斥鎖上呼叫pthread_mutex_unlock時會發生什么
下一篇:結構成員的地址是否定義了行為?
