中毒某些API函数(不是维护者)

时间:2017-01-25 17:39:02

标签: c++

例如: How to poison an identifier in VC++?
What does it mean to "poison a function" in C++?

有没有办法正确地#34;毒化一些声明(或实现)不受我控制的函数?

具体来说,我试图阻止某些Windows API函数的使用,这可能会导致意外结果,例如CreateFileA(使用T宏隐藏了混合ANSI和Unicode的事实)或SetWindowLong(这会导致程序无声地失败,没有错误,也没有机会知道出了什么问题在64位系统上)。

我对中毒的定义"正确"是试图调用该函数以可读错误打破构建(如果错误发生在源代码中的正确位置,则奖励积分)。
优选地,这应该是可移植的,并且至少它必须与GCC和clang一起工作。

到目前为止我尝试/看过的内容:

预处理

最简单,最明显的解决方案是利用预处理器:

#define CreateFileA __poison_ANSI_CreateFileA

这对于一个特定功能以及其他几个功能非常有效,完全按照预期工作,具有提示问题的精确错误并指向源中的正确位置:

error: no matching function for call to '__poison_ANSI_CreateFileA'
note: expanded from macro 'CreateFile'
note: expanded from ... (precise location in source)

对于不同的功能,每个人都需要有一个唯一的名称,以避免冲突的定义错误,这很繁琐但很容易完成。到目前为止一切都很好,除了适用于MinGW-w64标头本身使用可变参数宏篡改名称的函数,例如CreateWindowSetWindowLong。无论如何,这些编译都很好,中毒或不中毒。

附注

#pragma GCC poison,其中碰巧也理解(并且还有自己的版本),外观和声音应该完全符合我的要求,并且以最惯用的方式。唉,它不起作用。或者更确切地说,非常好 当中毒名称出现在声明中时,使用pragma指令中毒的函数已经触发错误,这意味着...... 每次,在包含标题的每个构建中。这对编写程序没什么帮助!

属性

这些都有一个巨大的缺点,你必须完全复制类型定义,以免得到"模糊函数调用"一方面合法通话的错误。另一方面,他们没有工作。 clang抱怨__attribute__((__error__))但忽略gnu::__error__gnu::error以及gnu::deprecated。此外,标准[[deprecated]]一切都很好,但似乎没有做任何事情(编译得很好,甚至没有警告?!)。

我能做些什么吗?

2 个答案:

答案 0 :(得分:1)

找到了一种方法,可以通过在命名空间中添加相同功能的另一个声明来实施歧义。不使用编译指示或属性,应该可移植到任何C ++ 11编译器中:

#include <stdio.h>

#define POISONED_BY_NAME(name) \
    namespace Poisoned \
    { \
        using function_type = decltype(::name); \
        extern function_type* name; \
    } \
    using namespace Poisoned;


POISONED_BY_NAME(puts)

int main()
{
    puts("Hello"); 
}

Visual Studio消息:

error C2872: 'puts': ambiguous symbol
1>c:\program files\windows kits\10\include\10.0.17763.0\ucrt\stdio.h(353): note: could be 'int puts(const char *)'
1>c:\test.cpp(43): note: or       'Poisoned::function_type (__cdecl *__cdecl Poisoned::puts)'

gcc消息:

main.cpp: In function 'int main()':
main.cpp:16:5: error: reference to 'puts' is ambiguous
     puts("Hello");
     ^~~~
main.cpp:12:22: note: candidates are: 'int (* Poisoned::puts)(const char*)'
     POISONED_BY_NAME(puts)
                      ^~~~
main.cpp:7:35: note: in definition of macro 'POISONED_BY_NAME'
             extern function_type* name; \
                                   ^~~~
In file included from main.cpp:1:
/usr/include/stdio.h:695:12: note:                 'int puts(const char*)'
 extern int puts (const char *__s);
            ^~~~

答案 1 :(得分:0)

我会把防御程序作为第一防线,因为它似乎可以很好地捕获某些情况,并在源代码中提供确切的位置。

通过确保您提供一个包含您想要中毒的功能的 own 变体的对象文件,我还可以放置 second 防御层,例如: / p>

HWND WINAPI CreateWindowEx(DWORD dwExStyle, LPCTSTR lpClassName,
    LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth,
    int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance,
    LPVOID lpParam)
{
    std::cerr << "Attempting to use poisoned function CreateWindowEx\n";
    exit(1);
}

如果将其链接为目标文件(不是库,因为库仅链接未解析的引用),则将出现三种可能的情况(一旦通过标头中毒):

  • 您的目标文件和实际代码都将被链接,并且链接时您将看到重复的定义。
  • 仅链接了目标文件(不是真正的目标文件),因此,在调用该文件时,会收到错误消息并终止。这应该在测试时很早就发现。不是在您要求的编译/链接过程中,而是希望在发货之前就已经完成。
  • 仅链接您的目标文件,而您调用它。在这种情况下,您浪费了一些代码空间,但是可执行文件可以正常运行。

要做到这一点,只需一个poisoned.hpoisoned.c就可以解决问题,您只需确保将要中毒的所有功能都添加到这两者中即可。

现在我还没有测试过此方法是否可以与MinGW一起使用,但是值得尝试一下,因为在最低级别上,它需要在某个时候调用 real Windows函数。

我可以预见您的MinGW发生问题的唯一方法是,它正在调用一些已经编译的MinGW对象,这些对象最终将调用Windows函数。如果发生这种情况,则链接器捕获也应捕获此错误。