16

コンパイル時に元の文字列がコンパイルされた実行可能ファイルに表示されないように、文字列を暗号化/エンコードしたいと思います。

いくつかの例を見てきましたが、引数として文字列リテラルを使用することはできません。次の例を参照してください。

template<char c> struct add_three {
    enum { value = c+3 };
};

template <char... Chars> struct EncryptCharsA {
    static const char value[sizeof...(Chars) + 1];
};

template<char... Chars>
char const EncryptCharsA<Chars...>::value[sizeof...(Chars) + 1] = {
    add_three<Chars>::value...
};

int main() {   
    std::cout << EncryptCharsA<'A','B','C'>::value << std::endl;
    // prints "DEF"
}

私は、各キャラクターを個別に提供したくありません。私の目標は、次のような文字列リテラルを渡すことです。

EncryptString<"String to encrypt">::value

このような例もいくつかあります。

#define CRYPT8(str) { CRYPT8_(str "\0\0\0\0\0\0\0\0") }
#define CRYPT8_(str) (str)[0] + 1, (str)[1] + 2, (str)[2] + 3, (str)[3] + 4, (str)[4] + 5, (str)[5] + 6, (str)[6] + 7, (str)[7] + 8, '\0'

// calling it
const char str[] = CRYPT8("ntdll");

ただし、文字列のサイズが制限されます。

私が望むことを達成する方法はありますか?

4

4 に答える 4

12

この質問は更新された答えに値すると思います。

数年前にこの質問をしたとき、私は難読化と暗号化の違いを考慮していませんでした。そのときこの違いを知っていたら、以前はタイトルに難読化という用語を含めていたでしょう。

C++11とC++14には、コンパイル時の文字列の難読化(および、まだ試していませんが、暗号化)を効果的かつ合理的に単純な方法で実装できる機能があり、すでに実行されています。

ADVobfuscatorは、Sebastien Andrivetによって作成された難読化ライブラリであり、C ++ 11/14を使用して、C ++コードだけでなく、外部ツールを使用せずにコンパイル時の難読化コードを生成します。追加のビルドステップを作成する必要はありません。それを含めて使用するだけです。外部ツールやビルドステップを使用しない、より優れたコンパイル時の文字列暗号化/難読化の実装はわかりません。もしそうなら、共有してください。

文字列を難読化するだけでなく、関数呼び出しをランダムに難読化できるコンパイル時FSM(有限状態マシン)やコンパイル時の疑似乱数ジェネレータなど、他の便利な機能もありますが、これらはこの範囲外です。答え。

ADVobfuscatorを使用した簡単な文字列難読化の例を次に示します。

#include "MetaString.h"

using namespace std;
using namespace andrivet::ADVobfuscator;

void Example()
{
    /* Example 1 */

    // here, the string is compiled in an obfuscated form, and
    // it's only deobfuscated at runtime, at the very moment of its use
    cout << OBFUSCATED("Now you see me") << endl;

    /* Example 2 */

    // here, we store the obfuscated string into an object to
    // deobfuscate whenever we need to
    auto narrator = DEF_OBFUSCATED("Tyler Durden");

    // note: although the function is named `decrypt()`, it's still deobfuscation
    cout << narrator.decrypt() << endl;
}

DEF_OBFUSCATEDマクロとOBFUSCATED独自のマクロを置き換えることができます。例えば。:

#define _OBF(s) OBFUSCATED(s)

...

cout << _OBF("klapaucius");

それはどのように機能しますか?

MetaString.hでこれら2つのマクロの定義を見ると、次のことがわかります。

#define DEF_OBFUSCATED(str) MetaString<andrivet::ADVobfuscator::MetaRandom<__COUNTER__, 3>::value, andrivet::ADVobfuscator::MetaRandomChar<__COUNTER__>::value, Make_Indexes<sizeof(str) - 1>::type>(str)

#define OBFUSCATED(str) (DEF_OBFUSCATED(str).decrypt())

