【发布时间】:2014-10-01 21:41:27
【问题描述】:
上下文
我一直在为我的期末作业编写一个程序,但我发现了以下奇怪的行为。
我编写了一个跟踪器,以便能够从子进程读取/写入内存。我的目的是在给定点读取当前执行的指令,然后将其反汇编以获得有关内存操作数等的一些信息。
出于测试目的,使用了一个用 C 编写的简单 HelloWorld。
信息
我写的追踪器的代码是这样的:
size_t tracer::readMem(ADDR_t offset, char *buff, size_t len) {
REQUIRE(_state != TRCS_UNINITIALISED);
if (_memsdescr < 0 || fcntl(_memsdescr, F_GETFL) < 0) {
_memsdescr = open(("/proc/" + to_string(_child_pid_t) + "/mem").c_str(), O_LARGEFILE);
if (_memsdescr < 0) {
logmanager::getInstance ().emplaceBasicLogger ("tracer")
.log ( SLVL_ERROR, "Process\' memory could not be "
" opened. \n");
PANIC;
} else {
logmanager::getInstance ().emplaceBasicLogger ("tracer")
.logfmt ( SLVL_DEBUG, "Opened process' memory. %lx bytes long\n",
lseek(_memsdescr, 0, SEEK_END));
}
}
ASSERT(offset <= lseek(_memsdescr, 0, SEEK_END));
int ret = pread(_memsdescr, buff, len, offset);
if (ret < 0) {
logmanager::getInstance ().emplaceBasicLogger ("tracer")
.logfmt( SLVL_ERROR, "Error reading from memory descriptor: %s\n", sys_errlist[errno]);
}
return ret;
}
控制执行的代码如下。基本上它所做的只是从 /proc/mem 读取 15 字节的块。这些块的地址是通过在调用 ptrace(PTRACE_SINGLESTEP) 后获取 RIP(指令指针)的值来获得的。这意味着我尝试读取的所有内存都应该映射到进程的内存空间中。
trc.load (filename);
trc.launchProgram();
cout << " Started with pid " << trc.getChildPid() << endl << endl;
//memspacy::memory_map::printSystemProcVmap(trc.getChildPid());
//inj.prop_setTraceSyscalls (true);
while (trc.prop_getState () != memspacy::TRCS_STOPPED) {
//if (trc.isSyscall()){
// trc.showSyscall();
//}
//HERE IS WHERE THE DISASSEMBLY takes place
if (trc.readMem(trc.peekReg(a_RIP), inst_buff, MAX_ARCH_INST_LEN)
&& dec.disassemble()) {
dec.printFormatted();
}
trc.singleStep();
}
问题
HelloWorld 应该由数千条指令组成,但我得到的输出是这样的。
mov %rsp, %rdi
add %al, (%rax)
push %rdi
push %rsi
push %rsp
mov %edi, %ebx
in %dx, %al
xor %ecx, -0x3f(%rax)
invalid
有用的事实
似乎在几条指令之后,读取功能完全停止获取任何数据。 没有报错,唯一的问题是读取内存返回0字节。这意味着根据 read() 联机帮助页中的信息到达 EOF,但 lseek() 返回的大小为 0xFFFFFFFFFFFF,因此这方面应该没有问题。 Aso,所有读取都在映射区域内,因为我使用程序计数器作为偏移量。
我真的想不出除了页面权限之外的任何东西,但是他们都设置了读取权限,否则它甚至不会执行。 过程已正确 Ptraced,执行运行良好,具有预期的行为,甚至寄存器与控制测试中的完全相同(用于检查原始行为的测试)。
我目前的猜测是,在某些时候它会到达映射区域的末尾,这会使描述符变得无用,因此最后的“无效”指令,但即使在每次读取时打开文件,结果也不会改变。
数据
这是最后一次有效读取的内存映射和读取偏移量。
00400000-00401000 r-xp 00000000 08:06 7602542 /home/amontes/workspace/memspacy_build/assets/test/test
00600000-00602000 rw-p 00000000 08:06 7602542 /home/amontes/workspace/memspacy_build/assets/test/test
**7fe3eb602000-7fe3eb625000 r-xp 00000000 08:11 657171 /lib/x86_64-linux-gnu/ld-2.19.so**
7fe3eb824000-7fe3eb826000 rw-p 00022000 08:11 657171 /lib/x86_64-linux-gnu/ld-2.19.so
7fe3eb826000-7fe3eb827000 rw-p 00000000 00:00 0
7fff57783000-7fff577a4000 rw-p 00000000 00:00 0 [stack]
7fff577fe000-7fff57800000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
最后一个有效偏移量 7fe3eb606a7c -> 这显示了无效指令
第一个无效偏移量 7fe3eb606a7d -> 这将返回 EOF
任何帮助或任何想法将不胜感激。谢谢。
【问题讨论】:
-
运行
ls -l /proc/*/mem。查看文件大小。阅读this。 -
对不起,但这似乎不是问题。文件大小只是一个侧面参数;在动态链接和进程创建之前,必须将 RIP 指向的地址作为程序映像的一部分映射到进程的内存中。大小只是为了指出永远不会发生 EOF。考虑到 ELF 格式和映射,即使阅读 .code 部分的末尾也应该是可能的。该链接与此问题不完全相关,也没有提供解决方案。还是谢谢你:)
-
对不起,我不明白这个问题。
mem不是一个常规文件,也没有义务表现得像一个文件。您正试图从头开始按顺序阅读。它行不通。您必须按照设计使用的方式使用它,即链接中概述的方式。如果您认为此处概述的方法无法让您做您想做的事,请提出一个问题来描述您使用此方法遇到的问题。 -
我添加了一些信息并重新构建了问题,以便稍微澄清这一点。顺便说一句,我认为问题中没有提到顺序读取。相反,有粗体文本解释我已经使用 ptrace,我没有收到任何错误,我设法提取一些指令,直到问题出现,并且代码显示使用 rax 作为读取的偏移量。
-
好的,我现在看到了问题。 “它到达映射区域的末尾,使描述符无用”。你能打印出有问题的偏移量并在 /proc/pid/maps 中查找吗?你不应该在这里得到 EOF,如果你读到超出范围的末尾,你应该将 errno 设置为 EIO。