C ++テンプレートを使用したデータ生成

タスクが小さなプログラムを作成するように設定されたら、何かをしなければなりませんでしたが、同時に、可能な限り逆アセンブリ中のコード分析のプロセスを複雑にする必要がありました。 方法の1つは、使用されているWinApi関数へのすべての参照をexeから削除することです。WinApi関数名ではなく、単純なアルゴリズムでスレッドによって計算されたハッシュコードを保存することにしました。 次に、dllライブラリのすべての関数を列挙するときに、これらのハッシュコードに必要な関数を見つけます。 最も一般的に使用される方法の1つは、小さなユーティリティを作成し、必要な関数の名前の入力リストを提供し、それらのハッシュコードを計算し、出力でコード付きの完成したテーブルが配置されているソースを提供することです。 次に、このソースコードはプロジェクトに接続され、完全に使用されます。 しかし、いつものように怠lazが介入しました。 そのようなユーティリティを書くのは面倒で、メイクファイルで必要なアクションを規定することすらできませんでした。 コンパイル時にすべてを計算したかったのです。 次に、C ++テンプレートに目を向けました...



C ++テンプレートでは、char *型の文字列を操作できません。それらに書き込むことも、読み取ることもできません。ポインターを使用して、さらにどこかに渡すことしかできません。 しかし、怠lazは頑固なものですが、テンプレートのパラメーターが文字列自体ではなく、個別に文字を指定するとどうなりますか? これはすでに非常に優れており、テンプレートにはデフォルトのパラメーターを設定できます。 パラメータが文字(char型)になるテンプレートクラスと、パラメータ値に基づいてハッシュを計算する関数を記述するだけで十分です。クラス自体は次のとおりです。

