ブラウザ上のurlからexeを(引数付きで)実行起動したい
やりたい事 + 概要
ブラウザから、.exe
もといアプリケーションを起動させたかった時のメモ記事です。
↓ こういうのをやりたい (やった)
<!-- xxxx 部分は差し替えてください -->
<a href="xxxx:,c:\Windows\explorer.exe,c:\windows\">C:\Windows\ フォルダをエクスプローラで開く</a>
<a href="xxxx:,c:\Windows\notepad.exe,d:\test.txt">notepad.exe に D:\test.txt を渡して開く</a>
<a href="xxxx:,C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe">Edge起動</a>
- 筆者環境
- Win10 x64
- GoogleChrome
- 常套句
- やるなら自己責任で ☆彡
- 補足
- JavaScript 不要
file:///
スキーム(ローカル)で開いている.html
を想定
実装手順
xxxx:
プロトコルで起動する機構を実現するための 4ステップメニューです😋
1. 独自スキーム名を決める
<a href="xxxx:,c:\Windows\notepad.exe">
における xxxx
の部分を決めてください。
命名ルールは以下(多分)
- レジストリ
HKEY_CLASSES_ROOT\
配下のキー名と被らないようにする
※ 他に 予約済みの名前 もあるらしいので念の為 - アンダーバー
_
は使えないっぽい。ドット.
はOK- OK :
foo-bar
- OK :
foo.bar
- NG :
foo_bar
- OK :
url
から始まる名前もNG?
個人的にurl-launcher
で名付けてみたものの、動作せず。- 命名規則的に全て小文字が一般的?
- 一応、大文字での起動も確認してはいます
=== 以降、出てくるスキーム名 xxxx
の部分を、全て独自の物に置き換えて進めてください ===
2. .vbs
を作成 & 配置
- 以下のコードを
.vbs
で作成
念のため、テキスト形式をutf-8
ではなくShift-jis
とした作成を推奨? - 本例では
D:\launcher.vbs
への仮配置としました
.vbs
の位置や名前を変更する場合、レジストリ側も編集する必要が出てきます(※後述) - この
.vbs
内ではスキーム名を扱わないので、書き換え不要です
※xxxx:
の部分が出てこない/扱わないので原則コピペでOK、という意
Dim arg_count: arg_count = WScript.Arguments.Count
If arg_count = 0 Then
WScript.Quit
End If
Dim arg0: arg0 = Wscript.Arguments(0)
Dim paramArr: paramArr = Split(arg0, ",")
DIm paramCount: paramCount = Ubound(paramArr)+1
If paramCount <= 1 Then
WScript.Quit
End If
Dim prm1: prm1 = DecodeUri(paramArr(1))
Dim WshShell
Set WshShell = WScript.CreateObject("WScript.Shell")
If paramCount = 2 Then
Dim RunCmd: RunCmd = """" & prm1 & """"
WshShell.Run RunCmd
ElseIf paramCount = 3 Then
Dim prm2: prm2 = DecodeUri(paramArr(2))
Dim RunCmd2: RunCmd2 = """" & prm1 & """" & " " & prm2
WshShell.Run RunCmd2
ElseIf paramCount >= 4 Then
' ,記号を使ったコマンドが含まれていると想定し、連結して処理を通す
Dim result: result = ""
For i = 2 To Ubound(paramArr)
If i > 2 Then
result = result & ","
End If
result = result & paramArr(i)
Next
prm2 = DecodeUri(result)
RunCmd2 = prm1 & " " & prm2
WshShell.Run RunCmd2
End If
Function DecodeUri(encodedString)
Dim objEncoder
Set objEncoder = CreateObject("ScriptControl")
objEncoder.Language = "JScript"
objEncoder.AddCode "function decodeUri(encodedString) { return decodeURIComponent(encodedString); }"
DecodeUri = objEncoder.Run("decodeUri", encodedString)
End Function
3. .reg
を作成 & 実行してレジストリに登録
.txt
から手付で.reg
を作る場合は保存時の文字コードに注意UTF-16 LE
でないと.reg
にしたとき弾かれましたWindows Registry Editor Version 5.00
の文言も必須
HKEY_CLASSES_ROOT\
に続くxxxx
の部分(4ヵ所)を、あなたが命名した独自スキーム名へ変更してください- 末尾行の
.vbs
パス値は、実際の配置場所に応じて変えてください
登録後にレジストリエディター側から編集しなおしても構いません
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\xxxx]
@="URL:uri app runner"
"URL Protocol"=""
[HKEY_CLASSES_ROOT\xxxx\shell]
[HKEY_CLASSES_ROOT\xxxx\shell\open]
[HKEY_CLASSES_ROOT\xxxx\shell\open\command]
@="\"C:\\Windows\\SysWOW64\\wscript.exe\" /b \"d:\\launcher.vbs\" \"%1\""
- レジストリ登録後
regedit
から当該箇所を確認したイメージ
4. ハイパーリンク <a>
等から起動
xxxx
スキーム部分は置き換えてください,
は引数の区切り記号として処理していますxxxx:
の後に続く、1つ目の,
からは、実行したいコマンド(exe)のパス- 2つ目の
,
から続く文字列は、実行時のオプション(引数)に見立てています
- "xxxx:,c:\Windows\notepad.exe,d:\test.txt"
- "スキーム名:,実行パス(或いはコマンド),オプション引数"
<a href="xxxx:,c:\Windows\explorer.exe,d:\">Dドライブをエクスプローラで起動</a>
<a href="xxxx:,c:\Windows\explorer.exe,c:\windows\">C:\Windows\ フォルダをエクスプローラで開く</a>
<a href="xxxx:,c:\Windows\notepad.exe">メモ帳をexe指定で起動</a>
<a href="xxxx:,c:\Windows\notepad.exe,d:\test.txt">notepad.exe に d:\test.txt を渡して開く</a>
<a href="xxxx:,C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe">Edge起動</a>
<a href="xxxx:,C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe,https://example.com/?foo=bar">Edge起動 URL引数(クエリ付き)</a>
解説:処理の流れ
- リンクとして
xxxx:
プロトコルを含んだURIを開く
ちなみに URIハンドラー とか プロトコルハンドラー とか呼ばれているらしい? - レジストリに登録した同名スキーム機構が反応
- そのままレジストリに登録されている
.vbs
にhref=
のURI引数を渡す - 引数を受け取った
.vbs
が起動
この時点で一部引数はパーセントエンコーディングされている .vbs
の内部で引数を分解
一行分の引数を,
で区切り、起動用とオプション用に分ける- 引数をデコードし、コマンド或いは指定パスの
.exe
を.vbs
から起動
起動用引数のみなら、そのまま実行。
オプション引数もあれば、2つを合わせて起動
セキュリティ上の懸念(?)
自由度高め?なぶん、コマンドも実行できてしまいます
<a href="xxxx:,cmd">コマンドプロンプト直接起動</a>
<a href="xxxx:,cmd,/c start c:\Windows\notepad.exe">コマンドプロンプトを\c付きで直接実行</a>
↑ の2行目は Win + R
で起動できる ファイル名を指定して実行 機能から、
↓ のコマンドを実行しているのとほぼ変わりません。
cmd /c "start c:\Windows\notepad.exe"
なので、やろうと思えばPC強制終了とかもできちゃいます (※注意※)
<a href="xxxx:,cmd,/c shutdown /s /f /t 0">強制的に即時PCシャットダウン注意</a>
他にも del
とか rd
で任意ファイル消しちゃったりとかも出来ると思うんで、一応注意は必要かなと。
ただ、攻撃者が居たとしても、以下の条件を全て成立させる必要はあります。
- このページと全く同じギミックを用いたユーザーをピンポイントターゲットにして、
- そのユーザーが独自に決めたスキーム(
xxxx
相当 )をエスパーで完全に模倣したリンクを用意しつつ - 何らかの手段でそれを踏ませる
「住んでる都道府県内のどこかで家の鍵を落とし、それを時間差で拾った赤の他人があなたの家をピンポイントで見つけて侵入する」
くらいの成立度だと思っています(?)
スキーム名が任意って時点で防御率は跳ね上がりますが、
心配なら、追加の引数を義務にしつつ精査する機構を .vbs
に追加して、それを二重のパスワード代わりにするとかでもアリ?
「常に開く」チェックボックスを有効にし続けたい
file:// がこのアプリケーションを開く許可を求めています。
file:// でのこのタイプのリンクは常に関連付けられたアプリで開く
最初に記事を書いてた頃は 一度チェック入れれば以降ダイアログ出ることは無かったんですが、
いつからかチェックを入れても再起動やブラウザ立ち上げ直し等で解除されるようになった気がします。
改めて調べてみたところ、どうやらレジストリ登録がポイントっぽい
-
Windows レジストリを使用して Chrome ポリシーを管理する
ここを参考に Chrome ブラウザバンドル zip をDL
ファイルに含まれていたchrome.reg
を覗いて解析
HKEY_LOCAL_MACHINE\Software\Policies\Google\Chrome
部分に以下の情報があった"AutoLaunchProtocolsFromOrigins"="[{\"allowed_origins\": [\"example.com\", \"http://www.example.com:8080\"], \"protocol\": \"spotify\"}, {\"allowed_origins\": [\"https://example.com\", \"https://.mail.example.com\"], \"protocol\": \"teams\"}, {\"allowed_origins\": [\"*\"], \"protocol\": \"outlook\"}]"
過去の経験則と照らし合わせて、この部分を踏襲したレジストリ情報を新規登録したらいけました。
-
chrome enterprise - AutoLaunchProtocolsFromOrigins
こちらにも完全に「それ」な情報があるようです -
レジストリを作成・登録
自環境だと以下でいけましたWindows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome] "AutoLaunchProtocolsFromOrigins"="[{\"allowed_origins\": [\"file://*"], \"protocol\": \"xxxx\"}]"
- 注意点 3つ
xxxx
部分は独自のスキームに置き換えてくださいWindows Registry Editor Version 5.00
の文言は必要.txt
から手付で.reg
を作る場合は保存時の文字コードに注意UTF-16 LE
でないと.reg
にしたとき弾かれた
- 注意点 3つ
その他メモ
引数のデコードについて
今回のギミックでは、実行したい .exe
の path をハイパーリンク内で引数風に指定しているのですが、
外部に渡されるタイミングで一部文字記号がパーセントエンコーディングされる様で、
そのままでは正常にファイルパスとして扱えません。
- 例
c:%5CWindows%5Cexplorer.exe
( エンコード前c:\Windows\explorer.exe
)
d:%5C%E3%83%A1%E3%83%A2.txt
( エンコード前d:\メモ.txt
)
そこで、起動用受け皿として使っている .vbs
の中で、
ScriptControl の力を借りたデコード処理をひとつ挟んでいます。
.vbs
実行時の諸事情
文字のデコードを行うにあたって CreateObject("ScriptControl")
を利用しているが、
最初、 activexコンポーネントはオブジェクトを作成できません なるエラーに遭遇。
適材適所 - VBScriptのCreateObjectにおける「Active X コンポーネントはオブジェクトを作成できません。」のエラーについて
wscript.exe
の利用元を変えると良いらしく、
最初のレジストリ登録箇所を
"wscript.exe" /b "d:\launcher.vbs" "%1"
↑ から ↓ に変更する事で対応
"C:\Windows\SysWOW64\wscript.exe" /b "d:\launcher.vbs" "%1"
-
wscript.exe
のタイプ
C:\Windows\System32\wscript.exe
と
C:\Windows\SysWOW64\cscript.exe
の2つがあり、
これが "activexコンポーネントはオブジェクトを作成できません" エラーの原因だった模様
自環境では純粋なwscript.exe
指定だと System32 側が呼ばれてしまうので、
SysWOW64 側を使うようにフルパスで指定して解決 -
/b
オプションについて
Microsoft - wscript
ログとか一切出さないようにする「バッチモード」らしいです。
処理中にデバッグの意図で、
WScript.Echo "ほにゃららら"
とか
Msgbox "なんちゃら"
とか仕込んでたのですが、
このオプションを入れると、そういう表示系をことごとくガン無視してたような印象があります
逆に.vbs
の内部処理を独自に追いたい場合は、/b
を外すと良いかも
"C:\Windows\SysWOW64\wscript.exe" "d:\launcher.vbs" "%1"
-
ウィンドウが一瞬表示される
また、どうやっても実行時にコンソールウィンドウが一瞬チラついてしまい、これもググっていたら
OKWAVE - VBS実行時にコマンドプロンプトが表示される
wscript.exe ではなく cscript.exe を指定してたのが原因だった模様 ( なので直した )
余談 : 1つのexeのみを引数無しで起動できればOKな場合
本例
独自名 yyyy
スキームだけで c:\hoge\fuga.exe
を起動するだけ。
引数への対応を切り捨てる事で .vbs
の機構を省略できる。
1. .reg
を作成 + 実行してレジストリ登録
.vbs
不要なので wscript.exe
を使わずに済むほか、
引数受け取りの %1
などが省かれている程度の違い。
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\yyyy]
@="URL:url scheme launcher"
"URL Protocol"=""
[HKEY_CLASSES_ROOT\yyyy\shell]
[HKEY_CLASSES_ROOT\yyyy\shell\open]
[HKEY_CLASSES_ROOT\yyyy\shell\open\command]
@="\"c:\\hoge\fuga.exe\"
2. レジストリ登録が済んだら <a>
リンクを開いて exe 起動
引数処理を完全無視する事で yyyy:
のみで、決め打ちの .exe
を立ちあげられる
<a href="yyyy:">レジストリ登録済みのc:\hoge\fuga.exeを起動</a>