tmyt/VDMHelper

実装どうしよう会

tmyt opened this issue · 53 comments

tmyt commented

実装方針で悩んだのでリポジトリ作りました

ご招待されました

tmyt commented

お悩み一覧

  • 64bitプロセスから32bitプロセスをHookできない
    • 両方作った
  • 32bitプロセスにSendMessageをすると頑張っても64bitまでしか送れない
    • GUIDが128bitなので困ったのでよそのプロセスでメモリ確保するヘルパ関数作った
  • 64bitプロセスから32bitプロセス上にメモリは確保できるみたい
  • アプリでどうやってこれら呼ぶのがいいでしょう

現時点で作った流れが、

  1. グローバル キーフック検知
  2. 入力されたキー + Modifiers が設定されているアクションとマッチするかチェック
  3. マッチしたら、GetForegroundWindow() でアクティブなウィンドウのハンドルをゲッツ
  4. MoveWindowToDesktop で 3 のハンドルをぶっこんで終了

フックの時点から作り直しぽよ…

よくわかってないんだけど、アプリから VDMHelper (32bit & 64bit) を DllImport するの?

tmyt commented

どうなんだろう.NET のWH_KEYBOARD_LL なら大丈夫なのかもしれない

で考えたのがこれ

  • アプリがロードするのは環境に最適なDLLと仮定
  • C++/CLIでC#向けにAPIを提供する
    • 64bitからでも32bitプロセスにメモリ確保はできるのでここはOK
  • 64bit環境では32bitプロセス用ヘルパプロセスを起動する
  • ぐらばくさんがC#でこんなコードを実行
var is64bit = Marshal.SizeOf(typeof(IntPtr));
var asm = Assembly.Load(is64bit ? "VDMHelperCLR64.dll" : "VDMHelperCLR32.dll");
var type = asm.GetType("VDM.VdmHelper");
var obj = (IVdmHelper)Activator.CreateInstance(type);
if(is64bit){
    // 64bit環境なら32bitプロセス用ヘルパプロセスを起動
    // obj.Init() 相当を面倒みてくれるプロセス
}
obj.Init(); /// <- ここでSetWindowsHookEx してる
---
obj.MoveWindowToDesktop(hwnd, guid); // こんな感じで呼ぶと内部でSendMessageに置換する

どうでしょう

tmyt commented

DLLImportするとstatic link されちゃうのでAnyCPUにしてるとはまる

ほーなるほど いい感じ

その場合、キーフックで検知した入力キーをどこから取るのかなと

tmyt commented

たぶんだけど、キーフックについては.NETで実装した場合64bit/32bitいい感じにしてくれているのではないだろうか、と予想。ちょっとためしてみるね

tmyt commented

結論:C#で書いたWH_KEYBOARD_LLはBit数の境界越えれる。強い。

tmyt commented

アプリ側で32bit用ヘルパプロセス起動しなくて良くなったからこんな感じでよくなった。
IVdmHelper は AnyCPUで作ってあるC#のライブラリ、VDMHelperCLR.Common に入ってます。

var is64bit = Marshal.SizeOf(typeof(IntPtr));
var asm = Assembly.Load(is64bit ? "VDMHelperCLR64.dll" : "VDMHelperCLR32.dll");
var type = asm.GetType("VDMHelperCLR.VdmHelper");
var obj = (IVdmHelper)Activator.CreateInstance(type);
obj.Init(); // <- ここでSetWindowsHookEx してる
---
obj.MoveWindowToDesktop(hwnd, guid); // こんな感じで呼ぶと内部でSendMessageに置換する
---
obj.Dispose(); // IVdmHelperがIDisposableなので適当に
tmyt commented

Inject**.exeで動作確認してたからバグってたのでなおした。
無事Explorer.exeに乗り込めているらしい。

image

DeInit と Dispose って使い分けるのん

tmyt commented

Disposeだけでいいよん

おっけー
ちょっと組み込んでみるから明日にでもレビューしてちょ

tmyt commented

おっけー

tmyt commented

is 何

