記事一覧

Help Ghost

サイト案内ゴースト

伺かゴースト更新フィードに何の説明もないのでうじゅら&うじゅうにヘルプ役を任せました。

ファイル 77-1.png

別個に解説ページとか用意したくない、余計なスペース取りたくない、ということで適任ではないでしょうか(選択肢にバグ抱えたままですが…)。officeのイルカ程度の存在感を発揮してくれればと思います。

里々の脆弱性をまとめてみた

何か色々勘違いしていた

里々で「らふらんす」を使う

里々のコードが外部から実行できるとかおかしいだろ常識的に考えて…。平たく言うと里々にバックドアが仕掛けられていたってことですね。なにそれこわい。

里々も更新されていますし、せっかくなので具体的な攻撃手法の例も併せて対策をまとめてみました。

利用者側で対策したい人のために(10/03/31 追記)

自動で里々を最新版に入れ替えて対策コードの書かれた辞書も生成するプラグインを作りました。
上記Wikiの一番下から入手できます。

NTCmanager更新

Shell,Balloon,Plugin,Headlineの更新も捕捉できるようになりました。

ファイル 74-1.png

伺かゴースト更新フィード

里々で「らふらんす」を使う

華和梨向けのトークテストツール「らふらんす」

配布元

里々で「らふらんす」を使う

以下のコードを里々の辞書の何処かに記述するだけ。

@ShioriEcho
(R0)

ファイル 71-1.png

ゴーストを公開するときは記述を消してください。

セルフツッコミ

それ「さとりて」で出来るよ!

「らふらんす」がDSSTPを投げて、華和梨がそれを再生しているだけらしい、というので、里々でも対応コード入れれば動くよね、ってのを確認したかっただけの話です。

10/03/10 追記

対応コード入れなくても動くらしいですよ!いつの間に。

SSP/2.02.19で狭くなったメニューを元に戻す

オーナードローメニューが狭くなった

昨日リリースされたSSP/2.02.19で Windows Vista / Windows 7 向けの改良が行われた模様です。
メイリオのフォント情報を読み取って行間を詰めたりしているようで、オーナードローメニューが以前より狭くなりました。
特に見づらいわけでもないですし慣れの問題だと思いますが、設定で行間を調整することも出来ます。

ファイル 67-1.png

行間の設定の方法

オーナードローメニューから、「設定」->「本体設定」->「表示」->「メニューの縦方向の余白設定」を「4」にする。

ファイル 67-2.png

これでおおよそ以前と同じくらいの行間になります。

ファイル 67-3.png

YAYAの関数ISVARはちょっと危険

ISVARとは何か

指定した名前の変数が存在しない場合に0, グローバル変数が存在する場合に1, ローカル変数が存在する場合に2 を返す関数です。変数の存在の有無の判定に用いられます。

ISVARの何が危険なの?

YAYAに限らず里々や華和梨においても、「変数の削除」をする際には「変数に空文字を代入する」という手法がよく用いられると思います。YAYAでも空文字を代入して終了すればセーブデータにも残らず次回起動時に変数は存在しないことになります。YAYA Tc538-3 にて空文字が代入された変数もセーブ対象に変更されました。(2010/01/21 追記)

でも実は空文字を代入しても変数は消えないのです。次の例を見てみましょう。

{
  hoge = 1          // 変数を作成
  --
  ISVAR('hoge')     // => 1
  --
  hoge != ''        // => 1
  --
  hoge = ''         // 変数に空文字を代入
  --
  ISVAR('hoge')     // => 1
  --
  hoge != ''        // => 0
  --
  ERASEVAR('hoge')  // 変数を明示的に削除
  --
  ISVAR('hoge')     // => 0
  --
  hoge != ''        // => 0
}

変数に空文字を代入した場合でもISVARは1を返します。本当の意味で変数を削除するためには専用の関数ERASEVARを使わなければならないのです。
明示的にERASEVARで削除した後にISVARの戻り値が0になっているのがわかると思います。また、削除後の変数と空文字との比較で、存在しない変数と空文字を比較すると真となることも確認できます。

しかしYAYAは空文字が代入されている変数をセーブデータに保存するようなことはしません。次回起動時には空文字が代入された変数は自動的に綺麗に消されてしまうので、「空文字の代入で変数が削除される」と誤解される方も多いと思われます。YAYA Tc538-3 にて空文字が代入された変数もセーブ対象に変更されました。

どうすればいいの?

