在php中提取'blob' python序列化元组数据

fxnxkyjh  于 2023-05-21  发布在  PHP
关注(0)|答案(2)|浏览(118)

我试图在一个基于php的网站工具中利用另一个程序的数据库,显然原始的是用python构建的,并将一些数据放入python元组中,并将其序列化以将其存储为sql表中的blob。
我不是一个python程序员,所以我甚至不知道如何看到这个blob中的内容,但我知道数据字段的一些“类型”指示符存储在那里,我想提取它们和其他有用的东西。
有没有办法在php中“反序列化”一个python元组?

jv4diomz

jv4diomz1#

blob数据原来是一个pickle tuple(这也是我鄙视python的部分原因--这两种数据类型都只有python才能读取!)Python程序员:标准化的惯例?谁需要标准化公约?!?!')
我想出了一个笨拙的方法来“解嵌”数据,并使用命令行对数据进行json序列化。为了将二进制blob数据放入命令行,我对它进行了base64编码。这是janky但它的作品为我所需要的:

/**
 * use a python exec call to 'unpickle' the blob_data
 *   to get the binary blob into a command line argument, base64 encode it
 *   to get the data back out of python, json serialize it
 * @param string $blob binary blob data
 * @return mixed
 */
public static function unpickle($blob) {
    $cmd = sprintf("import pickle; import base64; import json; print(json.dumps(pickle.loads(base64.b64decode('%s'))))", base64_encode($blob));
    $pcmd = sprintf("python -c \"%s\"", $cmd);
    $result = exec($pcmd);
    $resdec = json_decode($result);
    return $resdec;
}
v1uwarro

v1uwarro2#

在这个概念上多玩一点,我给自己多了几个选择。首先,我采用了前面答案中的命令行版本,并将其转换为功能更强的Python脚本:

unpickle.py

#!/usr/bin/env python3

import pickle
import json
import sys
import base64
import select

def isBase64(s):
    try:
        return s == base64.b64encode(base64.b64decode(s)).decode('ascii')
    except Exception:
        return False

bblob = None
if (len(sys.argv) > 1) and isBase64(sys.argv[1]):
    bblob = base64.b64decode(sys.argv[1])
elif select.select([sys.stdin, ], [], [], 0.0)[0]:
    try:
        with open(0, 'rb') as f:
            bblob = f.read()
    except Exception as e:
        err_unknown(e)

if bblob != None:
    unpik = pickle.loads(bblob)
    jsout = json.dumps(unpik)
    print(jsout)

此脚本允许您在命令行上将pickle元组'byte'中的blob数据指定为base64编码字符串,或者将原始blob数据通过管道传输到脚本中。如果数据有效且格式正确,这两种变体都将输出json。(如果没有则为空)
如果需要的话,可以使用pyinstaller -F将其转换为自包含的二进制文件,以便在没有python的系统上plop。如果我在使用pyinstaller二进制文件的系统上运行它,或者使用python脚本的系统上运行它,或者只使用python的系统上运行它,我在我的laravel模型中创建了以下静态方法。(我最终会把它移到服务模块中)

/**
 * call either a pyinstaller binary or python script with raw blob data to be unpickled
 *
 * @param string $b  binary data of blob
 * @return false|mixed
 */
public static function unpickle($b)
{
    $cmd = base_path(env('UNPICKLE_BINARY', 'bin/unpickle'));
    if(!(is_file($cmd) && is_executable($cmd))) { // make sure unpickle cmd exists

        // check for UNPICKLE_BINARY with .py after and python binary
        $pyExe = env('PYTHON_EXE', '/usr/bin/python');
        if (is_file($cmd.".py") && (is_file($pyExe) && is_executable($pyExe))) {
            $cmd = sprintf("%s %s.py", $pyExe, $cmd);
        } else
            return static::unpyckle($b); // try direct python call
    }

    $descriptorspec = [
        ["pipe", "r"],
        ["pipe", "w"],
        ["pipe", "w"]
    ];

    $cwd = dirname($cmd);
    $env = [];

    $process = proc_open($cmd, $descriptorspec, $pipes, $cwd, $env);

    if (is_resource($process)) {
        fwrite($pipes[0], $b);
        fclose($pipes[0]);

        $output = stream_get_contents($pipes[1]);
        fclose($pipes[1]);

        $return_value = proc_close($process);

        if(static::isJson($output))
            return json_decode($output);
        else
            return false;
    }
    return false;
}

/**
 * use a python exec call to 'unpickle' the blob_data
 *   to get the binary blob into a command line argument, base64 encode it
 *   to get the data back out of python, json serialize it
 * @param string $blob binary blob data
 * @return mixed
 */
public static function unpyckle($blob) {
    $pyExe = env('PYTHON_EXE', '/usr/bin/python');
    if (!(is_file($pyExe) && is_executable($pyExe)))
        throw new Exception('python executable not found!');

    $bblob = base64_encode($blob);
    $cmd = sprintf("import pickle; import base64; import json; print(json.dumps(pickle.loads(base64.b64decode('%s'))))", $bblob);

    $pcmd = sprintf("%s -c \"%s\"", $pyExe, $cmd);
    $result = exec($pcmd);
    $resdec = json_decode($result);
    return $resdec;
}

/**
 * try to detect if a string is a json string
 *
 * @param $str
 * @return bool
 */
public static function isJson($str) {
    if(is_string($str) && !empty($str)) {
        json_decode($str);
        return (json_last_error() == JSON_ERROR_NONE);
    }
    return false;
}

示例。env值:

UNPICKLE_BINARY=bin/unpickle
PYTHON_EXE=/usr/bin/python3

基本上展示了三种不同的方式来调用python来做本质上相同的事情。

相关问题