web-dev-qa-db-ja.com

バイナリコードで文字列を非表示にする方法

バイナリ(実行可能ファイル)から文字列を非表示にすると便利な場合があります。たとえば、バイナリから暗号化キーを隠すことは理にかなっています。

「隠す」と言うときは、コンパイル済みのバイナリで文字列を見つけにくくすることを意味します。

たとえば、次のコード:

const char* encryptionKey = "My strong encryption key";
// Using the key

コンパイル後、データセクションに以下を含む実行可能ファイルが生成されます。

4D 79 20 73 74 72 6F 6E-67 20 65 6E 63 72 79 70   |My strong encryp|
74 69 6F 6E 20 6B 65 79                           |tion key        |

秘密の文字列を簡単に見つけたり変更したりできることがわかります。

ひもを隠すことができた…

char encryptionKey[30];
int n = 0;
encryptionKey[n++] = 'M';
encryptionKey[n++] = 'y';
encryptionKey[n++] = ' ';
encryptionKey[n++] = 's';
encryptionKey[n++] = 't';
encryptionKey[n++] = 'r';
encryptionKey[n++] = 'o';
encryptionKey[n++] = 'n';
encryptionKey[n++] = 'g';
encryptionKey[n++] = ' ';
encryptionKey[n++] = 'e';
encryptionKey[n++] = 'n';
encryptionKey[n++] = 'c';
encryptionKey[n++] = 'r';
encryptionKey[n++] = 'y';
encryptionKey[n++] = 'p';
encryptionKey[n++] = 't';
encryptionKey[n++] = 'i';
encryptionKey[n++] = 'o';
encryptionKey[n++] = 'n';
encryptionKey[n++] = ' ';
encryptionKey[n++] = 'k';
encryptionKey[n++] = 'e';
encryptionKey[n++] = 'y';

…しかし、それはナイスな方法ではありません。より良いアイデアはありますか?

PS:私はただ秘密を隠すことは決意した攻撃者に対して機能しないことを知っていますが、それは何もないよりもはるかに優れています...

また、非対称暗号化については知っていますが、この場合は受け入れられません。 Blowfish暗号化を使用し、暗号化されたデータをサーバーに渡す既存のアプリケーションをリファクタリングしています(サーバーは同じキーでデータを解読します)。

I できない後方互換性を提供する必要があるため、暗号化アルゴリズムを変更します。 I できない暗号化キーを変更します。

63
Dmitriy

Paviumの answer へのコメントで述べたように、2つの選択肢があります。

  • キーを保護する
  • 復号化アルゴリズムを保護する

残念ながら、キーとアルゴリズムの両方をコード内に埋め込む必要がある場合、どちらも真に秘密ではないため、 セキュリティによる不明瞭さ の(はるかに弱い)選択肢が残っています。言い換えれば、あなたが述べたように、あなたはあなたの実行可能ファイルの中にそれらのどちらかまたは両方を隠す賢い方法が必要です。

いくつかのオプションがありますが、これらのどれもが暗号化のベストプラクティスに従って真に安全ではないことを覚えておく必要があります、それぞれに欠点があります:

  1. 通常はコード内に表示される文字列としてキーを偽装します。1つの例は、数値を持つ傾向があるprintf()ステートメントのフォーマット文字列です。文字、および句読点。
  2. ハッシュ 起動時にコードまたはデータセグメントの一部またはすべてをキーとして使用します。 (キーが予期せず変更されないようにするには、これについて少し賢くする必要があります!)これには、実行するたびにコードのハッシュ部分を検証するという潜在的に望ましい副作用があります。
  3. たとえば、ネットワークアダプターのMACアドレスをハッシュすることにより、システム固有の(およびシステム内で一定の)ものから実行時にキーを生成します
  4. 他のデータからバイトを選択してキーを作成します。静的またはグローバルデータがある場合、タイプに関係なく(intchar、- etc。)、初期化された後(もちろん、ゼロ以外の値に)、変更される前に、各変数内のどこかからバイトを取得します。

問題の解決方法をお知らせください!

編集:既存のコードをリファクタリングしているとコメントしたので、必ずしも自分でキーを選択できるとは限りません。その場合は、上記の方法のいずれかを使用してキー自体を暗号化し、次にthatキーを使用してユーザーのデータを復号化します。

47
Adam Liss

長い間ごめんなさい。

