PHP 7.2.5扩展中Hook函数时如何获取函数参数
Hey there! Let's figure out how to capture function arguments (like the 'ls' in system('ls')) in your PHP 7.2.5 extension using the ZEND_DO_ICALL opcode hook. Your initial setup is already on the right track—we just need to expand the handler function to access the execution context and extract the parameters.
Here's the step-by-step solution:
First, understand that the ZEND_OPCODE_HANDLER_ARGS gives you access to execute_data, a zend_execute_data structure that holds all the details about the current function call, including the function itself and its arguments. We'll use this to pull out the info we need.
Updated Handler Function
Replace your existing do_fcall_handle with this expanded version:
PHP_MINIT_FUNCTION(hello) { zend_set_user_opcode_handler(ZEND_DO_ICALL, do_fcall_handle); return SUCCESS; } static int do_fcall_handle(ZEND_OPCODE_HANDLER_ARGS) { zend_execute_data *ex = execute_data; zend_function *called_func = ex->func; // Skip if this isn't a standard function type if (called_func->type != ZEND_INTERNAL_FUNCTION && called_func->type != ZEND_USER_FUNCTION) { return ZEND_USER_OPCODE_DISPATCH; } const char *func_name = ZSTR_VAL(called_func->common.function_name); fprintf(stderr, "🔍 Called function: %s\n", func_name); // Get number of arguments passed to the function uint32_t arg_count = ex->func->common.num_args; fprintf(stderr, "📊 Argument count: %u\n", arg_count); // Iterate through each argument for (uint32_t i = 0; i < arg_count; i++) { zval *arg = zend_get_arg_value(ex, i); if (!arg) { fprintf(stderr, "⚠️ Argument %u is null\n", i); continue; } // Handle different argument types switch (Z_TYPE_P(arg)) { case IS_STRING: fprintf(stderr, "✅ Argument %u: '%s'\n", i, Z_STRVAL_P(arg)); break; case IS_LONG: fprintf(stderr, "✅ Argument %u: %ld\n", i, Z_LVAL_P(arg)); break; case IS_ARRAY: fprintf(stderr, "✅ Argument %u is an array\n", i); // You can further inspect arrays using Zend API functions if needed break; default: fprintf(stderr, "ℹ️ Argument %u is of type %d\n", i, Z_TYPE_P(arg)); break; } } // Let PHP continue executing the original opcode return ZEND_USER_OPCODE_DISPATCH; }
Key Details Explained:
zend_execute_data *ex: This is the execution context for the current function call—it's our gateway to all call-related data.called_func->common.function_name: Gets the name of the function being called (e.g., "system").zend_get_arg_value(ex, i): Retrieves thezvalpointer for the i-th argument (indexes start at 0).- Type Checking: We use
Z_TYPE_P(arg)to check the argument's type before accessing its value—this prevents crashes from invalid type access. fprintf(stderr, ...): We output to stderr instead of stdout to avoid mixing extension debug output with PHP's normal output.
Example Output for system('ls'):
When you run your test PHP script, you'll see this in your console (stderr):
🔍 Called function: system 📊 Argument count: 1 ✅ Argument 0: 'ls'
Optional: Target Specific Functions
If you only want to capture arguments for specific functions (like system), add a string comparison check:
if (strcmp(func_name, "system") == 0) { // Your argument capture logic here }
This will limit your handler to only act on the system function, which might be useful if you don't want to process every function call.
内容的提问来源于stack exchange,提问作者mashiro

