C++ 转换为基数和 "overwriting"vptr 问题

标签 c++ windows visual-c++

我刚刚读到了一个新的 C++ 挑战: http://blogs.msdn.com/b/vcblog/archive/2014/02/04/challenge-vulnerable-code.aspx

所提供的代码充满了问题,有些对于具有良好编程习惯的人来说是显而易见的,有些则仅对 C++ 本地人可见:-)

评论中描述了一个特定的行(37)特别危险:

ImageFactory::DebugPrintDimensions((ImageFactory::CustomImage*)image);

然后,该函数调用 CustomImage 的虚拟方法(在 CustomImage 中首次定义)。

据称,这会导致 CustomImage 的第一个成员被视为实例的 vptr(实际上是一个 unique_ptr),并使其指向的二进制文件被视为可执行文件(可能是恶意的)代码..

虽然我可以理解这一点,但我想知道为什么这真的有效。

CustomImage 是一个虚拟类,因此(可能)它的前 4 个字节(假设 X86)是 vptr,接下来是 unique_ptr 成员。 Actor 阵容似乎没有改变任何东西......

...如何执行unique_ptr保存的数据?

最佳答案

我的看法(我非常乐意得到纠正):

在这里,CustomImage是一个多态类(vptr 作为 Windows ABI 下的第一个“成员”),但是 Image不是。定义的顺序意味着ImageFactory函数知道CustomImage Image ,但是main()没有。

所以当工厂这样做时:

Image* ImageFactory::LoadFromDisk(const char* imageName)
{
    return new (std::nothrow) CustomImage(imageName);
}

CustomImage*指针转换为Image*一切都很好。因为Image不是多态的,指针调整为指向(POD)Image CustomImage 内的实例-- 但重要的是,这是在 vptr 之后,因为它总是在 MS ABI 的多态类中排在第一位(我认为)。

但是,当我们到达

ImageFactory::DebugPrintDimensions((ImageFactory::CustomImage*)image);

编译器看到从一个它一无所知的类到另一个类的 C 风格转换。它所做的就是获取它所拥有的地址并假装它是 CustomImage*反而。这个地址实际上指向Image在自定义图像内;但因为Image为空,并且可能空基类优化已生效,它最终指向 CustomImage 内的第一个成员,即 unique_ptr .

现在,ImageFactory::DebugPrintDimensions()假设它已被传递给一个指向完全完整的 CustomImage 的指针,这样该地址就等于 vptr 的地址。但它还没有——它被交给了 unique_ptr 的地址。 ,因为在调用 is 时,编译器不知道任何更好的信息。所以现在它取消引用它认为的 vptr(实际上是我们控制的数据),查找虚拟函数的偏移量并盲目执行它 - 现在我们遇到了麻烦。

有一些事情可以帮助缓解这种情况。首先,由于我们通过基类指针操作派生类,Image应该有一个虚拟析构函数。这将使得 Image多态,并且很可能我们不会遇到问题(而且我们也不会泄漏内存)。

其次,因为我们是从基类到派生类进行转换,dynamic_cast应该使用而不是 C 风格的转换,这将涉及运行时检查和正确的指针调整。

最后,如果编译器在编译时掌握了所有信息 main() ,它可能能够警告我们(或正确执行转换,调整 CustomImage 的多态性)。因此移动上面的类定义 main()也推荐。

关于C++ 转换为基数和 "overwriting"vptr 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21595943/

相关文章:

mysql - Rails - 安装 Gem 时出错

mysql - 构建用于接收 SMS 的 SMS 服务器

c++ - 居民纹理?

c++ - 如何处理 .dump/.dump 文件?

c++ - 如何在 vc++ 中以编程方式检查互联网速度/带宽

c++ - 有没有办法在不重建任何项目的情况下重新链接解决方案?

c++ - 在运行时删除持有 pthread 的变量

c++ - 强制强类型枚举参与模板重载决策

c++ - std::exception_ptr 线程安全吗?

c++ - 从另一个应用程序启动一个 C++ 应用程序,并与之通信