HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2016
0527
Sado【手直しお願いします】はりぼて暗号化ルーチン3解決


Sado

リンク

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;*/



この記事に返信する


KOMARI

リンク

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とかを駆使シテネ。

以上。誤字脱字はご愛敬。
いろいろと間違っている突っ込みに対する突っ込みもどうぞ。



Sado

リンク

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;*/



KOMARI

リンク

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
としたくなるのは私だけだろうか(・ω・)
(重複がない以上)切り上げちゃっても問題はないヨネ。


とまあ、言い出したらキリがなくなって元のソースはどこへやらで。
他人のソースを、自分風になるまで改造したってしょうがないしね。このへんにしときます(・ω・)



ONION software Copyright 1997-2021(c) All rights reserved.