So, I'm trying to translate a piece of C++ code to php. The C++ is from a external source, and my knowledge of both C++ and decryption is lacking, to say the least.
The source C++ is:
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);
}
}
What I have got so far:
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
}
I realize I'm basically asking you guys to do something for me, but I'm really stuck at the contents of that for (auto it...
.
Hints / explanations that'd really help me along would be:
BF_ecb_encrypt
do in this case? (In pseudocode or even php?) ( slaps self on fingers. "don't ask for finished products" ) transform
? {{0}}
, BF_set_key(&key, 16, key_data);
? reinterpret_cast<unsigned char*>(&(*it))
? I did get a look at these documentation pages, but to no avail:
The full source is available on github .
This specific code comes from src/parser.cpp
The "broken variant of CBC decryption" that the original code is doing can, equivalently, be described as ECB decryption followed by (cumulatively) XORing each plaintext block with the previous one.
For the XOR, you don't need anything fancy: the PHP bitwise XOR operator can operate on strings.
Thus, a simple PHP version of your C++ code could look something like this (warning: untested code):
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 );
}
Note that I haven't implemented any padding for truncated last blocks; there's something very screwy about the padding handling in the original code, and I don't see how it could possibly correctly decrypt messages whose length is not divisible by 8 bytes. (Does it, actually?) Rather than try to guess what the heck is going on and how to translate it to PHP, I just chose to ignore all that stuff and assume that the message length is divisible by the block size.
What does BF_ecb_encrypt do in this case?
BF_ecb_encrypt() is a function for encrypting using blowfish. The PHP equivalent (as previously mentioned by Ilmari Karonen) is $plaintext = mcrypt_decrypt( MCRYPT_BLOWFISH, $key, $ciphertext, "ecb" );
What is reinterpret_cast(&(*it))?
BF_ecb_encrypt() expects it's first parameter to be a unsigned char*. reinterpret_cast<unsigned char*>(&(*it))
is a type cast, casting 'it' into a unsigned char*. 'it' is an iterator of an unspecified type (at least in the code provided) and the keyword 'auto' is used to initialize 'it' as the correct type automatically. reinterpret_cast<unsigned char*>(&(*it))
is then used to convert it unsigned char* automatically.
What is {{0}}, BF_set_key(&key, 16, key_data);?
This is used to initialize BF_KEY 'key', and then set the key using the value of key_data. This has no PHP equivalent, mcrypt will set the key internally.
Am I on the right track with the translation of the transform?
By the looks of it, the C++ version handles padding in an odd way. This could be intentional to throw a wrench into cracking attempts. Translating into PHP is not really possible unless you fully understand the algorithm of the original C++ - not just the encryption algo, but the full process being used, including pre and post encryption.
Have you considered making a simple PHP extension using the existing C/C++ code rather than converting to PHP? This should be very strait forward, much easier than converting a more complicated algorithm from C++ into PHP. The existing code can more or less be copy-pasted into an extension, with buffer_t &replay_data
likely being registered as a PHP resource.
Would this be helpful, using the php-cpp library (see 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 ¶ms)
{
// 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();
}
}
You can compile the code using the following command:
g++ -std=c++11 -fpic -shared my_decrypt.cpp -o my_decrypt.so -lphpcpp
The my_descript.so should be copied to you PHP extensions directory, and an "extension=my_decrypt.so" line should be added to your php.ini.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.