追記

野良犬日記 2.1

RDF/RSS
この日記の検索

最近のツッコミ:


2019-07-20 (Sat) [長年日記]

_ NSIS (アン)インストーラ

NSIS でインストーラを作っていて、インストール前に既存バージョンを消したいとき、.onInit に何をどう書けばいいか悩んでいましたが、結局こうしました。

; GetTime のために FileFunc を使う
!Include "FileFunc.nsh"
Function .onInit
  ; これは普通どおりレジストリを見て既インストールか確認してるだけ
  ReadRegStr $R0 ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString"
  StrCmp $R0 "" done

  ; これも別に要らないけど、まあ動作を知らせている
  MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "既存の ${PRODUCT_NAME} を先にアンインストールしてください。$\r$\n$\r$\nOK を押すと続行します。その後で、改めてインストール画面を出します。" IDOK uninst
  Abort

uninst:
  ClearErrors
  ${GetTime} "" "L" $0 $1 $2 $3 $4 $5 $6
  ; セキュリティ的にはもっと推測や衝突を考えた方がいいだろうが
  StrCpy $R1 "$TEMP\uninstall-$2-$1-$0-$4-$5-$6.exe"
  CopyFiles "$R0" "$R1"
  ExecWait '"$R1" _?=$INSTDIR' $R2
  Delete "$R1"
  ; アンインストーラが正常終了したときだけ続行するなら以下のようにしておく
  StrCmp $R2 0 done
  Abort

done:

FunctionEnd

アンインストーラは自分を消す必要があるので、_? が指定されていなければ一時フォルダに自分をコピーしてからそっちを呼びます。だから ExecWait (子を待つが孫は待たない)が効かないぞ、というのは FAQ みたいです。

で、どうするのかというと、その動作を止め(つつ消去先を指定す)るオプション _? をただ呼べばいいのではありません。それだけでは「自分が起動中なので消せない」というエラーになったり、エラーを消そうとして REBOOTOK にしたら再起動後に(新しい)アンインストーラが消えちゃってアンインストールできなくなったり、という落とし穴があるみたいです。

なのでアンインストーラを ExecWait したいなら自分で TEMP にコピーするというのが一番簡単みたいです。

ちなみに、上記のようにするまでは、アンインストーラが新規インストーラの上に来るように MessageBox を遅らせたりしていました。

Function un.onInit
  Sleep 800
  MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "$(^Name) を削除していいですか?" IDYES +2
  Abort
FunctionEnd

2019-07-15 (Mon) [長年日記]

_ [ahk]ホイールとか

Windows で何かを押しながらマウス移動でホイールをシミュレートするやつ2019版。その前に普通のホイールを加速するやつも付いてるけど、もちろんこっちは消してもいい。

CoordMode, Mouse, Screen

; あとで使う透明マウスポインタ
VarSetCapacity(AFF, 32 * 4, 0xFF)
VarSetCapacity(X00, 32 * 4, 0x00)

; cf. https://www6.atwiki.jp/eamat/pages/32.html
WheelUp::
WheelDown::
 MouseGetPos, x, y, hwnd, ctrl, 1
 ; UWP?
 If (ctrl == "")
  WinGetClass, ctrl, ahk_id %hwnd%
 If (ctrl ~= "^Windows\.UI\.Core\.CoreWindow")
 {
  ; 適当に加速 (間隔700ms未満から加速し、400ms未満はもっと加速)
  v := 1
  If ((A_PriorHotkey == A_ThisHotkey)
      and (A_TimeSincePriorHotkey < 700))
   v += Floor((700 - A_TimeSincePriorHotkey) / 300) ** 3
  SendInput, {Blind}{%A_ThisHotkey% %v%}
  Return
 }
 v := (A_ThisHotkey == "WheelUp") ? 1 : -1
 If ((A_PriorHotkey == A_ThisHotkey)
     and (A_TimeSincePriorHotkey < 700))
  v *= 1 + ((700 - A_TimeSincePriorHotkey) / 300) ** 3
 ; 適当に進む (120は、マウスの設定で1行にしているとき用って感じ)
 PostMessage, 0x20A, Floor(120 * v) << 16
                   , (y << 16) | x
                   , %ctrl%, ahk_id %hwnd%
 Return

