3

所以,我正在尝试将一段 C++ 代码翻译成 php。C++ 来自外部来源,至少可以说,我缺乏 C++ 和解密方面的知识。

源 C++ 是:

void parser_t::decrypt(buffer_t &replay_data, const unsigned char *key_data) {  
    /*\ 
    |*| Performs an in place decryption of the replay using the given key.
    |*| The decryption is a (broken) variant of CBC decryption and is performed as follows:
    |*| -# Set the variable previous_block (with size of 16 bytes) to 0
    |*| -# Decrypt a block with the given key
    |*| -# XOR the block with the previous (decrypted) block
    |*| -# Go back to step 2, until there are no more blocks.
    \*/
    BF_KEY key = {{0}};
    BF_set_key(&key, 16, key_data);

    const int block_size = 8;

    size_t padding_size = (block_size - (replay_data.size() % block_size));
    if (padding_size != 0) {
        size_t required_size = replay_data.size() + padding_size;
        replay_data.resize(required_size, 0);
    }

    unsigned char previous[block_size] = {0};
    for (auto it = replay_data.begin(); it != replay_data.end(); it += block_size) {
        unsigned char decrypted[block_size] = { 0 };
        BF_ecb_encrypt(reinterpret_cast<unsigned char*>(&(*it)), decrypted, &key, BF_DECRYPT);
        std::transform(previous, previous + block_size, decrypted, decrypted, std::bit_xor<unsigned char>());
        std::copy_n(decrypted, block_size, previous);
        std::copy_n(decrypted, block_size, reinterpret_cast<unsigned char*>(&(*it)));
    }

    if (padding_size != 0) {
        size_t original_size = replay_data.size() - padding_size;
        replay_data.resize(original_size, 0);
    }
}

到目前为止我得到了什么:

function decrypt($data){ // $data is a encrypted string
    $key = array(0xDE, <.....>, 0xEF); // (16 entries in the array)

    //BF_KEY key = {{0}};             // ?
    //BF_set_key(&key, 16, key_data); // ?

    $block_size = 8;

    $padding_size = ($block_size - (strlen($data) % $block_size));
    if ($padding_size != 0) {
        $required_size = strlen($data) + $padding_size;
        //replay_data.resize(required_size, 0);
        // Seems unnecessary in php? string lengths are pretty dynamic.
    }

    $keyString = '';
    for($i = 0; $i < count($key); $i++){
        $keyString .= chr($key[$i]);
    }
    $output = '';

    for ($i = 0; $i < stlen($data); $i += $block_size) {
        $char = array(0, 0, 0, 0, 0, 0, 0, 0);                     // ?
        $decrypted_piece = mcrypt_decrypt(MCRYPT_BLOWFISH, $keyString, $data, "cbc"); // ??
        // And this is where I completely get lost.
        $output = transform($in, $start, $end, $in2);
    }
}

function transform($in, $start, $end, $in2){
    $out = ''; // Yea, that won't work...
    for($x = $start; $x < $end; $x++){
        $out[$x] = $in[$x] ^ $in2[$x];
    }
    return $output
}

