PHP FFI
什么是FFI
FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。在PHP 7.4版本作为扩展。
使用 FFI 调用 C 的函数
首先要启用PHP7.4的ext / ffi,需要注意的是PHP-FFI要求libffi-3以上。
然后,我们需要告诉PHP FFI我们要调用的函数原型是咋样的,这个我们可以使用FFI :: cdef
,它的原型是:
public static FFI::cdef ( string $code = "" , string|null $lib = null ) : FFI
Creates a new FFI object.
code: 包含常规 C 语言声明序列(类型、结构、函数、变量等)的字符串。 实际上,这个字符串可能是从 C 头文件复制粘贴的。
lib: 要加载并与定义链接的共享库文件的名称。也就是我们需要的.ll
或.so
文件,它与我们声明字符串是对应的。
example:
// 创建一个FFI对象,加载libc并且导入printf函数
$ffi_print = FFI::cdef(
"int printf(const char *format, ...);", // C的定义规则
"libc.so.6"); // 指定libc库
// 调用C的printf函数
$ffi_printf->printf("Hello %s!\n", "world");
// 加载math并且导入pow函数
$ffi_pow = FFI::cdef(
"double pow(double x, double y);",
"libboost_math_c99.so.1.66.0");
// 这里调用的是C的pow函数,不是PHP自己的
echo $ffi_pow->pow(2,3),PHP_EOL;
定义变量和数组
当然,FFI也是可以定义变量和数组的。使用FFI::new()
创建一个C的数据结构,也就是变量声明,这些变量的内容将保存在cdata属性钟。而数组则直接就可以操作这个函数的返回值。当我们要结束使用的时候,要用FFI::free()
来释放变量。
public static FFI::new ( mixed $type [, bool $owned = TRUE [, bool $persistent = FALSE ]] ) : FFI\CData
type: Type是字符串形式的有效C声明,或者是已创建的FFI\ctype的实例。
owned: 是创建自有(即托管)还是非托管数据。 托管数据与返回的 FFI\CData 对象一起存在,并在该对象的最后一个引用被常规 PHP 引用计数或 GC 释放时释放。 当不再需要时,应通过调用 ·FFI::free()
来释放非托管数据。
persistent: 是在系统堆(使用 malloc()
)还是在 PHP 请求堆(使用 emalloc()
)上永久分配 C 数据结构。
example:
// 创建一个int变量
$x = FFI::new("int");
var_dump($x->cdata); // int(0)
// 为变量赋值
$x->cdata = 5;
var_dump($x->cdata); // int(5)
// 计算变量
$x->cdata +=2;
var_dump($x->cdata); // int(7)
// 结合上面两个FFI对象操作
echo "pow value:", $ffi_pow->pow($x->cdata,3),PHP_EOL;
// pow value:343
$ffi_printf->printf("Int Pow value is : %f\n", $ffi_pow->pow($x->cdata, 3));
// Int Pow value is : 343.000000
// 创建一个数组
$a = FFI::new("long[1024]");
// 为数组赋值
for ($i = 0; $i < count($a); $i++) {
$a[$i] = $i;
}
var_dump($a[25]); // int(25)
$sum = 0;
foreach ($a as $n) {
$sum += $n;
}
var_dump($sum); // int(523776)
var_dump(count($a)); // int(1024) 数组长度
var_dump(FFI::sizeof($a)); // int(8192), 内存大小
利用FFI命令执行
使用C的system()
函数可命令执行
$ffi = FFI::cdef("int system(const char *command);");
$ffi->system("/readflag >flag.txt");