; 中央ボタンを押しながらマウス移動でホイール動作のふり
*MButton::
 MouseGetPos, sx, sy, hwnd, ctrl, 1
 ; UWP?
 If (ctrl == "")
  WinGetClass, ctrl, ahk_id %hwnd%
 uwp := (ctrl ~= "^Windows\.UI\.Core\.CoreWindow")
 ; カーソル隠しとこう
 bc := DllCall("CreateCursor", "UInt", 0, "Int", 0, "Int", 0, "Int", 32, "Int", 32, "UInt", &AFF, "UInt", &X00)
 DllCall("SetSystemCursor", "UInt", bc, "Int", 32512) ; ノーマル
 bc := DllCall("CreateCursor", "UInt", 0, "Int", 0, "Int", 0, "Int", 32, "Int", 32, "UInt", &AFF, "UInt", &X00)
 DllCall("SetSystemCursor", "UInt", bc, "Int", 32513) ; 文字Iビーム
 moved := false
 While GetKeyState("MButton", "P")
 {
  MouseGetPos, x, y
  If ((delta := sy - y) = 0)
   Continue
  Else
   moved := true
  If (!uwp)
  {
   PostMessage, 0x20A, ((delta*Abs(delta)) << 16) | mods()
                     , (sy << 16) | sx
                     , %ctrl% , ahk_id %hwnd%
  }
  Else
  {
   wud := (delta > 0) ? "WheelUp" : "WheelDown"
   SendInput, {Blind}{%wud%}
  }
  ; マウスカーソルが動いてしまっているので、元に戻す
  MouseMove, sx, sy, 0
  Continue
 }
 ; カーソル表示
 DllCall("SystemParametersInfo", "UInt", 0x57, "UInt", 0, "UInt", 0, "UInt", 0)
 ; ここでモディファイアが効いてるかどうかは知らん
 If (!moved)
  MouseClick, Middle
 Return

mods()
{
 Return (1 << 0) * GetKeyState("LButton")
      | (1 << 1) * GetKeyState("RButton")
      | (1 << 2) * GetKeyState("Shift")
      | (1 << 3) * GetKeyState("Ctrl")
      | (1 << 4) * GetKeyState("MButton")
      | (1 << 5) * GetKeyState("XButton1")
      | (1 << 6) * GetKeyState("XButton2")
}

2019-07-02 (Tue) [長年日記]

_ IP ID によるカーネルアドレスのリーク (翻訳)

tedu flak の "random ip id comments" を勝手に翻訳してみました。リンク先の論文は一切見ていませんし、記事内容もすべて理解しているとは言えませんが、興味深い内容だったので。

以前の記事 "KARL"に関連しています。

ランダムな IP ID について一言

最近「IP ID をデバイスの ID、そして KASLR 回避に」という論文があり、気に入った。この論文が位置するのはネットワークと、古くても現役な標準規格と、乱数、セキュリティ、そして実装依存の挙動のすべてが交わった場所だ。どうか、何とかして原文を読んでほしい。ただ、本当に短くまとめると、著者たちは次の二つのことを成し遂げたと言える。彼らは Windows と Linux のネットワーク トラフィックから、ホスト固有の乱数シードをリバース エンジニア解析することにより、フィンガープリンティング (非匿名化) を可能にした。そして、さらに驚くことに、これを Linux の KASLR 突破にまで発展させた。ワイルドだろぉ?

ムカシ

