通过网络将指针发送到静态功能是安全的吗?

Is it safe to send a pointer to a static function over the network?

本文关键字:功能 安全 静态 网络 指针      更新时间:2023-10-16

我正在考虑我必须在C 中实现的一些RPC代码,我想知道它是否安全(在哪个假设下)将其通过网络发送到同一二进制代码(假设完全相同,并且它们在同一体系结构上运行)。我猜虚拟内存应该在这里有所不同。

我只是出于好奇而问它,因为无论如何它都是不良设计,但是我想知道它是否是可能的(以及是否可以将其扩展到其他类型的指针到其他静态数据以外的静态数据程序可能包括)。

通常,由于许多原因,它并不安全,但是有限的情况下起作用。首先,我将假设您在协议中使用某种签名或加密,以确保数据流的完整性;如果没有,您已经有严重的安全问题已经通过传递功能指针而复杂的

如果完全相同的程序在连接的两端都运行,如果该函数在主程序中(或从静态库链接的代码中)而不是在共享库中,并且未构建程序是作为独立于位置的可执行文件(pie),那么函数指针在两端都相同,并且将其传递到网络上应该有效。请注意,这些条件非常严格,必须记录在使用您的程序的一部分,它们非常脆弱。例如,如果有人在一侧升级该软件,而忘记了同时升级连接另一侧的版本,那么事情将会可怕而危险。

我会完全避免使用这种低级RPC,完全支持高级命令结构或抽象的RPC框架,但是如果您真的想这样做,则稍微更安全的方法是传递功能名称并使用dlsym或等同于查找它们。如果这些符号位于主程序二进制而不是库中,则根据您的平台,您可能需要-rdynamic(GCC)或类似的选项使其可用于dlsymlibffi也可能是抽象的有用工具。

另外,如果要避免根据dlsymlibffi避免使用,则可以将自己的"符号表"保留在二进制中,将其用作static const线性表或哈希表映射符号名称以函数指针。用于此目的的ELF中使用的哈希表格式非常容易理解和实现,因此我可能会考虑将您的实现基于此。

是什么指向?

的指针

它是指向静态程序内存的指针吗?如果是这样,请不要忘记它是一个地址,而不是偏移,因此您首先需要相应地在两者之间进行转换。

第二,如果它不是静态内存(即:在构建时间而不是运行时间中创建的静态分配的数组),这是完全不可能的。

最后,您如何确保这两个代码相同?都是二进制的位相同(例如:diff -a binary1 binary2)。即使它们相同,根据每台机器上的虚拟内存管理,整个程序的程序内存段也可能不存在,或者在单个页面中可能不存在,或者在每个系统上跨多个页面的对齐方式可能有所不同。

无论您如何切片,这确实是一个坏主意。这是传递和API的内容。

我不知道任何形式的RPC,可以让您通过网络发送指针(至少不采取诸如首先施放到int之类的事情)。如果您确实在发送端转换为int,然后将其转换回远端指针,那么您将获得与将任何其他任意int转换为指针:未定义的行为几乎相同的,如果您尝试解释它。<<<<<<<<

通常,如果将指针传递给RPC函数,它将进行编组 - 即,它指向的数据将被打包,发送,存储在内存中,并指向该本地副本的指针数据传递给另一端的功能。这就是为什么/idl如何变得有些丑陋的一部分 - 您需要告诉它如何在/如果通过指针时/如果通过指针来弄清楚多少数据。大多数人知道零终止的字符串。对于其他类型的数组,通常需要指定数据的大小(某种程度上或其他)。

这是高度系统依赖的。在具有虚拟地址的系统上,每个过程都认为它在每次执行时都以相同的地址运行,这可能适用于可执行代码。Darren Kopp关于ASLR的评论和链接很有趣 - Wikipedia文章的快速阅读建议Linux&amp;Windows版本专注于数据而不是可执行的代码,除了Linux上的"网络面向守护程序",并且在Windows上仅在"专门链接到ASLR启用"时才适用。

仍然可以通过静态链接来保证"相同的二进制代码" - 如果加载了不同的共享对象/库,或者它们以不同的顺序加载(也许是由于动态加载 - dlopen-由配置文件中的不同顺序驱动

在网络上发送指针通常不安全。两个主要原因是:

  • 可靠性:由于程序或其库的不同位置或内存中动态分配的对象,数据/功能指针可能无法指向另一台机器上的相同实体(数据结构或功能)。可重新定位的代码 ASLR可以打破您的设计。至少,如果要指向静态分配的对象或应发送其偏移W.R.T.的函数。图像基础如果您的平台是Windows或对您的操作系统进行类似的操作。
  • 安全性:如果您的网络打开并且有黑客(或者它们已经闯入您的网络),他们可以冒充您的第一台机器并使第二台机器挂起或崩溃,从而导致拒绝服务,或执行任意代码,并访问敏感信息或对其进行篡改,或劫持机器,然后将其变成邪恶的机器人,以发送垃圾邮件或攻击其他计算机。当然,这里有措施和对策,但是...

如果我是你,我会设计一些不同的东西。而且,我将确保传输数据不重要或加密,并且接收零件在使用它之前对其进行必要的验证,因此没有缓冲区溢出或执行任意事物。

如果您正在寻找一些正式的保证,我将无法为您提供帮助。您必须查看所使用的编译器和操作系统的文档 - 但是,我怀疑您会找到必要的保证 - 除了可能有一些专门的嵌入式系统OS'。

我可以为您提供一种情况,即我99.99%确定它会毫无问题:

  • Windows
  • 32位进程
  • 功能位于没有重定位信息的模块中
  • 所讨论的模块已经加载&amp;在客户端初始化
  • 所讨论的模块在两侧的100%相同
  • 编译器不做非常疯狂的事情(例如,MSVC和GCC都应该没问题)

如果要在DLL中调用函数,则可能会遇到问题。根据上面的列表,模块(= dll)可能没有重新定位信息,这当然使其无法重新安置(这是我们需要的)。不幸的是,这也意味着如果"首选负载地址"使用其他东西,则加载DLL将失败。所以这是有风险的。

如果该函数位于EXE中,则可以。32位EXE不需要重定位信息,并且大多数不需要包含它(MSVC默认设置)。顺便说一句:ASLR不是这里的问题

以上大多数仅确保该函数在双方都具有相同的地址。唯一剩下的问题 - 至少我能想到的 - 是:通过我们通过网络收到的某些字节初始化的指针来调用一个函数,假设如果我们采用了所需功能的地址,那么字节模式与我们会得到的相同?这肯定是C 标准不能保证的,但是我期望当前的现实世界编译器中有任何现实世界中的问题。

说,我会不是建议这样做,除了安全性和鲁棒性并不重要的情况。