此篇文章記錄一下我這些天PHP HACKING的經(jīng)驗,亂七遭八的。各位看官莫見笑,有問題多交流。
先大概描述一下PHP內(nèi)核的情況,PHP內(nèi)核分為2個部分,ZEND和PHP CORE。ZEND可以說是PHP的內(nèi)核的內(nèi)核,他負責(zé)與溝通。分配內(nèi)存,初始化什么什么的,他就負責(zé)了,同時把可讀的腳本代碼系統(tǒng)化。而PHP CORE呢,負責(zé)一些外圍處理,比如與SAPI溝通。APACHE、IIS啊之類的東西。php.ini他也管。 還負責(zé)網(wǎng)絡(luò)和文件I/O。最后提醒一句,PHP是用C寫的,而不是C++,大多數(shù)資源,類型,在PHP內(nèi)核中需要轉(zhuǎn)換。
在C語言中,套接字這樣定義:SOCKET socket(int af,int type,int protocol);
在PHP中是:
typedef int PHP_SOCKET;
typedef struct {
PHP_SOCKET bsd_socket;
int type ;
int error;} php_socket;
看起來是一樣的。那就來看看一個最簡單的網(wǎng)絡(luò)函數(shù)。
PHP_FUNCTION(socket_write)
{
zval *arg1;
php_socket *php_sock;
int retval, str_len;
long length;
char *str;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l", &arg1, &str,
&str_len, &length) == FAILURE)
return;
ZEND_FETCH_RESOURCE(php_sock, php_socket *, &arg1, -1, le_socket_name,
le_socket);
if (ZEND_NUM_ARGS() < 3) {
length = str_len;
}
#ifndef PHP_WIN32
retval = write(php_sock->bsd_socket, str, MIN(length, str_len));
#else
retval = send(php_sock->bsd_socket, str, min(length, str_len), 0);
#endif
if (retval < 0) {
PHP_SOCKET_ERROR(php_sock, "unable to write to socket", errno);
RETURN_FALSE;
}
RETURN_LONG(retval);
}
先說zval,ZVAL是PHP內(nèi)核中的結(jié)構(gòu),里面包含任何數(shù)據(jù)類型。所以在PHP內(nèi)核中最好用ZVAL,而少用char *。這樣可能會帶來一些安全問題??瓷厦娴拇a,好象是沒問題。其實不然,看char * str。他作為第2個參數(shù)被傳入。
執(zhí)行socket_write ( $sock, "hellosdfs35434sdvx.plpop34][[]" );
正常得很。但是如果我們提交一個PHP數(shù)組他就會暴錯了。為什么呢?雖然說char str[]從某種意義上等于char * str。但是PHP中的數(shù)組和C語言可不一樣。PHP程序本身這種錯誤太多了,PHP的開發(fā)者可能已經(jīng)習(xí)慣了。
繼續(xù)PHP SOCK.zend_parse_parameters函數(shù)是得到傳入?yún)?shù)的,當CHAR類型被傳入的時候,必須指定CHAR LEN.ZEND_NUM_ARGS()是得到函數(shù)個數(shù)的。
那TSRMLS_CC是什么東西。這里得專門講一下TSRM。他的全稱是Thread Safe Resource Management。
PHP是作為網(wǎng)絡(luò)腳本來使用的,這意味著,成千上萬的人在同1秒鐘里可能調(diào)用同一個頁面??蛻鬉請求的是ID1,客戶B請求的是ID2,以下省去數(shù)萬字。
如果沒有TSRM的話,單線程的SAPI還好說,像apache,iis這樣的話。那么PHP可能亂套。TSRM的作用就是保持變量的中立性。在PHP中,基本上從全局傳入?yún)?shù)都要使用TSRM.PHP hacking中也應(yīng)該使用TSRM。
ZEND_FETCH_RESOURCE(php_sock, php_socket *, &arg1, -1, le_socket_name, le_socket);
相當于是把參數(shù)1 當做PHP_SOCK類型在ZEND注冊資源。關(guān)于SOCKET_WRITE后面都是一些簡單的C函數(shù),就不談了這個了。
關(guān)于PHP內(nèi)核里面建立套接字還有一種方法,那就是用PHP流。PHP流是很強大的,是I/O的代名詞。無論是FILE I/O,還是NETWORK I/O。
拿php_stream_open_wrapper API來說,他支持FILE、TCP、UDP、、SCP、HTTP、。
php_stream * stream = php_stream_open_wrapper
("http://www.php.net", "rb", REPORT_ERRORS, NULL);
if (stream) {
while(!php_stream_eof(stream)) {
char buf[1024];
if (php_stream_gets(stream, buf, sizeof(buf))) {
printf(buf);
} else {
break;
}
}
php_stream_close(stream);
}