IP ID は IP パケット内のフィールドの一つで、フラグメンテーション (断片化) を扱う。パケットが断片化されていると、受け手側ホストは、どの断片がどのパケットに属するかを知る必要がある。パケットを識別するときに使う IP アドレス (と、おそらくポートも) みたいなものだ。とはいえ、IP ID は 16 ビットしかないフィールドだから、衝突を避ける工夫は大切だ。さもないと、断片を正しく組み直せない。昔の、いちばん単純な方法では、単に断片化のたびにカウンタをインクリメントしていた。想定としては、カウンタがゼロに戻る頃には古い断片がすべて相手に届いているだろうということだ。

これは単純すぎたため、いくつかのワザを許してしまった。初期の論文に「NAT 下のホスト数を知るテクニック」があり、これはグローバルでリニアに増える IP ID を使ってホストを数えている。それより後の論文「TCP/IP フィールドの挙動を観察することによる NAT 下ホストの計数」は、これにもっと多くのフィールドを含めることで改良を加えた。最後に「現場での IP-ID 動作に対する詳細な観察」がある。これは各種の IP ID 生成技術を駆使してホスト数を測定した。

OpenBSD は乱数生成技術に後述のヒネリを加えて使っている。他のシステムではカウンタの亜種を使っているが、前述の検出テクニックその他の残念な結果にならないよう、単純なグローバル カウンタではなく、その接続に由来するオフセットを計算している。

アルゴ

論文において、IP ID 生成のアルゴリズムは三つ説明されている。グローバル カウンタ、ローカル オフセット付きのグローバル カウンタ、そしてルックバック付きの乱数だ。

最後のテクニックは Apple が使っているもので、最近使われた ID のキューを保持しつつ、新しい乱数を生成し、それが重複していないかチェックする。これにより問題のいくつかが解決される。元情報につながるグローバル カウンタやオフセットがなくなる。純粋な乱数アルゴリズムと異なり、重複の確率は減る。

