php反序列化原理常见的魔术方法

news2024/10/7 20:31:53

序列化是什么?

要想了解反序列化,就先要知道序列化是什么。下面是是一串序列化数组:

a:2:{s:4:"name";s:6:"cike_y";s:3:"age";i:18;}
  • a表示array(数组),2表示这个数组有两个元素。
  • 第一个元素s代表字符串,长度为4,键值是name,然后分号分隔;接下来name的值为cike_y,它的长度为六,也是字符串。
  • 第二个元素age键名,类型为字符串,键名的值是18,i代表的是整数。

构造序列化函数使用serialize函数,将一个可读的字符串打包成难理解的序列化数组

<?php

// 构造数组
$data = array(
    "name" => "cike_y",
    "age" => 18
);

// 序列化数组
$serializedData = serialize($data);

// 输出序列化字符串
echo $serializedData;

image.png

反序列化

将序列化数组进行逆转过来,就是反序列化,简而言之就是一串很难理解的字符串反序列化为很直观的形式,如前面的示例:

<?php
$serialize_array = 'a:2:{s:4:"name";s:6:"cike_y";s:3:"age";i:18;}';
var_dump (unserialize($serialize_array)); //unserialize函数为反序列化函数

访问控制

php对属性或者方法控制,是通过在前面添加关键字来进行实现的。

  • public (公有):公有的类成员可以在任何地方被访问。
  • protected (受保护): 受保护的类成员则可以被其自身以及其子类和父类访问。
  • private (私有) :私有的类成员则只能被其定义所在的类访问。

演示代码:

<?php
class Myclass {
    public $name1;
    protected $name2;
    private $age;
    
    function __construct($name1,$name2,$age) {
        $this->name1 = $name1;
        $this->name2 = $name2;
        $this->age = $age;
    }
}

// 序列化
$a = new Myclass();
$a->name1 = "cike";
#$a->name2 = "_y";
#$a->age = "18";
$str = serialize($a);
$str2 = urlencode($str);
echo "序列化的内容:<br>";
echo "没有url编码:".$str."<br>";
echo "有url编码的:".$str2."<br>";

// 反序列化
$a2 = unserialize($str);
echo "<br>反序列化的内容:<br>";
echo var_dump($a2)."<br>";
print_r($a2);

?>
    

运行php文件后发现只有cike进行输出了,其他的值为空,可能觉得认为是注释掉了,所以才没有进行回显,但并不是
image.png
public变量是公共变量,可以使用属性赋值,而其他成员却是不能直接使用属性赋值的方法,这时候取消剩下两个不同成员的属性赋值注释:

<?php
class Myclass {
    public $name1;
    protected $name2;
    private $age;
    
    function __construct($name1,$name2,$age) {
        $this->name1 = $name1;
        $this->name2 = $name2;
        $this->age = $age;
    }
}

// 序列化
#$a = new Myclass("cike", "_y" , 18);
$a = new Myclass();
$a->name1 = "cike";
$a->name2 = "_y";
$a->age = "18";
$str = serialize($a);
$str2 = urlencode($str);
echo "序列化的内容:<br>";
echo "没有url编码:".$str."<br>";
echo "有url编码的:".$str2."<br>";

// 反序列化
$a2 = unserialize($str);
echo "<br>反序列化的内容:<br>";
echo var_dump($a2)."<br>";
print_r($a2);

?>
    

可以看见出现了报错,中文意思为以下

致命错误:无法访问 test.php在第18行中的受保护属性myclass :: $ name2

说明了不同的成员变量的访问性质也不同
image.png

不同成员变量的编码

<?php
class Myclass {
    public $name1;
    protected $name2;
    private $age;
    
    function __construct($name1,$name2,$age) {
        $this->name1 = $name1;
        $this->name2 = $name2;
        $this->age = $age;
    }
}

// 序列化
$a = new Myclass("cike", "_y" , 18);

$str = serialize($a);
$str2 = urlencode($str);
echo "序列化的内容:<br>";
echo "没有url编码:".$str."<br>";
echo "有url编码的:".$str2."<br>";