var is64Bit = Marshal.SizeOf(typeof(IntPtr)) == 8;
var asm = Assembly.Load(is64Bit ? @"VDMHelperCLR64.dll" : @"VDMHelperCLR32.dll");
var type = asm.GetType("VDMHelperCLR.VdmHelper");
this.helper = (IVdmHelper)Activator.CreateInstance(type);
this.helper.Init();

の asm.GetType で吐かれるかんじ

Load じゃなくて LoadFile だった…

ss150908115237

これは何が考えられるとおもう?

VDMHelper のビルドでなにかミスってるのかな…

tmyt commented

おやぁ、なんだこれ、帰ったらためしてみる

tmyt commented

実行ファイルと同じ所にDLL一式32/64全部つっこんだらちゃんと動いてるっぽい

VDMHelper32.dll
VDMHelper64.dll
VDMHelperCLR.Common.dll
VDMHelperCLR32.dll
VDMHelperCLR64.dll
でたりてる?

tmyt commented

たりてる、このコードで動いたのは確認しました

var dir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\";
var is64Bit = Marshal.SizeOf(typeof(IntPtr)) == 8;
var asm = Assembly.LoadFile(dir + (is64Bit ? @"VDMHelperCLR64.dll" : @"VDMHelperCLR32.dll"));
var type = asm.GetType("VDMHelperCLR.VdmHelper");
this.helper = (IVdmHelper)Activator.CreateInstance(type);
this.helper.Init();

ってことは dll のビルドで何かミスってる予感

しょうたいしてしまえ

tmyt commented

いくつかばぐあったからなおしてるところいま

tmyt commented

バグ治ったけど他人のウィンドウがうまく動かないので調査中

tmyt commented

あ、dllのビルドの時、ターゲットプロセッサをx64にしてますか、もしかしてanyとかになってませんか

tmyt commented

うおおおお うまくうごきました すげぇ

あ、dllのビルドの時、ターゲットプロセッサをx64にしてますか、もしかしてanyとかになってませんか

これかもしれない!

つまりは凡ミスだった

tmyt commented

結合ためしてみるね

とりあえず DLL 読むの成功するところまでプッシュした

最新のコミットで、起動してから 左Ctrl + 左Alt + 左Win + ←→ でアクティブウィンドウを飛ばす
ようにしてるんだけど、動いてないのでたぶんどこかがわるい

tmyt commented

なんかちょっと直したけどうまくうごいたよん

どこ直したのん…

tmyt commented

diffみる、あとウィンドウ閉じてもApplication.OnExit 呼ばれない謎で困ってる、フック外せない

あー、ウィンドウは表示しない前提だから ShutdownMode.OnExplicitShutdown にしてる
自分で Shutdown() しないと落ちないぽよ…

ところで

var is64Bit = Marshal.SizeOf(typeof(IntPtr)) == 8;

var is64Bit = Environment.Is64BitProcess 

っておんなじなのかね。
referencesource 見ると #if で作られててわからん

tmyt commented

どうなんだろう、いっしょなのかもしれない?

tmyt commented

とりあえずInjectDll32.exeもassembliesに足してあげてほしい

ぷっしゅした

tmyt commented

32bitプロセスで CoCreateInstance(CLSID_ImmersiveShell, nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&pServiceProvider)); するとE_ACCESSDENIED 出るのでウィンドウが移動できない現象を確認しました

こっちが 64bit で移動しようとしてるウィンドウが 32bit プロセスのぱたーんか

tmyt commented

そーそーそれそれ、なんかWoW64な環境で32bitプロセスからCoCreateInstanceするとだめらしい 謎

explorer だと、2 回叩いて 2 回目で別仮想デスクトップに飛んでいく、ということに気付いたなど
(1 回目は反応しない)

tmyt commented

謎だ

tmyt commented

あぷりのリポジトリ、ブランチ切ってpushしてPRとかしていいんだろうか

ぜんぜんおっけー

tmyt commented

とりあえず実装はいい感じになってきたしここはClose。なんかあったら別のissueにしよう。