歡迎來到 黑吧安全網 聚焦網絡安全前沿資訊,精華內容,交流技術心得!

玩轉php的編譯與執行

來源:本站整理 作者:佚名 時間:2019-07-03 TAG: 我要投稿

曾幾何時php一不小心闖入了我生活,php語法竟然和C語言那么莫名的相似,這是最初php給我的感受,當接觸的php時間越來越多的時候,php也沒有那般生澀難懂,但是偶爾一些的新的php 設計思想,也會思考許久,不知是從什么時候開始了php另一個世界。我想應該是從那次的類型轉換開始的,"1e12"字符串類型在轉化為數字類型變量時,不同的php版本下轉換結果截然不同,有的就變成了數字1,有的卻可以正常的識別為科學計數法10^12,在這個地方就已經悄悄的埋下了一枚種子。
到后來的使用php://filter/string.strip_tags/resource包含文件時為什么會出現SegmentFault,在HCTF2017上初識orange帶來phar的metadata反序列化0day,溯源使用imap_open到底是如何繞過disable_function限制的,在WP5.0 RCE中mkdir的差異,到今年四月份在twitter看見的chdir 配合ini_set繞過open_basedir的限制。echo,eval 語法結構的分析,create_function的代碼注入,各種各樣的PHP內部的hook,php擴展的編寫,到最近的SG的zend擴展加密....
這一路看來,我早已經陷入php的魅力無法自拔。不知道在這篇文章面前的你們,是否也曾有過像我那般想要領略php神秘內部的沖動?有些人卻忘而生畏,無從下手。希望你們讀完此篇,能點燃那顆微弱甚至熄滅的向往,或者是在你們的沖動上再加一把火。讀完之后若有所感,便是對本文最大的肯定了。
0x01 概述
php是一門針對web的專屬語言,但是隨著這么長時間發展,其實已經可以用php做很多事了,甚至語法結構的復雜度在趨近于java,還有即將出來的JIT,php的未來變的很難說。
盡管如此php還是一門解釋型語言。解釋型語言相對于靜態編譯型語言最大的特點就是他有一個特殊的解釋器。利用解釋器去執行相應的操作,例如php代碼是不會再去被翻譯成機器語言再去執行的。
例如在php 中
那么在相應的解釋器里面比如存在,一個與之相對應的解釋過程,可能是一個函數例如
int add(int a, int b){
    return a+b;
}
在這里面就僅需要調用這個add函數去解釋這個加法表達式的賦值過程。那么問題來了php的解釋器是怎樣的一種呈現過程呢?由此引出php的核心ZendVM(虛擬機)。
如果想要弄清楚我們寫的phpCode最后是如何被正確的運行的,就需要去了解Zend VM到底做了什么?也正是因為ZendVM賦予了php跨平臺的能力。所以相同的phpCode可以不需要修改就運行在處于不同平臺的解釋器上。這一點需要知道。
其實虛擬機大多都一樣,都是模擬了真實機器處理過程。不同是的運算符,數據類型的定義存在差異。在具體的語法邏輯結構上,大多都大同小異,例如if,switch,for這些流程控制,還有在函數的調用上。所以在探究一個虛擬機的內部結構時,你需要有一個明確的目標:
虛擬機內部用來描述整個執行過程的指令集。
單個指令對應的解釋過程。
清楚以上兩點,再來探究ZendVM。同樣ZendVM有編譯和執行兩個模塊。編譯過程就是將phpCode編譯為ZendVM內部定義好的一條一條的指令集合,再通過執行器去一步一步的解釋指令集合。
單條的指令在php里面被稱為"opline",指令的定義內容可以結合匯編的相關知識理解。例如匯編語言中
add eax,edx
jmp    10000
其中有兩個關鍵字add和jmp,這是匯編語言內部定義的指令集合中的兩個。同樣在php也有像類似的指令關鍵字叫做opcode,指令關鍵字后面是改指令處理的數據,簡稱為操作數。單條指令可能有兩個操作數op1,op2,也可能只有一個op1,也可能存在一個操作數都沒有的情況,但至多只有兩個操作數。那么指令是如何使用操作數,首先必須知道它的類型和具體的數據內容。這里可以具體看一下ZendVM內部定義的單條opline結構:
Opline
struct _zend_op {
    const void *handler;
    znode_op op1;
    znode_op op2;
    znode_op result;
    uint32_t extended_value;
    uint32_t lineno;
    zend_uchar opcode;
    zend_uchar op1_type;
    zend_uchar op2_type;
    zend_uchar result_type;
};
 
typedef struct _zend_op zend_op;
可以看到不僅有兩個操作數的op1和op2的定義,還有一個result變量,這個是變量是標識單條opline執行的返回值,當出現使用函數返回值賦值時,多個變量連續賦值,變量賦值出現在if判斷語句里面時,在這幾種情況下result變量就會被用到。
如果有想看到底定義了哪些opcode的同學,可以在zend/zend_vm_opcodes.h里面去看,本文使用的php版本為7.4.0-dev,一共有199條opcode。
下面簡單解釋一下,zend_op這個結構里面znode_op,zend_uchar這些結構的含義。可以看到一個操作數是有前面這兩種結構定義的相關變量,分別指向的是操作數內容和操作數類型,操作數的類型可以分為下面5種
#define IS_UNUSED    0        /* Unused operand */
#define IS_CONST    (1
UNUSED 表示這個操作數并未使用
CONST 表示操作數類型是常量。
TMP_VAR為臨時變量,是一種中間變量。出現再復雜表達式計算的時候,比如在進行字符串拼接(雙常量字符串拼接的時候是沒有臨時變量的)。
VAR 一種PHP內的變量,大多數情況下表示的是單條opline的返回值,但是并沒有顯式的表現出來,列如在if判斷語句包含某個函數的返回值,if(random()){},在這種情況下random()的返回值就是VAR變量類型。
CV變量,是在php代碼里面顯式的定義的出來的變量例如$a等。
Znode_op
接下來是操作數的內容znode_op
typedef union _znode_op {
    uint32_t      constant;
    uint32_t      var;
    uint32_t      num;
    uint32_t      opline_num; /*  Needs to be signed */

[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]  下一頁

【聲明】:黑吧安全網(http://www.zjtpzs.live)登載此文出于傳遞更多信息之目的,并不代表本站贊同其觀點和對其真實性負責,僅適于網絡安全技術愛好者學習研究使用,學習中請遵循國家相關法律法規。如有問題請聯系我們,聯系郵箱[email protected],我們會在最短的時間內進行處理。
  • 最新更新
    • 相關閱讀
      • 本類熱門
        • 最近下載
        神秘东方电子游艺