Googleドライブアプリの新規作成コンテキストメニューを消す方法

2021年11月05日

前回の記事でGoogleドライブアプリの個人的に不要な機能を無効化する方法を書きましたが、
新規作成コンテキストメニューを無効化する方法に副作用が出てしまっていました。

具体的には30秒~数分ごとに↓のようにアイコンがちらついてしまうというものです。


これを何とか打開すべく10月初旬から約1か月間、仕事とゲームの合間を縫っていろいろ調べて試行錯誤し、ついに打開策を見つけました!

挙動調査編

挙動を見た限りだとGoogleドライブアプリが
1. レジストリへ新規作成コンテキストメニュー用の設定値を書き込む。
2. アイコンキャッシュを再構築するコマンド(ie4uinit.exe -show)と同等のキャッシュクリア処理を行う。
というような処理を行っていると思われるので、実際にどういう事が行われているのか調べてみることにしました。

調査に使うツールはAPI Monitor( http://www.rohitab.com/apimonitor )です。
これは調査したいアプリケーションがどういうAPIを呼び出しているかをモニタリングできるツールです。

1. http://www.rohitab.com/downloads から「API Monitor v2 (Alpha-r13) - Portable」をダウンロードし、「apimonitor-x64.exe」を起動します。

2. 起動すると以下のような画面が表示されるので、左下のRunning Processs欄から「GoogleDriveFS.exe」を探し出し、
 右クリックし、「Start Monitoring」をクリックします。


同じ名前のプロセスが7個ほど居るのですべて同じように操作すると、Monitored Processes欄に先ほど選択したプロセスが表示されます。


3. 何のAPIが呼ばれているかわからないのでAPI Filter欄に表示されているものすべてにチェックを入れます。


4. 数秒待った後にMonitored Processes欄のプロセスをクリックするとSummary欄にAPIの呼び出しログが表示されます。


5. 後はプロセスを切り替えながらレジストリへアクセスしているプロセスを見つけ出します。
 Summary欄でCtrl + Fキーを押すか、双眼鏡アイコンで検索ダイアログを呼び出し、「.gdoc\ShellNew」を検索します。
 すると上から2番目のプロセスが「RegOpenKeyExW」を呼び出して新規作成コンテキストメニューのレジストリにアクセスしていることがわかりました。


新規作成コンテキストメニューのレジストリを消したりしながら確認してみると、

■読み込み
RegOpenKeyExW ( HKEY_CURRENT_USER, "SOFTWARE\Classes\.gdoc\ShellNew", 0, KEY_READ, 0x000000ac2591d498 )
RegQueryValueExW ( 0x000000000000063c, "command", NULL, 0x000000ac2591d498, NULL, 0x000000ac2591d4a4 )

■書き込み
RegCreateKeyExW ( HKEY_CURRENT_USER, "SOFTWARE\Classes\.gdoc\ShellNew", 0, NULL, 0, KEY_WRITE, NULL, 0x000000ac2591d4a0, 0x000000ac2591d4ac )
RegSetValueExW ( 0x00000000000012e8, "command", 0, REG_SZ, 0x0000020ccc5b4100, 250 )

という感じでAPIを呼び出して読み書きしていることがわかりました。
さらに書き込み後に呼び出されているAPIを確認してみると、「SHChangeNotify」が呼び出されていることがわかりました。


SHChangeNotify ( SHCNE_ASSOCCHANGED, SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, NULL, NULL )

このAPIを解説してくれているサイトによると、
https://www.tokovalue.jp/function/SHChangeNotify.htm
第二引数に指定されている「SHCNF_FLUSH」は「システムのイベントバッファをフラッシュ」するそうなので、
おそらくこれでアイコンのキャッシュをクリアしているのだと思われます。

これでGoogleドライブアプリがどういう動きをしているのかがわかりました。

対策検討編

どういう挙動なのかわかったら次は対策を考えなければいけません。
思いついた事を挙げてみました。

案1. 前回の記事 で書いたように、新規作成コンテキストメニューのレジストリキーのアクセス許可を変更してレジストリをいじらせないようにする。
  ⇒Googleドライブアプリがレジストリへの書き込みに失敗した場合でも「SHChangeNotify」APIを呼び出してアイコンをちらつかせてしまうため、デメリットがデカいです。

案2. WindowsにGoogleドライブ専用のユーザーを作成し、Googleドライブアプリをそのユーザーで起動させる。
  そして案1の方法を行いログインユーザーがGoogleドライブ関連の新規作成コンテキストメニューのレジストリへアクセスできないようにする。
  そうするとGoogleドライブは新規作成コンテキストメニューのレジストリへ読み書きが失敗することなく行え、
  ログインユーザーはGoogleドライブ関連のレジストリキーを読めない状態になるので、
  アイコンのちらつきが起こらず、新規作成コンテキストメニューからGoogleドライブ関連のメニューが消えた状態となる。
  ⇒別ユーザーでの起動はrunasコマンドを使うか、タスクスケジューラを使えば行えそうです。
   だた、別ユーザーで起動してしまうとおそらく別ユーザーのユーザーフォルダ配下にあるマイドライブフォルダ(C:\Users\{別ユーザー}\マイドライブ)にファイルを同期してしまうのではないかと思うので、
   そのフォルダのアクセス許可の設定でログインユーザーが読み書きできるようにし、
   さらにジャンクションを作って「別ユーザーのユーザーフォルダ配下にあるマイドライブフォルダ」たどり着けるようにすれば実現できるかもしれません。

案3. Googleドライブ同期専用のパソコンを別途用意し、「マイドライブ」フォルダを共有フォルダにして別のパソコンからアクセスできるようにする。
  そしてメインで使用するパソコンから↑の共有フォルダにアクセスしてファイルの閲覧・編集を行う。
  ⇒別のパソコンを起動させないといけないため、かなりもったいないです。
   また、共有フォルダ経由で読み書きした場合に何か変な動きをしないか心配です。

案4. Googleドライブアプリを使うのを止め、自分でGoogleドライブ上のファイルをローカルに持ってくる同期アプリを作る。
  ⇒定期的にGoogleドライブ上の全ファイルをローカルにコピーバッチ処理であれば頑張ればできなくもなさそうですが、ちょっとしんどいですね。

案5. Googleドライブのバイナリを改変し、新規作成コンテキストメニューのレジストリへの書き込み処理を動かなくさせる。
  ⇒一番確実な方法ではありますが、Googleドライブアプリがアップデートされるたびにパッチをあてないといけなくなるので、これも少々しんどいですね。
   っと思ったらすでにやってる人が居ました!!!
   https://qiita.com/libraplanet/items/4460663b18873be79e83
   アップデートにも対応してて素晴らしいですね!

案6. Googleドライブアプリがアイコンキャッシュクリアのために行う「SHChangeNotify」APIの呼び出しを何かしらの方法で妨害し、
  Windows側まで到達できないようにさせる。
  ⇒仮にこれができたとすると新規作成コンテキストメニュー用のキャッシュクリアだけでなく、
   ファイル同期中等を表すアイコンを表示するためのキャッシュクリア処理も動かなくなってしまうと思われるので、
   今度はGoogleドライブ絡みのオーバーレイアイコンがうまく表示されなくなる副作用が出てしまいそうですね。
   ただ、オーバーレイアイコンが表示されなくても個人的にはそこまで困らないので、個人的には許容範囲の副作用です。

案7. 「SHChangeNotify」API本体を改変し、第二引数でSHCNF_FLUSHを受け取っても無視するようにする。
  ⇒Windows全体に影響が及ぶのでデメリットがすごそうですね。

案8. Googleドライブアプリが新規作成コンテキストメニュー作成のために行うレジストリ操作APIを何かしらの方法で妨害し、
  「~\ShellNew\command」に書き込ませないようにする。
  ⇒Googleドライブアプリに「うまくレジストリの読み書きができてる」と錯覚させることができれば「SHChangeNotify」APIが呼び出されることもなく、
   平和に終わりそうな予感です。

案9. レジストリの読み書きをするAPI本体を改変し、GoogleDrive絡みの「~\ShellNew\command」へ書き込ませないようにする。
  ⇒API本体を書き換えるのはちょっとアレですね。

案10. Googleドライブ側でレジストリへの書き込みができない場合は「SHChangeNotify」APIを呼び出さないようにアップデートしてもらう。
  ⇒ダメ元で10月頭(v51.0.14.0)にフィードバックを出してみましたが、現時点(v52.0.6.0)で変化はありませんでした。

思いつく限り挙げるとこんなものです。
いろいろ調べていくと「DLLインジェクション」(IATフック)という手法があり、これを使うとAPI呼び出しをHOOKして任意の処理に置き換えることができることがわかりました。
モンキーパッチやクラスメソッドのオーバーライドと同じような感じですね。
面白そうな手法なので案8のパターンで進めることにしました。

DLLインジェクション(IATフック)計画編

GoogleドライブとAPI、レジストリは以下のような関係性になっています。
※IATの部分はもしかしたら解釈間違ってるかもしれませんが、今回やりたいことはうまく表現できてるかと思います。

DLLインジェクション(IATフック)を行うとGoogleドライブアプリからADAVAPI32.dllへの参照を任意のDLLへ変えることができるので、
以下のような流れにしてしまおうと思います。


DLLインジェクションを行うためには
・DLLをインジェクション(注入)するためのexe
・インジェクションされるDLL
の2個を作る必要があります。
DLLインジェクションを行うのはメモリ上だけなのでバイナリを改変する必要がなく、
DLLインジェクションを行ってしまえばDLLインジェクションするためのexeは常駐する必要がありません。
ただし、メモリ上でしかできないので、アプリを再起動した場合は再度DLLインジェクションを実行する必要があります。

また、DLLインジェクションは別のアプリケーションに別の処理を注入できるとても便利な機能でですが、
便利であるがゆえにそれを悪用したウイルスがあるらしく、DLLインジェクションを行うexeがウイルス対策ソフトに誤検知されてしまうことがあります。
実際にDLLインジェクションを行うexeを作ってタスクスケジューラからの自動起動を試している最中にWindows Defenderにウイルス判定を食らうことがありました。
改めてスキャンをかけてみてもウイルス判定されることはありませんでしたが、悪意のないコードでも「アヤシイ」と判断されやすい手法なようです。

DLLインジェクション(IATフック)実践編

計画を立てるところまでは割とスムーズにいったんですが、実際にやってみるとなかなか難航しました。

完成したものがこちらです。
名前は「disable_google_drive_new_contextmenu」ですが、exeとdllの2個が必要なため別々にプロジェクトを作ってあります。

■disable_google_drive_new_contextmenu_injector
https://github.com/forest-soft/disable_google_drive_new_contextmenu_injector

■disable_google_drive_new_contextmenu_dll
https://github.com/forest-soft/disable_google_drive_new_contextmenu_dll

Visual Studio 2019にてC言語で開発しました。
こちら側でビルドしたexeとdllのセットは
https://github.com/forest-soft/disable_google_drive_new_contextmenu_injector/releases/
の「disable_google_drive_new_contextmenu.zip」からダウンロードできます。

ソースからビルドするか上記からzipをダウンロードするとフォルダ内に以下の2ファイルがある状態になります。


Googleドライブアプリを起動した後に「injector.exe」をダブルクリックすると一瞬このようなコマンドプロンプトが表示されます。


ちなみにもしもGoogleドライブアプリが起動されていない状態で「injector.exe」を実行するとこのようにGoogleドライブアプリが立ち上がるまで待機状態になります。


成功すれば
コンピューター\HKEY_CLASSES_ROOT\.gdoc\ShellNew
コンピューター\HKEY_CLASSES_ROOT\.gsheet\ShellNew
コンピューター\HKEY_CLASSES_ROOT\.gslides\ShellNew
のレジストリキーの配下から「command」が消え、「command_xxx」に置き換わります。


そしてバッチリ新規作成コンテキストメニューからGoogleDrive関連のメニューが消えるハズです!



肝心のちらつき問題ですが、しっかり解消できています。
まずは10月頭時点(v51.0.14.0) + レジストリのアクセス許可を変更した場合です。
ファイルがたくさんあった方が確認しやすかったので「C:\Windows\Cursors」にて確認しました。
Googleドライブアプリの起動直後かどうかによって周期が変化するみたいですが、確認したときは5分周期でアイコンキャッシュがクリアされ、ちらつきがおこってました。


次は「disable_google_drive_new_contextmenu」の完成直後(v52.0.6.0)です。
この時は1分周期でアイコンキャッシュがクリアされ、ちらつきが起こっていました。


そして↑の後に「disable_google_drive_new_contextmenu」の「injector.exe」を起動した場合です。
静止画だとどうにも確認しようが無かったりはしますが、5分経ってもちらつきが起こりません!
そして3日ほど使ってみましたが、ちらつきは起こっていません!
ちょっと特殊なことをしなければいけませんでしたが、これでバッチリ満足いく状態になりました!



ただ、DLLインジェクションの性質上Googleドライブアプリを再起動するとDLLインジェクションが無効になってしまいます。
という事はWindowsを再起動するたびに「injector.exe」を起動する必要があるわけです。
これだと面倒なのでタスク スケジューラを使って自動的に「injector.exe」を立ち上げるようにしたいと思います。

コントロールパネルを開き、「すべてのコントロールパネル項目 > 管理ツール > タスク スケジューラ」と辿るか、
Windowsキー + Rキーで「ファイル名を指定して実行」を表示し、「taskschd.msc」と入力してください。
タスク スケジューラが起動したら画面左側の「タスク スケジューラ ライブラリ」をクリックし、
画面右側の「タスクの作成」をクリックしてください。


タスクの作成ダイアログが表示されるので、わかりやすい名前を入力してください。


「トリガー」タブをクリックし、「新規」ボタンをクリックしてください。


「タスクの開始」項目で「ログオン時」を選び、「OK」ボタンをクリックしてください。


「操作」タブをクリックし、「新規」ボタンをクリックしてください。


「参照」ボタンをクリックし、「disable_google_drive_new_contextmenu」の「injector.exe」を選択してください。


「引数の追加」項目に「--hide」と入力してください。
この引数を指定すると「injector.exe」を起動したときにコマンドプロンプトが表示されなくなります。
その後「OK」ボタンをクリックしてください。


「条件」タブをクリックし、電源の「コンピューターをAC電源で使用している場合のみタスクを開始する」のチェックを外し、
「OK」ボタンをクリックしてください。


これでWindowsの起動時に「injector.exe」を自動で実行するタスクが作れました。
Windowsを再起動せずに動作確認をしたい場合は画面右側の「実行」ボタンを押すとその場で試せます。


ちなみに先ほどの「操作」タブのところで引数に「--hide」と入れてもらいましたが、
この状態でGoogleドライブアプリが起動してない時に「実行」すると、裏でinjector.exeがGoogleドライブの起動待ち状態になってしまい、待機し続けてしまいます。
終了させる場合は1回Googleドライブアプリを立ち上げるか、タスクマネージャーの詳細タブから「injector.exe」を探して「タスクの終了」で終了させてください。



最後に「disable_google_drive_new_contextmenu」のアンインストール方法ですが、
「disable_google_drive_new_contextmenu」のフォルダごと削除し、タスクスケジューラに登録したタスクを消せばアンインストール完了です。
ただしGoogleドライブアプリが起動している間はGoogleドライブアプリが「disable_google_drive_new_contextmenu.dll」を開き続けてしまうので、ファイルの削除が行えません。
いったんGoogleドライブアプリを終了させてからフォルダごと消してください。

また、レジストリキーが気になる場合は
コンピューター\HKEY_CLASSES_ROOT\.gdoc\ShellNew
コンピューター\HKEY_CLASSES_ROOT\.gsheet\ShellNew
コンピューター\HKEY_CLASSES_ROOT\.gslides\ShellNew
の配下にある「command_xxx」を消してもらえれば痕跡残さず完全アンインストール完了です。


これにて新規作成コンテキストメニューの無効化対策完了です!

後々DLLインジェクションのプログラムのメモ記事を書いていこうと思います。

書いた人:木本
コメント一覧
コメントはまだありません。
コメントを投稿する
お名前
E-Mail
[必須]コメント
Top