OpenBSD は、Dragonfly を起源とするアルゴリズムを使って、この第三のアルゴリズムの亜種を実行している。存在しうる 64K 個の ID すべてを入れた配列があり、これはシャッフルされている。隣のアイテムを選んだら、それを 32K ほどオフセットしたあたりに混ぜ戻しておく。(訳注: 訳者が誤解しているかもしれないので知りたい人は https://github.com/openbsd/src/blob/master/sys/netinet/ip_id.c 参照) こうして、それほど早く再使用されないようにしているのだ。

デレズ

Klein と Pinkas は論文で、様々な接続から IP ID を取り出して、なんかすごい数学で元のグローバル カウンタを算出している。そうしたらまたあの、1999年みたいなパーティの始まりだ。(訳注: de-rez というのは映画 TRON の用語で de-resolve のこと、つまり分解消滅みたいな意味らしい)

ここでさらにまた興味深いのは、Linux で使っている計算の一部にはグローバル変数のアドレスが含まれており、それゆえにグローバル シードを計算すればカーネルのロード アドレスも分かってしまうということだ。

KASLR

ASLR には粗挽き vs 細挽き、それぞれの利点に関する議論がある。粗挽きなら、プログラム (やカーネル) のロード アドレスだけをランダマイズする。細挽きならプログラムを複数に分割し、それらをすべてランダマイズする。そうすれば攻撃するために当てる必要のあるアドレスが増えるからだ。しかし、ひとつリーク源があれば好きなだけ繰り返してアドレスを知ることができるのだから複雑にしても無意味だという意見もある。

今回のバグは、そういった意見に対して興味深い反論を提供する。ここでは一つのアドレスしかリークしないのだ。攻撃者がさらにパケットを送っても、さらに多くのアドレスを知ることはできない。

つまり、Android デバイスの種類が分かればカーネル レイアウトが分かるし、今回の IP ID derezzing テクを使えば、ロード アドレスまで分かってしまうということだが、一方で、同じカーネルでもデバイスごとに異なるリンク方法を使って、シンボルを異なるレイアウトに配置していたら、一つカーネル アドレスがリークしたところで大して役に立たないはずだった。

16bits

ちょっとした脚注として言っておくと、DNS も 16 bit ID で要求を識別している。

Posted 01 Jul 2019 01:23 by tedu Updated: 01 Jul 2019 01:23
本日のツッコミ(全1件) [ツッコミを入れる]

_ tamo [random は IP ID じゃなくて comments にかかってるのかもしれないな。comments が複数形..]


2018-12-12 (Wed) [長年日記]

_ Windows でショートカット(.lnk)ファイルに対する「ファイルの場所を開く」がエラーになっていた

よく覚えていないけど「関連付けがない」みたいなエラーだった。

結局レジストリがおかしくなっていたみたいで、 HKEY_CLASSES_ROOT\Folder\shellの中身を(既定も)削除 すると直った。

問題の原因

よくわからないけど、自作インストーラが変なキーを入れていたのかもしれない。既定の値が自作ツールっぽい名前になっていた。

解決に至るまで

セーフモードで sfc /scannow してもダメ、CCleaner でレジストリをチェックしてもダメ、MS の diag ツールも意味なしだった。

その MS 純正ツールが勝手にゴミ箱を空にしてくれたので、あとで戻そうと思っていたファイルが永遠に失われた。


2018-08-19 (Sun) Google フォームを Google スプレッドシートに反映するときのお節介を回避 [長年日記]

_ 軽く検索しても見つからなかったのでメモ

_ Google フォームに入力があると、スプレッドシート側で手作業が必要になることがある

たとえば、入力が一つだけあるフォームの結果をシート1にリンクしている場合、シート1はこんな感じになってる。

TimestampOK?
19700101000000Yes

これを別のシートから

='シート1'!A2='シート1'!B2
='シート1'!A3='シート1'!B3

というように参照して、次の入力 (A3 / B3) に備えてあるとき、値は

19700101000000Yes
  

となっているはず。 ここでフォームに二つめの入力 (行3) が来ると、そのシートは

='シート1'!A2='シート1'!B2
='シート1'!A4='シート1'!B4

に書き変えられてしまう。自動的に入力を反映して

19700101000000Yes
20180819153000No

になってほしかったのに、

19700101000000Yes
  

のままだということ。

つまり、シート1側の認識としては、それまで A3 / B3 だった行に最新のフォーム入力が入るのではなく、行2 と行3 の間に新しい行を挿入して (A3 / B3 を A4 / B4 に移動して) から、その挿入された新しい A3 / B3 に入力しているらしい。

でも自動で入力を反映したいんです

という場合に、スマートな方法があるのかどうか分からない。

とりあえずは INDIRECT でも使えばいいかな。

='シート1'!A2='シート1'!B2
='シート1'!A3='シート1'!B3

ではなく

=INDIRECT("'シート1'!A2")=INDIRECT("'シート1'!B2")
=INDIRECT("'シート1'!A3")=INDIRECT("'シート1'!B3")

にしてしまうとか。

それはあまりにもアホすぎるというのであれば

=INDIRECT("'シート1'!R" & ROW()+1 & "C" & COLUMN(), FALSE)=INDIRECT("'シート1'!R" & ROW()+1 & "C" & COLUMN(), FALSE)
=INDIRECT("'シート1'!R" & ROW()+1 & "C" & COLUMN(), FALSE)=INDIRECT("'シート1'!R" & ROW()+1 & "C" & COLUMN(), FALSE)

とでも何でも、好きなようにすればいい。(上記の例は実際に試してないので、バグってるかもしれない)

セル指定を文字列にすることによって、アプリ側から認識できないようにしているわけだから、とうぜん副作用として不便になるわけで、できるだけ避けたいことではあるが。


2002|06|07|08|09|10|11|12|
2003|01|02|03|04|05|06|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|10|11|12|
2011|02|04|05|06|07|08|09|10|11|12|
2012|01|03|04|05|06|09|11|12|
2013|02|03|09|10|11|
2014|02|03|04|06|09|
2015|11|
2016|01|04|09|10|
2017|04|05|07|
2018|04|08|12|
2019|07|