Я пытаюсь использовать cryptopp, приведенный ниже код вызывает нарушение прав доступа в функции stringsource. Что может быть возможной причиной этого? Ранее я успешно запускал подобный код без особых различий.
AesHelper.cpp
#include "dll.h"#include "AesHelper.h"
#include "aes.h"using CryptoPP::AES;
#include "ccm.h"using CryptoPP::CBC_Mode;
#include "filters.h"using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::StreamTransformationFilter;
#include "hex.h"using CryptoPP::HexEncoder;
using CryptoPP::HexDecoder;
#include <string>
using namespace std;
#include "osrng.h"using CryptoPP::AutoSeededRandomPool;
byte AesHelper::_key[AES::DEFAULT_KEYLENGTH];
byte AesHelper::_iv[AES::BLOCKSIZE];
void AesHelper::encrypt(const char* str, char ** outIv, char ** encrypted )
{
try
{
AutoSeededRandomPool prng;
byte key[AES::DEFAULT_KEYLENGTH];
prng.GenerateBlock(key, sizeof(key));
byte iv[AES::BLOCKSIZE];
prng.GenerateBlock(iv, sizeof(iv));
string cipher, encoded;
string plain = "CBC Test Mode";
CBC_Mode<AES>::Encryption e;
e.SetKeyWithIV(key, sizeof(key), iv);
// The StreamTransformationFilter removes
// padding as required.
StreamTransformationFilter *stf = new StreamTransformationFilter(e,
new StringSink(cipher),
CryptoPP::BlockPaddingSchemeDef::ZEROS_PADDING
);
StringSource s(plain, true, stf); // This line cause Access Violation
StreamTransformationFilter filter(e);
filter.Put((const byte*)plain.data(), plain.size());
filter.MessageEnd();
const size_t ret = filter.MaxRetrievable();
cipher.resize(ret);
filter.Get((byte*)cipher.data(), cipher.size());
//encode the cipher to hexadecimal
StringSource(cipher, true,
new HexEncoder(
new StringSink(encoded)
) // HexEncoder
); // StringSource//set the output parameter
outIv = (char**)_iv;
encrypted = (char**)cipher.c_str();
}
catch(const CryptoPP::Exception& e)
{
cerr << "exception : " << e.what() << endl;
exit(1);
}
}
ошибка
Необработанное исключение в 0x550714CA (cryptopp.dll) в
PaymentManager.exe: 0xC0000005: место чтения нарушения прав доступа
0x74736554.cryptopp.dll! memcpy (unsigned char * dst, unsigned char * src, unsigned long count) Строка 188 Неизвестно
ОБНОВИТЬ :
Проблема решена после того, как и DLL и Exe программа «Release». Но теперь появилась новая проблема.
в этой строке проблема также в функции stringsource
StringSource(cipher, true,
new HexEncoder(
new StringSink(encoded)
) // HexEncoder
); // StringSource
ошибка
PaymentManager.exe вызвал точку останова.
Программа останавливается на
void __cdecl _free_base (void * pBlock) {
int retval = 0;if (pBlock == NULL)
return;
RTCCALLBACK(_RTC_Free_hook, (pBlock, 0));
retval = HeapFree(_crtheap, 0, pBlock); // program stop at this line
if (retval == 0)
{
errno = _get_errno_from_oserr(GetLastError());
} }
Я не знаю эту библиотеку, но, как я вижу StringSource
получить std::string
в качестве первого параметра. В этом случае вы должны быть абсолютно уверены, что и DLL, и ваша программа скомпилированы и связаны с одним и тем же STL, с одинаковыми компиляторами и их параметрами.
0x74736554 является шестнадцатеричным для четырех символов ASCII "tseT"
(big-endian) или "Test"
(little-endian) — последний из которых является именно байтами с индексами 4-7 вашего string plain
, Тот факт, что StringSource
конструктор пытается прочитать этот адрес предполагает, что ваш исполняемый файл и DLL не согласны с тем, что std::string
похоже. В частности, библиотека разыменовывает адрес памяти по смещению 4 от объекта, который вы передаете, но объект, который вы передаете, не имеет действительного значения указателя там.
Другими словами, string
вы передаете (или, возможно, какой-то его подобъект) в памяти выглядит так:
Offset 0 1 2 3 4 5 6 7
+------+------+------+------+------+------+------+------+--
Value | 0x43 | 0x42 | 0x43 | 0x20 | 0x54 | 0x65 | 0x73 | 0x74 | ...
| 'C' | 'B' | 'C' | ' ' | 'T' | 'e' | 's' | 't' | ...
+------+------+------+------+------+------+------+------+--
Но библиотека воспринимает это так:
Offset 0 1 2 3 4 5 6 7
+------+------+------+------+------+------+------+------+--
Value | ????? | Pointer to character data | ...
+------+------+------+------+------+------+------+------+--
Я понял все это, просто осознав, что адрес, вызывающий ошибку, целиком состоит из значений ASCII, которые соответствуют значениям из исходного кода.
Причиной этого почти наверняка является то, что ваш код и библиотека используют разные std::string
реализации, которые имеют разные макеты объектов. Это точно такая же проблема, как Выделение и освобождение памяти через границы модуля. Чтобы передавать объекты C ++ между модулями (то есть основным исполняемым файлом и любыми DLL-библиотеками, которые он загружает), оба модуля должны согласовать расположение объекта. Если модули были скомпилированы в разное время, вам нужно больше работать, чтобы убедиться, что они скомпилированы с одинаковыми заголовочными файлами.
Если вы компилируете DLL из исходного кода, то проще всего убедиться, что и DLL, и ваш исполняемый файл используют одну и ту же реализацию стандартной библиотеки C ++. Если вы используете библиотеку DLL, которая уже была скомпилирована кем-то другим, вам нужно попросить их или проверить документацию, чтобы найти стандартную библиотеку C ++, для которой она была скомпилирована, а затем скомпилировать исполняемый файл для той же библиотеки.
Если вы не можете этого сделать, то следующим лучшим решением будет избежать передачи объектов C ++ через границы модуля в все случаи — используйте только API, которые принимают предопределенные типы данных (например, целые числа и необработанные указатели) или типы данных, определенные в заголовочных файлах DLL. Это полностью исключит проблему, но также сделает ваш код намного труднее писать, так как вы больше не можете передавать или получать std::string
,
В дополнение к ответам Бориса и Адама, я видел, что это вызывает проблему в Linux (я понимаю, что вы находитесь в Windows):
StringSource(cipher, true,
new HexEncoder(
new StringSink(encoded)
) // HexEncoder
); // StringSource
Анонимные объявления являются законными C / C ++. Но конкретная версия GCC (около 4.3 или 4.4 или 4.5) слишком рано начнет запускать деструкторы в сгенерированном коде. Я говорил об этом с Джонатаном Уэйкли еще в 2011 году, но так и не смог выяснить причину. Вот поток, который заставил m начать смотреть на него: Запуск примеров RSA и некоторые проблемы установки.
Обходной путь был просто:
StringSource ss(cipher, true,
new HexEncoder(
new StringSink(encoded)
) // HexEncoder
); // StringSource