如何打印当前进程内存中实时的GLOBAL_OFFSET_TABLE(GOT)内容
如何获取当前进程实时的.got段内容(无需解析静态ELF)
核心思路
.got段在进程运行时会被映射到内存中,要拿到它的实时内容,关键是找到该段在当前进程内存中的起始地址和大小,然后直接读取对应内存区域即可,完全不需要解析静态ELF文件。
方法1:利用链接器导出的符号(C语言示例)
如果你的程序是C语言编写的,直接用链接器在编译时注入的边界符号就能定位.got段:
#include <stdio.h> // 链接器自动导出的符号,标记.got段的起始与结束位置 extern char __got_start[]; extern char __got_end[]; int main() { size_t got_size = (size_t)(__got_end - __got_start); printf("GOT段起始地址: %p\n", __got_start); printf("GOT段大小: %zu 字节\n", got_size); // 以十六进制格式打印GOT段的每个条目 printf("GOT段内容:\n"); for (size_t i = 0; i < got_size; i += sizeof(void*)) { void** entry = (void**)(__got_start + i); printf(" 偏移0x%zx: %p\n", i, *entry); } return 0; }
编译命令:gcc -o read_got read_got.c
这种方式最直接,符号是编译时就确定的,运行时直接调用就能拿到实时的内存地址,完全跳过静态ELF解析步骤。
方法2:解析/proc/self/maps获取映射信息
如果没法用链接器符号(比如处理动态加载的模块),可以读取/proc/self/maps文件——这个文件实时记录当前进程的所有内存映射,从中筛选出.got段的条目:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { FILE* maps_file = fopen("/proc/self/maps", "r"); if (!maps_file) { perror("打开/proc/self/maps失败"); return 1; } char line_buf[256]; void* got_start = NULL; size_t got_size = 0; // 遍历文件行,匹配包含[got]的条目 while (fgets(line_buf, sizeof(line_buf), maps_file)) { if (strstr(line_buf, "[got]")) { // 解析地址范围:格式为"起始地址-结束地址 权限 ..." char* dash_pos = strchr(line_buf, '-'); if (!dash_pos) continue; *dash_pos = '\0'; got_start = (void*)strtoull(line_buf, NULL, 16); char* end_addr_str = dash_pos + 1; char* space_pos = strchr(end_addr_str, ' '); if (!space_pos) continue; *space_pos = '\0'; void* got_end = (void*)strtoull(end_addr_str, NULL, 16); got_size = (size_t)(got_end - got_start); break; } } fclose(maps_file); if (!got_start) { fprintf(stderr, "未找到GOT段映射\n"); return 1; } printf("GOT段起始地址: %p\n", got_start); printf("GOT段大小: %zu 字节\n", got_size); // 读取并打印GOT段内容 printf("GOT段内容:\n"); for (size_t i = 0; i < got_size; i += sizeof(void*)) { void** entry = (void**)((char*)got_start + i); printf(" 偏移0x%zx: %p\n", i, *entry); } return 0; }
编译命令:gcc -o read_got_proc read_got_proc.c && ./read_got_proc
/proc/self/maps是实时更新的,能拿到当前进程.got段的最新映射状态,适合动态场景。
注意事项
- 部分环境中.got段可能被命名为
.got.plt,需要根据实际情况调整筛选关键词。 - .got段默认是可读权限,直接读取内存不会有问题;如果是修改操作则需要额外处理内存权限。
- 32位程序指针大小为4字节,循环时要替换
sizeof(void*)为4;64位程序保持原样即可。
内容的提问来源于stack exchange,提问作者Fee
相关产品推荐
相关产品推荐

