【安全攻防】网络安全中的序列化与反序列

1.序列化与反序列化

首先要了解序列化与反序列化的定义,以及序列化反序列化所用到的基本函数。

序列化:把对象转换为字节序列的过程称为对象的序列化,相当于游戏中的存档。

PHP中的序列化函数serialize()

**serialize()**函数用于序列化对象数组,并返回一个字符串。serialize()函数序列化对象后,可以很方便的将它传递给其他需要它的地方,且其类型和结构不会改变

语法string serialize ( mixed $value )
参数说明$value: 要序列化的对象或数组。
返回值返回一个字符串。

示例:

<?php
highlight_file(__FILE__);
$sites=array('I', 'Like', 'PHP');
echo'<br/>';
var_dump(serialize($sites));    //把这个对象进行序列化
echo'<br/>';
​
classman{
public$name="xiaocui";
public$sex="man";
private$age=26;
}
$M=newman();//创建一个对象
var_dump(serialize($M)); //把这个对象进行序列化
​
?>

输出结果为:

string(47) "a:3:{i:0;s:1:"I";i:1;s:4:"Like";i:2;s:3:"PHP";}"
string(79) "O:3:"man":3:{s:4:"name";s:7:"xiaocui";s:3:"sex";s:3:"man";s:8:"manage";i:26;}"

参数说明:

数组的序列化:
a 代表一数组  
3 代表数组中有3个元素
i 代表数组的下标
0 代表I元素的下标值
s 代表元素I的数据类型为字符型
1 代表元素I的长度为1
对象的序列化:
O 代表是一个对象
3 代表类名man的长度
3 代表类中的字段数
s 代表属性name的类型为字符型
4 代表属性name的长度//后面的以此类推,序列化字符串中字段内容以{开始,;}结束

反序列化:把字节序列恢复为对象的过程称为对象的反序列化,相当于游戏中的读档。

【一一帮助安全学习,所有资源获取处一一】
①网络安全学习路线
②20份渗透测试电子书
③安全攻防357页笔记
④50份安全攻防面试指南
⑤安全红队渗透工具包
⑥信息收集80条搜索语法
⑦100个漏洞实战案例
⑧安全大厂内部视频资源
⑨历年CTF夺旗赛题解析

PHP中的反序列化函数unserialize()

**unserialize()**函数用于将序列化后的字符串在还原为原来的数组或对象的过程。**unserialize()**函数可以快速将序列化的字符串还原出来,用来完成它本来的功能。

语法mixed unserialize ( string $str )
参数说明$str: 序列化后的字符串。
返回值返回的是转换之后的值,可为 integer、float、string、array 或 object。不可反序列化时返回FALSE,并抛出提醒。

示例代码:

<?php
highlight_file(__FILE__);
$sites=array('I', 'Like', 'PHP');
echo'<br/>';
echo$ser=serialize($sites).'<br/>';    //把这个对象进行序列化
var_dump(unserialize($ser)); //把序列化的字符串进行反序列化
echo'<br/>';
classman{
public$name="xiaocui";
public$sex="man";
private$age=26;
}
$M=newman();//创建一个对象
echo$ser=serialize($M).'<br/>'; //把这个对象进行序列化
var_dump(unserialize($ser));   //把序列化的字符串进行反序列化
​
?>

输出结果为:

a:3:{i:0;s:1:"I";i:1;s:4:"Like";i:2;s:3:"PHP";}
array(3) { [0]=>string(1) "I"[1]=>string(4) "Like"[2]=>string(3) "PHP"}
​
O:3:"man":3:{s:4:"name";s:7:"xiaocui";s:3:"sex";s:3:"man";s:8:"manage";i:26;}
object(man)#2 (3) { ["name"]=> string(7) "xiaocui" ["sex"]=> string(3) "man" ["age":"man":private]=> int(26) } 

可以看出上述反序列化后的数组或对象,与原数据没有任何变化。

序列化与反序列化在系统中的作用

①把对象的字节序列永久放在磁盘中,需要时可以随时调用,大大节省磁盘占用空间。

②在传输过程中可以直接传输字节序列,而不是对象,这可以大大提高传输速率。