我意识到我基本上是在要求你们为我做点什么,但我真的被那些内容所困扰for (auto it...

真正帮助我的提示/解释是:

  • 在这种情况下BF_ecb_encrypt 什么?(在伪代码甚至 php 中?)(拍自己的手指。“不要要求成品”
  • 我的翻译是否在正确的轨道上transform
  • 什么是{{0}}BF_set_key(&key, 16, key_data);
  • 是什么reinterpret_cast<unsigned char*>(&(*it))

我确实查看了这些文档页面,但无济于事:

完整的源代码可在 github 上找到
这个具体的代码来自src/parser.cpp

4

3 回答 3

2

原始代码正在执行的“CBC解密的破坏变体”可以等效地描述为ECB解密,然后(累积)将每个明文块与前一个明文块进行异或。

对于 XOR,您不需要任何花哨的东西:PHP按位 XOR 运算符可以对字符串进行操作。

因此,您的 C++ 代码的简单 PHP 版本可能如下所示(警告:未经测试的代码):

function decrypt( $ciphertext, $key ) {
    $plaintext = mcrypt_decrypt( MCRYPT_BLOWFISH, $key, $ciphertext, "ecb" );

    $block_size = 8;  // Blowfish block size = 64 bits = 8 bytes
    $blocks = str_split( $plaintext, $block_size );

    $previous = str_repeat( "\0", $block_size );
    foreach ( $blocks as &$block ) {
        $block ^= $previous;
        $previous = $block;
    }

    return implode( $blocks );
}

请注意,我没有为截断的最后一个块实现任何填充;原始代码中的填充处理有些棘手,看不出它如何正确解密长度不能被 8 个字节整除的消息。(实际上是这样吗?)我没有尝试猜测到底发生了什么以及如何将其转换为 PHP,而是选择忽略所有这些内容并假设消息长度可以被块大小整除。

于 2014-02-23T12:03:44.513 回答
2

在这种情况下 BF_ecb_encrypt 做什么?

BF_ecb_encrypt() 是一个使用河豚加密的函数。PHP 等价物(如 Ilmari Karonen 之前提到的)是$plaintext = mcrypt_decrypt( MCRYPT_BLOWFISH, $key, $ciphertext, "ecb" );

什么是 reinterpret_cast(&(*it))?

BF_ecb_encrypt() 期望它的第一个参数是无符号字符*。reinterpret_cast<unsigned char*>(&(*it))是一种类型转换,将“它”转换为无符号字符*。'it' 是未指定类型的迭代器(至少在提供的代码中),关键字 'auto' 用于自动将 'it' 初始化为正确的类型。reinterpret_cast<unsigned char*>(&(*it))然后用于自动将其转换为 unsigned char*。

{{0}}, BF_set_key(&key, 16, key_data); 是什么?

这用于初始化 BF_KEY 'key',然后使用 key_data 的值设置密钥。这没有 PHP 等效项,mcrypt 将在内部设置密钥。

我是否在转换转换的正确轨道上?

从外观上看,C++ 版本以一种奇怪的方式处理填充。这可能是有意为破解尝试使用扳手。除非你完全理解原始 C++ 的算法,否则翻译成 PHP 是不可能的——不仅仅是加密算法,还有所使用的完整过程,包括前加密和后加密。

您是否考虑过使用现有的 C/C++ 代码而不是转换为 PHP 来制作一个简单的 PHP 扩展?这应该是非常困难的,比将更复杂的算法从 C++ 转换为 PHP 容易得多。现有代码或多或少可以复制粘贴到扩展中,buffer_t &replay_data可能会注册为 PHP 资源。

于 2014-02-24T01:26:59.690 回答
0

使用 php-cpp 库会有所帮助吗(请参阅http://www.php-cpp.com):

/**
 *  Decrypt function made accessible from PHP
 */

/**
 *  Dependencies
 */
#include <phpcpp.h>
#include <openssl/blowfish.h>
#include <algorithm>

/**
 *  Define buffer_t to be a vector
 */
typedef std::vector<uint8_t> buffer_t;

/**
 *  Function that should be ported to PHP
 *  @param  data
 *  @param  key_data
 */
static void decrypt(std::string &replay_data, const unsigned char *key_data) {  
    /*\ 
    |*| Performs an in place decryption of the replay using the given key.
    |*| The decryption is a (broken) variant of CBC decryption and is performed as follows:
    |*| -# Set the variable previous_block (with size of 16 bytes) to 0
    |*| -# Decrypt a block with the given key
    |*| -# XOR the block with the previous (decrypted) block
    |*| -# Go back to step 2, until there are no more blocks.
    \*/
    BF_KEY key = {{0}};
    BF_set_key(&key, 16, key_data);

    const int block_size = 8;

    size_t padding_size = (block_size - (replay_data.size() % block_size));
    if (padding_size != 0) {
        size_t required_size = replay_data.size() + padding_size;
        replay_data.resize(required_size, 0);
    }

    unsigned char previous[block_size] = {0};
    for (auto it = replay_data.begin(); it != replay_data.end(); it += block_size) {
        unsigned char decrypted[block_size] = { 0 };
        BF_ecb_encrypt(reinterpret_cast<unsigned char*>(&(*it)), decrypted, &key, BF_DECRYPT);
        std::transform(previous, previous + block_size, decrypted, decrypted, std::bit_xor<unsigned char>());
        std::copy_n(decrypted, block_size, previous);
        std::copy_n(decrypted, block_size, reinterpret_cast<unsigned char*>(&(*it)));
    }

    if (padding_size != 0) {
        size_t original_size = replay_data.size() - padding_size;
        replay_data.resize(original_size, 0);
    }
}

/**
 *  The PHP function that will take care of this
 *  @param  parameters
 *  @return Value
 */
static Php::Value php_decrypt(Php::Parameters &params)
{
    // check number of parameters
    if (params.size() != 2) throw Php::Exception("2 parameters expected");

    // read in the parameters
    std::string replay_data = params[0];
    std::string key_data = params[1];

    // decrypt it
    decrypt(replay_data, (const unsigned char *)key_data.c_str());

    // return the result
    return replay_data;
}

/**
 *  Symbols are exported according to the "C" language
 */
extern "C" 
{
    // export the "get_module" function that will be called by the Zend engine
    PHPCPP_EXPORT void *get_module()
    {
        // create extension
        static Php::Extension extension("my_decrypt","1.0");

        // add custom function
        extension.add("my_decrypt", php_decrypt);

        // return the extension module
        return extension.module();
    }
}

您可以使用以下命令编译代码:

g++ -std=c++11 -fpic -shared my_decrypt.cpp -o my_decrypt.so -lphpcpp

my_descript.so 应该被复制到您的 PHP 扩展目录中,并且应该在您的 php.ini 中添加一个“extension=my_decrypt.so”行。

于 2014-03-01T10:38:29.767 回答