日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

通过php extension使disable_function支持通配符

發布時間:2025/3/19 php 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 通过php extension使disable_function支持通配符 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

為什么80%的碼農都做不了架構師?>>> ??

??本人學C語言不久,對指針內存管理等都還沒入門,php擴展的編寫更是胡亂在拼湊,以下是我“亂搞”的一點記錄,希望大家指點和輕噴。

? 一天翻php.ini的時候看到了一堆“同族”的函數

; This directive allows you to disable certain functions for security reasons. ; It receives a comma-delimited list of function names. This directive is ; *NOT* affected by whether Safe Mode is turned On or Off. ; http://php.net/disable-functions disable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited, pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig, pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask, pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority

當時就想要是支持通配符那么直接寫成 pcntl_* 這樣就簡便多了。想法是有了,但是不知道怎么實現好。偶然的機會看到了《淺談從PHP內核層面防范PHP WebShell》這文章,當中提到 zend_disable_function 這個函數,于是感覺先前的通配符想法可以實現了。

說一下簡單的思路吧:在php.ini讀取配置,遍歷函數表,正則匹配函數然后刪除掉,注冊一個同名函數以便給前端提示。

先用C模擬一下實現吧,代碼如下

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pcre.h>#define OVERCCOUNT 30 #define MAX_REGEX_COUNT 50 //最大支持規則數量char *replace_start(char *src) { //替換通配符*號static char buffer[4096];char *p, *str;char *orig = "*";char *rep = "(\\w+)";str = (char *)malloc(4096);p = strstr(src, orig);if (p == src) {sprintf(str, "%s%s", src, "$");} else {sprintf(str, "%s%s%s", "^", src, "$");}if (!(p = strstr(str, orig))) {return str;}strncpy(buffer, str, p-str); // Copy characters from 'str' start to 'orig' st$buffer[p-str] = '\0';sprintf(buffer+(p-str), "%s%s", rep, p+strlen(orig));free(str);return buffer; }int matchpattern(char *src, char *pattern) {pcre *re;const char *error;int erroffset;int ovector[OVERCCOUNT];int rc;re = pcre_compile(pattern, PCRE_CASELESS|PCRE_DOTALL, &error, &erroffset, NULL);if (re == NULL)return 0;rc = pcre_exec(re, NULL, src, strlen(src), 0, 0, ovector, OVERCCOUNT);free(re);return rc; }int main(int argc, char **argv) {char *function_table[] = \{"array_diff", "array_pop", "array_shift", "var_dump", "time", \"date", "str_replace", "strstr", "test", "abc_str"};char *ini = "array_*, *str test";char *s, *p;char *delim = ", ";//這里支持,號和空格來分割規則char *regex_list[MAX_REGEX_COUNT] = {0};int i = 0;s = strndup(ini, strlen(ini));p = strtok(s, delim);if (p) {do {p = replace_start(p);regex_list[i] = strndup(p, strlen(p));i++;} while ((p = strtok(NULL, delim)));}int match = -1, k;char *func, *regex;for (i = 0; i < 10; i++) {func = function_table[i];for (k = 0; k < MAX_REGEX_COUNT; k++) {regex = regex_list[k];if (!regex) break;//printf("regex:%s\n", regex);match = matchpattern(func, regex);if (match >= 0) {printf("function:%s() are disabled!!\n", func);}}}//free memoryfor(i = 0; i < MAX_REGEX_COUNT; i++) {regex = regex_list[i];if (regex) {free(regex);regex_list[i] = NULL;}}free(s);s = NULL;return 0; } 因為要使用正則,我在這里選擇了pcre庫,于是我們編譯的時候要帶上 -lpcre。運行看看我們的效果。


嗯,好像還不錯的樣子。接下來就是關鍵了,怎么改編成php擴展。