基本的に、クラスには3つの異なるバリアントがありますMetaString(文字列の難読化のコア)。それぞれに独自の難読化アルゴリズムがあります。これらの3つのバリアントの1つは、ライブラリの疑似乱数ジェネレーター(MetaRandom)を使用して、コンパイル時にランダムに選択されます。またchar、選択されたアルゴリズムによってxor文字列文字に使用されるランダムも使用されます。

「ねえ、でも計算すると、3つのアルゴリズム* 255の可能なcharキー(0は使用されない)=難読化された文字列の765のバリアント」

あなたが正しい。同じ文字列は、765の異なる方法でのみ難読化できます。より安全なものが必要な理由がある場合(パラノイアである/アプリケーションがセキュリティの強化を要求している場合)、より強力な難読化または暗号化を使用して、ライブラリを拡張し、独自のアルゴリズムを実装できます(ホワイトボックス暗号化はライブラリのロードマップにあります)。


難読化された文字列はどこに/どのように保存されますか?

この実装について私が興味深いと思うことの1つは、実行可能ファイルのデータセクションに難読化された文字列を格納しないことです。代わりに、MetaStringオブジェクト自体(スタック上)に静的に格納され、アルゴリズムが実行時にその場でデコードします。このアプローチでは、静的または実行時に、難読化された文字列を見つけるのがはるかに困難になります。

自分で実装を深く掘り下げることができます。これは非常に優れた基本的な難読化ソリューションであり、より複雑なソリューションへの出発点になる可能性があります。

于 2016-02-13T21:50:49.620 に答える
8

テンプレートメタプログラミングでトラブルの山を救い、文字列を暗号化してcppソースファイルを生成するスタンドアロンプ​​ログラムを作成します。このプログラムはコンパイル前に実行され、cppやcppを生成します。使用する暗号化された文字列を含むヘッダーファイル。

だからここにあなたが始めるものがあります:

  1. crypto_string.cppおよびencrypted_string.h(空白)
  2. テキストファイルを入力として受け取り、encrypted_string.cppとencrypted_string.hを上書きするスクリプトまたはスタンドアロンアプリ

スクリプトが失敗すると、存在しない変数への参照がコード内にあるため、コンパイルは失敗します。あなたはもっと賢くなるかもしれませんが、それはあなたが始めるのに十分です。

于 2011-08-03T23:49:59.547 に答える
3

見つけた例で文字列リテラルをテンプレート引数として使用できない理由は、ISOC++標準で許可されていないためです。これは、c ++には文字列クラスがありますが、文字列リテラルはconstchar*であるためです。したがって、そのようなコンパイル時の文字列リテラルの文字にアクセスできる場合でも、変更することはできません(または変更しないでください(未定義の動作につながります)。

私が見る唯一の方法は、定義を使用することです。定義は、コンパイラの前にプリプロセッサによって処理されるためです。たぶんブーストはその場合あなたに助けの手を与えるでしょう。

于 2011-08-03T22:56:09.233 に答える
2

マクロベースの解決策は、可変引数を取り、文字列の各部分を単一のトークンとして渡すことです。次に、トークンを文字列化して暗号化し、すべてのトークンを連結します。最終結果は次のようになります

CRYPT(m y _ s t r i n g)

ここで、_は空白文字リテラルのプレースホルダーです。ひどく厄介で、私はこれよりも他のすべての解決策を好みます。

Boost.PPシーケンスはそれをより美しくしていませんが、このような何かがそれを行うことができます。

#include <iostream>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>

#define GARBLE(x) GARBLE_ ## x
#define GARBLE_a x
#define GARBLE_b y
#define GARBLE_c z

#define SEQ (a)(b)(c)
#define MACRO(r, data, elem) BOOST_PP_STRINGIZE(GARBLE(elem))

int main() {
  const char* foo = BOOST_PP_SEQ_FOR_EACH(MACRO, _, SEQ);
  std::cout << foo << std::endl;
}
于 2011-08-03T23:11:27.690 に答える