咱們能夠根據其餘擴展的config.m4文件來修改爲咱們的必要編譯配置信息。這裏這個模塊幾乎是一個空的config.m4文件就行,
而後利用phpize來生成configure文件而後是 ./configure && make && make install執行就能編譯一份咱們的動態庫
test.php
<?php
echo helloworld_module();
?>
輸出:
"Hello,world"
完成了PHP擴展,咱們已經深刻了php的c代碼內部,可是在一些狀況下,這還不夠。咱們須要深刻到c語言調用c庫的過程中,在linux下面一個很給力的工具是LD_PRELOAD環境變量
LD_PRELOAD環境變量是編譯器找到程序中所引用的函數或全局變量所存在的位置的一個過濾器,好比在php的c代碼裏調用一個開始網絡鏈接的方法connect,事實上就是經過動態連接
去尋找linux的c庫的函數connect,這些連接文件通常放在lib下面,這也就爲咱們影響php的代碼執行提供了一個切入點。由於php程序在動態載入lib下面的函數connect以前會檢查LD_PRELOAD
提供的動態庫裏有沒有這個connect函數,咱們能夠在這裏對php的行爲進行干涉。
下面以一個簡單的過濾網絡訪問的例子說明如何實現:
先是一個準備做爲LD_PRELOAD環境變量的值的so文件的代碼。
lp_demo.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
#include <dlfcn.h>
//定義咱們本身的connect函數
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t
addrlen){
static int (*connect_linuxc)(int, const struct sockaddr*, socklen_t)=NULL;
unsigned char *ip_char;
//利用 lsym的RTLD_NEXT選項繞過LD_PRELOAD環境變量的connect方法找到c庫的函數
if (!connect_linuxc) connect_linuxc=dlsym(RTLD_NEXT,"connect");
ip_char=serv_addr->sa_data;
ip_char+=2;
//192.168.2.3 找到了
if ((*ip_char==192)&&(*(ip_char+1)==168)&&(*(ip_char+2)==2)&&(*(ip_char+3)==3)) {
//簡單返回一個權限錯誤的代碼
return EACCES;
}
// 調用真正的connect方法
return connect_linuxc(sockfd,serv_addr,addrlen);
}
編譯成so文件
$ gcc -o lp_demo.so -shared lp_demo.c -ldl
測試文件 test.php
<?php
file_get_contents("http://192.168.2.3/");
?>
使用方法
LD_PRELOAD=lp_demo.so php test.php
這樣他將不可能訪問的到192.168.2.3這種咱們內部的網址。起到一個很好的沙盒做用。
除此以外咱們還能夠利用fwrite fopen等函數將php對文件系統的讀寫操做轉移到mencache,nosql之類的後端資源當中。
最後,即便咱們已經深刻了c庫的內部,也不意味着咱們走到了最底層,在c庫下面,還有一堆sys_開頭的函數,他們纔是內核空間裏的真正函數,在此就不在探討了。
php