通过PID读取其他进程内存异常:仅获取零值或换行符
无法读取进程内存内容的问题
我写了两段C代码,尝试通过PID读取其他进程的内存:
第一段代码(allocate_memory.c)
这段代码会在内存中存储一个字符串并保持运行:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char* ptr = "Hello from allocate_memory!"; printf("Allocated memory address: %p\n", (void*)ptr); printf("String written to memory: %s\n", ptr); // Keep the program running to examine memory while(1); return 0; }
第二段代码(read_process_memory.c)
这段代码通过fork创建子进程,尝试读取子进程的内存:
#include <stdio.h> #include <sys/ptrace.h> #include <sys/wait.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <string.h> #include <fcntl.h> #include <stdlib.h> #include <signal.h> #define MEM_DUMP_RANGE 0x10000 int main() { pid_t pid = fork(); if (pid == 0) { // Child process printf("Child process ID: %d\n", getpid()); execl("./allocate_memory", "allocate_memory", NULL); } else { // Parent process printf("Parent process ID: %d\n", getpid()); printf("Child process ID: %d\n", pid); // wait(NULL); // Open file for reading char filename[20]; sprintf(filename, "/proc/%d/mem", pid); int fd = open(filename, O_RDONLY); if (fd == -1) { perror("open"); return 1; } // Read memory unsigned char* buffer = malloc(MEM_DUMP_RANGE); read(fd, buffer, MEM_DUMP_RANGE); // Dump memory to file FILE* file = fopen("memory_dump_file_proc.txt", "w"); if (file == NULL) { perror("fopen"); return 1; } for (unsigned long addr = 0; addr < MEM_DUMP_RANGE; addr++) { fprintf(file, "%02x ", buffer[addr]); if ((addr + 1) % 16 == 0) fprintf(file, "\n"); } fclose(file); // Search for string char* search_str = "Hello from allocate_memory!"; for (unsigned long addr = 0; addr < MEM_DUMP_RANGE; addr++) { if (memcmp(&buffer[addr], search_str, strlen(search_str)) == 0) { printf("Found string at address: %lx\n", addr); // Kill child process kill(pid, SIGKILL); printf("Child process killed\n"); return 0; } } close(fd); free(buffer); } return 0; }
问题现象
运行后,内存转储文件里全是零值或无效内容,无法找到目标字符串:
0x00CED010 30 36 35 32 39 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 065290.............................. 0x00CED034 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ....................................
问题原因
- 未等待子进程初始化完成:父进程fork后立刻读取内存,此时子进程可能还没完成
execl加载,目标字符串还没进入内存。 - 读取了错误的内存区域:
/proc/PID/mem对应进程的整个虚拟地址空间,直接从地址0开始读的是低地址(通常是未映射或无效区域),而目标字符串存储在代码段的高虚拟地址中。 - 缺少进程访问权限:读取
/proc/PID/mem需要目标进程处于停止状态(比如被ptrace附加),否则无法正确读取有效内存页。
解决方法
方法1:修正/proc/PID/mem读取逻辑
- 等待子进程启动完成并输出地址
- 捕获子进程输出的虚拟地址,定位到该地址后读取
- 确保进程处于可读取状态(可通过ptrace附加暂停进程)
方法2:使用ptrace读取指定地址内存(更可靠)
ptrace是Linux下进程跟踪的标准接口,能安全读取目标进程的指定虚拟地址内容。
修改后的代码(使用ptrace实现)
第一步:编译allocate_memory.c
gcc allocate_memory.c -o allocate_memory
第二步:修改后的读取代码(read_process_memory_ptrace.c)
#include <stdio.h> #include <sys/ptrace.h> #include <sys/wait.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <string.h> #include <stdlib.h> #include <signal.h> int main() { pid_t pid = fork(); if (pid == 0) { // 子进程:允许父进程ptrace附加 if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) { perror("ptrace TRACEME"); exit(1); } printf("Child process ID: %d\n", getpid()); execl("./allocate_memory", "allocate_memory", NULL); exit(1); // execl失败则退出 } else { // 父进程:等待子进程暂停 wait(NULL); printf("Parent process ID: %d\n", getpid()); printf("Child process ID: %d\n", pid); // 这里需要手动输入子进程输出的字符串地址,或者通过管道捕获输出 // 示例:假设子进程输出的地址是0x555555554000(实际运行时替换为真实地址) unsigned long target_addr = 0x555555554000; char* search_str = "Hello from allocate_memory!"; size_t str_len = strlen(search_str); char buffer[256] = {0}; // 逐字节读取目标地址的内存 for (size_t i = 0; i < str_len; i++) { errno = 0; long data = ptrace(PTRACE_PEEKDATA, pid, target_addr + i, NULL); if (data == -1 && errno != 0) { perror("ptrace PEEKDATA"); kill(pid, SIGKILL); return 1; } buffer[i] = (char)data; } // 验证读取结果 if (strcmp(buffer, search_str) == 0) { printf("Successfully read string: %s\n", buffer); printf("String located at virtual address: %lx\n", target_addr); } else { printf("Failed to find the target string\n"); } // 恢复并终止子进程 ptrace(PTRACE_CONT, pid, NULL, NULL); kill(pid, SIGKILL); printf("Child process killed\n"); } return 0; }
运行说明
- 先运行
allocate_memory,记录它输出的内存地址,比如:
Allocated memory address: 0x555555554000 String written to memory: Hello from allocate_memory!
- 修改读取代码中的
target_addr为上面的地址,编译并运行:
gcc read_process_memory_ptrace.c -o read_process_memory_ptrace ./read_process_memory_ptrace
另一种自动捕获地址的改进方案
如果不想手动输入地址,可以通过管道捕获子进程的输出,提取地址:
#include <stdio.h> #include <sys/ptrace.h> #include <sys/wait.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <string.h> #include <stdlib.h> #include <signal.h> #include <ctype.h> // 从字符串中提取十六进制地址 unsigned long extract_address(const char* output) { const char* addr_str = strstr(output, "0x"); if (!addr_str) return 0; return strtoul(addr_str, NULL, 16); } int main() { int pipefd[2]; if (pipe(pipefd) == -1) { perror("pipe"); return 1; } pid_t pid = fork(); if (pid == 0) { // 子进程:重定向stdout到管道 close(pipefd[0]); dup2(pipefd[1], STDOUT_FILENO); close(pipefd[1]); if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) { perror("ptrace TRACEME"); exit(1); } execl("./allocate_memory", "allocate_memory", NULL); exit(1); } else { // 父进程:读取管道中的子进程输出 close(pipefd[1]); char output[256] = {0}; read(pipefd[0], output, sizeof(output)-1); close(pipefd[0]); wait(NULL); printf("Parent process ID: %d\n", getpid()); printf("Child process ID: %d\n", pid); unsigned long target_addr = extract_address(output); if (target_addr == 0) { printf("Failed to extract memory address\n"); kill(pid, SIGKILL); return 1; } printf("Extracted target address: %lx\n", target_addr); char* search_str = "Hello from allocate_memory!"; size_t str_len = strlen(search_str); char buffer[256] = {0}; for (size_t i = 0; i < str_len; i++) { errno = 0; long data = ptrace(PTRACE_PEEKDATA, pid, target_addr + i, NULL); if (data == -1 && errno != 0) { perror("ptrace PEEKDATA"); kill(pid, SIGKILL); return 1; } buffer[i] = (char)data; } if (strcmp(buffer, search_str) == 0) { printf("Successfully read string: %s\n", buffer); } else { printf("Failed to find the target string\n"); } ptrace(PTRACE_CONT, pid, NULL, NULL); kill(pid, SIGKILL); printf("Child process killed\n"); } return 0; }
内容的提问来源于stack exchange,提问作者rajpoot mhm
相关产品推荐
相关产品推荐

