赞
踩
要想了解反序列化,就先要知道序列化是什么。下面是是一串序列化数组:
a:2:{s:4:"name";s:6:"cike_y";s:3:"age";i:18;}
构造序列化函数使用serialize函数,将一个可读的字符串打包成难理解的序列化数组
<?php
// 构造数组
$data = array(
"name" => "cike_y",
"age" => 18
);
// 序列化数组
$serializedData = serialize($data);
// 输出序列化字符串
echo $serializedData;
将序列化数组进行逆转过来,就是反序列化,简而言之就是一串很难理解的字符串反序列化为很直观的形式,如前面的示例:
<?php
$serialize_array = 'a:2:{s:4:"name";s:6:"cike_y";s:3:"age";i:18;}';
var_dump (unserialize($serialize_array)); //unserialize函数为反序列化函数
php对属性或者方法控制,是通过在前面添加关键字来进行实现的。
演示代码:
<?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进行输出了,其他的值为空,可能觉得认为是注释掉了,所以才没有进行回显,但并不是
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
说明了不同的成员变量的访问性质也不同
<?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,就会被过滤掉
当表示字符串的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
绕过的版本范围:
具体可以参考我之前这篇文章
https://blog.csdn.net/weixin_53912233/article/details/128069937
实例化该方法的类后,再以函数的方式调用类,就可以触发__invoke方法
<?php
class Myclass
{
public function __invoke()
{
echo '__invoke方法调用成功';
}
}
$a = new Myclass();
$a(); //实例化类后,以函数的形式进行调用,可以触发__invoke方法
;?>
可以发现当$a()执行之后就会成功调用invoke方法
该方法在反序列化对象的时候会进行调用,在php中unserialize函数为反序列化函数
<?php
class Myclass{
public function __wakeup(){
echo "我已经被反序列化了,可以执行苏醒函数了";
}
}
$obj = new Myclass();
$serialize_obj = serialize($obj);
unserialize($serialize_obj); //反序列化对象
当unserialize函数执行完毕之后,可以看见成功调用了__weakeup()魔术方法
当该对象执行serialize函数的时候,进行序列化的时候会进行调用,如下
<?php
class Myclass{
public function __sleep(){
echo "我已经被序列化了,可以执行睡觉函数了";
}
}
$obj = new Myclass();
serialize($obj); //序列化对象
可以看见执行serialize函数的时候,会调用__sleep()方法
构造方法,实例化对象的时候自动调用的方法,如下:
<?php
class MyClass {
public function __construct() {
echo "对象创建成功!";
}
}
$obj = new MyClass(); //实例化MyClass对象
可以看见实例化的时候,会调用__construct()魔术方法。
销毁函数,当脚本执行结束的时候就会运行该方法,如下:
<?php class Myclass { function __construct() { echo "aa "; } function __destruct() { print "bb"; } } $obj = new Myclass();
创建 $obj 对象时,调用 __construct() 方法,输出 aa。执行完上述之后,就会调用销毁函数,最后输出bb
当该方法的对象时被当作字符串输出,会返回__toString方法的字符串。比如:
<?php
class Myclass {
function __toString() {
return "执行toString魔术方法";
}
}
$obj = new Myclass();
echo $obj;
可以看见输出对象当作字符串的时候,就会成功调用__toString魔术方法,并且返回它的字符串信息
该方法当访问一个对象的不可访问属性时被调用,会自动调用,如下
<?php
class Myclass{
public function __get($name){
echo $name;
}
}
$obj = new Myclass();
$obj->hello;
可以看见调用了一个不存在的hello,最后触发了__get()方法,输出了hello
__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()方法
该方法,当用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。
该魔术方法同__isset()方法差不多。当使用unset()判断一个该对象不可见属性的时候就会调用__isset()方法
<?php
class Myclass{
//获取不可见属性时,被调到触发
public function __unset($a){
echo $a."<br>";
}
}
$obj = new Myclass();
unset($obj->a);
可以看见成功调用了_unset()方法
__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方法的参数。
如下例子:
<?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()方法,并且执行了不存在的方法
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。