template<char c01 = 0, char c02 = 0, char c03 = 0, char c04 = 0, char c05 = 0, char c06 = 0, char c07 = 0, char c08 = 0, char c09 = 0, char c10 = 0, char c11 = 0, char c12 = 0, char c13 = 0, char c14 = 0, char c15 = 0, char c16 = 0, char c17 = 0, char c18 = 0, char c19 = 0, char c20 = 0, char c21 = 0, char c22 = 0, char c23 = 0, char c24 = 0, char c25 = 0, char c26 = 0, char c27 = 0, char c28 = 0, char c29 = 0, char c30 = 0, char c31 = 0, char c32 = 0 > class hash_text { public: //   static unsigned int get_hash() { unsigned int hash = c01; if( c02 ) { hash ^= (hash << 8) | c02; } if( c03 ) { hash ^= (hash << 8) | c03; } if( c04 ) { hash ^= (hash << 8) | c04; } if( c05 ) { hash ^= (hash << 8) | c05; } if( c06 ) { hash ^= (hash << 8) | c06; } if( c07 ) { hash ^= (hash << 8) | c07; } if( c08 ) { hash ^= (hash << 8) | c08; } if( c09 ) { hash ^= (hash << 8) | c09; } if( c10 ) { hash ^= (hash << 8) | c10; } if( c11 ) { hash ^= (hash << 8) | c11; } if( c12 ) { hash ^= (hash << 8) | c12; } if( c13 ) { hash ^= (hash << 8) | c13; } if( c14 ) { hash ^= (hash << 8) | c14; } if( c15 ) { hash ^= (hash << 8) | c15; } if( c16 ) { hash ^= (hash << 8) | c16; } if( c17 ) { hash ^= (hash << 8) | c17; } if( c18 ) { hash ^= (hash << 8) | c18; } if( c19 ) { hash ^= (hash << 8) | c19; } if( c20 ) { hash ^= (hash << 8) | c20; } if( c21 ) { hash ^= (hash << 8) | c21; } if( c22 ) { hash ^= (hash << 8) | c22; } if( c23 ) { hash ^= (hash << 8) | c23; } if( c24 ) { hash ^= (hash << 8) | c24; } if( c25 ) { hash ^= (hash << 8) | c25; } if( c26 ) { hash ^= (hash << 8) | c26; } if( c27 ) { hash ^= (hash << 8) | c27; } if( c28 ) { hash ^= (hash << 8) | c28; } if( c29 ) { hash ^= (hash << 8) | c29; } if( c30 ) { hash ^= (hash << 8) | c30; } if( c31 ) { hash ^= (hash << 8) | c31; } if( c32 ) { hash ^= (hash << 8) | c32; } return hash; } };
      
      





ご覧のとおり、すべてがそれほど複雑ではありません。 このクラスの唯一の制限は、長さが32文字以下の文字列を使用できることですが、さらに必要な場合は簡単に拡張できます。

今このクラスを使用する方法は? すべてが非常に単純で、関数の名前を取得して、テンプレートパラメーターに文字ごとに書き込みます。

 typedef hash_text<'C','r','e','a','t','e','F','i','l','e'> hashCreateFile; //  
      
      





どうですか?



そして、ハッシュ自体を取得するには:

 hashCreateFile::get_hash();
      
      





それだけです! これで、必要な数の関数型を宣言し、必要な場所でハッシュを使用するだけで十分です。

コンパイラーが不必要なコードを大量に生成すると考えている場合、間違っていることになります。1つの数字しかなく、他に何もありません(アセンブリー言語でコンパイルしてみてください)。

ただし、これは制限ではありません。この方法では、コンパイル時に文字列をその場で暗号化することができます。



文字列暗号化



単純なアルゴリズムを使用して文字列を暗号化します。コードパラメータに応じて、文字の各ペアをその場で変更します。 これを行うには、追加のバッファをクラスに追加します(そして暗号化を保存する他の場所は?)そして、いくつかの特別な関数と出来事:

 template<unsigned int code, char c01 = 0, char c02 = 0, char c03 = 0, char c04 = 0, char c05 = 0, char c06 = 0, char c07 = 0, char c08 = 0, char c09 = 0, char c10 = 0, char c11 = 0, char c12 = 0, char c13 = 0, char c14 = 0, char c15 = 0, char c16 = 0, char c17 = 0, char c18 = 0, char c19 = 0, char c20 = 0, char c21 = 0, char c22 = 0, char c23 = 0, char c24 = 0, char c25 = 0, char c26 = 0, char c27 = 0, char c28 = 0, char c29 = 0, char c30 = 0, char c31 = 0, char c32 = 0 > class code_text { //     static const char count = (c01 ? 1 : 0) + (c02 ? 1 : 0) + (c03 ? 1 : 0) + (c04 ? 1 : 0) + (c05 ? 1 : 0) + (c06 ? 1 : 0) + (c07 ? 1 : 0) + (c08 ? 1 : 0) + (c09 ? 1 : 0) + (c10 ? 1 : 0) + (c11 ? 1 : 0) + (c12 ? 1 : 0) + (c13 ? 1 : 0) + (c14 ? 1 : 0) + (c15 ? 1 : 0) + (c16 ? 1 : 0) + (c17 ? 1 : 0) + (c18 ? 1 : 0) + (c19 ? 1 : 0) + (c20 ? 1 : 0) + (c21 ? 1 : 0) + (c22 ? 1 : 0) + (c23 ? 1 : 0) + (c24 ? 1 : 0) + (c25 ? 1 : 0) + (c26 ? 1 : 0) + (c27 ? 1 : 0) + (c28 ? 1 : 0) + (c29 ? 1 : 0) + (c30 ? 1 : 0) + (c31 ? 1 : 0) + (c32 ? 1 : 0); char buf[count + 1]; //     char encode_char( bool first, int bit, char b1, char b2 ) { if( b2 == 0 ) //  return b1; unsigned int change = code & (1 << bit); //    if( first ) return change ? b2 : b1; // 1-  else return change ? b1 : b2; // 2-  } //    , n -      bool put_char( int n, char c1, char c2 ) { if( c1 == 0 ) return false; //  buf[n] = encode_char( true, n / 2, c1, c2 ); if( c2 == 0 ) return false; //  buf[n + 1] = encode_char( false, n / 2, c1, c2 ); return true; } //   void encode() { int v = 0; //    ,  if   ,      ) if( put_char( 0, c01, c02 ) ) if( put_char( 2, c03, c04 ) ) if( put_char( 4, c05, c06 ) ) if( put_char( 6, c07, c08 ) ) if( put_char( 8, c09, c10 ) ) if( put_char( 10, c11, c12 ) ) if( put_char( 12, c13, c14 ) ) if( put_char( 14, c15, c16 ) ) if( put_char( 16, c17, c18 ) ) if( put_char( 18, c19, c20 ) ) if( put_char( 20, c21, c22 ) ) if( put_char( 22, c23, c24 ) ) if( put_char( 24, c25, c26 ) ) if( put_char( 26, c27, c28 ) ) if( put_char( 28, c29, c30 ) ) if( put_char( 30, c31, c32 ) ) v = 0; //   if   -  buf[count] = 0; } public: code_text() { encode(); } const char* ptr() const //    { return buf; } int len() { return count; } char* str( char* to ) { return decode( buf, count, to ); //  to } };
      
      





ご覧のとおり、もう少し複雑ですが、意味は同じです。 少し異なる方法で使用する必要があります。

 code_text<44717397, 'E','n','c','o','d','e','S','t','r','i','n','g'> EncodeString;
      
      





オブジェクトは上記ではなく、ここで作成されます-タイプ。 さて、次のように適用できます:

 printf( "%s\n", EncodeString.ptr() );
      
      





画面上に暗号化された文字列があります、または

 char to[33]; printf( "%s\n", EncodeString.str(to) );
      
      





画面に解読された文字列が表示されます。 もちろん、デコード機能はどこかで説明する必要があります。

タイプではなくオブジェクトを作成する場合、1つのことがありますが、受信したデータが.dataセクションにこのように配置されない(通常の行のように)コンパイラーは、mov命令を使用して計算されたデータを保存するコードを生成します。 これはいわゆる初期化コードで、main()関数が呼び出される前に実行されます。つまり、すべてが透過的であり、結果を通常の行として使用できます。 このような巨大なインライン関数は最終的なexeに残る可能性があるため、コンパイラを最適化するためのオプションで作業する必要がある場合があります。



前菜



まあ、スナックはまだそのような例です。 C ++には組み込みの2進数はありません。 しかし、この方法を使用してこれを回避できます。 以下は、32ビットの数値を変換できるクラスです。

 template<int c01 = -1, int c02 = -1, int c03 = -1, int c04 = -1, int c05 = -1, int c06 = -1, int c07 = -1, int c08 = -1, int c09 = -1, int c10 = -1, int c11 = -1, int c12 = -1, int c13 = -1, int c14 = -1, int c15 = -1, int c16 = -1, int c17 = -1, int c18 = -1, int c19 = -1, int c20 = -1, int c21 = -1, int c22 = -1, int c23 = -1, int c24 = -1, int c25 = -1, int c26 = -1, int c27 = -1, int c28 = -1, int c29 = -1, int c30 = -1, int c31 = -1, int c32 = -1 > class bin_to_dec { static unsigned int get_bit( int res, int c ) { return (res << 1) | (c ? 1 : 0); //c      0  1 } public: static unsigned int dec() { unsigned int res = 0; if( c01 >= 0 ) res = get_bit( res, c01 ); if( c02 >= 0 ) res = get_bit( res, c02 ); if( c03 >= 0 ) res = get_bit( res, c03 ); if( c04 >= 0 ) res = get_bit( res, c04 ); if( c05 >= 0 ) res = get_bit( res, c05 ); if( c06 >= 0 ) res = get_bit( res, c06 ); if( c07 >= 0 ) res = get_bit( res, c07 ); if( c08 >= 0 ) res = get_bit( res, c08 ); if( c09 >= 0 ) res = get_bit( res, c09 ); if( c10 >= 0 ) res = get_bit( res, c10 ); if( c11 >= 0 ) res = get_bit( res, c11 ); if( c12 >= 0 ) res = get_bit( res, c12 ); if( c13 >= 0 ) res = get_bit( res, c13 ); if( c14 >= 0 ) res = get_bit( res, c14 ); if( c15 >= 0 ) res = get_bit( res, c15 ); if( c16 >= 0 ) res = get_bit( res, c16 ); if( c17 >= 0 ) res = get_bit( res, c17 ); if( c18 >= 0 ) res = get_bit( res, c18 ); if( c19 >= 0 ) res = get_bit( res, c19 ); if( c20 >= 0 ) res = get_bit( res, c20 ); if( c21 >= 0 ) res = get_bit( res, c21 ); if( c22 >= 0 ) res = get_bit( res, c22 ); if( c23 >= 0 ) res = get_bit( res, c23 ); if( c24 >= 0 ) res = get_bit( res, c24 ); if( c25 >= 0 ) res = get_bit( res, c25 ); if( c26 >= 0 ) res = get_bit( res, c26 ); if( c27 >= 0 ) res = get_bit( res, c27 ); if( c28 >= 0 ) res = get_bit( res, c28 ); if( c29 >= 0 ) res = get_bit( res, c29 ); if( c30 >= 0 ) res = get_bit( res, c30 ); if( c31 >= 0 ) res = get_bit( res, c31 ); if( c32 >= 0 ) res = get_bit( res, c32 ); return res; } };
      
      





次のように使用します。

 typedef bin_to_dec<1,1,1,0,0,0,1,1,1> flags; printf( "%u", flags::dec() );
      
      







あとがき




1つのプロジェクトでこの方法を使用してハッシュとエンコードされた文字列を生成しますが、これまでのところすべてが素晴らしいですが、特にお勧めしません。別のユーティリティを作成する方が良いでしょう。



All Articles