あなたの答えは絶対に正しいですが、質問は文字列を非表示にしてうまくやる方法でした。

私はそのような方法でそれをしました:

_#include "HideString.h"

DEFINE_HIDDEN_STRING(EncryptionKey, 0x7f, ('M')('y')(' ')('s')('t')('r')('o')('n')('g')(' ')('e')('n')('c')('r')('y')('p')('t')('i')('o')('n')(' ')('k')('e')('y'))
DEFINE_HIDDEN_STRING(EncryptionKey2, 0x27, ('T')('e')('s')('t'))

int main()
{
    std::cout << GetEncryptionKey() << std::endl;
    std::cout << GetEncryptionKey2() << std::endl;

    return 0;
}_

HideString.h:

_#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/enum.hpp>

#define CRYPT_MACRO(r, d, i, elem) ( elem ^ ( d - i ) )

#define DEFINE_HIDDEN_STRING(NAME, SEED, SEQ)\
static const char* BOOST_PP_CAT(Get, NAME)()\
{\
    static char data[] = {\
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO, SEED, SEQ)),\
        '\0'\
    };\
\
    static bool isEncrypted = true;\
    if ( isEncrypted )\
    {\
        for (unsigned i = 0; i < ( sizeof(data) / sizeof(data[0]) ) - 1; ++i)\
        {\
            data[i] = CRYPT_MACRO(_, SEED, i, data[i]);\
        }\
\
        isEncrypted = false;\
    }\
\
    return data;\
}_

HideString.hで最も難しい行は次のとおりです。

BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO, SEED, SEQ))

ラインを説明します。コードの場合:

DEFINE_HIDDEN_STRING(EncryptionKey2, 0x27, ('T')('e')('s')('t'))
BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO、SEED、SEQ)
_( 'T'  ^ ( 0x27 - 0 ) ) ( 'e'  ^ ( 0x27 - 1 ) ) ( 's'  ^ ( 0x27 - 2 ) ) ( 't'  ^ ( 0x27 - 3 ) )_
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO、SEED、SEQ))
'T' ^ ( 0x27 - 0 ), 'e' ^ ( 0x27 - 1 ), 's' ^ ( 0x27 - 2 ), 't' ^ ( 0x27 - 3 )

そして最後に、

DEFINE_HIDDEN_STRING(EncryptionKey2, 0x27, ('T')('e')('s')('t'))
_static const char* GetEncryptionKey2()
{
    static char data[] = {
        'T' ^ ( 0x27 - 0 ), 'e' ^ ( 0x27 - 1 ), 's' ^ ( 0x27 - 2 ), 't' ^ ( 0x27 - 3 ),
        '\0'
    };
    static bool isEncrypted = true;
    if ( isEncrypted )
    {
        for (unsigned i = 0; i < ( sizeof(data) / sizeof(data[0]) ) - 1; ++i)
        {
            data[i] = ( data[i] ^ ( 0x27 - i ) );
        }
        isEncrypted = false;
    }
    return data;
}
_

「強力な暗号化キー」のデータは次のようになります。

_0x00B0200C  32 07 5d 0f 0f 08 16 16 10 56 10 1a 10 00 08  2.]......V.....
0x00B0201B  00 1b 07 02 02 4b 01 0c 11 00 00 00 00 00 00  .....K........._

ご回答ありがとうございました!

51
Dmitriy
  1. コードゴルフの問題として投稿する
  2. Jで書かれたソリューションを待つ
  3. Jインタープリターをアプリに埋め込む
20
Ken

コード内でパスワードを非表示にすることは、あいまいさによるセキュリティです。実際にはほとんど持っていないのに、ある程度の保護があると思わせるので、これは有害です。何かを保護する価値がある場合、適切に保護する価値があります。

PS:実際のハッカーに対しては機能しないことは知っていますが、何もないよりはましです...

実際、多くの状況では、弱いセキュリティほど優れたものはありません。少なくともあなたは自分の立場を正確に知っています。埋め込まれたパスワードを回避するために「本当のハッカー」である必要はありません...

編集:このコメントへの応答:

キーのペアについては知っていますが、この場合は受け入れられません。 Blowfish暗号化を使用する既存のアプリケーションをリファクタリングします。サーバーに渡される暗号化されたデータとサーバーがデータを復号化します。下位互換性を提供する必要があるため、暗号化アルゴリズムを変更できません。

