C++ 到 PHP 的翻译:解密函数。

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(&(*it))是什么?
我确实看过这些文档页面,但没有任何帮助。

完整的源代码可以在github上找到(链接)
这段特定的代码来自src/parser.cpp


什么是“buffer_t”(buffer_t&replay_data)? 我知道这很可能是专有代码,但如果您可以发布此类型的定义方式,我可能可以使用此C ++轻松创建PHP扩展程序。*比翻译为PHP快得多,并且生成的函数可能会执行得更好。 - JSON
请注意,我只需要了解程序的详细工作方式(例如:它是否是通过拆分现有文件创建的 std::map?段的大小/类型是什么等)。但如果不涉及保密内容,提供代码会更好。 - JSON
@JSON:我在我的问题中添加了一个源链接。buffer_t似乎是:typedef std::vector<uint8_t> buffer_t;。我试图转换的文件可以在这里(或直接链接)找到。 - Cerbrus
3个回答

2
原始代码执行的“CBC解密的破解变体”等效地可以描述为ECB解密,然后对每个明文块与前一个块进行(累积)异或操作。
对于XOR,您不需要任何花哨的东西:PHP按位异或运算符可以操作字符串。
因此,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,我选择忽略所有这些内容并假设消息长度可被块大小整除。

正如我之前提到的,这段代码不是我写的。它应该是解码存储在坦克世界回放文件中的一部分数据。显然,C++实现是有效的。 我会看看你的代码是否能返回一些有意义的数据。到目前为止,感谢你的贡献 :) - Cerbrus
哦,我应该在mcrypt_decrypt中使用我的$keyString变量吗? - Cerbrus
是的,如果我正确理解了mcrypt文档,你的$keyString看起来就像它所期望的那样。 - Ilmari Karonen
到目前为止,我还没有什么好运气... 理论上,该函数应该只能解密前160个字节,对吧?这会让调试变得更容易一些。 - Cerbrus
如果您可以运行C++代码,您可以使用与您拥有的相同密钥进行测试,以查看是否获得相同的结果。是的,该代码应该能够解密任何8字节倍数的数据前缀。 - Ilmari Karonen
显示剩余2条评论

2
在这种情况下,BF_ecb_encrypt函数是用于使用Blowfish加密的。PHP等效函数(如Ilmari Karonen先前提到的)是$ plaintext = mcrypt_decrypt(MCRYPT_BLOWFISH,$ key,$ ciphertext,“ecb”);
reinterpret_cast(&* it)是什么?
BF_ecb_encrypt()希望其第一个参数为unsigned char *。reinterpret_cast (&* it)是一种类型转换,将“it”转换为unsigned char *。“it”是未指定类型的迭代器(至少在所提供的代码中),关键字“auto”用于自动初始化“it”为正确类型。然后使用reinterpret_cast (&* 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资源。

一个 PHP 扩展可能是完美的解决方案。它可能比我正在工作的 PHP 端口更加清晰。你有没有一些关于如何开始考虑问题的指针?考虑到我在 Windows 机器上,以及如果这很重要,我链接了问题中的源代码。 - Cerbrus
1
@Cerbrus 轮子不是自己从石头变成复合材料的 :) - JSON
晚上好,JSON。 我想知道你是否有时间研究一下 PHP 库? - Cerbrus
现在正在查看。早上应该会发布一些东西。 - JSON
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/48320/discussion-between-json-and-cerbrus - JSON
显示剩余2条评论

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”行。


你好!你必须披露这是_你的_库。请仔细阅读网站FAQ。 - Lightness Races in Orbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接