簡體   English   中英

是否可以恢復單個鏈表的頭指針?

[英]Is it possible to recover head pointer of a single linked list?

如果可以做出以下假設,則可以恢復鏈表的頭節點。

  1. 鏈表是使用malloc創建的,可以從堆區域進行訪問。
  2. 堆的開始和結束地址可以從/ proc / self / maps找到(至少在Linux中)。
  3. 原始鏈接列表中的至少一個節點是可訪問的。
  4. 指向前一個節點的指針將在堆中的某個地方找到。
  5. 可以遞歸搜索它,直到找到實際的頭部為止。

為了更好地說明它,請使用以下程序,該程序可以在默認配置下至少通過Ubuntu / WSL與gcc一起成功編譯。

程序

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

typedef struct node
{
    int val;
    struct node *next;
} node_t;
node_t *head = NULL;
unsigned long start_address = 0;
unsigned long end_address = 0;

node_t *getLastNode()
{
    node_t *iter = head;
    for (; iter->next != NULL; iter = iter->next)
        ;
    return iter;
}
void addToLinkedList(int value)
{
    node_t *data = malloc(sizeof(node_t));
    data->val = value;
    data->next = NULL;

    if (head == NULL)
        head = data;
    else
        getLastNode()->next = data;
}

void createLinkedList()
{
    // Add 10 nodes to the linked list
    int start_val = 0x10101010;
    for (int i = 1; i <= 10; i++)
        addToLinkedList(start_val * i);
}

void printLinkedList()
{
    printf("Head pointer of Linked List : %p\n", head);
    for (node_t *iter = head; iter != NULL; iter = iter->next)
        printf("%p -> value = %X, next = %p \n", iter, iter->val, iter->next);
    printf("\n");
}

void resetHeadPtr()
{
    // Lets make head point to the last node
    head = getLastNode();
}

void findHeapBoundary()
{
    // Code inspired from https://unix.stackexchange.com/a/251769/152334
    char mapsFilename[1024];
    char line[256];

    char area[1024];
    sprintf(mapsFilename, "/proc/%d/maps", getpid());
    FILE *pMapsFile = fopen(mapsFilename, "r");

    while (fgets(line, 256, pMapsFile) != NULL)
    {
        // Dirty hack to get the heap start and end address
        sscanf(line, "%08lx-%08lx%*[^[]%s\n", &start_address, &end_address, area);
        if (strcmp(area, "[heap]") == 0)
            break;
    }
    fclose(pMapsFile);
    printf("Heap memory start address : %p\n", (int *)start_address);
    printf("Heap memory end address   : %p\n", (int *)end_address);
}

node_t *findPointerInMemory()
{
    for (int *ptr = (int *)start_address; ptr < (int *)(end_address - sizeof(node_t)); ptr++)
    {
        if (((node_t *)ptr)->next == head)
            return (node_t *)ptr;
    }
    return NULL;
}

void recoverHeadPtr()
{
    node_t *ptr = findPointerInMemory();
    if (ptr == NULL)
    {
        printf("Cannot find %p in heap memory\nStopping Search\n\n", head);
        return;
    }
    printf("Found %p at %p\n", head, ptr);
    head = ptr;
    recoverHeadPtr();
}

int main(int argc, char const *argv[])
{
    createLinkedList();
    printf("Original Linked List Contents\n*****************************\n");
    printLinkedList();

    resetHeadPtr();
    printf("Linked List Contents after reset\n********************************\n");
    printLinkedList();

    findHeapBoundary();
    recoverHeadPtr();

    printf("Recovered Linked List Contents\n******************************\n");
    printLinkedList();
    return 0;
}

輸出量

Original Linked List Contents
*****************************
Head pointer of Linked List : 0x1db6010
0x1db6010 -> value = 10101010, next = 0x1db6030
0x1db6030 -> value = 20202020, next = 0x1db6050
0x1db6050 -> value = 30303030, next = 0x1db6070
0x1db6070 -> value = 40404040, next = 0x1db6090
0x1db6090 -> value = 50505050, next = 0x1db60b0
0x1db60b0 -> value = 60606060, next = 0x1db60d0
0x1db60d0 -> value = 70707070, next = 0x1db60f0
0x1db60f0 -> value = 80808080, next = 0x1db6110
0x1db6110 -> value = 90909090, next = 0x1db6130
0x1db6130 -> value = A0A0A0A0, next = (nil)

Linked List Contents after reset
********************************
Head pointer of Linked List : 0x1db6130
0x1db6130 -> value = A0A0A0A0, next = (nil)

Heap memory start address : 0x1db6000
Heap memory end address   : 0x1dd7000
Found 0x1db6130 at 0x1db6110
Found 0x1db6110 at 0x1db60f0
Found 0x1db60f0 at 0x1db60d0
Found 0x1db60d0 at 0x1db60b0
Found 0x1db60b0 at 0x1db6090
Found 0x1db6090 at 0x1db6070
Found 0x1db6070 at 0x1db6050
Found 0x1db6050 at 0x1db6030
Found 0x1db6030 at 0x1db6010
Cannot find 0x1db6010 in heap memory
Stopping Search

Recovered Linked List Contents
******************************
Head pointer of Linked List : 0x1db6010
0x1db6010 -> value = 10101010, next = 0x1db6030
0x1db6030 -> value = 20202020, next = 0x1db6050
0x1db6050 -> value = 30303030, next = 0x1db6070
0x1db6070 -> value = 40404040, next = 0x1db6090
0x1db6090 -> value = 50505050, next = 0x1db60b0
0x1db60b0 -> value = 60606060, next = 0x1db60d0
0x1db60d0 -> value = 70707070, next = 0x1db60f0
0x1db60f0 -> value = 80808080, next = 0x1db6110
0x1db6110 -> value = 90909090, next = 0x1db6130
0x1db6130 -> value = A0A0A0A0, next = (nil)

背景

我的一個朋友在采訪中被問到以下問題。 “如果給出了最后一個節點,您能找到單個鏈表的標題嗎?”。 他回答“否”,即使面試官對回答不完全滿意,也得到了工作。 這讓我開始思考,是否有可能這樣做。

因此,真正的問題是。

  1. 這種方法正確嗎?
  2. 我們是否需要搜索其他內存段?
  3. 可以將此方法推廣到Windows嗎?
  4. ASLR是否會對這種方法產生影響?

正確的答案應該是:

如果設計不干凈,那么嘗試這樣做會是一個危險的解決方法。
如果您確實確實嘗試搜索內存中任何內容的地址(以任何方式都不能保證僅包含該地址(如果它是有問題的指針),那么您可能會發現任何似乎意外包含的內存一個看起來像該地址的數字。
如果您隨后繼續使用它(假設它是指針),則會引發各種問題。
如果您反復執行此操作以向后瀏覽單鏈接列表,則實際上可以保證找到至少一個廢話。

簡而言之:不。

一個簡單的“不”。 太短了,可能因此使考官皺眉。

在標准C中是不可能的。

除了通過malloc給定的指針(或這些指針的副本等)訪問堆以外,您將獲得不確定的行為。

但是,如果您了解分配器並可以遍歷分配的內存塊的結構,則可以找到候選列表節點。 其中之一將是負責人。

暫無
暫無

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

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