空文字が代入される度にそれを検出してERASEVARで削除する、なんて煩わしいやり方は褒められたものではないので、ISVARを使わずに、空文字との比較で判定するのが良さそうです。
例えばユーザ名を入力してもらった時に空打ちされた場合、変数に空文字が入った状態ですのでISVARで判定すると「ユーザ名が入力されている」と誤判定する恐れがあります。また、初回起動などの本当に変数がまだ存在しない場合でも空文字と比較すれば存在の判定は可能です。

YAYAの関数TOAUTOはちょっと危険

先に結論を画像で

ファイル 62-1.png

便利な関数TOAUTO

YAYAにはTOAUTOというとても便利な関数があります。

YAYAには「文字列型」と「数値型」の2種類の変数の型が存在し(配列とかは除く、厳密には整数型と実数型に分かれる)、扱いが全く異なります。

{
  1 + 1     // => 2 (数値型)
  --
  '1' + '1' // => '11' (文字列型)
}

数値型に変換できる文字列は数値型に、そうでないものは文字列型のまま返す関数がTOAUTOです。

{
  TOAUTO('123')  // => 123 (数値型)
  --
  TOAUTO('hoge') // => 'hoge' (文字列型)
}

便利ですね。

TOAUTOの何が危険なの?

例えばこんな場合
{
  TOAUTO('0123')  // => 123 (数値型)
}

'0'が何処かへ行ってしまいました。これでは困ります。
これは文字列のままにして欲しいケースが考えられます。
(ユーザ名とかパスワードとか)

TOAUTO使わなきゃいいじゃん

お好きなYAYAゴーストを立たせてユーザ名を'0123'と教えてあげて下さい。
9割方「123さん」として覚えられてしまいます。TOAUTOなんて使った覚えも無いのに。

これはYAYAのせいではなく、現在広く普及しているYAYAミドルウェアでSHIORIリクエストの値がデフォルトでTOAUTOを経由させてしまっていることに起因します。referenceを参照した時点で既に'123'に書き換えられているのです。(ウチのGHOSTもだよ!/(^o^)\)

どうすればいいの?

「はろーYAYAわーるど」を例にすると、yaya_shiori3.dicの412行目にTOAUTOを使っているので、これをやめることです。(SHIORI3FW.AUTO_DATA_CONVERTをOFFにしているという前提の話)
でもreferenceの値が自動で型変換されているという前提で今まで辞書を書いていた場合GHOSTがまともに動かなくなるでしょう。それは困ります。

なので、こんな方法で回避しては如何でしょうか。

{
  _value = '0123'
  if TOSTR(TOAUTO(_value)) == _value {
    var.req.value ,= TOAUTO(_value)
  }
  else {
    var.req.value ,= _value
  }
}

再度文字列として評価したときに元々の文字列と一致した場合のみ数値に変換。そうでない場合は文字列のまま。

今のところ困ってないしそこまでする必要あるの?

必要ないと思います。必要になったときに対策しましょう。

余談

AYA as SAORIのシステム辞書はTOAUTOを使っていません(AYA5にはTOAUTO無いのかな?)。数値と評価され得る場合もreferenceレベルでは文字列として参照されます。

SSPから言語の設定の通知が来てる

バイリンガルゴーストを目指して

最近何となくSSPの言語をEnglishで使ってみてるのですが、SSPからPLUGINに言語についての通知がされていることを、SSPのバグ報告レポート作成時に発見しました。

// request
GET PLUGIN/2.0
Charset: UTF-8
Language: english
ID: version

これをゴーストが読めば、ユーザの環境に合わせてEnglishの時だけ英語で喋るバイリンガルゴーストが作れる!と思ってGHOSTのssp_shiori_logも見てみました。

=====send=====
GET SHIORI/3.0
ID: version
Charset: UTF-8
SecurityLevel: local
Sender: SSP

ゴーストには通知されないのね。残念。

PLUGIN作者は非日本語圏ユーザの環境も考慮して英語でのインターフェースも用意しておけ、というSSPからの熱いメッセージだろうか。

HTTPクライアントSAORI"chttpc.dll"を試してみた

chttpc.dllというSAORIがCSAORIプロジェクトにてリリースされたそうです。

他にもRoy様作のSAORIがいくつかCSAORIプロジェクトにてダウンロード可能となっています。

chttpc.dllとは何か

続きを読む

YAYAでBase64エンコード

09/07/27 追記

YAYA/Tc535-2で追加されたFREADENCODEを使うことで簡単にBase64エンコードが可能となりました。
ありがとうございます。

