编译相关
#pragma init_seg
- 概述
- 是微软
C++编译器中的一个编译指令,专门用于精细控制全局静态对象(包括静态变量)的构造和析构顺序 - 在解决复杂的初始化依赖或
DLL加载问题时非常有用
- 是微软
- 语法
|
1 2 3 |
#pragma init_seg( { compiler | lib | user | "section-name" [, func-name] } ) #pragma init_seg("user_defined") |
- 优先级
compiler
最高优先级
此组别的对象最先构造,最后析构
通常保留给C/C++运行时库本身使用(如cin,cout的初始化)lib
中等优先级
此组别的对象在compiler组之后构造,在user组之前析构。适用于第三方类库供应商user
默认及最低优先级
大多数用户代码默认属于此组。此组别的对象最后构造,最先析构section-name
如user_defined,特殊模式
此组别的对象不会自动初始化
它允许你显式指定一个自定义的段(节)名称,编译器只会将所需对象的构造函数指针放置在该命名的段中,而不会自动调用它们
你的代码必须手动遍历该段并调用每个构造函数来完成初始化
- 示例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
#include <stdio.h> // 1. 定义一个函数指针类型,用于指向构造函数/析构函数 typedef void (__cdecl *PF)(void); // 2. 定义两个标记,用来确定自定义段的起始和结束边界 // 编译器会将位于这两个标记之间的函数指针视为需要手动初始化的对象 #pragma section(".my_initializer$a", read) __declspec(allocate(".my_initializer$a")) const PF InitSegStart = (PF)1; #pragma section(".my_initializer$z", read) __declspec(allocate(".my_initializer$z")) const PF InitSegEnd = (PF)1; // 3. 声明手动初始化函数 void InitializeMyObjects() { const PF *x = &InitSegStart; // 遍历段中的所有函数指针并调用 for (++x; x < &InitSegEnd; ++x) { if (*x) { (*x)(); // 调用构造函数 } } } // 4. 使用自定义段,并声明一个全局对象 #pragma init_seg(".my_initializer$m") // 注意段名格式 class MyCriticalClass { public: MyCriticalClass() { puts("MyCriticalClass constructed manually!"); } ~MyCriticalClass() { puts("MyCriticalClass destroyed!"); } }; MyCriticalClass CriticalObj; // 这个对象的构造将由 InitializeMyObjects 控制 // 5. 在程序合适的地方(如DLL的初始化函数)调用手动初始化 int main() { // ... 其他初始化代码 ... InitializeMyObjects(); // 此时才会真正构造 CriticalObj // ... 程序逻辑 ... return 0; } |
dll相关
关于dll延迟加载
- 隐式加载
- 一个
dll的使用需要依赖头文件和Lib文件,直接以普通的接口的方式来调用dll的函数,这种方式就是隐式加载
加载dll,链接了lib,这种情况lib是作为导入库,区别于加载静态库时lib是作为静态库 dll的隐式加载默认会在mian之前进行dll的加载工作
- 一个
- 显示加载
- 即
dll的加载是通过loadLibrary实现,函数调用通过GetProcAddress来实现
- 即
- 延迟加载-显示加载
- 对于
DLL的显示加载实现延迟加载比较容易,因为LoadLibrary的调用时机可以通过代码来控制
- 对于
- 延迟加载-隐式加载
- 默认的隐式加载的
dll是在mian函数发生之前触发,无法做到延迟加载 - 但
windows提供了一种解决方案,在工程项目中设置延迟加载的dll即可实现延迟加载 - 链接器-输入-延迟加载的
Dll
- 默认的隐式加载的
- 相关注意项
- 延迟加载是 针对隐式链接
DLL的 - 一个导出了字段(如全局变量)的
DLL是无法延迟载入的 - 系统的
DLL一般都是无法延迟载入的,如Kernel32.dll
- 延迟加载是 针对隐式链接
- 需要链接下面的库
|
1 |
delayimp.lib |
dll双形态在不同系统的表现
- 在
win10,win11,运行正常 - 在
win7上面,调试发现,会在BuildRuntime内部这一行奔溃- 解决:
- 调试发现,编出来的
release开着优化时存在此问题,而禁用优化(/Od)后则不存在 - 于是把
release的优化关掉了来处理此问题
|
1 |
GET_ADDR(kernel32_module, GetModuleHandleW); |
vs相关
符合模式
- 编译器选项
/permissive- 核心目标是让编译器以更严格的标准
C++规则来检查你的代码
- 特点
- 严格遵循C++标准,禁用大部分微软特定扩展
- 严格,能识别并报告不符合标准的代码
threadSafeInit
-
概述
C++11标准规定局部静态变量的初始化必须是线程安全的,MSVC从VS2015起默认启用了这个特性(/Zc:threadSafeInit)- 编译器会自动在静态局部变量初始化时加锁,防止多线程重复初始化
-
开启
/Zc:threadSafeInit- 当代码首次执行到局部静态变量的声明时,编译器会插入额外的线程安全保证代码
- 这通常涉及使用互斥锁或类似原子操作进行双重检查(
Double-Checked Locking),确保即使多个线程同时调用,该变量也只会被初始化一次
-
禁用
/Zc:threadSafeInit-- 表示禁用
C++11的线程安全静态局部变量初始化 - 编译器会移除以保证线程安全而添加的同步机制
- 意味着,如果多个线程在同一时刻首次访问该局部静态变量,可能会同时执行初始化代码,导致未定义行为
- 常见后果包括重复构造对象、资源泄漏,或者更严重地,一个线程可能使用到另一个线程尚未完成初始化的对象,进而引发程序崩溃
-
常见禁用原因:
- 避免隐式依赖
CRT同步原语,比如在DLL加载早期(DllMain中)使用静态局部变量可能导致死锁 - 兼容老代码,某些项目原本就不依赖这个保证,关掉可以避免引入额外开销或兼容性问题
- 减小代码体积,省去编译器生成的锁相关代码
- 避免隐式依赖
-
注意
- 关掉后,如果多个线程同时首次访问同一个局部静态变量,就可能出现竞态条件,需要自己保证线程安全
-
位置
- 配置属性-
c/c++-命令行-附加选项
- 配置属性-
strictStrings
- 概述
- 一个一致性模式设置,它直接影响编译器对字符串字面量类型转换的严格程度
- 开启
/Zc:strictStrings- 严格遵循
C++标准 - 类型:严格,要求指针有
const限定
- 禁用
/Zc:strictStrings-- 宽松处理,允许非标准转换
- 类型:宽松,允许字符串字面量赋值给非
const指针
- 配置属性-
c/c++-命令行-附加选项
SAFESEH
- 概述
- 是
Windows上针对x86(32位) 程序的一种安全机制,用于防止SEH覆盖攻击(SEH Overwrite Attack)
- 是
SEHWindows的结构化异常处理(SEH)在x86下是通过栈上的链表实现的- 每个异常处理记录(
EXCEPTION_REGISTRATION_RECORD)存放在栈上: - 当异常发生时,系统沿着这个链表依次调用
Handler,直到某个handler处理了异常
|
1 2 3 4 |
struct EXCEPTION_REGISTRATION_RECORD { EXCEPTION_REGISTRATION_RECORD *Next; // 指向链表下一个节点 PEXCEPTION_HANDLER Handler; // 异常处理函数指针 }; |
- 攻击原理
- 因为这个链表存放在栈上,所以如果攻击者通过缓冲区溢出覆盖了栈数据,就可以:
- 覆盖
Handler指针,让它指向攻击者的shellcode - 触发一个异常(比如访问非法地址)
- 系统沿链表调用被篡改的
Handler,跳转执行shellcode
|
1 2 3 4 5 6 7 8 9 10 |
低地址 ┌──────────────────────┐ │ 局部变量 / buffer │ ← 溢出起点 ├──────────────────────┤ │ Next 指针(被覆盖) │ ├──────────────────────┤ │ Handler(被覆盖) │ ← 指向 shellcode ├──────────────────────┤ │ ... │ 高地址 |
- 位置
- 项目属性 → 链接器 → 高级 → 映像具有安全异常处理程序
- 备注
/SAFESEH仅影响x86(32位)项目,x64项目不存在这个问题
rc相关
vs rc里面覆盖资源的问题
- 资源视图添加了一张
bitmap,png下面显示IDB_PNG_TEST - 在
png下面拷贝了IDB_PNG_TEST,生成了IDB_PNG_TEST1 - 修改了
IDB_PNG_TEST1对应的ID和文件名 - 用同样的流程添加了很多张图片后发现,本地对应的其他图片被修改成第一张的内容
声明:本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 静态库03/15
- ♥ Visual Studio:条件断点与字符串相关12/04
- ♥ Visual Studio:子进程调试相关07/15
- ♥ MASM:概述一08/04
- ♥ Visual Studio:管理工程文件10/14
- ♥ C++_trunk相关10/24