在业务系统中,需要对一些对象进行序列化存储,让他们离开内存空间,存放在一个文件中,以便持久化保存。例如:在用户注册并登录系统时,会将用户信息如用户名,密码,cookie等信息先通过序列化存储起来,当用户再次登录时将序列化后的字节序列通过反序列化成原对象到内存进行使用,可以大大节省内存开销。

2.魔术方法

PHP将所有以__(两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以__为前缀。

PHP中常见魔术方法

__construct()

具有 __construct函数的类会在每次创建新对象时先调用此方法,适合在使用对象之前做一些初始化工作。

代码示例:

<?php
highlight_file(__FILE__);
classdemo{
public$name="xiaocui";
public$sex="man";
private$age=26;
publicfunction__construct()
{
echo"<br/>"."类被实例化时调用我!";
}
}
$D=newdemo();   //实例化对象
​
?>

输出结果:

类被实例化时调用我!

__destruct()

该函数会在到某个对象的所有引用都被删除或者当对象被销毁时执行

代码示例:

<?php
highlight_file(__FILE__);
class demo{
public $name="xiaocui";
public $sex="man";
private $age=26;
public function __construct()
{
echo "<br/>"."类被实例化时调用我!"."<br/>";
}
public function num($a,$b){
echo $c = $a + $b.'<br/>';
return $c;
}
public function __destruct(){
echo "当类中的所有方法都被销毁时调用我!";
}
public function person($per){
echo "We are $per !!!".'<br/>';
}
}
$D=new demo();   //实例化对象
$D->num(5,6);   //调用num()方法
$D->person(man);  //调用person()方法
?>

输出结果:

类被实例化时调用我!
11
Weareman!!!
当类中的所有方法都被销毁时调用我!

上述输出结果可以很直观的看到代码的执行顺序:__construct()=>num(5,6)=>person(nanren)=>__destruct()

实例化对象时首先要执行构造方法__construct(),然后接着执行类中的实例num()person(),当所有方法都执行完成销毁时,最后调用析构方法__destruct()

__wakeup()

在使用unserialize()时,会检查是否存在一个__wakeup()魔术方法。如果存在,则该方法会先被调用,预先准备对象需要的资源。

代码示例:

<?php
highlight_file(__FILE__);
classdemo{
public$name="xiaocui";
protected$sex="man";
private$age=26;
publicfunction__construct()
{
echo"<br/>"."类被实例化时调用我!"."<br/>";
}
publicfunction__destruct(){
echo"<br/>"."当类中的所有方法都被销毁时调用我!"."<br/>";
}
publicfunction__wakeup()
{
echo"<br/>"."当反序列时首先调用我 !".'<br/>';
}
}
$D=newdemo();   //实例化对象
echo$ser=serialize($D);      //序列化对象$D
var_dump(unserialize($ser));    //反序列化字符串$ser
​
?>

输出结果:

由于$age为私有属性,在序列化时会在前后加空白字符%00,原本的字符长度为7,而在两侧加入空白字符则字符长度就会变为9

类被实例化时调用我!
O:4:"demo":3:{s:4:"name";s:7:"xiaocui";s:6:"*sex";s:3:"man";s:9:"demoage";i:26;}
当反序列时调用我 !
object(demo)#2(3) { ["name"]=> string(7) "xiaocui" ["sex":protected]=> 
string(3) "man" ["age":"demo":private]=> int(26) }
当类中的所有方法都被销毁时调用我!
​
当类中的所有方法都被销毁时调用我!

上述输出结果可以很直观的看到代码的执行顺序:__construct()=>serialize($D)=>__wakeup()=>unserialize($ser)=>__destruct()=>__destruct()

实例化对象时首先要执行构造方法__construct(),然后执行序列化serialize($D),然后再反序列化之前要执行__wakeup()方法,然后执行反序列化unserialize($ser),当所有方法都执行完成销毁时最后执行__destruct()析构方法,由于反序列化后将原来的序列化字符串还原又执行一次__destruct()

__toString()

__toString()方法用于定义一个类被当成字符串时该如何处理。

示例代码:

<?php
highlight_file(__FILE__);
class demo{public $name="xiaocui";protected $sex="man";private $age=26;public function __construct(){echo "<br/>"."类被实例化时调用我!"."<br/>";}public function __destruct(){echo "<br/>"."当类中的所有方法都被销毁时调用我!"."<br/>";}public function __wakeup(){echo "<br/>"."当反序列时调用我 !".'<br/>';}public function __toString(){return "<br/>"."类被当成字符串处理时调用我!"."<br/>";}
}
$D=new demo();   //实例化对象
echo $D;   //类被当成字符串输出?>

输出结果:

类被实例化时调用我!
​
类被当成字符串处理时调用我!
​
当类中的所有方法都被销毁时调用我!

上述输出结果可以看到,当类被当成字符串echo时,__toString()方法被调用并执行。

如果该类中没有__toString()方法,进行echo输出时,PHP会抛出致命性错误,错误如下:

Catchablefatalerror: ObjectofclassdemocouldnotbeconvertedtostringinD:\XXXX\phpstudy_pro\WWW\two\demo.phponline30

__sleep()

在使用serialize()函数时,程序会检查类中是否存在一个__sleep()魔术方法。如果存在,则该方法会先被调用,然后再执行序列化操作。

__sleep()方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,该函数就起到了很好的清理作用。

示例代码:

