PHP語言作為腳本語言的一種,由于不需要進行編譯,所以通常PHP程序的分發都是直接發布源代碼。對于一些開源軟件來說,這并沒有什么問題,因為它本來就希望有更多的人閱讀代碼,希望有更多的人參與進來,而對于商業代碼來說,這卻是一個不太好的消息,不管是從商業秘密,還是從對公司產權的保護來說卻是一個問題,基于此,從而引出了對PHP代碼的加密和解密的議題。例如國內的Discuz論壇程序在開源之前要運行是必須安裝Zend Optimizer的,Zend官方的代碼加密軟件是[Zend Guard](http://www.zend.com/en/products/guard/),可以用來加密和混淆PHP代碼,這樣分發出去的代碼就可以避免直接分發源代碼,不過加密后的代碼是無法直接運行的,在運行時還需要一個解密的模塊來運行加密后的程序,要運行Zend Guard加密后的代碼需要安裝Zend Optimizer(PHP5.2之前的版本),或者安裝Zend Guard Loader(PHP5.3版本)擴展才能運行。
## 加密的本質[]()
本質上程序在運行時都是在執行機器碼,而基于虛擬機的語言的加密通常也是加密到這個級別,也就是說PHP加密后的程序在執行之前都會解密成opcode來執行。
PHP在執行之前有一個編譯的環節,編譯的結果是opcode,然后由Zend虛擬機執行,從這里看如果只要將源代碼加密,然后在執行之前將代碼解密即可。
從這里看,只要代碼能被解密為opcode,那么總有可能反編譯出來源代碼,其他的語言中也是類似,比如objdump程序能將二進制程序反匯編出來,.NET、Java的程序也是一樣,都有一些反編譯的程序,不過通常這些廠商同時還會附帶代碼混淆的工具,經過混淆的代碼可讀性極差,很多人都留意過Gmail等網站經過混淆的JS代碼吧,他們閱讀起來非常困難,經過混淆的代碼即使反編譯出來,讀者也很難通過代碼分析出代碼中的邏輯,這樣也就極大的增加了應用的安全性。
## 簡單的代碼加密解密實戰[]()
根據前文的介紹,作為實例,本文將編寫一個簡單的代碼加密擴展用于對PHP代碼的加密,我們只需要能把源碼加密,簡單通過瀏覽源代碼的方法無法獲取到源代碼那我們的目標就達到了,為了能正確執行加密后的代碼,我們還需要另一個模塊:解密模塊。
簡單的思路是把所有的PHP文件代碼進行加密,同時另存為同名的PHP文件,這是一種很簡單的做法,只是為了防止源代碼赤裸裸的暴露在代碼中。
加密也有很多種做法,第一種簡單的方法可以簡單的把源碼本身進行一些可逆加密,這樣我們可以在運行之前把真實的源碼反解出來執行,不過這種方式存在一種問題,只要知道了加密算法我們就可以把代碼給解出來,采用這種方式唯一能做的就是盡量增加加密的復雜度,既然正式的代碼在運行之前會被轉化成PHP源代碼,通過hack的方式是可以完完整整的獲得PHP源碼的,保密的效果就很有限了。
因為Zend引擎最終執行的是opcode,那么我們只要保證能解密出opcode則能滿足需求,我們只要簡單的將opcode進行簡單的序列化或者像Zend Guard那樣進行混淆,在運行之前將opcode還原,那么源代碼的信息就不存在了,這樣我們就能保證源代碼的安全,而不至于泄露。
### 加密[]()
前面提到加密的目的就是為了防止輕易獲取程序源碼的一種手段,對于PHP來說,將源碼編譯為opcode已經能達到目的了,因為PHP引擎最終都是需要執行opcode的。雖然可以將加密進一步,但是如果需要修改Zend引擎,那么成本就有點大了,因為需要修改Zend引擎了,而這是無法通過簡單的擴展機制來實現了,所以解密的成本也會變的太大,也就沒有實際意義了。
在本例中為了方便,代碼的加密和解密實現均實現在同一個模塊中。
熟悉PHP的同學可能會發現,這種加密方式和opcode緩存本質上沒有太大差別,opcode緩存的工作是將源碼編譯為opcode然后緩存起來,在執行的時候繞過編譯直接執行opcode,的確是沒錯的。這里唯一的區別是:opcode緩存是動態透明的,而加密后我們要做的是分發加密后的代碼。這么說我們是不是可以直接將APC之類的緩存擴展進行改造就可以了,其實理論上是可以的。不過這兩者的定位還是有差別的:加密的目的是為了減少源碼被分析破解的可能,而緩存只是為了提高程序運行的速度。
### 解密[]()
本例中的代碼其實并沒有進行加密,相對源代碼來說,opcode編譯本身也可以算做一種加密了,因為畢竟通過閱讀opcode來理解程序的邏輯還是比較困難的。
### 實現[]()
TODO
- 第一章 準備工作和背景知識
- 第一節 環境搭建
- 第二節 源碼結構、閱讀代碼方法
- 第三節 常用代碼
- 第四節 小結
- 第二章 用戶代碼的執行
- 第一節 生命周期和Zend引擎
- 第二節 SAPI概述
- Apache模塊
- 嵌入式
- FastCGI
- 第三節 PHP腳本的執行
- 詞法分析和語法分析
- opcode
- opcode處理函數查找
- 第四節 小結
- 第三章 變量及數據類型
- 第一節 變量的結構和類型
- 哈希表(HashTable)
- PHP的哈希表實現
- 鏈表簡介
- 第二節 常量
- 第三節 預定義變量
- 第四節 靜態變量
- 第五節 類型提示的實現
- 第六節 變量的生命周期
- 變量的賦值和銷毀
- 變量的作用域
- global語句
- 第七節 數據類型轉換
- 第八節 小結
- 第四章 函數的實現
- 第一節 函數的內部結構
- 函數的內部結構
- 函數間的轉換
- 第二節 函數的定義,傳參及返回值
- 函數的定義
- 函數的參數
- 函數的返回值
- 第三節 函數的調用和執行
- 第四節 匿名函數及閉包
- 第五節 小結
- 第五章 類和面向對象
- 第一節 類的結構和實現
- 第二節 類的成員變量及方法
- 第三節 訪問控制的實現
- 第四節 類的繼承,多態及抽象類
- 第五節 魔術方法,延遲綁定及靜態成員
- 第六節 PHP保留類及特殊類
- 第七節 對象
- 第八節 命名空間
- 第九節 標準類
- 第十節 小結
- 第六章 內存管理
- 第一節 內存管理概述
- 第二節 PHP中的內存管理
- 第三節 內存使用:申請和銷毀
- 第四節 垃圾回收
- 新的垃圾回收
- 第五節 內存管理中的緩存
- 第六節 寫時復制(Copy On Write)
- 第七節 內存泄漏
- 第八節 小結
- 第七章 Zend虛擬機
- 第一節 Zend虛擬機概述
- 第二節 語法的實現
- 詞法解析
- 語法分析
- 實現自己的語法
- 第三節 中間代碼的執行
- 第四節 PHP代碼的加密解密
- 第五節 小結
- 第八章 線程安全
- 第二節 線程,進程和并發
- 第三節 PHP中的線程安全
- 第九章 錯誤和異常處理
- 第十章 輸出緩沖
- 第十六章 PHP語言特性的實現
- 第一節 循環語句
- foreach的實現
- 第二十章 怎么樣系列(how to)
- 附錄
- 附錄A PHP及Zend API
- 附錄B PHP的歷史
- 附錄C VLD擴展使用指南
- 附錄D 怎樣為PHP貢獻
- 附錄E phpt測試文件說明
- 附錄F PHP5.4新功能升級解析
- 附錄G:re2c中文手冊