例如:
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标头本身使用可变参数宏篡改名称的函数,例如CreateWindow
或SetWindowLong
。无论如何,这些编译都很好,中毒或不中毒。
#pragma GCC poison
,其中碰巧也理解(并且还有自己的版本),外观和声音应该完全符合我的要求,并且以最惯用的方式。唉,它不起作用。或者更确切地说,非常好
当中毒名称出现在声明中时,使用pragma指令中毒的函数已经触发错误,这意味着...... 每次,在包含标题的每个构建中。这对编写程序没什么帮助!
这些都有一个巨大的缺点,你必须完全复制类型定义,以免得到"模糊函数调用"一方面合法通话的错误。另一方面,他们没有工作。 clang抱怨__attribute__((__error__))
但忽略gnu::__error__
或gnu::error
以及gnu::deprecated
。此外,标准[[deprecated]]
一切都很好,但似乎没有做任何事情(编译得很好,甚至没有警告?!)。
我能做些什么吗?
答案 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.h
和poisoned.c
就可以解决问题,您只需确保将要中毒的所有功能都添加到这两者中即可。
现在我还没有测试过此方法是否可以与MinGW一起使用,但是值得尝试一下,因为在最低级别上,它需要在某个时候调用 real Windows函数。
我可以预见您的MinGW发生问题的唯一方法是,它正在调用一些已经编译的MinGW对象,这些对象最终将调用Windows函数。如果发生这种情况,则链接器捕获也应捕获此错误。