// 反序列化
$a2 = unserialize($str);
echo "<br>反序列化的内容:<br>";
echo var_dump($a2)."<br>";
print_r($a2);

?>
    

可以看见 protected成员和private成员如果是直接输出会略过特殊的字符串,因为不会显示%00。而public变量的成员则没有特殊字符

序列化的内容:
没有url编码:O:7:"Myclass":3:{s:5:"name1";s:4:"cike";s:8:"*name2";s:2:"_y";s:12:"Myclassage";i:18;}
有url编码的:O%3A7%3A%22Myclass%22%3A3%3A%7Bs%3A5%3A%22name1%22%3Bs%3A4%3A%22cike%22%3Bs%3A8%3A%22%00%2A%00name2%22%3Bs%3A2%3A%22_y%22%3Bs%3A12%3A%22%00Myclass%00age%22%3Bi%3A18%3B%7D

反序列化的内容:
object(Myclass)#2 (3) { ["name1"]=> string(4) "cike" ["name2":protected]=> string(2) "_y" ["age":"Myclass":private]=> int(18) }
Myclass Object ( [name1] => cike [name2:protected] => _y [age:Myclass:private] => 18 )

O:7:“Myclass”:3 // O表示对象,类名长度为7,类名为Myclass,有三个成员,大括号里面是三个成员变量的信息

  • public成员变量的名字直接写

  • protected成员变量的名字前需要加%00类名*%00

  • private成员变量等待名字前需要加%00类名%00

    其中%00为一个字符

常见的绕过

十六进制绕过

这是一个简单的例子:

<?php
class test{
    public $username;
    public function __construct(){
        $this->username = 'admin';
    }
}
function waf($data){
    if(stristr($data, 'username')!==False){
        echo("hcaker!");
    }
    else{
        echo "flag{@_@}";
    }
}


$a = $_GET['source'];
$a = waf($a);
unserialize($a);

可以看见如果想要输入username,就会被过滤掉
image.png
当表示字符串的s变成大写时,会被当成十六进制进行解析

O:4:"test":1:{s:8:"username";s:5:"admin";}
//改成以下
O:4:"test":1:{S:8:"\\75sername";s:5:"admin";}
//u的十六进制就是\\75

引用绕过

比如题目对 c o n r r e c t 变量进行 b a s e 64 解密什么的,必须要 conrrect变量进行base64解密什么的,必须要 conrrect变量进行base64解密什么的,必须要input变量等于$correct 才能获得flag

<?php
class BUU {
   public $correct = "";
   public $input = "";
}

$a = new BUU();

/*主要的绕过方式*/
$a->correct = ""; //先让correct的值为空
$a->input = &$a->correct; 
#在 PHP 中,使用 & 符号表示引用赋值,意味着两个变量将指向同一个数据空间,从而使他们两个相等
echo serialize($a);

?>

具体可以参考我之前的刷题文章

https://blog.csdn.net/weixin_53912233/article/details/128105583

__wakeup绕过

绕过的版本范围:

  • PHP5 < 5.6.25
  • PHP7 < 7.0.10

具体可以参考我之前这篇文章

https://blog.csdn.net/weixin_53912233/article/details/128069937

php反序列化常见的魔术方法

__invoke()方法

实例化该方法的类后,再以函数的方式调用类,就可以触发__invoke方法

<?php
class Myclass
{
    public function __invoke()
    {
        echo '__invoke方法调用成功';
    }

}


$a = new Myclass();
$a(); //实例化类后,以函数的形式进行调用,可以触发__invoke方法
;?>

可以发现当$a()执行之后就会成功调用invoke方法
image.png

__wakeup()方法

该方法在反序列化对象的时候会进行调用,在php中unserialize函数为反序列化函数

<?php
class Myclass{

    public function __wakeup(){
        echo "我已经被反序列化了,可以执行苏醒函数了";
	}
}
$obj = new Myclass();
$serialize_obj = serialize($obj);
unserialize($serialize_obj); //反序列化对象