至于怎么快速創建一個php 擴展的就不介紹了,可以參考《快速開發一個PHP擴展》,我在這里新建了一個叫"solutest"的擴展。接著我們把上面的函數(main函數對應的改一下名字,我這里改為 static void remove_function())貼到solutest.c(文件名對應你創建時候輸入的名字)里面,對應的內存操作函數可以換成由php內核提供的e*系列函數,malloc->emalloc, free->efree ...還有一點是用e*系列申請的內存才用efree來釋放,要不然不會有錯,囧在這里吃過虧。(詳細參考《PHP擴展開發與內核應用》- 內存管理)。然后在?PHP_MINIT_FUNCTION 里面調用我們的 remove_function,為什么選擇?PHP_MINIT_FUNCTION ?或者你可以嘗試在?PHP_RINIT_FUNCTION 調用 (參考《PHP擴展開發與內核應用》- PHP啟動與終結)。編譯看看效果,別忘了需要pcre庫的支持,所以要加上 pcre.h 后,然后編輯 Makefile 在EXTRA_LIBS 加上 -lpcre。

OK,make && sudo make install,接著編輯php.ini加上我們的擴展(我測試環境是nginx + php-fpm,對應php.ini在 /etc/php5/fpm/php.ini,如果不確定你加載的配置文件路徑可以查看phpinfo的Loaded Configuration File

[solutest] extension=solutest.so

sudo /etc/init.d/php5-fpm restart

我們重啟fpm看看效果(如果apache環境直接重啟apache服務器即可)

嘛嘛~跑起來了。

? 怎么獲取系統的函數呢?我們可以參考一下zend_disable_function的實現

//file:"Zend/zend_API.c" line:2524 ZEND_API int zend_disable_function(char *function_name, uint function_name_length TSRMLS_DC) /* {{{ */ {if (zend_hash_del(CG(function_table), function_name, function_name_length+1)==FAILURE) {return FAILURE;}disabled_function[0].fname = function_name;return zend_register_functions(NULL, disabled_function, CG(function_table), MODULE_PERSISTENT TSRMLS_CC); } /* }}} */ ? ?嗯,從函數我們可以知道CG(function_table)保持了我們要的函數表,而且它是一個 HashTable 結構,我們可以通過 zend_hash_del 刪除函數表內某個函數。跟進去 zend_hash_del函數看看,
//file:"Zend/zend_hash.h" line:154 #define zend_hash_del(ht, arKey, nKeyLength) \zend_hash_del_key_or_index(ht, arKey, nKeyLength, 0, HASH_DEL_KEY) 是一個宏,繼續展開深入在 file:"Zend/zend_hash.c" line:486,函數有點就不貼了,可以看出是對HashTable的遍歷和一些鏈表刪除的操作,還有得到一個重要信息是函數名保存在了Bucket的arKey。以下是HashTbale的定義
//file:"Zend/zend_hash.h" line:52 struct _hashtable;typedef struct bucket {ulong h; /* Used for numeric indexing */uint nKeyLength;void *pData;void *pDataPtr;struct bucket *pListNext;struct bucket *pListLast;struct bucket *pNext;struct bucket *pLast;const char *arKey; } Bucket;typedef struct _hashtable {uint nTableSize;uint nTableMask;uint nNumOfElements;ulong nNextFreeElement;Bucket *pInternalPointer; /* Used for element traversal */Bucket *pListHead;Bucket *pListTail;Bucket **arBuckets;dtor_func_t pDestructor;zend_bool persistent;unsigned char nApplyCount;zend_bool bApplyProtection; #if ZEND_DEBUGint inconsistent; #endif } HashTable;

詳細的解釋可以參考《深入理解PHP內核》- PHP哈希表實現

? 好吧,依葫蘆畫瓢,嘗試遍歷一下function_table。把 remove_function 函數對應修改為

static void remove_function() { #ifdef ZEND_SIGNALSTSRMLS_FETCH(); #endifchar *ini = "array_*, *str test";char *s, *p;char *delim = ", ";//這里支持,號和空格來分割規則char *regex_list[MAX_REGEX_COUNT] = {0};int i = 0;s = estrndup(ini, strlen(ini));p = strtok(s, delim);if (p) {do {//p = replace_str(p, "*", "(\\w+)");p = replace_start(p);regex_list[i] = estrndup(p, strlen(p));i++;} while ((p = strtok(NULL, delim)));}int match = -1, k;char *regex;HashTable ht_func, *pht_func;Bucket *pBk;//拷貝一份CG(function_table)進行操作zend_hash_init(&ht_func, zend_hash_num_elements(CG(function_table)), NULL, NULL, 0);zend_hash_copy(&ht_func, CG(function_table), NULL, NULL, sizeof(zval*));pht_func = &ht_func;for (pBk = pht_func->pListHead; pBk != NULL; pBk = pBk->pListNext) {printf("%s()\n", pBk->arKey);}//free memoryzend_hash_destroy(&ht_func); //銷毀HashTablepht_func = NULL;for(i = 0; i < MAX_REGEX_COUNT; i++) {regex = regex_list[i];if (regex) {efree(regex);regex_list[i] = NULL;}}efree(s);s = NULL; } 保存以后又是一輪的? make && sudo make install。sudo /etc/init.d/php5-fpm restart,刷啦啦的一大片,嚇壞了吧,保存下來看看有多少。

應該差不多了吧,后面有...省略號是不是buffer什么的滿了所以還沒輸出完呢???

? OK,下面是重點了,刪除對應的函數。其實我們抄一下zend_disable_function就OK了,有同學會問為什么不直接調用zend_disable_function,別急,下面我會說道。再次修改我們的remove_function函數,這次修改便利的循環體和 char *ini 就好

char *ini = "array_p*,"; //使用array族函數測試


for (pBk = pht_func->pListHead; pBk != NULL; pBk = pBk->pListNext) {for (k = 0; k < MAX_REGEX_COUNT; k++) {regex = regex_list[k];if (!regex) break;//regex = "^array_p(\\w+)";match = matchpattern(pBk->arKey, regex);if (match >= 0) {printf("function:%s are disabled!!\n", pBk->arKey);//zend_disable_function(func, sizeof(func));if (zend_hash_del(CG(function_table), pBk->arKey, strlen(pBk->arKey)+1) == FAILURE) {printf("disable %s error\n", pBk->arKey);};disabled_function[0].fname = pBk->arKey;zend_register_functions(NULL, disabled_function, CG(function_table), MODULE_PERSISTENT TSRMLS_CC);}}} 因為把系統的函數刪除了,不知請者調用會產生一個php函數不存在的錯誤,腳本也會停止運行,于是需要注冊一個同名的函數回去,而這個函數什么也不做,輸出提示就好。那么我們需要在 remove_function 函數之前定義函數入口和提示函數
PHP_FUNCTION(print_disabed_info) { //I don't know why I can't use get_active_function_name in here// Maybe "EG"zend_error(E_WARNING, "*** function has been disabled! (°Д°≡°д°)エッ!?"); //get_active_function_name(TSRMLS_C) }static zend_function_entry disabled_function[] = {PHP_FALIAS(display_disabled_function, print_disabed_info, NULL)PHP_FE_END };

估計有同學吐槽為什么用***代替了顯示的函數名,這就是為什么我不調用zend_disable_function的原因。當時卡在這里很久,一直段錯誤,后來無意中注釋了?get_active_function_name(TSRMLS_C) 就跑起來了╯-__-)╯ ╩╩,求告知。。和上面一個編譯重啟服務器什么的,然后看效果,因為我們配置寫的是array_p*,所以一下函數被禁用了。(測試完以后記得關閉輸出)

然后隨便寫個腳本,調用一下array_pop函數什么的,然后執行之。

It's work!! :)

? 呼,不知不覺寫了這么長了,也懶得分兩篇了。接下來把讀取php.ini配置代碼寫上就完成了。其實這部門工作在擴展自動生成的代碼已經有了,只要稍微加工一下就好。

/* Declare any global variables you may need between the BEGINand END macros here: */ ZEND_BEGIN_MODULE_GLOBALS(solutest)char *disable_functions; ZEND_END_MODULE_GLOBALS(solutest) php_solutest.h 大概47行左右的樣子,去掉注釋加入我們的disable_functions變量 /* If you declare any globals in php_solutest.h uncomment this:*/ ZEND_DECLARE_MODULE_GLOBALS(solutest) solutest.c 30行左右,去掉注釋 /* {{{ PHP_INI*/ /* Remove comments and fill if you need to have entries in php.ini*/ PHP_INI_BEGIN()STD_PHP_INI_ENTRY("solutest.disable_functions", "", PHP_INI_ALL, OnUpdateString, disable_functions, zend_solutest_globals, solutest_globals) PHP_INI_END()/* }}} */ solutest.c 71行左右,去掉注釋,修改為我們的變量 /* If you have INI entries, uncomment these lines */REGISTER_INI_ENTRIES(); PHP_MINIT_FUNCTION 函數里面,去掉注釋 /* Remove comments if you have entries in php.ini */DISPLAY_INI_ENTRIES(); PHP_MINFO_FUNCTION? 函數里面,去掉注釋

然后編輯你的php.ini文件,加入配置

[solutest] extension=solutest.so solutest.disable_functions = array_p*, 編譯重啟服務器,然后瀏覽phpinfo會發現我們的配置已經被讀取了。
最后把我們的配置利用上,可以通過SOLUEXT_G(disable_functions)宏來訪問,對應修改 remove_function 函數。去掉 char *ini 因為已經不需要了,配置從php.ini 讀取,然后修改 s
s = estrndup(SOLUTEST_G(disable_functions), strlen(SOLUTEST_G(disable_functions))); OK,保存編譯重啟服務器測試。

:)預期的效果達到了。打完收工。

PS:
? 此擴展是本人YY的產物,沒有經過嚴格測試,請勿在生產機上使用。

代碼下載: http://pan.baidu.com/share/link?shareid=207778&uk=436715329

參考資料:
《淺談從PHP內核層面防范PHP WebShell》
《PHP擴展開發及內核應用》
《鳥哥博客》
《快速開發一個PHP擴展》
《深入理解PHP內核》

轉載于:https://my.oschina.net/s01u/blog/107911

總結

以上是生活随笔為你收集整理的通过php extension使disable_function支持通配符的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产一区二区内射 | 日韩一区二区三区av | a级黄色录像 | 中文字幕人妻熟女在线 | 宅男噜噜噜666在线观看 | 精品人伦一区二区三 | 久久一卡二卡 | 五月综合在线 | 香蕉一级片 | 97免费看 | 一级免费大片 | 久久久欧美 | 成人免费毛片xxx | 免费av在线网址 | 樱花视频在线观看 | 成人片在线看 | 日本高清视频一区二区三区 | 一起艹在线观看 | www.99色| 亚洲最大av网站 | 色偷偷噜噜噜亚洲男人 | 日本国产一区二区 | 人人人人干 | 青青草国产成人99久久 | 国内精品毛片 | 国产香蕉视频在线 | 黄色的网站在线观看 | 欧洲精品一区二区 | 96免费视频 | 在线观看你懂的网站 | 中文区中文字幕免费看 | 91传媒在线播放 | 亚洲成人播放 | 尹人香蕉| 欧美性色网站 | 伊人96| 国产中文欧美日韩在线 | 理伦毛片| 中文字幕3页 | 一区二区精彩视频 | 亚洲国产精品欧美久久 | 日本天天操| a v免费视频 | aaaaa级少妇高潮大片免费看 | 免费观看亚洲 | 波多野结av衣东京热无码专区 | 午夜av剧场 | 噼里啪啦高清 | 91精品国产综合久久久蜜臀九色 | 一区二区三区日韩电影 | 亚洲天堂午夜 | 免费精品在线观看 | 亚洲国产精品成人av | 国产91一区二区三区 | 男人的天堂va | 啦啦啦av| 色七七久久 | 亚洲开心网 | 欧美一a一片一级一片 | 狠狠干2020| 中文字幕在线国产 | www.88av| 久草综合在线观看 | 男人天堂伊人 | 亚洲黄色av网站 | 国产精品99久久久久久动医院 | 在线国产一区二区三区 | 四虎啪啪| 久久久久久久久久久久久久久 | 浮力影院国产第一页 | 捆绑japanhdxxxxvideos | www.色com | 天天做天天干 | 小毛片网站 | 欧美女优在线观看 | 免费无遮挡无码永久视频 | 大黑人交交护士xxxxhd | 99999精品视频 | 欧美三级a做爰在线观看 | 日本免费一区二区视频 | 一区二区三区视频免费看 | 亚洲国产成人一区 | 99香蕉网 | 亚洲激情久久久 | av在线导航| 精品乱子伦一区二区 | 日韩人妻无码一区二区三区99 | 天天干夜夜爱 | 免费在线观看一区二区三区 | 人妻在线一区二区三区 | www操| 国产精品社区 | 黄色最新网址 | 欧美变态视频 | 色干干| 91九色成人| 射在线| 久久97久久97精品免视看 | 国产精品久久av |