sys.fnc.base64encode
{
  _filename = _argv[0]
  FCHARSET('binary')
  if !FOPEN(_filename, 'rb') {
    return
  }
  _base64 = FREADENCODE(_filename, 0, 'base64')
  FCLOSE(_filename)

  _base64
}

以下で紹介するやり方では容量の大きいデータを扱うことはできないようです。
100kB程度でフリーズします。

追記ここまで

さまざまなアプローチがある

YAYAにはBase64エンコードする関数が既に用意されています。

単純なテキストデータならこれでエンコードできるのですが、画像ファイルなどのバイナリデータとなるとそうもいきません。

上記関数の注意書きにある通り、YAYAではバイト値 0x00を文字列に含むことができないのです。
素直にSAORI等の外部プラグインモジュールを作成するのが良さそうですが、残念なことに私はC/C++がわかりません。

悩んでいたところ、「それ100% Pure YAYAでできるよ」といったアドバイスを受け、YAYAで書いてみることにしました。

バイト値 0x00問題をどう解決したものか思い付きませんでしたが、 @lre 氏に、「FREADBIN の引数charを変えて二回読み込んで差分をとる」というアイディアを頂きました。その発想は無かった。多謝。

ソース

以下のようなコードで動作しました。Base64デコードすると画像ファイルもちゃんと復元できました。
対象となる画像ファイルをあらかじめコピーしておいて2つを同時に読み込んで比較します。冗長なやり方になりますが100% Pure YAYAなのが最大の魅力です。

sys.fnc.base64encode
{
  _filename1 = _argv[0]
  _filename2 = _argv[1]
  _outputfilename = _argv[2]
  _ret = ''
  _bin = ''
  _chr1 = CHR(0x01)
  _chr2 = CHR(0x02)
  _base64table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  FCHARSET('binary')
  if !FOPEN(_filename1, 'rb') {
    return
  }
  FCHARSET('binary')
  if !FOPEN(_filename2, 'rb') {
    FCLOSE(_filename1)
    return
  }
  _buffer = 1
  while 1 {
    _line1 = FREADBIN(_filename1, _buffer, _chr1)
    _line2 = FREADBIN(_filename2, _buffer, _chr2)
    if (_line1 == -1 || _line1 == '' || _line2 == -1 || _line2 == '') {
      break;
    }
    if _line1 != _line2 {
      _bin += '00000000'
    }
    else {
      _bin += sys.fnc.size8(TOBINSTR(CHRCODE(_line1)))
    }
  }
  FCLOSE(_filename1)
  FCLOSE(_filename2)
  for _i = 0; _i < (STRLEN(_bin) % 6); _i++ {
    _bin += '0'
  }
  for _i = 0; _i < (STRLEN(_bin) / 6); _i++ {
    _str = SUBSTR(_bin, _i * 6, 6)
    _int = BINSTRTOI(_str)
    _chr = SUBSTR(_base64table, _int, 1)
    _ret += _chr
  }
  for _i = 0; _i < (STRLEN(_ret) % 4); _i++ {
    _ret += '='
  }
  // 第三引数が無ければ文字列をそのまま返す
  if _outputfilename == '' {
    _ret
    return
  }
  // 第三引数にファイルパスがあればそれに書き出す
  _filename = _outputfilename
  FCHARSET('UTF-8')
  if !FOPEN(_filename, 'w') {
    return
  }
  for _i = 0; _i <= (STRLEN(_ret) / 76); _i++ {
    _str = SUBSTR(_ret, _i * 76, 76)
    FWRITE(_filename, _str)
  }
  FCLOSE(_filename)
}

sys.fnc.size8
{
  _str = _argv[0]
  _ret = ''
  if STRLEN(_str) > 8 {
    _ret = SUBSTR(_str, -8, 8)
  }
  else {
    _ret = _str
    for _i = 0; _i < (8 - STRLEN(_str)); _i++ {
      _ret = '0' + _ret
    }
  }
  _ret
}

1バイトずつ読み込んでいるのでCHRCODEの戻り値は0x??になると思っていたのですが、何故か0xFF??という0xFF00がくっついたデータも存在しました。何ですかこれは。
関数sys.fnc.size8で、なんとなく0xFF00を取り除いてみたところ正しくエンコードできたのですが、きちんと理解できずに何となく動くからそうする、というのは気持ち悪いですね。
YAYAのソースを覗いてみたところintegerにキャストしてるっぽいところまではわかりましたがどうして0xFF00がくっついてくるのかはわかりません。どなたかわかる方いらっしゃいましたら教えて頂けるとありがたいです。