ブラウザ上のurlからexeを(引数付きで)実行起動したい
やりたい事 + 概要
file:///
スキームで開いてるブラウザの .html
から、アプリケーションを起動させたかった時のメモ記事です
↓ こういうのをやりたい (やった)
<!-- 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
- 常套句
- やるなら自己責任で ☆彡
実装手順
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
を作成 & 配置
utf-8
ではなくShift-jis
形式での作成を推奨?- 本例では
D:\launcher.vbs
への仮配置としました
.vbs
位置はレジストリから参照するので、変更の際にはレジストリ側も編集する必要が出てきます(※後述) - この
.vbs
内ではスキーム名xxxx
を扱わないので、書き換え不要です
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
を作成 & 実行してレジストリに登録
HKEY_CLASSES_ROOT\
に続くxxxx
の部分(4ヵ所)を、全て同じスキーム名に変更してください- 末尾の
.vbs
パス値は、実際の配置場所に応じて変えてください
登録後にregedit
から編集しなおしても構いません
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
に追加して、それを二重のパスワード代わりにするとかでもアリ?
おまけA : 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>
おまけB : vscode 起動例
ご存じ Visual Studio Code
こちらは vscode:
のプロトコルが HKEY_CLASSES_ROOT\vscode\shell\open\command
に登録済みらしく、
下準備ナシで、以下の様なURIから普通に開ける模様。
<a href="vscode://file/c:\hoge\fuga.txt">Cドライブ hoge フォルダの fuga.txt を開く</a>
その他メモ
引数のデコードについて
今回のギミックでは、実行したい .exe
の path をハイパーリンク内で引数風に指定しているのですが、
外部に渡されるタイミングで一部文字記号がパーセントエンコーディングされる様で、
そのままでは正常にファイルパスとして扱えません。
- 例
c:%5CWindows%5Cexplorer.exe
( エンコード前c:\Windows\explorer.exe
)
d:%5C%E3%83%A1%E3%83%A2.txt
( エンコード前d:\メモ.txt
)
そこで、起動用受け皿として使っている .vbs
の中で、
ScriptControl の力を借りたデコード処理をひとつ挟んでいます。
「常に開く」チェックボックスの解除相当について
file:// がこのアプリケーションを開く許可を求めています。
file:// でのこのタイプのリンクは常に関連付けられたアプリで開く
このチェックを一度入れれば以降は起動ごとに訊かれなくなる。
ただ、逆に再び解除したい場合はどうなのかと調べた。
chrome://settings/handlers
から消せるかなと思ったけど、だめぽでした。
GoogleChromeヘルプ - 「このタイプのリンクは常に関連付けられたアプリで開く」を解除
マアムの旅日記 - 【Google Chrome】「CVLauncherを開く」をもう一度押したい!
GoogleChrome を完全にすべて終了させた後に、
C:\Users\ユーザー名\AppData\Local\Google\Chrome\User Data\Default\Preferences
をエディタで開き、中身を部分的に編集削除すると良さそう
"protocol_handler":{"allowed_origin_protocol_pairs":{"file://":{"xxxx":true}}}
↓
"protocol_handler":{}
.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 を指定してたのが原因だった模様 ( なので直した )