当unserialize函数执行完毕之后,可以看见成功调用了__weakeup()魔术方法
image.png

__sleep()方法

当该对象执行serialize函数的时候,进行序列化的时候会进行调用,如下

<?php
class Myclass{

    public function __sleep(){
        echo "我已经被序列化了,可以执行睡觉函数了";
	}
}
$obj = new Myclass();
serialize($obj); //序列化对象

可以看见执行serialize函数的时候,会调用__sleep()方法
image.png

__construct()方法

构造方法,实例化对象的时候自动调用的方法,如下:

<?php
class MyClass {
	public function __construct() {
		echo "对象创建成功!";
	}
}

$obj = new MyClass(); //实例化MyClass对象

可以看见实例化的时候,会调用__construct()魔术方法。
image.png

__destruct()方法

销毁函数,当脚本执行结束的时候就会运行该方法,如下:

<?php

class Myclass {

    function __construct() {
		echo "aa ";
	}
    function __destruct() {


        print "bb";
    }
}

$obj = new Myclass();


创建 $obj 对象时,调用 __construct() 方法,输出 aa。执行完上述之后,就会调用销毁函数,最后输出bb
image.png

__toString()方法

当该方法的对象时被当作字符串输出,会返回__toString方法的字符串。比如:

<?php

class Myclass {

    function __toString() {
		return "执行toString魔术方法";
	}
}

$obj = new Myclass();
echo $obj;

可以看见输出对象当作字符串的时候,就会成功调用__toString魔术方法,并且返回它的字符串信息
image.png

__get()方法

该方法当访问一个对象的不可访问属性时被调用,会自动调用,如下

<?php
  class Myclass{
  public function __get($name){
    echo $name;
  }
}
  $obj = new Myclass();
$obj->hello;

可以看见调用了一个不存在的hello,最后触发了__get()方法,输出了hello
image.png

__set()方法

__set($name, v a l u e ) 方法,当给对象的不可访问属性赋值时被调用。其中 value)方法,当给对象的不可访问属性赋值时被调用。其中 value)方法,当给对象的不可访问属性赋值时被调用。其中name指的是键名,$value指的是值。

<?php
class Myclass{
    public function __set($name,$value){
        echo $name."-----".$value;
	}
}
$obj = new Myclass();
$obj->me = "hello";

最后可以看见成功调用__set()方法
image.png

__isset()方法

该方法,当用isset()或empty()判断该当前对象不可见属性的时候,就会调用

<?php
class Myclass{
	
	//获取不可见属性时,被调到触发
    public function __isset($a){
        echo $a."<br>";
	}
}
$obj = new Myclass();
isset($obj->a);
empty($obj->b);

可以看见当·访问量不可见的a和b属性的时候就成功输出了a和b。
image.png

__unset()方法

该魔术方法同__isset()方法差不多。当使用unset()判断一个该对象不可见属性的时候就会调用__isset()方法

<?php
class Myclass{
	
	//获取不可见属性时,被调到触发
    public function __unset($a){
        echo $a."<br>";
	}
}
$obj = new Myclass();
unset($obj->a);

可以看见成功调用了_unset()方法
image.png

__call()方法

__call()该方法在调用的方法不存在时会自动调用,这个方法接受两个参数。如下:

<?php
class MyClass
{
	function __call($class_name, $args)
    {
		echo "__call方法调用成功<br>";
        var_dump($class_name,$args);
	}
}
    

$a=new MyClass();
$a->test("test1","test2");

test()方法是不存在的,访问php文件,可以看见成功调用__call()方法。
test是方法名,test1和test2是test方法的参数。
image.png

__callStatic()方法

  1. 当该方法的对象要调用的静态方法不存在或者权限不足时候会调用该方法
  2. 接受的参数为数组形式

如下例子:

<?php
class Myclass
{
    private static function name()  
    {
        echo 'hhhhhhhhh';
    }
 
    public function __callStatic($function_name, $arg)
    {
        echo  "__callStatic方法调用成功";
		var_dump($function_name,$arg);
		die;
    
    }
}

$a = new Myclass();
$a::test("参数1","参数2");
//或 Myclass::test("参数1","参数2"); 
?>

可以发现当该对象调用私有的静态name方法时,成功调用了__callStatic()方法,并且执行了不存在的方法
image.png

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机网络Day1--计算机网络体系

1.三网合一 电信网络、广播电视网络、计算机网络&#xff08;最基础最重要发展最快&#xff09; 2.Internet 名为国际互联网、因特网&#xff0c;指当前全球最大的、开放的、由众多网络相互连接而成的特定互连网&#xff0c;采用TCP/IP 协议族作为通信的规则&#xff0c;前…

NoSQL 数据库管理工具,搭载强大支持:Redis、Memcached、SSDB、LevelDB、RocksDB,为您的数据存储提供无与伦比的灵活性与性能!

NoSQL 数据库管理工具&#xff0c;搭载强大支持&#xff1a;Redis、Memcached、SSDB、LevelDB、RocksDB&#xff0c;为您的数据存储提供无与伦比的灵活性与性能&#xff01; 【官网地址】&#xff1a;http://www.redisant.cn/nosql 介绍 直观的用户界面 从单一应用程序中同…

设计模式----开题

简介&#xff1a; 本文主要介绍设计模式中的六大设计原则。开闭原则&#xff0c;里氏代换原则&#xff0c;依赖倒转原则&#xff0c;接口隔离原则&#xff0c;迪米特原则和合成复用原则。这几大原则是设计模式使用的基础&#xff0c;在使用设计模式时&#xff0c;应该牢记这六大…

IDEA 如何运行SpringBoot项目(手把手超详细截图)

IDEA 如何运行SpringBoot项目&#xff08;手把手超详细截图&#xff09; 第一章:github 导入项目 在GitHub上面找到我们需要部署项目的URL&#xff0c;并且复制粘贴到IDEA中&#xff0c;如下图所示。 第二章&#xff1a;Maven的准备与部署 由于idea自带Maven环境&#xff0c…

利用vite快速搭建vue3项目

1、选择一个文件夹&#xff0c;在vscode终端打开&#xff0c;输入命令【npm create vitelatest】 npm create vitelatest 2、提示你输入项目名称之后&#xff0c;我这里设置的是【rookiedemo】 3、回车之后&#xff0c;出现选择框架的提示&#xff0c;我们选择【vue】回车 4、…

Python 数据可视化之密度散点图 Density Scatter Plot

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 密度散点图&#xff08;Density Scatter Plot&#xff09;&#xff0c;也称为密度点图或核密度估计散点图&#xff0c;是一种数据可视化技术&#xff0c;主要用于展示大量数据点在二维平面上的分布情况…

ElementUI table表格组件实现双击编辑单元格失去焦点还原,支持多单元格

在使用ElementUI table表格组件时有时需要双击单元格显示编辑状态&#xff0c;失去焦点时还原表格显示。 实现思路&#xff1a; 在数据中增加isFocus:false.控制是否显示在table中用cell-dblclick双击方法 先看效果&#xff1a; 上源码&#xff1a;在表格模板中用scope.row…

【Linux】主机搭建 Linux服务器环境 笔记

目录 前言选择系统软件1. 用U盘装系统2. 安装 Centos7.93. 网络套件 应用软件1. ngnix2. 防火墙配置3. nodejs 后记 前言 过年买了个 mini 主机当玩具玩一下&#xff0c;这里记录下。 选择 已有主力机 (windows) 的情况下&#xff0c;使用过如下四种 Linux宿主环境。这里总…

数据结构-邻接链表

介绍 邻接矩阵是运用较多的一种储存图的方法&#xff0c;但如果一张网图边数较少&#xff0c;就会出现二维矩阵中大部分数据为0的情况&#xff0c;浪费储存空间 为了避免空间浪费&#xff0c;也可以采用数组与链表结合的方式来存储图 假设有这样一张图 我们可以先用一个数组…

关于本地docker启动xxl-job

之前通过github拉取xxl-job到本地启动&#xff0c;已经验证完了&#xff0c;主要要记住以下几个步骤: 1.拉取代码 GitHub地址&#xff1a;https://github.com/xuxueli/xxl-job Gitee地址&#xff1a;https://gitee.com/xuxueli0323/xxl-job 2.idea打开&#xff0c;找到tabl…

鸿蒙Next怎么升级,有便捷的方法?

早在2023年11月&#xff0c;市场上有自媒体博主表示&#xff0c;华为HarmonyOS NEXT的升级计划是2X年底到2X年初完成一亿部&#xff0c;2X年底完成三亿部。虽然该博主没有明确具体年份&#xff0c;但预计是2024年底2025年初升级一亿部HarmonyOS NEXT设备&#xff0c;2025年底完…

java数据结构与算法刷题-----LeetCode503. 下一个更大元素 II

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 解题思路&#xff1a;时间复杂度和空间复杂度都是O(n) 此题是739题的衍生题…

2.19C语言学习

关于memset和0x3f int a[100]; memset(a,0x3f,sizeof(a) ); 0x3f0011 111163 C中int型变量所占的位数为4个字节&#xff0c;即32位 0x3f显然不是int型变量中单个字节的最大值&#xff0c;应该是0x7f0111 1111 B 那为什么要赋值0x3f&#xff1a; 作为无穷大使用 因为4个字节均…

python在flask中的请求数据“无限流”

文章目录 一、问题描述二、解决方案 一、问题描述 在flask请求中&#xff0c;有个需求是让调用方一直调接口&#xff0c;并立马返回&#xff0c;而接口方缓存请求&#xff0c;依次执行。 二、解决方案 from flask import Flask, request, jsonify from queue import Queue i…

找到包含两/三个平面坐标点的椭圆点集

直接上代码 import numpy as np def fit_ellipse(points:np.array None, extension_ratio: float 0.2, eccentricity: float 0.8, n_points: int 100):center np.mean(points, axis0)if points.shape[0] 2:axis_vector points[1] - points[0]distance np.linalg.norm(…

K8s ingress-nginx根据请求目录不同将请求转发到不同应用

K8s ingress-nginx根据请求目录不同将请求转发到不同应用 1. 起因 有小伙伴做实验想要实现以下需求: 输入www.pana.com/app1访问app1的svc 输入www.pana.com/app2访问app2的svc 2. 实验 2.1 Dockerfile 先准备Dockerfile FROM nginx:1.20ADD index.html /usr/share/ngin…

.net6 webapi log4net完整配置使用流程

前置&#xff1a;为项目安装如下两个依赖 1.创建文件夹cfgFile 2.创建log4net.Config <?xml version"1.0" encoding"utf-8" ?> <log4net><appender name"ConsoleAppender" type"log4net.Appender.ConsoleAppender"…

Sora给中国AI带来的真实变化

OpenAI的最新技术成果——文生视频模型Sora&#xff0c;在春节假期炸裂登场&#xff0c;令海内外的AI从业者、投资人彻夜难眠。 如果你还没有关注到这个新闻&#xff0c;简单介绍一下&#xff1a;Sora是OpenAI使用超大规模视频数据&#xff0c;训练出的一个通用视觉模型&#x…

搜索中关于稀疏检索和稠密向量检索的召回效果比较

不同检索方式说明 最近在做搜索召回提升相关的研究工作。对比了稀疏检索和稠密向量检索的效果。其中使用的搜索引擎为elasticsearch8.x版本。稀疏检索包括BM25的检索方式&#xff0c;以及es官方在8.8之后版本提供的稀疏向量模型的方式。稠密向量检索&#xff0c;是指借助机器学…

OAuth2.0 最简向导

本文是一篇关于OAuth2.0的启蒙教程&#xff0c;图文并茂&#xff0c;通俗易懂&#xff0c;力求用最简洁明了的方式向初学者解释OAuth2.0是什么。本文并不是冗杂难懂的长篇大论&#xff0c;一图胜千言&#xff0c;深入浅出OAuth2.0&#xff0c;知其然知其所以然。 参考文献 首…