毒害某些API函数(不是维护者)
Poisoning certain API functions (not being maintainer)
与例如:如何在VC++中毒害标识符
这意味着什么"毒害函数";在C++中?
有没有办法";适当地";毒害一些声明(或实现)不在我控制之下的函数?
具体来说,我正在努力防止意外使用某些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
。不管怎样,这些编译都很好,中毒与否。
杂注
clang碰巧也理解#pragma GCC poison
(它也有自己的版本),看起来和听起来都应该按照我的意愿,以最地道的方式。唉,它不起作用。或者更确切地说,它工作得太好了
当中毒的名称出现在声明中时,被pragma指令中毒的函数已经触发错误,这意味着每次,在所有包含标头的构建中。这对编写程序没有多大帮助!
属性
这些具有巨大的缺点,即必须精确地复制类型定义;模糊函数调用";一方面是合法呼叫的错误。另一方面,它们不起作用。clang抱怨__attribute__((__error__))
,但忽略gnu::__error__
或gnu::error
以及gnu::deprecated
。此外,标准[[deprecated]]
非常好,但似乎什么都没做(编译得很好,甚至没有警告?!)。
我能做什么吗?
已经找到了一种方法,通过在命名空间中添加同一函数的另一个声明来强制执行模糊性。不使用任何杂注或属性,应可移植到任何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 fileswindows kits10include10.0.17763.0ucrtstdio.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);
^~~~
我会保留预处理器破解作为第一道防线,因为它似乎很好地捕捉了一些情况,为您提供了源代码中的确切位置。
我还将加入第二层防御,通过确保您提供一个对象文件,其中包含您想要中毒的函数的自己的变体,比如:
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 CreateWindowExn";
exit(1);
}
如果你将其链接为一个对象文件(而不是库,因为库只会链接到未解析的引用),将有三种可能的情况(一旦它通过了标头中毒):
- 您的对象文件和实际代码都将被链接,并且在链接时您将看到重复的定义
- 只有您的对象文件是链接的(而不是真正的),所以,当您调用它时,您会收到错误消息并终止。这应该在测试时很早就发现了。不是按照您的要求在编译/链接期间,但希望在您发货之前
- 只有你的对象文件被链接,而你不会调用它。在这种情况下,你浪费了一点代码空间,但你的可执行文件会运行良好
就这一点而言,一个poisoned.h
和poisoned.c
就可以了,你只需要确保所有你想要中毒的函数都添加到这两个函数中。
现在我还没有测试过这种方法是否适用于MinGW,但值得一试,因为在最低级别,它需要在某个时候调用realWindows函数。
我唯一能想到的是,它正在调用一些已经编译的MinGW对象,这些对象最终会调用Windows函数。如果发生了这种情况,那么链接器捕获也应该捕捉到这种情况。
- "error: no matching function for call to"构造函数错误
- 什么时候调用组成单元对象的析构函数
- 继承函数的重载解析
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- C++模板来检查友元函数的存在
- 递归函数计算序列中的平方和(并输出过程)
- 对RValue对象调用的LValue ref限定成员函数
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 为什么使用 "this" 指针调用派生成员函数?
- 将对象数组的引用传递给函数
- 函数调用中参数的顺序重要吗
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用不带参数的函数访问结构元素
- 代码在main()中运行,但在函数中出现错误
- 内置函数可查看CPP中的成员变量
- 如何获取std::result_of函数的返回类型
- 如何在c++中为模板函数实例创建快捷方式
- 毒害某些API函数(不是维护者)