|
|
|
2016/5/27(Fri) 01:53:19|NO.75614
ある時点まではそれっぽく動作していたのですが、気がついたらどうにもならなくなってしまったので、
スレに丸投げしてしまいました……。
以下の2点について、ご協力お願いします。
1.
このコード、既にバグを含むわけですが、
復号化ルーチンのpeekで、「パラメータ値が異常」と出るのです。
peekの第二引数のインデックスに、const定数が使われていますが、
それを取り除くとエラーが出なくなります。
2.
暗号化ルーチンで、XYテーブルの暗号化終了時点でなぜか予想サイズを6byte程上回ります。
しかし、確保されたメモリサイズが変動している様子はないし、
それなら何故バッファオーバーフローが起こらないのか。
色々こんがらがってます。
/*
簡易暗号復号化モジュール(by Sado in Team EMER on March 25, 2016)
SimpleEncrypt命令で、テキストデータを暗号バイナリーデータへ暗号化
SimpleDecrypt命令で、暗号をテキストデータへ復号化
・テキストデータは、全てUnicode $20~$BEであること(改行コードなどは非対応)
・見かけを編集しづらいようにするためのものであり、セキュリティは紙装甲
*/
#module m_SimpleCiphering
#const ShiftKeyNum 16
#const XKeyNum 13
#const YKeyNum 13
#const AllKeyNum ShiftKeyNum+XKeyNum+YKeyNum
;数値のループ
#defcfunc local limitloop int p1,int min,int max,local d
if min==max : return min
if min<=p1 & p1<=max : return p1
if p1<min : return max-(min-p1-1)\(max-min+1)
if p1>max : return min+(p1-max-1)\(max-min+1)
return d
;暗号化命令
#deffunc SimpleEncrypt var _edata_,var _data_
//鍵生成--------
;シフト鍵生成
;(このまま保存するためNull文字を除外)
dim en_shiftkey,ShiftKeyNum
wait 1:randomize
foreach en_shiftkey
en_shiftkey(cnt)=rnd($FE)+1
loop
;X鍵生成
;(最後の暗号化に利用するためNull文字を除外)
wait 1:randomize
dim en_xkey,XKeyNum
foreach en_xkey
keycnt=cnt
;重複検知
repeat
;仮鍵生成
tmpcnt=0
tmp=rnd($FE)+1
repeat keycnt
;仮鍵が重複しない→カウント加算
if en_xkey(cnt)!=tmp:tmpcnt++
loop
if keycnt=tmpcnt : break;重複なし
loop
en_xkey(cnt)=tmp
loop
;Y鍵生成
;(最後の暗号化に利用するためNull文字を除外)
wait 1:randomize
dim en_ykey,YKeyNum
foreach en_ykey
keycnt=cnt
repeat
tmpcnt=0
tmp=rnd($FE)+1
repeat keycnt
if en_ykey(cnt)!=tmp:tmpcnt++
loop
if keycnt=tmpcnt : break
loop
en_ykey(cnt)=tmp
loop
//暗号化--------
sdim b_edata, AllKeyNum + strlen(_data_)*2
;DEBUG
mes "暗号予想サイズ:"+(AllKeyNum+strlen(_data_)*2)+"B"
;暗号鍵の統合
tmp=0
foreach en_shiftkey
poke b_edata, cnt, en_shiftkey(cnt)
loop
foreach en_xkey
poke b_edata, length(en_shiftkey)+cnt, en_xkey(cnt)
loop
foreach en_ykey
poke b_edata, length(en_shiftkey)+length(en_xkey)+cnt, en_ykey(cnt)
loop
app=""
code=0
index=0
maxindex=strlen(_data_)
repeat maxindex
;1byte抽出
code=peek(_data_,cnt)
;シフト暗号化
code-$20
code=limitloop@m_SimpleCiphering(code+en_shiftkey(cnt \ length(en_shiftkey)), $00, $9E)
;テーブル暗号化
;(何故か、最後に~6byte多く謎のデータが挿入される)
tmp=code
poke b_edata,AllKeyNum+ cnt*2 , en_xkey(tmp \ length(en_xkey))
poke b_edata,AllKeyNum+ cnt*2+1, en_ykey(tmp / length(en_ykey))
loop
;暗号データを返す
_edata_=""
memexpand _edata_,AllKeyNum+strlen(_data_)*2
memcpy _edata_,b_edata,AllKeyNum+strlen(_data_)*2,0,0
return
;復号化命令
#deffunc SimpleDecrypt var _data_,var _edata_
//鍵を取り出す--------
dim en_shiftkey,ShiftKeyNum
dim en_xkey,XKeyNum
dim en_ykey,YKeyNum
foreach en_shiftkey
en_shiftkey(cnt)=peek(_edata_,cnt)
loop
foreach en_xkey
en_xkey(cnt)=peek(_edata_,ShiftKeyNum+cnt)
loop
Foreach en_ykey
en_ykey(cnt)=peek(_edata_,ShiftKeyNum+XKeyNum+cnt)
loop
//復号化--------
tmp=(strlen(_edata_)-length(en_shiftkey)-length(en_xkey)-length(en_ykey))/2
sdim b_data,tmp
maxindex=tmp
code=0
repeat maxindex
;テーブルXY座標を求める
tmpx=peek(_edata_,AllKeyNum + cnt*2)
tmpy=peek(_edata_,AllKeyNum + cnt*2+1)
foreach en_xkey
if en_xkey(cnt)==tmpx: x=cnt
loop
foreach en_ykey
if en_ykey(cnt)==tmpy: y=cnt
loop
;XY座標から文字を算出する
code=x+y*length(en_xkey)
;シフト鍵による復号化
code=limitloop@m_SimpleCiphering(code-en_shiftkey(cnt \ length(en_shiftkey)), $00, $9E) + $20
;複合データ集積
poke b_data,cnt,code
loop
;複合データを返す
_data_ = b_data
return
#global
//test----------------------------------------------------------
screen 0,1024,256
*main
cls 4:pos 0,0
data="This is debug words. hello world! space space space Yeaaaaaa!!!"
color $FF,$FF,$FF:mes "■原文("+strlen(data)+"B)"+data
SimpleEncrypt edata,data
color $FF,$60,$60
mes "■暗号("+strlen(edata)+"B)"
mes edata
; bsave "save.dat",edata
data=""
SimpleDecrypt data,edata
color $30,$FF,$80:mes "■複合("+strlen(data)+"B)\n"+data
onclick goto*main
stop;*/
| |
|
2016/5/27(Fri) 23:07:43|NO.75624
さて、いろいろ突っ込みどころはありますが、まずは要点だけ。
;暗号データを返すの下3行
_edata_=""
memexpand _edata_,AllKeyNum+strlen(_data_)*2
memcpy _edata_,b_edata,AllKeyNum+strlen(_data_)*2,0,0
を
sdim _edata_,AllKeyNum+strlen(_data_)*2+1
memcpy _edata_,b_edata,AllKeyNum+strlen(_data_)*2,0,0
もしくは
_edata_=""
memexpand _edata_,AllKeyNum+strlen(_data_)*2+1
memset _edata_,0,1,AllKeyNum+strlen(_data_)*2
memcpy _edata_,b_edata,AllKeyNum+strlen(_data_)*2,0,0
に変更する
これで多分治ると思います。あんまり時間かけてないので怪しいですが。
一応、文字列の中身や長さを変えたりして、検証してから安心して下さい。
_edata_の終端文字がNULL文字でないため、strlenを使った時に困る。
メモリの確保領域をはみ出て、NULL文字("00")にあたるまでメモリを読んでそのバイト数を返す。
そのサイズに合わせて複合化した結果がこれだよ!……という流れかと。
不具合が起きていた原因はざっとこんな感じですかね?(´・ω・)
下記の突っ込みについては、時間があれば読んでください。
※長々と突っ込み。自分の主義主張を語ってるだけなので、半分くらいスルーしてね(・ω・)
・X,Y鍵生成は関数化しましょう。コピペダメ絶対。余計なエラーを防ぎましょう。
・app=""みたいな、使われてない変数がものっそい気になりました。
・repeatやforeachのインデントェ……するのかしないのか統一してください。
・105行目のtmp=codeって…いるの?
・配列変数に関して、foreachだったりrepeatだったり統一感が…。
せっかくconstで配列サイズ指定してるのに、使わないんです?
foreachって、変数の長さが可変の時に使うイメージでしたけど。
・wait 1:randomizeって3回も必要なんです?
基本的に、プログラム開始時に1回randomizeを呼べば大丈夫ですよ。
seed値つきのrandomizeを使う時は別ですが。
・limitloop関数の呼び出しにlimitloop@m_SimpleCipheringと書いているけど、なぜだろう?
せっかく同じモジュールなのに、limitloopと書かないのかな。なにか特別な理由がありそう。
個人的には、暗号化モジュールにlimitloop関数は入れない。このモジュール内でしか使わないとしても。
なぜなら、limitloopのやってること自体は、暗号化(SimpleCipheringというモジュール名)に関係ないからね。
・limitloopのlocal dはいりません。return dもreturnでいいと思う。あえてエラーを出す方針(・ω・)
limitloopを呼び出す度に、使いもしないdのメモリ食うなんて…気持ち悪くない?
localがなかったとしても、dがモジュール内の共有変数になるから、起動してある間ずっと食うしねえ。
まあこの前の質問の時に、消し忘れてたこちらの落ち度でもあるのだが。
・テストコードのmes edataは、ほとんど意味をなさない気がする。
デバッグウィンドウからメモリダンプでedataを覗いた方が、まだ有意義な気がする。
ちなみに修正後のコードでmes edataをすると、たまにHSP側のデバッグデータが表示される。
これはデバッグモードの時だけおこる。多分。
原因はよくわからなかったが、恐らくたまたま誤認識されただけだと思う。
(暗号化されたりした)よくわからない文字コードをmesで表示されるのが、仕様想定外なのだと思う。
・tempやらcodeやらはlocal指定しとくといいですよ。(私個人の主義なだけ?)
基本的にメモリ管理が雑にしかできないHSPですけど、localだけは別でちゃんと関数外に出たら解放してくれる。(と思って今まで使っていた)
そして同モジュールの他関数との変数の共有を防げるので、未初期化忘れによる思わぬ不具合が防げます。
ただしデバッグウィンドウでは中身が見れなくなります('A`)マアトウゼンダネ
どうしてもデバッグ中に中身をみたい時は、気合でlocalを外すかlogmesとかdialogとかを駆使シテネ。
以上。誤字脱字はご愛敬。
いろいろと間違っている突っ込みに対する突っ込みもどうぞ。
| |
|
2016/5/28(Sat) 01:10:32|NO.75625
>KOMARIさん
わー、ありがとうございます! なるほど、なるほど……
いまいちNull文字の扱い方が分かりませんが
とりあえずは「必ず一番最後にNull文字が必要!」と覚えておきます。
foreachは確かに、自分でも何だこりゃとは思っていました。
たしか、XY鍵を可変にしようとして、でも諦めてしまった後ほったらかしだったんですね。
tmpとかindexとかが放置されて良いのはglobal空間だけですよね……
この関数・命令自体はループ中で使うものでは無いので、
コマリさんのアドバイスに従ってlocal指定します。
他、指摘のあった箇所は粗方修正できたかと思います。
↓
/*
簡易暗号復号化モジュール(by Sado in Team EMER on March 27, 2016)
SimpleEncrypt命令で、テキストデータを暗号バイナリーデータへ暗号化
SimpleDecrypt命令で、暗号バイナリーデータをテキストデータへ復号化
・テキストデータは、全てUnicode $20~$BEであること(改行コードなどは非対応)
・見かけを編集しづらいようにするためのものであり、セキュリティは紙装甲
*/
#module m_SimpleCiphering
#const ShiftKeyNum 16
#const XKeyNum 13
#const YKeyNum 13
#const AllKeyNum ShiftKeyNum+XKeyNum+YKeyNum
;数値のループ
#defcfunc limitloop int p1,int min,int max
if min==max : return min
if min<=p1 & p1<=max : return p1
if p1<min : return max-(min-p1-1)\(max-min+1)
if p1>max : return min+(p1-max-1)\(max-min+1)
return
;配列へ乱数代入(重複なし)
#deffunc SubstitutionRndNoOverlap array p1,int max,int min, local tmp,local tmpcnt,local keycnt
foreach p1
keycnt=cnt
;重複検知
repeat
;仮乱数
tmpcnt=0
tmp=rnd(max)+min
repeat keycnt
;仮乱数が重複しない→カウント加算
if p1(cnt)!=tmp:tmpcnt++
loop
if keycnt=tmpcnt : break;重複なし
loop
p1(cnt)=tmp
loop
return
;暗号化命令
#deffunc SimpleEncrypt var _edata_,var _data_, local b_edata,local code,local maxindex
//鍵生成--------
wait 1:randomize
;シフト鍵生成
;(このまま保存するためNull文字を除外)
dim en_shiftkey,ShiftKeyNum
foreach en_shiftkey
en_shiftkey(cnt)=rnd($FE)+1
loop
;X鍵生成
;(最後の暗号化に利用するためNull文字を除外)
dim en_xkey,XKeyNum
SubstitutionRndNoOverlap en_xkey,$FE,1
;Y鍵
dim en_ykey,YKeyNum
SubstitutionRndNoOverlap en_ykey,$FE,1
//暗号化--------
sdim b_edata, AllKeyNum + strlen(_data_)*2
;DEBUG
mes "暗号予想サイズ:"+(AllKeyNum+strlen(_data_)*2)+"B"
;暗号鍵の統合
foreach en_shiftkey
poke b_edata, cnt, en_shiftkey(cnt)
loop
foreach en_xkey
poke b_edata, ShiftKeyNum+cnt, en_xkey(cnt)
loop
foreach en_ykey
poke b_edata, ShiftKeyNum+XKeyNum+cnt, en_ykey(cnt)
loop
code=0
maxindex=strlen(_data_)
repeat maxindex
;1byte抽出
code=peek(_data_,cnt)
;シフト暗号化
code-$20
code=limitloop(code+en_shiftkey(cnt \ ShiftKeyNum), $00, $9E)
;テーブル暗号化
poke b_edata,AllKeyNum+ cnt*2 , en_xkey(code \ XKeyNum)
poke b_edata,AllKeyNum+ cnt*2+1, en_ykey(code / YKeyNum)
loop
;暗号データを返す
sdim _edata_,AllKeyNum+strlen(_data_)*2+1;(+1)=Null文字分
memcpy _edata_,b_edata,AllKeyNum+strlen(_data_)*2,0,0
return
;復号化命令
#deffunc SimpleDecrypt var _data_,var _edata_, local tmp,local b_data,local code,local maxindex
//鍵を取り出す--------
dim en_shiftkey,ShiftKeyNum
dim en_xkey,XKeyNum
dim en_ykey,YKeyNum
repeat ShiftKeyNum
en_shiftkey(cnt)=peek(_edata_,cnt)
loop
repeat XKeyNum
en_xkey(cnt)=peek(_edata_,ShiftKeyNum+cnt)
loop
repeat YKeyNum
en_ykey(cnt)=peek(_edata_,ShiftKeyNum+XKeyNum+cnt)
loop
//復号化--------
tmp=(strlen(_edata_)-AllKeyNum)/2
sdim b_data,tmp
maxindex=tmp
code=0
repeat maxindex
;テーブルXY座標を求める
tmp=peek(_edata_,AllKeyNum + cnt*2)
repeat XKeyNum
if en_xkey(cnt)==tmp: x=cnt
loop
tmp=peek(_edata_,AllKeyNum + cnt*2+1)
repeat YKeyNum
if en_ykey(cnt)==tmp: y=cnt
loop
;XY座標から文字を算出する
code=x+y*XKeyNum
;シフト鍵による復号化
code=limitloop(code-en_shiftkey(cnt \ ShiftKeyNum), $00, $9E) + $20
;複合データ集積
poke b_data,cnt,code
loop
;複合データを返す
_data_ = b_data
return
#global
//test----------------------------------------------------------
screen 0,1024,256
*main
cls 4:pos 0,0
data="This is debug words. hello world! space space space Yeaaaaaa!!!"
color $FF,$FF,$FF:mes "■原文("+strlen(data)+"B)\n"+data
SimpleEncrypt edata,data
color $FF,$60,$60
mes "■暗号("+strlen(edata)+"B)"
mes edata
bsave "save.dat",edata
data=""
SimpleDecrypt data,edata
color $30,$FF,$80:mes "■複合("+strlen(data)+"B)\n"+data
onclick goto*main
stop;*/
| |
|
2016/5/28(Sat) 02:52:46|NO.75626
どうやら解決したようでなにより。
あと、複合化→復号化でしたすいません(・ω・)
>>必ず一番最後にNull文字が必要!
かどうかは、状況次第です。
が、文字列型変数を"文字列として扱う"場合なら、念のためつけておいてもいいでしょうね。
(↑の意味は、mesはもちろん、str系命令とかを使う時ってことネ。)
今更気づいたのですが、
en_shiftkey, en_xkey, en_ykey
に分けてますけど、結局連続して配置されてる値なら、変数を一本化した方がいいような気もする。
どうせ全部rnd($FE)+1が中身のようだし。
コピペが減って、冗長な部分がなくなります。
見た目もすっきりしますヨ(・ω・)地味にlocalを書く手間的にも嬉しい。
そのかわり、配列番号の管理が少々複雑に。
そこは#defineでゴリ押すなり、仲介関数でも用意するなりして
配列の管理が楽になるように工夫するのもアリですね。
(私は#defineのごり押しが得意ですが、可読性はひどいものです。)
ついでに2つほど。(速度重視でないのならどうでもいいこと)
;仮乱数
tmpcnt=0
tmp=rnd(max)+min
repeat keycnt
;仮乱数が重複しない→カウント加算
if p1(cnt)!=tmp:tmpcnt++
loop
if keycnt=tmpcnt : break;重複なし
↓
#define global FALSE 0
#define global TRUE 1
//↑を頭に用意する。多分module外にglobal指定で置いてOK。
b_cont = FALSE
tmp=rnd(max)+min
repeat keycnt
;仮乱数が重複したらcontinue確定
if (p1(cnt)==tmp) : b_cont = TRUE : break
loop
if ( b_cont == FALSE ) : break
こっちのが見栄え(?)が良いような気もする。
復号化部分でも、
repeat XKeyNum
if en_xkey(cnt)==tmp: x=cnt
loop
↓
repeat XKeyNum
if en_xkey(cnt)==tmp: x=cnt : break
loop
としたくなるのは私だけだろうか(・ω・)
(重複がない以上)切り上げちゃっても問題はないヨネ。
とまあ、言い出したらキリがなくなって元のソースはどこへやらで。
他人のソースを、自分風になるまで改造したってしょうがないしね。このへんにしときます(・ω・)
| |
|