[英]Is it possible to recover head pointer of a single linked list?
如果可以做出以下假設,則可以恢復鏈表的頭節點。
為了更好地說明它,請使用以下程序,該程序可以在默認配置下至少通過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)
背景
我的一個朋友在采訪中被問到以下問題。 “如果給出了最后一個節點,您能找到單個鏈表的標題嗎?”。 他回答“否”,即使面試官對回答不完全滿意,也得到了工作。 這讓我開始思考,是否有可能這樣做。
因此,真正的問題是。
正確的答案應該是:
如果設計不干凈,那么嘗試這樣做會是一個危險的解決方法。
如果您確實確實嘗試搜索內存中任何內容的地址(以任何方式都不能保證僅包含該地址(如果它是有問題的指針),那么您可能會發現任何似乎意外包含的內存一個看起來像該地址的數字。
如果您隨后繼續使用它(假設它是指針),則會引發各種問題。
如果您反復執行此操作以向后瀏覽單鏈接列表,則實際上可以保證找到至少一個廢話。
簡而言之:不。
一個簡單的“不”。 太短了,可能因此使考官皺眉。
在標准C中是不可能的。
除了通過malloc給定的指針(或這些指針的副本等)訪問堆以外,您將獲得不確定的行為。
但是,如果您了解分配器並可以遍歷分配的內存塊的結構,則可以找到候選列表節點。 其中之一將是負責人。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.