扫描调用进程的内存
scan memory of calling process
我必须在 C 中扫描调用进程的内存 space。这是作业。我的问题是我不完全理解虚拟内存寻址。
我正在通过尝试读取和写入内存地址来扫描内存 space。我不能使用 proc 文件或任何其他方法。
所以我的问题是设置指针。
根据我的理解,"User Mode Space" 从地址 0x0 开始,但是,如果我将函数的起点设置为 0x0,那么我是否没有扫描地址 space 以获取当前进程?您建议如何调整指针(如果有的话)以寻址父进程地址 space?
编辑:好的,对于造成的混乱,我深表歉意,感谢您的帮助。我们不能使用 proc 文件系统,因为赋值是为了让我们了解信号。
所以,基本上我将尝试读取然后写入每个内存页中的地址,以测试它是 R
、RW
还是不可访问。为了查看我是否成功,我将倾听某些信号——我还不确定如何处理那部分。我将创建一个结构链表来表示内存的可访问性。该程序将被编译为 32 位程序。
关于父进程和子进程:确切的文本状态
When called, the function will scan the entire memory area of the calling process...
也许我对子和父交互的理解有误,因为我们已经在 class 中介绍了这个(fork 函数等),所以我假设我的函数将扫描父过程。我要请教授澄清一下。
因此,从这张图片来看,我只是从 0x0 开始。
这比第一眼看上去要难一些。在 Linux 中,每个进程都有自己的内存 space。使用任意内存地址仅指向此进程的内存space。然而,有一些机制允许一个进程访问另一个进程的内存区域。某些 Linux 函数允许此 共享内存 功能。例如看看
this link 给出了一些在 Linux 下使用 shmget
、shmctl
和其他系统调用使用共享内存的示例。您也可以搜索 mmap
系统调用,它用于将文件映射到进程的内存,但也可用于访问另一个进程的内存。
您可以(从您的程序中)读取 /proc/self/maps
文件。首先在终端中尝试以下两个命令
cat /proc/self/maps
cat /proc/$$/maps
(至少要明白地址是什么space)
然后阅读proc(5), mmap(2) and of course wikipages about processes, address space, virtual memory, MMU, shared memory, VDSO。
如果要在两个进程之间共享内存,请先阅读shm_overview(7)
如果你不能使用/proc/
(很遗憾)考虑mincore(2)
您也可以不可移植 尝试从某些地址读取(并可能使用 volatile int*
重写相同的值)并捕获 SIGSEGV
信号(在信号处理程序中使用 sigsetjmp(3)),并在 -dichotomical- 循环(4K 字节的倍数)中执行 - 从一些合理的开始和结束地址(当然不是从 0,但可能从 (void*)0x10000
最多 (void*)0xffffffffff600000
)
参见 signal(7)。
您还可以使用特定于 Linux (Gnu libc) 的 dladdr(3). Look also into ptrace(2)(应该经常在某些 other 进程中使用)。
此外,您可以研究elf(5) and read your own executable ELF 文件。规范地它是 /proc/self/exe
(一个符号链接)但是你应该能够从你的 main
的 argv[0]
中得到它(也许按照你的程序应该以它的完整路径名开始的约定).
注意 ASLR 并在老师允许的情况下禁用它。
PS。我不知道你的老师对你有什么期望。
从用户态进程的角度来看,它的地址 space 从地址 0x0
开始,但并非 space 中的每个地址都有效或可访问的过程。特别是,地址 0x0
本身永远不是有效地址。如果进程试图访问实际未分配给该进程的内存(在其地址 space 中),则会产生分段。
您实际上可以使用分段错误行为来帮助您确定地址 space 的哪些部分实际上分配给了进程。为 SIGSEGV
安装信号处理程序,并跳过整个 space,尝试从每个页面的某处读取内容。每次捕获 SIGSEGV 时,您就知道该页面未映射到您的进程。之后返回并扫描每个可访问的页面。
但是,请只阅读。不要尝试写入随机内存,因为程序可访问的大部分内存是程序本身及其使用的共享库的二进制代码。您不仅不想让程序崩溃,而且大部分内存可能都被标记为进程只读。
编辑:一般来说,一个进程只能访问它自己的(虚拟)地址space。但是,正如@cmaster 所观察到的,有一个系统调用(ptrace()
)允许某些进程在观察到的进程地址space 的上下文中访问其他一些进程的内存。这就是通用调试器通常的工作方式。
我必须在 C 中扫描调用进程的内存 space。这是作业。我的问题是我不完全理解虚拟内存寻址。
我正在通过尝试读取和写入内存地址来扫描内存 space。我不能使用 proc 文件或任何其他方法。
所以我的问题是设置指针。
根据我的理解,"User Mode Space" 从地址 0x0 开始,但是,如果我将函数的起点设置为 0x0,那么我是否没有扫描地址 space 以获取当前进程?您建议如何调整指针(如果有的话)以寻址父进程地址 space?
编辑:好的,对于造成的混乱,我深表歉意,感谢您的帮助。我们不能使用 proc 文件系统,因为赋值是为了让我们了解信号。
所以,基本上我将尝试读取然后写入每个内存页中的地址,以测试它是 R
、RW
还是不可访问。为了查看我是否成功,我将倾听某些信号——我还不确定如何处理那部分。我将创建一个结构链表来表示内存的可访问性。该程序将被编译为 32 位程序。
关于父进程和子进程:确切的文本状态
When called, the function will scan the entire memory area of the calling process...
也许我对子和父交互的理解有误,因为我们已经在 class 中介绍了这个(fork 函数等),所以我假设我的函数将扫描父过程。我要请教授澄清一下。
因此,从这张图片来看,我只是从 0x0 开始。
这比第一眼看上去要难一些。在 Linux 中,每个进程都有自己的内存 space。使用任意内存地址仅指向此进程的内存space。然而,有一些机制允许一个进程访问另一个进程的内存区域。某些 Linux 函数允许此 共享内存 功能。例如看看
this link 给出了一些在 Linux 下使用 shmget
、shmctl
和其他系统调用使用共享内存的示例。您也可以搜索 mmap
系统调用,它用于将文件映射到进程的内存,但也可用于访问另一个进程的内存。
您可以(从您的程序中)读取 /proc/self/maps
文件。首先在终端中尝试以下两个命令
cat /proc/self/maps
cat /proc/$$/maps
(至少要明白地址是什么space)
然后阅读proc(5), mmap(2) and of course wikipages about processes, address space, virtual memory, MMU, shared memory, VDSO。
如果要在两个进程之间共享内存,请先阅读shm_overview(7)
如果你不能使用/proc/
(很遗憾)考虑mincore(2)
您也可以不可移植 尝试从某些地址读取(并可能使用 volatile int*
重写相同的值)并捕获 SIGSEGV
信号(在信号处理程序中使用 sigsetjmp(3)),并在 -dichotomical- 循环(4K 字节的倍数)中执行 - 从一些合理的开始和结束地址(当然不是从 0,但可能从 (void*)0x10000
最多 (void*)0xffffffffff600000
)
参见 signal(7)。
您还可以使用特定于 Linux (Gnu libc) 的 dladdr(3). Look also into ptrace(2)(应该经常在某些 other 进程中使用)。
此外,您可以研究elf(5) and read your own executable ELF 文件。规范地它是 /proc/self/exe
(一个符号链接)但是你应该能够从你的 main
的 argv[0]
中得到它(也许按照你的程序应该以它的完整路径名开始的约定).
注意 ASLR 并在老师允许的情况下禁用它。
PS。我不知道你的老师对你有什么期望。
从用户态进程的角度来看,它的地址 space 从地址 0x0
开始,但并非 space 中的每个地址都有效或可访问的过程。特别是,地址 0x0
本身永远不是有效地址。如果进程试图访问实际未分配给该进程的内存(在其地址 space 中),则会产生分段。
您实际上可以使用分段错误行为来帮助您确定地址 space 的哪些部分实际上分配给了进程。为 SIGSEGV
安装信号处理程序,并跳过整个 space,尝试从每个页面的某处读取内容。每次捕获 SIGSEGV 时,您就知道该页面未映射到您的进程。之后返回并扫描每个可访问的页面。
但是,请只阅读。不要尝试写入随机内存,因为程序可访问的大部分内存是程序本身及其使用的共享库的二进制代码。您不仅不想让程序崩溃,而且大部分内存可能都被标记为进程只读。
编辑:一般来说,一个进程只能访问它自己的(虚拟)地址space。但是,正如@cmaster 所观察到的,有一个系统调用(ptrace()
)允许某些进程在观察到的进程地址space 的上下文中访问其他一些进程的内存。这就是通用调试器通常的工作方式。