セキュリティにまったく関心がある場合、下位互換性を維持することは、組み込みパスワードで脆弱なままにする本当に悪い理由です。安全でないセキュリティスキームとの後方互換性を破ることは良いことです。

通りの子供たちが玄関の鍵をマットの下に置いていることに気付いたときのようですが、おじいちゃんはそこにあると思っているので、それを続けます。

11
Stephen C

この例では、文字列をまったく非表示にしません。文字列は引き続き出力に一連の文字として表示されます。

文字列を難読化するにはさまざまな方法があります。単純な substitution cypher があります。または、各文字(たとえばXOR)で数学演算を実行し、結果が次の文字の演算などに送られる場合などがあります。

目標は文字列のように見えないデータになることです。たとえば、ほとんどの西洋言語で作業している場合、ほとんどの文字値は32〜127の範囲になります。したがって、目標はほとんどの場合、操作ではその範囲外にout配置されるため、注意を引くことはありません。

8
T.J. Crowder

これは、中央駅近くのオランダのアムステルダムで自転車のロックを解除したままにするのと同じくらい安全です。 (点滅し、消えました!)

アプリケーションにセキュリティを追加しようとすると、保護スキームが失敗するため、最初から失敗する運命にあります。ハッカーが必要な情報を見つけるのをより複雑にするだけです。それでも、いくつかのトリック:

*)文字列がバイナリにUTF-16として保存されていることを確認してください。

*)文字列に数字と特殊文字を追加します。

*)文字列の代わりに32ビット整数の配列を使用してください!それぞれを文字列に変換し、それらをすべて連結します。

*)GUIDを使用し、バイナリとして保存し、使用する文字列に変換します。

事前に定義されたテキストが本当に必要な場合は、それを暗号化し、暗号化された値をバイナリに保存します。実行時に暗号化を解除します。暗号化解除するキーは、前述したオプションの1つです。

ハッカーはこれ以外の方法でアプリケーションをクラックする傾向があることを理解してください。暗号の専門家であっても、何かを安全に保つことはできません。一般的に、あなたを保護する唯一のことは、ハッキングのコストと比較して、ハッカーがコードをハッキングすることで得られる利益です。 (多くの場合、これらのコストはかなりの時間になりますが、アプリケーションをハッキングするのに1週間、他の何かをハッキングするのに2日しかかからない場合、他の何かが攻撃される可能性が高くなります。)


[〜#〜] guid [〜#〜] の使用は、GUIDをテキスト形式ではなくバイナリ形式で保存する場合に実用的です。A= GUIDは16バイト長で、ランダムに生成できます。したがって、GUIDそれがパスワードとして使用されていることを推測することは困難です。 、a GUIDは、「3F2504E0-4F89-11D3-9A0C-0305E82C3301」のような文字列表現に変換できます。(または、「7QDBkvCA1 + B9K/U0vrQx1A ==」としてBase64エンコードされます。 。)しかし、ユーザーにはコードにプレーンテキストは表示されず、明らかにランダムなデータが表示されます。GUIDのすべてのバイトがランダムではありません。GUIDにはバージョン番号が隠されています。 a GUIDは、暗号化の目的には最適なオプションではありません。MACアドレスまたは擬似乱数に基づいて計算されるため、合理的に予測可能です。それでも、作成は簡単です。簡単に保存、変換、使用できます。何かを長く作成しても、ハッカーは単にindセキュリティをクラックするその他のトリック。バイナリの分析により多くの時間を費やす意志があるかどうかについての質問です。

一般的に、アプリケーションを安全に保つために最も重要なことは、アプリケーションに関心を持つ人々の数です。誰もあなたのアプリケーションを気にしないなら、誰もそれをハッキングすることを気にしません。 5億人のユーザーを抱えるトップ製品の場合、アプリケーションは1時間以内にクラックされます。

7
Wim ten Brink

私はかつて同様に厄介な立場にいました。バイナリ形式である必要があるが、プレーンテキスト形式ではないデータがありました。私の解決策は、プログラムの残りの部分のように見える非常に単純なスキームを使用してデータを暗号化することでした。文字列を取得し、すべての文字をASCIIコード(3桁の数字を取得するために必要に応じてゼロを埋める)に変換)に変換するプログラムを作成して暗号化し、 3桁のコードの始まりと終わりです。したがって、文字列の各文字は、暗号化された文字列で5文字(すべての数字)で表されます。その文字列を定数としてアプリケーションに貼り付け、結果を解読し、必要なことを行うのに十分な長さの変数に結果を保存しました。

