繁体   English   中英

是否可以检查取消引用任意内存是否会使C中的程序先验崩溃?

[英]Is it possible to check if dereferencing arbitrary memory will crash the program apriori in C?

我想编写一个交互式的解释性C外壳程序,使您可以寻址任意内存并在这些内存地址上执行命令。

例如(正在运行程序外壳):

prompt> 10 bytes starting 0x400000 

该指令将尝试访问地址0x400000并显示从此处开始的10个字节。 例如范围[0x400000,0x400009]。

并会产生如下输出:

{0x00, 0x01, 0x02, 0x03, 0x04, <bad>, <bad>, 0x07, 0x08, 0x09, 0x0a}

“不良”表示将尝试解决“非法”记忆。

我想知道C中是否有一种标准方法来检查程序是否被允许访问我尝试访问的内存,或者访问此内存是否会导致程序崩溃(在实际崩溃之前),并报告信息向用户表明该程序不允许访问该内存。

我之所以这样问是因为,关于该主题的大多数问题都倾向于通过“您不能肯定地检查指针是否有效”来回答,但是我敢肯定,必须有某种方法可以检查指针是否至少“绝对无效且将会崩溃”或“可能无效,但不会崩溃”,很遗憾,我找不到该问题的答案。

提前谢谢。

我不认为仅使用标准C就能做到这一点。

但是,您可以使用特定于平台的恶作剧技巧来了解内存映射的外观。 在Linux上,文件/proc/(pid)/maps将列出进程pid的内存映射,包括读/写权限状态。 这是在我的机器上寻找简单的cat进程的样子:

00400000-0040c000 r-xp 00000000 00:13 1237228                            /usr/bin/cat
0060b000-0060c000 r--p 0000b000 00:13 1237228                            /usr/bin/cat
0060c000-0060d000 rw-p 0000c000 00:13 1237228                            /usr/bin/cat
01864000-01885000 rw-p 00000000 00:00 0                                  [heap]
7fe7a5e0b000-7fe7a6121000 r--p 00000000 00:13 1487092                    /usr/lib/locale/locale-archive
7fe7a6123000-7fe7a62ba000 r-xp 00000000 00:13 1486770                    /usr/lib/libc-2.23.so
7fe7a62ba000-7fe7a64ba000 ---p 00197000 00:13 1486770                    /usr/lib/libc-2.23.so
7fe7a64ba000-7fe7a64be000 r--p 00197000 00:13 1486770                    /usr/lib/libc-2.23.so
7fe7a64be000-7fe7a64c0000 rw-p 0019b000 00:13 1486770                    /usr/lib/libc-2.23.so
7fe7a64c0000-7fe7a64c4000 rw-p 00000000 00:00 0 
7fe7a64cb000-7fe7a64ee000 r-xp 00000000 00:13 1486769                    /usr/lib/ld-2.23.so
7fe7a66cc000-7fe7a66ee000 rw-p 00000000 00:00 0 
7fe7a66ee000-7fe7a66ef000 r--p 00023000 00:13 1486769                    /usr/lib/ld-2.23.so
7fe7a66ef000-7fe7a66f0000 rw-p 00024000 00:13 1486769                    /usr/lib/ld-2.23.so
7fe7a66f0000-7fe7a66f1000 rw-p 00000000 00:00 0 
7fe7a66f5000-7fe7a66f8000 rw-p 00000000 00:00 0 
7ffe398e8000-7ffe39909000 rw-p 00000000 00:00 0                          [stack]
7ffe3999b000-7ffe3999e000 r--p 00000000 00:00 0                          [vvar]
7ffe3999e000-7ffe399a0000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

因此,您可以从中看到程序映像本身映射在虚拟内存的开头附近,堆稍高一些,堆栈映射到7ffe398e8000-7ffe39909000 ,并且C库和动态链接器也已加载到内存中。

请注意,每个文件都映射了几次。 例如,/ usr / bin / cat具有只读,可执行和读写段。 这是为了防止进程写入const内存和执行数据。

从映射表中,您可以很清楚地了解内存的布局方式以及在内存的这些部分上可以执行的操作。

这是一个好主意吗? 不行

除非您正在编写调试器或类似的开发工具,否则极有可能不会。


顺便说一句,您正在考虑编写的“外壳”听起来很像调试器。 gdb之类的调试器可以完成您所谈论的事情,包括评估C表达式和检查内存。


顺便说一句,因为我觉得这很有趣,所以这里是一个小练习:

如您所见,在ffffffffff600000映射了一些内核内存。 如果这个理论是正确的,即使我们一般无法访问内核的内存,我们也应该能够读取该内存。 我们试试吧:

int main(void)
{
  unsigned long *p = 0xffffffffff600000;

  for (;;)
    printf("0x%lx, ", *p++);
}

我们得到

0xf00000060c0c748, 0xccccccccccccc305, 0xcccccccccccccccc, ... Segmentation fault

如果您想知道为什么用户空间进程可以读取此内存,那么它将加速某些系统调用,例如gettimeofday并允许它们工作而不必像其他系统调用那样切换到内核模式。 参见例如这个问题

我想知道C中是否有一种标准方法来检查程序是否被允许访问我尝试访问的内存,或者访问此内存是否会导致程序崩溃(在实际崩溃之前),并报告信息向用户表明该程序不允许访问该内存。

不,没有。

该标准仅表示您不能取消引用空指针。 除此之外,有效的指针值范围取决于平台。 您希望实现的功能无法在独立于平台的代码中实现。

从C99标准的脚注87:

一元*运算符用于解引用指针的无效值包括空指针,针对所指向对象的类型不适当地对齐的地址以及对象生命周期结束后的地址

标准方式? 不会。您遇到的崩溃是未定义行为的结果。 该标准未涉及此类细节。

Windows API提供了IsBadReadPtr函数,这似乎正是您所追求的。 文档非常明确,您不应使用它。

您忽略的是某些无效的访问,您无法从中恢复。 如果您触摸保护页面,但在没有给予保护页面访问处理程序运行机会的情况下捕获错误,则您错过了机会。 下次您访问相同的地址时,会遇到访问冲突和核心转储。 尽管按正常执行,这会很好。 参见Raymond Chen的IsBadXxxPtr应该确实称为CrashProgramRandomly

在Unix上,可以通过将指针传递给write(2)来让内核为您完成肮脏的工作。 如果返回EFAULT ,则意味着您将使进程崩溃。

请注意,虽然看起来像先验先验,但实际上是在检查后果。 预先检查是不可靠的(在检查和实际访问之间,映射可能会更改)。

如果要在失败后得到通知,请为UNIX上的SIGSEGV编写信号处理程序。 在Windows上,请处理EXCEPTION_ACCESS_VIOLATION SEH异常。


附录:您想要做的听起来有点像mmbbq所做的。 它将lua解释器注入到外部应用程序中,并允许调用和取消引用地址。 如果您不予理会,则只会影响重新启动的线程,并且程序本身会继续工作(至少一段时间)。 该网站不再在线,但是也许您成功找到了镜子。

如前所述,我们离“标准C”还差得很远。

但是,您可以(通过某种方式)通过处理分段错误来完成此任务。 当然有一个库: GNU libsigsegv

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM