php序列化和反序列化知识点
主要作用是把本来不能直接存储的数据转换成可存储的数据,如当你想把内存中的对象状态保存到一个文件中或者数据库中的时候
$this->是伪变量,具体用法如下:
简单来说就是在一个类中,调用该类中的属性或者方法。

在php的语句中,有一些系统自带的方法名,均以双下划线开头,它会在特定的情况下被调用。即所谓的魔法函数。他们在面向编程中起着至关重要的作用。
也就是说这些函数会在特定情况下执行函数里面的语句,如果没有调用此函数,或者调用了却没有内容,相当于没有执行任何东西。
一些魔术方法(牢记函数发生情况):
方法名 |
调用条件 |
__call |
调用不可访问或不存在的方法时被调用 |
__callStatic |
调用不可访问或不存在的静态方法时被调用 |
__clone |
进行对象clone时被调用,用来调整对象的克隆行为 |
__construct |
构建对象的时被调用; |
__debuginfo |
当调用var_dump()打印对象时被调用(当你不想打印所有属性)适用于PHP5.6版本 |
__destruct |
明确销毁对象或脚本结束时被调用; |
__get |
读取不可访问或不存在属性时被调用 |
__invoke |
当以函数方式调用对象时被调用 |
__isset |
对不可访问或不存在的属性调用isset()或empty()时被调用 |
__set |
当给不可访问或不存在属性赋值时被调用 |
__set_state |
当调用var_export()导出类时,此静态方法被调用。用__set_state的返回值做为var_export的返回值。 |
__sleep |
当使用serialize时被调用,当你不需要保存大对象的所有数据时很有用 |
__toString |
当一个类被转换成字符串时被调用 |
__unset |
对不可访问或不存在的属性进行unset时被调用 |
__wakeup |
当使用unserialize时被调用,可用于做些对象的初始化操作 |
- 反序列化的常见起点
__wakeup
一定会调用
__destruct
一定会调用
__toString
当一个对象被反序列化后又被当做字符串使用
- 反序列化的常见中间跳板:
__toString
当一个对象被当做字符串使用
__get
读取不可访问或不存在属性时被调用
__set
当给不可访问或不存在属性赋值时被调用
__isset
对不可访问或不存在的属性调用isset()或empty()时被调用。形如 $this->$func();
- 反序列化的常见终点:
__call
调用不可访问或不存在的方法时被调用
call_user_func
一般php代码执行都会选择这里
call_user_func_array
一般php代码执行都会选择这里
主要还是三点:
- 起点
- 跳板
- 代码执行
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| <?php
class test{
public $varr1="abc";
public $varr2="123";
public function echoP(){
echo $this->varr1."<br>";
}
public function __construct(){
echo "__construct<br>";
}
public function __destruct(){
echo "__destruct<br>";
}
public function __toString(){
return "__toString<br>";
}
public function __sleep(){
echo "__sleep<br>";
return array('varr1','varr2');
}
public function __wakeup(){
echo "__wakeup<br>";
}
}
$obj = new test();
$obj->echoP();
echo $obj;
$s =serialize($obj);
echo unserialize($s);
?>
|
输出结果如下

对象注入
当用户的请求在传给反序列化函数unserialize()之前没有被正确的过滤时就会产生漏洞。因为PHP允许对象序列化,攻击者就可以提交特定的序列化的字符串给一个具有该漏洞的unserialize函数,最终导致一个在该应用范围内的任意PHP对象注入。
对象漏洞出现得满足两个前提:
一、unserialize的参数可控。
二、 代码里有定义一个含有魔术方法的类,并且该方法里出现一些使用类成员变量作为参数的存在安全问题的函数。
例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php
class A{
var $test = "demo";
function __destruct(){
echo $this->test;
}
}
$a = $_GET['test'];
$a_unser = unserialize($a);
?>
|
比如这个列子,直接是用户生成的内容传递给unserialize()函数,那就可以构造这样的语句
1
| ?test=O:1:"A":1:{s:4:"test";s:5:"lemon";}
|
在脚本运行结束后便会调用_destruct函数,同时会覆盖test变量输出lemon。

再看一个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?php
class A{
var $test = "demo";
function __destruct(){
@eval($this->test);
}
}
$test = $_POST['test'];
$len = strlen($test)+1;
$pp = "O:1:\"A\":1:{s:4:\"test\";s:".$len.":\"".$test.";\";}";
$test_unser = unserialize($pp);
?>
|
其实仔细观察就会发现,其实我们手动构造序列化对象就是为了unserialize()函数能够触发__destruc()函数,然后执行在__destruc()函数里恶意的语句。
所以我们利用这个漏洞点便可以获取web shell了

绕过魔法函数的方法
__wakeup():当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 wakeup 函数的执行(PHP5<5.6.25 PHP7<7.0.10)
(以上部分内容摘自php中文网,但略有改动)
php反序列化例题
例题1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| <?php
class crow { public $v1; public $v2;
function eval() { echo new $this->v1($this->v2); }
public function __invoke() { $this->v1->world(); } }
class fin { public $f1;
public function __destruct() { echo $this->f1 . '114514'; }
public function run() { ($this->f1)(); }
public function __call($a, $b) { echo $this->f1->get_flag(); }
}
class what { public $a;
public function __toString() { $this->a->run(); return 'hello'; } } class mix { public $m1;
public function run() { ($this->m1)(); }
public function get_flag() { eval('#'.$this->m1); }
}
|
例题2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| <?php
class My_ClassLoader{
public $class_name; public $class_value;
public function __construct($class_name, $class_value) { $this->class_name = $class_name; $this->class_value = $class_value; }
public function __wakeup() { if(!in_array($this->class_name, ["Person_Bean", "Person_identify"])){ $this->class_name = ""; } }
public function __destruct() { $a = new $this->class_name($this->class_value); $a->work(); } }
class Manage_test{ public $func; public function __construct($func) { $this->func = $func; }
public function work(){ $a = $this->func; $a(); }
}
class Person_identify{ public $value = "ERROR";
public function __invoke() { $this->work($this->value); }
public function work($key){ if(md5($key) != "8cd80ad332a109f5f6ab764505564c60"){ exit(0); } else{ echo "identify Success!!!"; } } }
class Person_Bean{ public $p_info_file;
public function __toString() { echo file_get_contents($this->p_info_file); return ""; } }
highlight_file(__FILE__); if (isset($_POST['cmd'])) { unserialize($_POST['cmd']); } error_reporting(0);
|