<?php
highlight_file(__FILE__);
class demo{public $name="xiaocui";protected $sex="man";private $age=26;public function __construct(){echo "<br/>"."类被实例化时调用我!"."<br/>";}public function __destruct(){echo "<br/>"."当类中的所有方法都被销毁时调用我!"."<br/>";}public function __wakeup(){echo "<br/>"."当反序列时调用我 !".'<br/>';}public function __sleep(){echo "<br/>"."当序列时调用我 !".'<br/>';return array("name","sex","age");    //这里必须返回一个数值,里边的元素表示返回的属性名称}}
$D=new demo();   //实例化对象echo $ser = serialize($D);  //序列化对象?>

输出结果:

类被实例化时调用我!
当序列时调用我!
O:4:"demo":3:{s:4:"name";s:7:"xiaocui";s:6:"*sex";s:3:"man";s:9:"demoage";i:26;}
当类中的所有方法都被销毁时调用我!

上述输出结果可以看到,当类被序列化时首先调用了__sleep()方法,该函数必须返回一个数值。如果该函数没有返回属性的话,序列化时会将属性清空。

__invoke()

当尝试以调用函数的方式调用一个对象时,__invoke方法会被自动调用。(本特性只在 PHP 5.3.0 及以上版本有效。)

代码示例:

<?php
highlight_file(__FILE__);
class demo{public $name="xiaocui";protected $sex="man";private $age=26;public function __construct(){echo "<br/>"."类被实例化时调用我!"."<br/>";}public function __destruct(){echo "<br/>"."当类中的所有方法都被销毁时调用我!"."<br/>";}public function __wakeup(){echo "<br/>"."当反序列化时调用我 !".'<br/>';}public function __sleep(){echo "<br/>"."当序列化时调用我 !".'<br/>';return array("name","sex","age");}public function __invoke(){echo "<br/>"."当以函数的方式调用对象时会调用我!".'<br/>';}}
$D=new demo();   //实例化对象$D();    //以函数的方式调用对象
?>

结果输出:

类被实例化时调用我!
​
当以函数的方式调用对象时会调用我!
​
当类中的所有方法都被销毁时调用我!

上述结果可以看到当使用函数的方法调用对象时,就会调用__invoke()方法.

如果没有类中没有该方法,那么PHP会报出致命性错误,如下:

Fatalerror:UncaughtError: 
FunctionnamemustbeastringinD:\xxxxx\phpstudy_pro\WWW\two\demo.php:42Stacktrace:#0 {main} thrown in D:\xxxxx\phpstudy_pro\WWW\two\demo.php on line 42

__call()

在对象中调用一个不存在或者不可访问方法时,__call会被调用。

代码示例:

<?php
highlight_file(__FILE__);
class demo{public $name="xiaocui";protected $sex="man";private $age=26;public function __construct(){echo "<br/>"."类被实例化时调用我!"."<br/>";}public function num($a,$b){echo "<br/>".$c = $a + $b.'<br/>';return $c;}public function __destruct(){echo "<br/>"."当类中的所有方法都被销毁时调用我!"."<br/>";}public function person($per){echo "<br/>"."We are $per !!!".'<br/>';}public function __wakeup(){echo "<br/>"."当反序列化时调用我 !".'<br/>';}public function __sleep(){echo "<br/>"."当序列时调用我 !".'<br/>';return array("name","sex","age");}public function __call($arg1,$arg2){echo  "<br/>"."当对象调用一个不存在或者不可访问的方法时调用我!".'<br/>';}}
$D=new demo();   //实例化对象
$D->num1(1,2);   //调用一个不存在的方法
?>

结果输出:

类被实例化时调用我!
​
当对象调用一个不存在或者不可访问的方法时调用我!
​
当类中的所有方法都被销毁时调用我!

__set()

给不可访问属性赋值时,__set会被调用。

代码示例:

<?php
highlight_file(__FILE__);
class demo extends demo1{public $name="xiaocui";protected $sex="man";private $age=26;public function __construct(){echo "<br/>"."类被实例化时调用我!"."<br/>";}public function __destruct(){echo "<br/>"."当类中的所有方法都被销毁时调用我!"."<br/>";}public function person($per){echo "<br/>"."We are $per !!!".'<br/>';}public function __set($arg1,$arg2){echo "<br/>"."当给不存在或者不可访问的属性赋值时调用我!"."<br/>";}
}class demo1{private $weight;public  $height;public function people(){echo $this->weight;echo $this->height;}
}$D=new demo();   //实例化对象
$D->weight=74; //给不可访问的属性赋值
?>

输出结果:

类被实例化时调用我!
当给不存在或者不可访问的属性赋值时调用我!
当类中的所有方法都被销毁时调用我!

__isset()

对不可访问属性调用isset()empty()时,__iset()会被调用。

__unset()

对不可访问属性调用unset()时,__unset()会被调用。

__get()

读取不可访问属性的值时,__get会被调用。

代码示例:

<?php
highlight_file(__FILE__);
class demo extends demo1{public $name="xiaocui";protected $sex="man";private $age=26;public function __construct(){echo "<br/>"."类被实例化时调用我!"."<br/>";}public function __destruct(){echo "<br/>"."当类中的所有方法都被销毁时调用我!"."<br/>";}public function __get($arg1){echo "<br/>"."读取不存在或不可访问的属性时调用我!";}
}class demo1{private $weight = 0;public  $height;public function people(){echo $this->weight;echo $this->height;}
}$D=new demo();   //实例化对象$D->weight;   //读取父类中不可访问的属性
?>

输出结果:

类被实例化时调用我!
读取不存在或不可访问的属性时调用我!
当类中的所有方法都被销毁时调用我!

3.反序列化漏洞

3.1 反序列化漏洞利用条件

① unserialize()函数中参数可控
② 存在可利用的类,且类中有魔术方法

代码示例1:

<?php
highlight_file(__FILE__);class demo
{public $arg1 = "0";public function __destruct(){echo $this->arg1;  //输出用户传递的arg1值}}$a=$_GET['arg'];     //接收前端传递的arg1变量
$unser = unserialize($a);    //反序列化传递的arg1
?>

上述的代码满足反序列化漏洞的两个条件,arg参数可控,也就是unserialize()函数的参数可控;存在可利用的类demo,且demo类中有可利用的魔术方法__destruct(),然而__destruct()魔术方法中使用echo输出变量了arg

通过arg参数,构造序列化字符串,通过unserialize()函数进行反序列化,最后通过__destruct()魔术方法输出变量,造成XSS

image.png

1656063927_62b587b782d1ce26d9662.png

示例代码2

<?php
highlight_file(__FILE__);class demo
{public $arg1 = "0";public function __destruct(){eval($this->arg1);  //eval()去执行用户传递的arg1值}}$a=$_GET['arg'];     //接收前端传递的arg1变量
$unser = unserialize($a);    //反序列化传递的arg1
var_dump($unser);
?>

如果魔术方法中存在eval(),且参数可控,那么通过构造序列化字符串,通过反序列化漏洞造成RCE

1656063978_62b587ea396a1888f7509.png

3.2 __wakeup()函数绕过

在序列化时传递对象的属性大于实际对象的属性时,__wakeup()魔术方法将不会被执行,从而导致绕过。

PHP版本<=5.6.25或者PHP版本<=7.0.11

示例代码:

<?php
highlight_file(__FILE__);class demo
{public $arg1 = "0";public function __destruct(){eval($this->arg1);  //eval()去执行用户传递的arg1值}public function __wakeup(){foreach(get_object_vars($this) as $k => $v) {$this->$k = '';          //将传入的参数遍历,全部赋值为空}}}$a=$_GET['arg'];     //接收前端传递的arg1变量
$unser = unserialize($a);    //反序列化传递的arg1
var_dump($unser);
?>

如果属性值与对象实际属性值相同,则会在反序列化时执行__wakeup()函数将传入的变量值替换为空。
1656064048_62b58830a44fdeca601a9.png
如果属性值设置大于实际对象属性值则会绕过__wakeup()函数。
1656064071_62b588476b877e1951bf6.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/369007.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Appium自动化测试框架3

滑动与拖拽 swipe 滑动时间的长短会影响最后的结果的 是有一定误差的 from appium import webdriver import time # 启动一个字典 包装相应的启动参数 desired_caps dict() # 平台的名字&#xff0c;安卓还是IOS 大小写无所谓 desired_caps[platformName] Android # 平台的…

【串口通信】之TTL电平

1. 什么是串口 串口,全称为串行通信端口,是一种计算机硬件接口,用于实现数据的串行传输。与并行通信不同,串口通信一次只传输一个比特,数据通过串行线按顺序传输。串口通信在嵌入式系统、工业控制、计算机与外围设备通信等领域非常常见 2. 什么是串口通信 串口通信是指通过…

Docker 部署 Minio 对象存储服务器

文章目录 Github官网文档简介dockerdocker-compose.ymlmc 客户端mc 基础命令Golang 示例创建 test 账号密钥文件上传示例 Github https://github.com/minio/minio 官网 https://min.io/https://www.minio.org.cn/ 文档 https://www.minio.org.cn/docs/minio/kubernetes/up…

刚办理的手机号被停用,你可能遇到这些问题了!

很多朋友都会遇到手机号被停用的情况&#xff0c;那么你知道你的手机号为什么会被停用吗&#xff1f;接下来&#xff0c;关于手机号被停用的问题&#xff0c;跟着小编一块来了解一下吧。 ​停机的两种形态&#xff1a; 1、第一个是局方停机&#xff0c;即语音、短信和流量都不…

C++|哈希应用->布隆过滤器

目录 一、概念 二、模拟实现 三、布隆过滤器扩展应用 上一篇章学习了位图的使用&#xff0c;但它只适用于整数&#xff0c;对于要查询字符串是否在不在&#xff0c;位图并不能解决。所以针对这一问题&#xff0c;布隆过滤器可以派上用场&#xff0c;至于布隆过滤器是什么&am…

Java支付宝沙箱支付环境配置及简单测试

Java支付宝沙箱环境配置(测试) 1. 沙箱配置环境 沙箱应用 - 开放平台 (alipay.com) 2. 需要用到的基本信息 3. Pom文件添加依赖 <!--支付宝依赖 --><dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-easysdk</artifactId…

周鸿祎:大模型不是风口和泡沫,将引领新工业革命

7月2日&#xff0c;2023全球数字经济大会人工智能高峰论坛举行。360集团创始人周鸿祎在论坛发表《构建“安全可信可控易用”的企业级AI大模型》主题演讲。他表示&#xff0c;大模型不是风口和泡沫&#xff0c;将引领新工业革命。在城市、行业、企业数字化转型到智能化的过程中&…

#### golang中【堆】的使用及底层 ####

声明&#xff0c;本文部分内容摘自&#xff1a; Go: 深入理解堆实现及应用-腾讯云开发者社区-腾讯云 数组实现堆 | WXue 堆&#xff08;Heap&#xff09;是实现优先队列的数据结构&#xff0c;Go提供了接口和方法来操作堆。 应用 package mainimport ("container/heap&q…

C++:类型转换

目录 一、C语言中的类型转换 二、为什么C要新的转换格式 三、 C强制类型转换 1.static_cast 2.reinterpret_cast 3.const_cast 4.dynamic_cast 一、C语言中的类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配&…

Land survey boundary report (template)

Land survey boundary report (template) 土地勘测定界报告&#xff08;模板&#xff09;.doc

可视化学习之pytorch可视化工具visdom

文章摘自详解PyTorch可视化工具visdom&#xff08;一&#xff09;-CSDN博客 模型训练过程中需要实时监听并可视化一些数据&#xff0c;如损失值loss&#xff0c;正确率acc等。在tensorflow中&#xff0c;使用的工具为tensorboard&#xff1b; 安装一下试试 1.安装 pip inst…

Android的课程学习助手APP-计算机毕业设计源码19307

基于Android的课程学习助手APP 摘 要 在数字化、信息化的时代背景下&#xff0c;移动学习已成为现代教育发展的重要趋势。为了满足广大学生对高效、便捷学习方式的迫切需求&#xff0c;一款基于Android平台的课程学习助手APP应运而生。这款APP巧妙地将先进的信息技术与学习体验…

通过一个单相逆变器仿真深度学习PR控制器

目录 前言 ​编辑 PR控制器的理论 PR控制器不同表达式及其建模 PR控制器连续积分组合及模型 PR控制器连续传递函数及模型 PR控制器离散积分及模型 PR控制器离散传递函数及模型 PR控制器差分方程及模型 系统仿真效果 总结 前言 在项目开发中常用PI控制器&#xff0c;这次在…

解读 Amazon Q | 用 AI 聊天机器人连接你与未来的无限可能

在美国当地时间11月28日&#xff0c;亚马逊云科技在拉斯维加斯举办了 re:Invent 大会&#xff0c;大会介绍了许多今年来新增的核心产品与功能&#xff0c;着重讲解了生成式 AI 引领人工智能未来的前进方向&#xff0c;亚马逊作为云计算领域的龙头&#xff0c;相信会继续给我们的…

基于路径长度的样条插补算法(自动驾驶和路径跟踪控制适用)

以前在做车辆跟踪控制的时候发现在针对有多个X和多个Y对应的路径插补时候&#xff0c;总是报错&#xff0c;因为MATLAB里面的interp1插补函数它要求x要唯一对应一个y&#xff0c;当路径以单独的x或者y来求插补时候的时候就报错。由于在使用Matlab的interp1函数进行插值时&#…

重生之算法刷题之路之链表初探(三)

算法刷题之路之链表初探&#xff08;三&#xff09; 今天来学习的算法题是leecode2链表相加&#xff0c;是一道简单的入门题&#xff0c;但是原子在做的时候其实是有些抓耳挠腮&#xff0c;看了官解之后才恍然大悟&#xff01; 条件 项目解释 有题目可以知道&#xff0c;我们需…

C#Modbus专题

1&#xff0c;辅助工具。 1&#xff0c;虚拟串口工具&#xff08;vspd.exe&#xff09; 2&#xff0c;Modubus模拟主站(Modbus Poll) 3&#xff0c;Modbus模拟从站(Modbus Slave) 4&#xff0c;OPC服务软件(KEPServerEx V4.0)。 下载链接&#xff1a;https://download.csdn.n…

Redisson框架

1. Redisson锁与Redis订阅与发布模式的联系&#xff1a; Redisson锁中&#xff0c;使用订阅发布模式去通知等待锁的客户端&#xff1a;锁已经释放&#xff0c;可以进行抢锁。 publish channel_name message&#xff1a;将消息发送到指定频道 解锁时&#xff0c;在Lua解锁脚本…

算法思想总结:优先级队列

一、最后一块石头的重量 . - 力扣&#xff08;LeetCode&#xff09; 我们每次都要快速找到前两个最大的石头进行抵消&#xff0c;这个时候用优先级队列&#xff08;建大堆&#xff09;,不断取堆顶元素是最好的&#xff01;每次删除堆顶元素后&#xff0c;可以自动调整&#xf…

[C++][CMake][CMake基础]详细讲解

目录 1.CMake简介2.大小写&#xff1f;3.注释1.注释行2.注释块 4.日志 1.CMake简介 CMake是一个项目构建工具&#xff0c;并且是跨平台的 问题 – 解决 如果自己动手写Makefile&#xff0c;会发现&#xff0c;Makefile通常依赖于当前的编译平台&#xff0c;而且编写Makefile的…