この例を使用すると、「My strong encryption key」は「207719121310329211541116181145111157110071030703283101101109309926114151216611289116161056811109110470321510787101511213」になります。次に、暗号化キーが必要な場合は、それをデコードしますが、プロセスを元に戻します。

確かに防弾ではありませんが、私はそれを目指していませんでした。

4
Corin

Cの場合、これを確認してください: https://github.com/mafonya/c_hide_strings

C++の場合:

class Alpha : public std::string
{
public:
    Alpha(string str)
    {
        std::string phrase(str.c_str(), str.length());
        this->assign(phrase);
    }
    Alpha c(char c) {
        std::string phrase(this->c_str(), this->length());
        phrase += c;
        this->assign(phrase);

        return *this;
    }
};

これを使用するには、アルファを含めるだけで:

Alpha str("");
string myStr = str.c('T').c('e').c('s').c('t');

したがって、mystrは「テスト」になり、文字列はバイナリの文字列テーブルから隠されます。

3
mafonya

暗号化技術は、重要なデータをセキュリティで保護するのに十分なほど強力ですなしでバイナリファイルに隠します。

または、バイナリファイルを使用して、何かが隠されているという事実を偽装することを考えていますか?

それは steganography と呼ばれます。

3
pavium

これはクライアントサーバーアプリケーションです!クライアント自体に保存しないでください。ハッカーが明らかに見える場所です。代わりに、このパスワードを取得するための追加のサーバー機能(HTTPS経由)を追加します(新しいクライアントのみ)。したがって、このパスワードがクライアントディスクにヒットすることはありません。

おまけとして、後でサーバーを修正するのがずっと簡単になります。毎回、クライアントごとに異なる時間制限パスワードを送信するだけです。新しいクライアントでより長いパスワードを許可することを忘れないでください。

3
MSalters

c ++ library を使用できます。この目的のために開発しました。 別の記事 これは実装がはるかに簡単で、2017年9月の最高のc ++記事として受賞しました。文字列を非表示にするより簡単な方法については、 TinyObfuscate を参照してください。

2

暗号化キーを逆に保存し( "yek noitpyrcne gnorts yM")、コード内でそれを逆にすると(String.Reverse)、暗号化キーのテキストをバイナリで簡単に検索できなくなります。

ただし、ここで他のすべてのポスターによって指摘されたポイントを繰り返しますが、これはセキュリティの面で実質的に何も達成しません。

2
MusiGenesis

文字列を簡単なエンコードを使用してエンコードできます。 xorとバイナリ01010101。もちろん本当の保護はありませんが、stringなどのツールの使用を妨げます。

2
sleske

ここに彼らが説明した例がありますが、これは「ハッカー」であるが、16進エディタでキディーを止めるだろう人によってかなり簡単に破られることに注意してください。提供された例iは、単に値80を追加し、その値からインデックスをサブトラックしてから、再び文字列を作成します。これをバイナリファイルに保存する場合は、文字列をbyte []配列に変換する方法がたくさんあります。

あなたのアプリでこれが機能しているとき、私は少し複雑な「数学」を使用します

わかりやすくするために理解していない人のために...クリアテキストで保存されないように、保存する前に文字列を暗号化します。暗号化されたテキストが変更されない場合は、リリースに暗号化機能を含めないでください。暗号化解除機能のみがあります。そのため、文字列を解読するには、ファイルを読み取ってからコンテンツを解読します。文字列がプレーンテキスト形式でファイルに保存されることはありません。

もちろん、暗号化された文字列をアプリケーションの定数文字列として保存し、必要なときに復号化して、文字列のサイズと変更頻度に応じて問題に最適なものを選択することもできます。

string Encrypted = EncryptMystring("AAbbBb");
string Decrypted = DecryptMystring(Encrypted);

string DecryptMystring(string RawStr)
    {
        string DecryptedStr = "";
        for (int i = 0; i < RawStr.Length; i++)
        {
            DecryptedStr += (char)((int)RawStr[i] - 80 + i);
        }

        return DecryptedStr;
    }

    string EncryptMystring(string RawStr)
    {
        string EncryptedStr = "";
        for (int i = 0; i < RawStr.Length; i++)
        {
            EncryptedStr += (char)((int)RawStr[i] + 80 - i);
        }

        return EncryptedStr;
    }
