0

私は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);
    }

}

エラー

PaymentManager.exeの0x550714CA(cryptopp.dll)で未処理の例外:0xC0000005:アクセス違反の読み取り場所0x74736554。

cryptopp.dll!memcpy(unsigned char * dst、unsigned char * src、unsigned long count)行188不明

更新: DLLとExeプログラムの両方を「リリース」した後に問題が解決しました。しかし今、新しい問題があります。この行では、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());
        } }
4

3 に答える 3

3

0x74736554は、4つのASCII文字"tseT"(ビッグエンディアン)または"Test"(リトルエンディアン)の16進数です。後者は、のインデックス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異なるオブジェクトレイアウトを持つ異なる実装を使用しているコードとライブラリです。これは、モジュールの境界を越えてメモリを割り当てたり解放したりするのとまったく同じ問題です。モジュール間(つまり、メインの実行可能ファイルとそれがロードするDLL)間でC ++オブジェクトを渡すには、両方のモジュールがオブジェクトのレイアウト方法について合意する必要があります。モジュールが異なる時間にコンパイルされた場合は、同じヘッダーファイルに対してコンパイルされるようにさらに努力する必要があります。

ソースからDLLをコンパイルする場合、最も簡単な方法は、DLLと実行可能ファイルの両方が同じC++標準ライブラリの実装を使用していることを確認することです。他の誰かによってすでにコンパイルされているDLLを使用している場合は、そのDLLに問い合わせるか、ドキュメントを確認して、コンパイル対象のC ++標準ライブラリを見つけてから、同じライブラリに対して実行可能ファイルをコンパイルする必要があります。

それができない場合、次善の解決策は、すべての場合にモジュールの境界を越えてC ++オブジェクトを渡さないようにすることです。事前定義されたデータ型(整数や生のポインターなど)またはDLLのヘッダーファイルで定義されたデータ型をとるAPIのみを使用してください。これにより、問題は完全に回避されますが、を渡すことも受け取ることもできなくなるため、コードの記述が非常に難しくなりますstd::string

于 2013-03-19T04:28:43.617 に答える
2

私はこのライブラリを知りませんが、私が見るように最初のパラメータとしてStringSourcegetを使用します。std::stringこの場合、DLLとプログラムの両方が、まったく同じコンパイラとそのオプションを使用して、まったく同じSTLでコンパイルおよびリンクされていることを絶対に確認する必要があります。

于 2013-03-19T03:51:17.267 に答える
0

BorisとAdamの回答に加えて、これが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
于 2013-10-09T02:36:27.457 に答える