天天看點

ed2k解析源碼php,PHP源碼調試分析

前言

在看代碼的時候遇到了PHP的一些函數,有些函數的特性很魔性,并不好了解。

于是嘗試搭建環境對PHP源碼進行調試,希望更加深入的一些了解PHP的特性。

必備安裝

目标:在Windows環境下,建構PHP7.2的源代碼編譯和調試環境

安裝VisualStudio

首先需要安裝最強IDEVisual Studio,這個軟體的版本有很多,我選擇的是Visual Studio Professional 2017 (version 15.7)。 下載下傳連結:

ed2k://|file|mu_visual_studio_professional_2017_version_15.3_x86_x64_11100064.exe|1069960|900673A59F0798822207F72FAA0DA6A9|/

需要其他版本(VS2015等),可以到MSDN上的開發人員工具欄目自行選擇下載下傳即可。

安裝過程非常簡單,注意兩個調試必備勾選,其他的選項根據自己需求選擇

ed2k解析源碼php,PHP源碼調試分析

php-sdk

PHP SDK是用于Windows PHP建構的工具包,也是必不可少的工具。 下載下傳位址:

https://github.com/Microsoft/php-sdk-binary-tools

我選擇的版本為最新版php-sdk-2.1.7。

PHP

本文主角,我選擇的版本為PHP 7.2.1。 下載下傳位址:

https://github.com/php/php-src

下載下傳完成後使用Git Bash切換分支

cd php-src

git checkout PHP-7.2.1

編譯PHP

進入php-sdk的目錄,可以看到目錄下面有4個Windows批處理檔案

ed2k解析源碼php,PHP源碼調試分析

前面安裝的是Visual Studio 2017,作業系統也是64位的,是以這裡選擇phpsdk-vc15-x64.bat,打算編譯64位的。

ed2k解析源碼php,PHP源碼調試分析

在php-sdk目錄打開CMD視窗,運作phpsdk-vc15-x64.bat。 可以發現有了新的Shell提示符$。

繼續在新的shell下運作指令

phpsdk_buildtree phpdev

這時候我們會發現php-sdk這個目錄下面會多一個名為phpdev的檔案夾。

ed2k解析源碼php,PHP源碼調試分析

注意Shell的運作路徑也發生了變化php-sdk\phpdev\vc15\x64\。

ed2k解析源碼php,PHP源碼調試分析

再将php-src整個檔案夾移動至php-sdk\phpdev\vc15\x64\下面。

ed2k解析源碼php,PHP源碼調試分析

然後shell中進入php-src目錄,執行指令,下載下傳依賴關系元件。

phpsdk_deps --update --branch maste

成功資訊如下:

ed2k解析源碼php,PHP源碼調試分析

運作buildconf.bat生成的configure檔案,配置好參數,執行指令如下

configure --disable-all --enable-cli --enable-debug

成功資訊如下:

ed2k解析源碼php,PHP源碼調試分析

執行編譯指令nmake

ed2k解析源碼php,PHP源碼調試分析

編譯成功資訊為:

SAPI sapi\cli build complete

可執行的二進制檔案路徑為

php-sdk\phpdev\vc15\x64\php-src\x64\Debug_TS\php.exe

觀察是否輸出php資訊,編譯成功則輸出

php.exe -v

調試配置

斷點調試的需要一個趁手的工具,可以使用之前安裝的Visual Studio 2017,但我個人選擇的是輕量級的Vs code。

安裝Vs code,然後這裡需要安裝C/C++的拓展,調試的方式為啟動調試。

ed2k解析源碼php,PHP源碼調試分析

點選調試 --> 打開配置,設定配置檔案launch.json的參數如下

ed2k解析源碼php,PHP源碼調試分析

program,二進制可執行檔案路徑。

args,同目錄下運作的PHP檔案,也就是我們要調試的檔案

cwd,二進制可執行檔案目錄

點選調試按鈕,即可開始調試。

ed2k解析源碼php,PHP源碼調試分析

調試getimagesize函數

這裡選擇getimagesize這個函數進行斷點調試。 探究為何這個函數如何加載網絡圖檔資源;為何這個函數在Windows下

修改調試的1.php内容如下

getimagesize("http://www.rai4over.cn/images/avatar.jpg");

#getimagesize("./avatar.jpg");

?>

在php-sdk\phpdev\vc15\x64\php-src\ext\standard\image.c中設定getimagesize的斷點

ed2k解析源碼php,PHP源碼調試分析

點選調試後程式會停在這裡則表示斷點成功。 通常會使用F10,F11進行調試。 - F10,單步跳過,調試時不進入函數内部。 - F11,單步調試,調試時進入函數内部。

比較麻煩的,單純的F11調試耗費時間,而F10又可能跳過函數關鍵函數,難以定位。

于是我便采F10為主,提升調試效率,一邊F10,一邊打開Wireshark觀察HTTP請求流量。

當過某個函數産生流量後,再F11進入函數内部進行調試。

最終得到函數調用棧(由下至上):

send

php_sockop_write

_php_stream_write_buffer

_php_stream_write

php_stream_url_wrap_http_ex

php_stream_url_wrap_http

_php_stream_open_wrapper_ex

php_getimagesize_from_any

PHP_FUNCTION

send()是Windows Api,能夠通過已經建立的連接配接發送資料。

在phpdev\vc15\x64\php-src\main\streams\xp_socket.c第77行被調用。

didwrite = send(sock->socket, buf, XP_SOCK_BUF_SIZE(count), (sock->is_blocked && ptimeout) ? MSG_DONTWAIT : 0);

這也是這個函數加載網絡圖檔資源的原因,之前印象中一直以為隻能擷取本地資源,踩過大坑。

修改為擷取本地圖檔資源

#getimagesize("http://www.rai4over.cn/images/avatar.jpg");

getimagesize("./avatar.jpg");

?>

函數棧調用

ed2k解析源碼php,PHP源碼調試分析

在php-sdk\phpdev\vc15\x64\php-src\Zend\zend_virtual_cwd.c第841行,發現調用了FindFirstFileExW()函數。

hFind = FindFirstFileExW(pathw, FindExInfoBasic, &dataw, FindExSearchNameMatch, NULL, 0);

這個函數就是