目錄
APM軟件原理分析 1
0x00 背景 1
0x01 安裝過程 1
0x02 啟動過程 5
0x03 觸發過程 7
0x04 整體流程 13
0x05 資源 13
APM軟件原理分析
本次使用PHP網站的APM監控插件作為研究對象,目的在於搞清:
1)分析安裝過程中對哪些進程和文件進行了修改;
2)插件啟動過程中對哪些進程進行了修改,調用了哪些函數;
3)程序運行過程中,如何觸發監控,相關進程之間的數據傳遞;
4)得到監聽插件對mysql,apache的hook點;
相關工具:IDA,gdb,strace,tcpdump,ps,lsof
相關知識:PHP擴展原理,PHP基本運行原理,socket進程間通信
環境:kail linux + apache2 + mysql + php5
Tips:APM 全稱Application Performance Management & Monitoring,應用性能管理 & 監控。用來監控和管理應用軟件是否有效運行的。主要指對企業的關鍵業務應用進行監測、優化,提高企業應用的可靠性和質量,保證用戶得到良好的服務,降低IT總擁有成本(TCO)。
百度搜索了一個APM軟件作為研究對象。
1. 用strace 跟蹤安裝過程,分析安裝軟件對哪些文件做了修改
用ps命令找到安裝文件的PID為7096
然後用strace –p 7096 –o log.txt(-p指定進程PID,-o指定輸出結果保存目錄)
安裝過程中會需要填寫licensekey,php安裝路徑等。
2. strace結果分析
安裝程序修改了php.ini
將安裝包中的so文件拷貝到php的擴展庫路徑下
將守護文件拷貝到/usr/bin/目錄下
將安裝過程寫入log日志中
找到日志文件可以直接對安裝過程進行分析,不過跟strace中找到的結果類似
還對php5的cli和apache2的conf.d進行了修改。
Php.ini
3. 安裝過程分析
安裝程序找到
這個過程中可以找到擴展oneapm.so,守護進程oneapm-daemon,可以丟到IDA中去分析了。
Tips: php擴展
有些頻繁使用的函數如果使用php語言編寫效率很低,如果采用php擴展,即用C語言編寫函數,然後編譯成so文件,修改php.ini文件,即可調用該函數。通過這種調用C語言庫函數的方式可以極大的提升函數效率。
參考:http://www.open-open.com/lib/view/open1392188698114.html
在oneapm.so文件中,有get_module入口,該函數通過獲取PC值,然後加一定的偏移,進入到apm_module_entry_ptr模塊入口。
在apm_module_entry_ptr中是一張函數表,包含了一下幾個函數
其中activate相關函數是激活模塊,發生在請求階段,需要觸發一定的條件才會執行,其他幾個函數在程序啟動時,初始化階段執行。
Tips:PHP運行原理
開始階段
PHP的整一個開始階段會經歷模塊初始化和模塊激活兩個階段。
MINIT 即模塊初始化階段,發生在Apache/Nginx啟動以後的整個生命周期或者命令行程序整個執行過程中,此階段只進行一次
RINIT模塊激活,發生在請求階段。
參考:http://www.mamicode.com/info-detail-1028100.html
Apache2 啟動時會自動調用其他幾個函數,因為php.ini中引入了oneapm.so,這幾個函數是在模塊初始化階段被執行的。通過ps與cat /proc/{PID}/maps可以看到apache對oneapm.so進行了引用。
zm_startup_apm
zm_startup_apm會fork()進程,然後去啟動oneapm-daemon守護進程。所以軟件安裝完成後,需要重啟apache。其余的則是做一些初始化工作。
zm_info_apm
zm_info_apm則是收集一些基礎信息。
整個啟動階段,apache2 加載oneapm.so,對模塊進行初始化,自動調用以上幾個函數,啟動守護進程oneapm-daemon,收集一些基本函數。
用tcpdump抓包,可以看到服務器向展示網站傳輸性能信息。
用strace對oneapm-daemon進程進行監控。
Strace –ff –p 1278 –s 1024 –o oneapm (-ff監控子進程子線程,與-o配合使用將結果寫到不同的文件中, -s指定一行的長度, -p指定進程PID)
oneapm-daemon會啟動4個子線程,strace生成4個文件oneapm.1278 oneapm.1279, oneapm.1280, oneapm.1281
oneapm.1280
線程1280不停獲取時間,然後向目標網站發送數據,發送的數據與tcpdump抓到的數據相同
oneapm.1279
線程1279 開啟了一個監聽,用accept()函數等待連接。 然後用read()函數從這個句柄讀取數據。句柄fd=6。
由此可知oneapm-daemon 通過一個線程開啟監聽,等待數據傳輸過來,讀取數據。然後用另外一個線程將數據發送到展示網站上。 這裡使用了socket進行本地進程間的通信。
由於socket 的fd是瞬時性的,所以無法用lsof直接看到該句柄。用gdb attach 上去。
Read調用有以下幾個點,都斷住。
Fd=6,buffer中讀出我們想要的數據。此時用lsof查看句柄信息
連接到/tmp/.oneapm-daemon.domain文件上。
由於只有apache2 連接了oneapm.so擴展,所以先監聽apache2。
Strace –ff –p 5046 –s 1024 –o apache
在結果文件中找到如下信息
Apache2 通過socket連接到/tmp/.oneapm-daemon.domain,向其發送數據。 通過這樣與oneapm-daemon實現進程間數據的傳遞。
Gdb attach上apache,進入子進程模式,並對oneapm.so的幾個激活函數下斷點。
斷點命中
可以看到調用的次序zm_activate_apm,zm_deactivate_apm,zm_post_zend_deactivate_apm
打印其調用棧,這幾個函數都是在一定條件達成後,php激活zend相關函數自動調用的。
分析這幾個函數的代碼
1)zm_activate_apm()
zm_activate_apm調用了很重要的函數hook_start()。
Hook_start()函數如下
這邊是監控程序的核心hook入口點,apm程序對網站性能監控的hook都可以在這裡找到。
2)zm_deactivate_apm()做的就是一些反激活的事
3)zm_post_zend_deactivate_apm()
send_connect_data()
talk_to_server()
do_write()
調用過程如下
zm_post_zend_deactivate_apm()àsend_connect_data()àtalk_to_server()à
do_connect(),do_write()->send()
zm_post_zend_deactivate_apm()負責發送數據。
監控階段代碼流程如上圖所示
1) 安裝文件將oneapm.so與oneapm-daemon拷貝到對應的文件夾下,然後修改php配置文件,以實現php擴展
2) Apache啟動過程中,載入擴展oneapm.so,自動執行一些初始化函數,拉起守護進程
oneapm-daemon,並收集一些系統信息。oneapm-daemon進程創建監聽,等待本地socket連接。
3) 網站請求與一些時間條件激發apache中的oneapm.so 激活函數,開始hook收集當前的一些服務器性能信息,然後反激活,接著將這些信息通過本地socket的方式進行進程通信,連接到oneapm-daemon的監聽上,然後向其發送收集到的數據。oneapm-daemon進程接收到連接,並讀取數據,用另外一個線程向展示網站發送數據。
Hook信息,位於hook_start()函數中
相關ELF文件