2
EKS

パスワードを静的なchar配列に割り当て、この関数へのポインターを返す関数を作成します。次に、難読化プログラムを介してこの関数を実行します。

プログラムが適切に機能する場合。 16進エディタを使用してプレーンテキストパスワードを読み取ってプログラムバイナリを調べることはできません。 (少なくとも、アセンブリ言語をリバースエンジニアリングしない限り。「文字列」または16進エディターで武装したすべてのスクリプトキディを停止する必要があります。

1
Bill

他の人が言ったように最初にそれをあいまいにした後、Assemblyブロックに文字列を埋め込み、指示のように見えるようにすることができますか?次に、「if 0」または「goto just_past_string_Assembly」を使用して、文字列を実際に隠している「コード」を飛び越えることができます。これにはおそらく、コード内の文字列を取得するためにもう少し作業が必要になります(1回限りのコーディングコスト)が、もう少しわかりにくいことが判明する可能性があります。

0
Harvey

m4をお勧めします。

  1. const string sPassword = _ENCRYPT("real password");のようなマクロで文字列を保存します

  2. ビルドする前に、マクロをm4で暗号化された文字列に展開します。これにより、コードはconst string sPassword = "encrypted string";

  3. ランタイム環境で復号化します。

0
Yudu Ban

私はあなたがそれを指示のように見せたいと思う、あなたの例

x [y ++] = 'M'; x [y ++] = 'y'; ...

それを行うと、わずかな変動のある繰り返し命令の長いシーケンスが目立つ可能性があり、それは悪いでしょう、問題のバイトは命令にそのままエンコードされ、それは悪いでしょう、それでおそらくxorメソッド、そしておそらくコードの長いセクションを目立たなくするためのいくつかの他のトリック、おそらくいくつかのダミー関数呼び出し。プロセッサにも依存します。ARMたとえば、バイナリデータを見て、データとそこから(デフォルトキーを探している場合)命令を選択するのは本当に簡単です。データであるがASCIIではないため、キーである可能性のあるものを選択する可能性があります。

0
old_timer

「文字列」プログラムからプレーンテキストのパスワードを隠すために難読化されたCコードを生成するPerlスクリプトを次に示します。

  obfuscate_password("myPassword123");

  sub obfuscate_password($) {

  my $string = shift;
  my @c = split(//, $string);
  Push(@c, "skip"); # Skip Null Terminator
                    # using memset to clear this byte
  # Add Decoy Characters
  for($i=0; $i < 100; $i++) {
    $ch = Rand(255);
    next if ($ch == 0);
    Push(@c, chr($ch));
  }                     
  my $count1 = @c;
  print "  int x1, x2, x3, x4;\n";
  print "  char password[$count1];\n";
  print "  memset(password, 0, $count1);\n";
  my $count2 = 0;
  my %dict  = ();
  while(1) {
    my $x = int(Rand($count1));
    $y = obfuscate_expr($count1, $x);
    next if (defined($dict{$x}));
    $dict{$x} = 1;
    last if ($count2+1 == $count1);
    if ($c[$x] ne "skip") {
      #print "  $y\n";
      print "  $y password[x4] = (char)" . ord($c[$x]) . ";\n";
    }
    $count2++;
  }
  }

  sub obfuscate_expr($$) {
    my $count  = shift;
    my $target = shift;
    #return $target;

    while(1) {

       my $a = int(Rand($count*2));
       my $b = int(Rand($count*2));
       my $c = int(Rand($count*2));
       next if (($a == 0) || ($b == 0) || ($c == 0));
       my $y = $a - $b;
       #print "$target: $y : $a - $b\n";
       if ($y == $target) {
          #return "$a - $b + $c";
          return "x1=$a; x2=$b; x3=$c; x4=x1-x2+x3; x5= +=x4;";
       }
    } 
  }
0
Bill

別のコードで暗号化キーを暗号化します。ユーザーに他のコードの画像を表示します。これで、ユーザーは表示されるキーを入力する必要があります(キャプチャのように、常に同じコード)。これにより、他のプログラムがコードを予測することもできなくなります。必要に応じて、コードの(塩漬け)ハッシュを保存して、ユーザーの入力を確認できます。

0
Van Uitkon