Press "Enter" to skip to content

Getting $_GET/$_POST/$_COOKIE Inside a PHP Module

Recently I was working on a PHP security module, in which I needed to get the user's Cookie inside a Module function in order to generate a signature. I searched Baidu/Google all day and found not a single relevant resource, so I had no choice but to send a plea to the Yahoo PHP dev mailing list. Later, by chance I spotted a variable called http_globals on Google, and my eyes lit up. Although there was no detailed documentation, after a round of trial and error — and N segmentation faults — I finally got it working!
Now let me share it with you, with examples:
Suppose we want to get $_GET['c'];
First, an introduction to http_globals;
1. http_globals is defined in php_globals.h;
zval * http_globals[6];
Its indices are:

#define TRACK_VARS_POST           0
#define TRACK_VARS_GET            1
#define TRACK_VARS_COOKIE         2
#define TRACK_VARS_SERVER         3
#define TRACK_VARS_ENV            4
#define TRACK_VARS_FILES          5
#define TRACK_VARS_REQUEST        6

For some reason, http_globals is defined with 6 elements but 7 indices are defined. My guess is that REQUEST is just the merge of GET and POST, and since access is done through macros, the macro may end up handling TRACK_VARS_REQUEST as the merge of GET and POST.
2. How to get it:

zval * arr;
zval ** temp;
char * key = "c", r_str;
int len = 2, r_len,duplicate=1;
arr = PG(http_globals)[TRACK_VARS_GET];
zend_hash_find(HASH_OF(arr), key, len, (void **)&temp);
r_str = Z_STRVAL_PP(temp);
r_len = Z_STRLEN_PP(temp);
ZVAL_STRINGL(return_value, r_str, r_len, duplicate)

3. Analysis
PG is a macro, defined in php_globals.h:
# define PG(v) TSRMG(core_globals_id, php_core_globals *, v)
And TSRMG is also a macro, defined in TSRM.h:
#define TSRMG(id, type, element) (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
And TSRM_UNSHUFFLE_RSRC_ID is also a macro, also defined in TSRM.h:
#define TSRM_UNSHUFFLE_RSRC_ID(rsrc_id) ((rsrc_id)-1)
So PG(http_globals), once expanded, becomes:

PG(http_globals) =>;
TSRM(core_globals_id, php_core_globals *, http_globals);
=>;
((php_core_globals *)(*((void ***))tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(core_globals_id)])->http_globals);
=>;
((php_core_globals *)(*((void ***))tsrm_ls))[(core_globals_id-1)])->http_globals);

HASH_OF is also a macro, defined in zend_API.h:

         #define HASH_OF(p) (Z_TYPE_P(p)==IS_ARRAY ? Z_ARRVAL_P(p) : ((Z_TYPE_P(p)==IS_OBJECT ? Z_OBJ_HT_P(p)->get_properties((p) TSRMLS_CC) : NULL)))

4. Getting it
Based on the test results, we can conclude that PG(http_globals)[TRACK_VARS_GET] is a hash table;
5. The problem
One issue is that in Zend, a string's len seems to have to count the terminating '\0'. It's precisely because I defined len=1 that it crashed N times... frustrating.
6. A bit more about return_value:

1. php.h:        #define PHP_FUNCTION                       ZEND_FUNCTION
2. zend_API.h: #define ZEND_FUNCTION(name)        ZEND_NAMED_FUNCTION(ZEND_FN(name))
3. zend_API.h: #define ZEND_FN(name)      zif_##name
4. zend_API.h: #define ZEND_NAMED_FUNCTION(name)       void name(INTERNAL_FUNCTION_PARAMETERS)
5. zend.h: #define INTERNAL_FUNCTION_PARAMETERS   int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC</blockquote>

So our function PHP_FUNCTION(getGetParam) becomes:
void zif_getGetParam( int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC);
As you can see, return_value is defined by default — it's the carrier of the return value.
Heh, that's all I'll write for now. I'll add more when I have time.
7. The original code:

PHP_FUNCTION(confirm_getCookie_compiled){
	char *arg = NULL;
	int arg_len, len;
	ulong ikey;
	char * strg, * skey;
	zval * arr;
	zval **data;
	HashTable* h;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
		WRONG_PARAM_COUNT;
	}
	arr = PG(http_globals)[TRACK_VARS_GET];
	h  = HASH_OF(arr);
	array_init(return_value);
	zend_hash_internal_pointer_reset(h);
	int count = zend_hash_num_elements(h);
	for(int i=0 ; i&lt;count; i++){
		zend_hash_get_current_data(h, (void**)&data);
		zend_hash_get_current_key(h, &skey, &ikey, 0);
		add_assoc_stringl(return_value, skey, Z_STRVAL_PP(data), Z_STRLEN_PP(data), 1);
		zend_hash_move_forward(h);
	}
	return;
	//      RETURN_STRINGL(strg, len, 0);
	//ZVAL_STRINGL(return_value, strg, len, 0);
}

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.