追記;
>22個文字列ですが、13個目以前は正常ですが
>13目以後、変換していないようです
22個文字列は暗号するとき、randValue[ra_i]は22個を使っています。
暗号後13個文字列になっています。
例
j=11 len=22
j=11 ik=0 randValue[55]=243
j=11 ik=1 randValue[56]=190
j=11 ik=2 randValue[57]=203
j=11 ik=3 randValue[58]=82
j=11 ik=4 randValue[59]=180
j=11 ik=5 randValue[60]=211
j=11 ik=6 randValue[61]=162
j=11 ik=7 randValue[62]=70
j=11 ik=8 randValue[63]=253
j=11 ik=9 randValue[64]=103
j=11 ik=10 randValue[65]=110
j=11 ik=11 randValue[66]=197
j=11 ik=12 randValue[67]=65 //[67]まで、正常です
j=11 ik=13 randValue[68]=188
j=11 ik=14 randValue[69]=103
j=11 ik=15 randValue[70]=77
j=11 ik=16 randValue[71]=16
j=11 ik=17 randValue[72]=202
j=11 ik=18 randValue[73]=172
j=11 ik=19 randValue[74]=221
j=11 ik=20 randValue[75]=104
j=11 ik=21 randValue[76]=129
j=11 len=13 //文字列13になっています。[68]~[76]までは変換していないようで
す。
複号部分に13個文字列はrandValue[ra_i]の13個しか使っていないですが、
13個目以降randValue[ra_i]は次ぎの文字列を使っています。
暗号後と複号部のrandValue[ra_i]は一致していない。
例
i=11 len2=13
i=11 ik=0 randValue[55]=243
i=11 ik=1 randValue[56]=190
i=11 ik=2 randValue[57]=203
i=11 ik=3 randValue[58]=82
i=11 ik=4 randValue[59]=180
i=11 ik=5 randValue[60]=211
i=11 ik=6 randValue[61]=162
i=11 ik=7 randValue[62]=70
i=11 ik=8 randValue[63]=253
i=11 ik=9 randValue[64]=103
i=11 ik=10 randValue[65]=110
i=11 ik=11 randValue[66]=197
i=11 ik=12 randValue[67]=65 //[67]まで、正常です
//次ぎの文字列(i=12)を使っています
i=12 ik=0 randValue[68]=188
i=12 ik=1 randValue[69]=103
i=12 ik=2 randValue[70]=77
i=12 ik=3 randValue[71]=16
i=12 ik=4 randValue[72]=202
i=12 ik=5 randValue[73]=172
i=12 ik=6 randValue[74]=221
i=12 ik=7 randValue[75]=104
i=12 ik=8 randValue[76]=129
よろしくお願いします。
とりあえず目先の問題について。(後で根こそぎひっくり返しますが)
> 必ずLen=Len2と思っていますが、これが間違っていますか?
> もし間違っているなら、理由を教えてください。
あ、ごめん。最後の'\0'は変換しないから、len < len2となることはありませんね。
ただし、len > len2となることはあり得ます。理由はこれ。
> 変換前のDFMData_Savedata.DfName[ik] == randValue[ra_i]がとなっていて、
> 変換後にゼロになるからでしょう。
つまり、変換前に、j = 11のときのDFMData_Savedata.DfmName[13]がrandvaluel[68]と同じ183
(charなら-68かな? Shift_JISと仮定すると半角の'シ')だったりするのではありませんか?
そういう訳なので、復号時にstrlen(暗号化済文字列)で復号すべき長さを求めるのは間違い。
変換前の文字列長の分だけを暗号化するなら、元々の長さ情報をどこか別のところに保存しておく必要があ
ります。
しかし、方針を変えて、変換前の文字列長の分だけを暗号化するのではなく、STR_NAME2分全部暗号化して
しまう方が簡単でしょう。
さて。以下に対する回答が、前半部分のあれこれをすべて無に帰してしまいます。
> ># ところで、文字列部分しか暗号化していないようですが、その程度のものでよいので
> >すか?
> いいえ、一行の場合(構造体)どうやるのでしょうか。
DFMData構造体のインスタンスを単なるバイト列と見做して、sizeof(DFMData)バイト分暗号化する。
元のコードに変数名や構成等を合わせて書くならこんな感じ。(動作未検証)
元のコードに較べて、ra_iが大きくなるので、randValueの長さに注意。
# 一から書くとしたら、任意のバイト数のデータを暗号化する関数を
# 作って、そこにDFMData構造体のインスタンス(何なら構造体配列全体)
# を放り込むようにするけどね。
// 変数宣言は元コードと同じなので略。
for(int j=0; j<30; j++){
DFMData_Savedata = mp_TextDfmSct[j].DfmLaye;
char *p = (char *)&DFMData_Savedata; // DFMData_Savedata をバイト列とみなして
// そのバイト列を構造体の長さ分だけ暗号化
for (int ik=0; ik<sizeof(DFMData_Savedata); ik++){
p[ik] ^= randValue[ra_i++];
}
fwrite(&DFMData_Savedata, sizeof(DFMData_Savedata), 1, fp9);
}
yoh2さん、
ご返事ありがとう。
>そういう訳なので、復号時にstrlen(暗号化済文字列)で復号すべき長さを求めるのは間
>違い。
なるほど、わかりました。
>変換前の文字列長の分だけを暗号化するなら、元々の長さ情報をどこか別のところに保
>存しておく必要があります。
そうですね、面倒になります。
>char *p = (char *)&DFMData_Savedata; // DFMData_Savedata をバイト列とみなして
複号時に、以下のように書きました。
for(int i=0; i<30; i++){
fread(&DFMData_Readdata, sizeof(DFMData_Readdata), 1, fp8);
char *p = (char *)&DFMData_Readdata; // DFMData_Savedata をバイト列とみなして
for(ik=0; ik<sizeof(DFMData_Readdata); ik++ ){
p[ik] = (char)(p[ik] ^ randValue[ra_i]);
ra_i++;
}
p[ik] = '\0';
//構造体に戻す
DFMData_Readdata=(DFMData)p; //ここで、どう書けばいいでしょうか
//今は、エラーです。
//検証するため
st2.Format(%d,%d,%d,%s,%d,%d,%d,%d,%d\n,
DFMData_Readdata.DfmLaye,
DFMData_Readdata.DfmBcode,
DFMData_Readdata.DfmType,
DFMData_Readdata.DfmName,
DFMData_Readdata.DfmSize,
DFMData_Readdata.DfmWide,
DFMData_Readdata.DfmColor,
DFMData_Readdata.DfmArrange,
DFMData_Readdata.TextDfmDraw_flag);
fputs(st2, fp7);
}
ここで、DFMData_ReaddataとDFMData_Savedataは同じ構造体です。
よろしくお願いします。
オレンジフィッシュです。
構造体で苦戦していますが文字列と同じように扱えば楽ですよ。
こんどは文字列でも構造体でも使えるものを紹介します。
// 暗号化/復元の両用関数(関数名は適当にどうぞ)
void MyPassword( void *buff, size_t size )
{
char *p = (char *)buff;
RandInit( 初期値 );
while ( size > 0 ){
*p++ = (char)(*p ^ Rand());
size--;
}
}
※RnadInit()、Rand() は自分で用意して下さい。
※標準ライブラリの srand()、rand() でも良いですが処理系を変えてしまうと
復元出来なくなるかもしれない。バージョンによっても出来なくなるので自作すべ
し。
----------------------------------------
// 文字列の場合(暗号化)
char str[] = 123,3334,QQQ,RRRR,4444;
int len = strlen(str);
MyPassword( str, len );
fwrite( str, len, 1, fp );
// 文字列の場合(復元)
char str[ 32 ] = { 0 };
len = 文字列の長さ(22バイト)
fread( str, len, 1, fp );
MyPassword( str, len );
printf( 復元文字列:%s\n, str );
----------------------------------------
// 構造体,バイナリの場合(暗号化)
struct DFMData DFMData_Savedata = { 初期化 };
MyPassword( &DFMData_Savedata, sizeof(struct DFMData) );
fwrite( &DFMData_Savedata, sizeof(struct DFMData), 1, fp );
// 構造体,バイナリの場合(復元)
struct DFMData DFMData_Readdata;
fread( &DFMData_Readdata, sizeof(struct DFMData), 1, fp );
MyPassword( &DFMData_Readdata, sizeof(struct DFMData) );
printf( DfmName=[%s]\n, DFMData_Readdata.DfmName );
※文字列は復元後に表示すること。
----------------------------------------
構造体の場合は文字列の部分だけではなく全体をバイナリ扱いして暗号化と復元を
データサイズ分だけ行えば良いです。また、暗号化した文字列はバイナリになります。
文字列の途中で \0 が出るかもしれません。strlen() で長さを数えてはいけません。
また構造体の配列を一度に暗号化、復元するには size に構造体サイズ×配列要素数の
バイト数を与えればよい。
たとえば DFMData_Savedata[ 10 ] なら
MyPassword( &DFMData_Savedata, (sizeof(struct DFMData) * 10) );
fwrite( &DFMData_Savedata, sizeof(struct DFMData), 10, fp );
fread( &DFMData_Readdata, sizeof(struct DFMData), 10, fp );
とすれば良い。
fread、fwrite のように個数 max を MyPassword() 関数に用意しても良いかも。
この辺は自由だ。
> //構造体に戻す
> DFMData_Readdata=(DFMData)p; //ここで、どう書けばいいでしょうか
> //今は、エラーです。
何も書く必要なし。
> char *p = (char *)&DFMData_Readdata; // DFMData_Savedata をバイト列とみなし
て
の意味を考えれば理由は分かるはずです。
復元時の for 文の後にある
>p[ik] = '\0';
これは必要ない処理です。
前は文字列だったのでその最後に \0 を入れていたのです。
構造体やバイナリデータでは必要ありません。
行うとメモリを破壊する恐れがあります。
注意して下さい。
あと i<30 になっているため 30 個の構造体データを一度に
暗号化や復元しても良さそうです。つまり、復元だけ記述すると
struct DFMData DFMData_Readdata[ 30 ];
fread( DFMData_Readdata, sizeof(DFMData_Readdata), 30, fp );
char *p = (char *)&DFMData_Readdata[0];
char *end = (char *)&DFMData_Readdata[30];
while ( p < end ){
*p++ = (char)(*p ^ randValue[ra_i]);
ra_i++;
}
という感じでも良さそうです。
yoh2さん、オレンジフィッシュさん
ご返事ありがとう。
>何も書く必要なし。
> char *p = (char *)&DFMData_Readdata; // DFMData_Savedata をバイト列とみなし
>て
>の意味を考えれば理由は分かるはずです。
ポイントを指しているのですが、すみませんでした。
うまく変換できました。
一つ覚えるのことはsizeof(DFMData_Savedata)です。
sizeof(DFMData_Savedata)より strlen(DFMData_Savedata)精確です。
> 構造体,バイナリの場合(暗号化)
…
>構造体,バイナリの場合(復元)
…
>また構造体の配列を一度に暗号化、復元するには size に構造体サイズ×配列要素数の
>バイト数を与えればよい。
やりました、うまくできました。
簡潔になりました。
>前は文字列だったのでその最後に \0 を入れていたのです。
>構造体やバイナリデータでは必要ありません。
>行うとメモリを破壊する恐れがあります。
>注意して下さい。
わかりました。
最後、もう一つの質問があります、
今回の暗号化複号化したのですが、
できたバイナリファイルを解読できるのでしょうか、
あるいは、専用のフリソフトを使って簡単に解読されました。(推測)
つまり、保護はどの程度でしょうか。
よろしくお願いします。
> できたバイナリファイルを解読できるのでしょうか、
> あるいは、専用のフリソフトを使って簡単に解読されました。(推測)
> つまり、保護はどの程度でしょうか。
フィールド位置は一定だし、変換方法が単純なxorでしかないので、内容を少し変えたファイルをいくつか
用意して変更点を比較されると、バイナリエディタのひとつでもあれば割とあっさり解読できそうです。
また、このプログラムの利用形態が直接実行 (CGIのように、実行ファイルが利用者から見えないのではな
く、通常のワープロソフトのように、実行ファイルが利用者の手元に存在する) の場合、プログラムを逆ア
センブルされたらファイルの比較などの手間をかけることなく丸分かりです。
暗号化というよりは難読化というレベルだと思います。
>できたバイナリファイルを解読できるのでしょうか
この内容だと容易に解析されます。
しかし、暗号処理が復号して欲しくない人の環境下で行われる場合は解読を
防ぐ事は不可能です。
完全な暗号化をする場合は、復号して欲しくない人には全くアクセスできない場
所で暗号化や復号を行う必要があります。
理論上、物理的に閉じたプログラムはソフトウェアの動作を1ステップずつ解析さ
れる事を防ぐ手立てはありません。
分解不能なハードウェアで処理をしたり、ネットワーク上で暗号処理を完全に隠
蔽する必要があります。
大抵はそこまでしないである程度の難読化で終わると思います。
とりあえず、今回の例を変更して簡単なバイナリ比較を防ぐ場合は
1、乱数列を毎回変更する。
乱数列が毎回同じなのでファイル又は構造体先頭に乱数の種を保存する領域
を追加して、ファイル又構造体ごとに違う乱数を使って暗号処理を行う。
乱数の種自体は暗号化しない。
乱数の種はまた別の乱数列から生成する。たとえば保存処理の前に現在時刻
などを元に乱数列を生成しておき、それを利用するなど。
2、構造体の保存順序を毎回乱す。
小手先だが、保存の前に構造体の位置をシャッフルしてから保存する。シャッフ
ルした順番で保存していく。
並び順に意味が無いならそのままで良いし、並び順を復元するのならばシャッフ
ルに使った乱数の種から復元したり、本来の位置を構造体の前後に追加して保
存しておく。
辺りが使えるかと思います。
これもやはり実行ファイル解析には歯が立ちません。
こちらは実行ファイルの難読化なども考慮したほうがいいのかもしれません。
yoh2さん、麩さん
ご返事ありがとう。
>暗号化というよりは難読化というレベルだと思います。
>こちらは実行ファイルの難読化なども考慮したほうがいいのかもしれません。
わかりました。
皆さん、いろいろ教えていただき、
ほんとにありがとうございました。