プログラミングの最近のブログ記事

GetProcAddressの罠 (2)

| コメント(0) | トラックバック(0)
その後Windows 7 x64環境で確認したところ、x86バイナリ・x64バイナリ共に再現せず。OSの違いなのか何なのか、と謎が深まってしまいました。

Visual Studioのデバッガにモジュールの一覧表示があったのに気付き、Vista x86で表示させたところ、shimeng.dllのアドレス範囲となっていました。Windows互換性テクノロジの中の人だそうな。

色々試した結果、slnファイルをUAC昇格して開くための自作ツールが原因だと判明しました。といっても、requireAdministratorなmanifestを持たせ、ShellExecuteExするだけの小さな小さなものなのですが。Vista環境でのみ、Send Toに入れて使っていたのです。

ファイル名に"launch"を含んでいると、問答無用で互換性テクノロジの餌食となるようです。互換性レイヤーを示す環境変数__COMPAT_LAYER=ElevateCreateProcessとありましたが、GetProcAddressに介入するのはWRPDllRegisterとかいうものだけではないのでしょうか。

インストーラー検出のinstall,setup,update,etc.とは異なり(他にpatchも)、今のところファイル名にlaunchを含むx86バイナリのみですが、Vista/7共にOSはx86/x64両方で再現しています。

asInvokerの中ILでも互換性レイヤーが入り込み、GetProcAddressをshimeng.dllに取られます。requestedPrivilegesがあってもダメということは、Vista以降用として作っても回避できない、ということです。

こんなテストコードを作りました。実行ファイル名を変えるとアドレスが変わることがあるのがわかります。
GetProcAddress.zip

昇格ランチャーのファイル名変更で回避できたのですが、真正面から行く場合、GetModuleInformationでkernel32.dllの範囲を取得し、その中にあるかどうかを確認して、外れていればLdrGetProcedureAddressする、という方法になるでしょうか。

余談ですがANSI_STRINGのMaximumLengthはLength+1になるようなので、RtlInitAnsiString/RtlFreeAnsiStringを使わずとも、構造体の決め打ち初期化で大丈夫でした。

ANSI_STRING str = {14,15,"GetProcAddress"};

上のサンプルもこのコードになっています。実はRtlFreeAnsiStringがx64でヒープエラーになったせいで、こう逃げたという側面もあったり。

GetProcAddressの罠

| コメント(0) | トラックバック(0)
GetProcAddressはGetProcAddress自身のアドレスを正しく取得できないことがあるようです。どうもUACで高ILにしたデバッガー下だと違うアドレスが返っています。

どうしてCreateRemoteThreadがロジックボムになるのかと思ったら、なんという罠でしょうか。

環境はVista x86、Visual Studio 2008 SP1。

関係あるのかないのか、ntdll.dllのUndocumentedなLdrGetProcedureAddressだと正しく取得できるという情報があり、試してみるとその通りでした。

単独で実行させる限り大丈夫なら、UndocumentedなAPIに手を出すこともありませんが、常時正しく動いてほしいのが本音です。

C文字列ではなくANSI_STRING構造体でAPI名を渡すので、RtlInitAnsiString/RtlFreeAnsiStringも使う必要があります。モジュールハンドルはLdrLoadDLLではなくLoadLibraryのもので大丈夫のようです。

ダミーウィンドウを作らせるだけですから、GetModuleHandleでuser32.dllの存在を確認した上で、DllMainから作ってしまうのが楽ではあります。

しかし「のどか」にて、これは共用メモリの話ですが、Adobe Readerという全くのGUIアプリでエラーが発生したことを考えると、ロードされていてもDllMainからuser32.dllのAPIを呼ぶのは危険かもしれません。
OpenProcessしてEnumProcessModulesしてDLLのハンドルを取得し、CreateRemoteThread経由でFreeLibraryに渡すという手がありました。

が、次にウィンドウが生成された時に、同じDLLがWH_CALLWNDPROCRETフックに指定されていると(つまりフックを使用するそのアプリがもう一度起動されていると)、アクセス違反で落ちました。

呼び出し履歴によるとCreateWindowから最終的にDispatchHookWで落ちており、名前からすると何事もなくフックを呼ぼうとしているのでしょう。DLLは開放できてもフックの状態は解除できなかったと推測されます。

となると何とかしてウィンドウプロシージャを呼ばせる、つまりCreateRemoteThreadからCreateWindowするしかありません。どんどんイケナイ道に踏み出している気がします。

LoadLibraryさせたDLLで、隠しダミーウィンドウを一瞬生成(WM_CREATEでDestroyWindow)すると開放できました。最初は論理試作としてDllMainでやったりしましたが、もちろん危険なのでエクスポート関数に実装しなければなりません。

コード注入だけで行なうのは無理そうです。CREATESTRUCTのlpCreateParams経由で受け取ったアドレスを、保存しておく場所がないためです。静的な変数が一切使えず、それがなくてはDefWindowProcすら呼べないのでは、ウィンドウプロシージャの実装は無理でしょう。

ウィンドウを生成するためにはRegisterClassする必要がありますが、クラス登録は相手プロセスの中ですから、同名クラスが既にあれば重複エラーとなります。時刻等を組み合わせてリトライする方向になるでしょうか。

メッセージフックとDLL開放

| コメント(0) | トラックバック(0)
DeltaEndでも使っているメッセージフック(WH_CALLWNDPROCRET、ウィンドウプロシージャ戻りフック)はDLLをプロセスに注入します。

開放されるタイミングは、UnhookWindowsHookExした後にウィンドウプロシージャが呼ばれた時だという話なのですが、開放されないプロセスがあるので調べてみました。

ehmsas.exeとjusched.exeの2つで、Spy++で見ると共にウィンドウがありません。前者はWindows Media Centerのモジュールで、ehtray.exeからCOMとして呼ばれています。後者はJavaのアップデート。

ログオン時タスクからでのみ発生し、起動後はタスクの手動起動でもDeltaEnd.exeの直接起動でも再現しません。

ログオン時タスクの起動はとても早いので、ひょっとして最初はウィンドウがあるんじゃなかろうか(少なくともjusched.exeはCreateWindowExAを使っている)、というわけで検証用のコンソールアプリを作ってみました。

WindowWithConsoleApp.zip
"w"入力でウィンドウを作り、"q"で終了します。ウィンドウを閉じるまでキー入力が利きません。

ProcessExplorerで監視してみると、最初は何をしても変化ありませんが、ウィンドウを作ると同時にDeltaEndDll.dllが入り、閉じてもそのままと見事に再現できました。

ウィンドウがなくなっても開放されるわけではなく、その場合はあくまでプロセス終了時の開放処理に任されているようです。ウィンドウ破棄後も動作するアプリは想定外なのでしょう。

DllMainでプロセス名を判別してFALSEを返し失敗させる、という方法は考えられます。しかし汎用的な方法はちょっと思いつきません。

他、Firefoxのplugin-container.exeでもたまに起きています。こちらもウィンドウがない場合ですが、このプロセスを除外するとIETabやFlash内に干渉できなくなるので、一度Firefoxを閉じて対応するしかななさそうです。

眺望は桜の散る頃に

| コメント(0) | トラックバック(0)
Windows Vistaの個人向け製品は4月にサポートが終了します。BusinessとEnterpriseはもう5年ありますが。

業務分担の都合でUltimateが必須のため、今の仕事が一段落したらWindows 7にしなければなりません。

64ビット版を検討しているところですが、開発環境はどうしたものかと思ったので、VMWare Player上で試してみました。

といっても、Visual Studioではなくマイコンの話です。V850はCubeSuite+が既に対応済みですが、H8SXのHEWは実際のところどうなのか。コンパイラのライセンスが古いのがネックです。

結論から言うと使えるようでした。E10A-USB他をWin7対応版にして、H8系コンパイラだけ6.02 Release 02の状態で、動作を確認できました。

念のためインストールはProgram Files下ではなくC:\Renesas\HEWディレクトリにしましたが、UACもAeroも有効なVistaで動いている組み合わせですから、デバッガさえ接続できれば、そう変わることもないのでしょう。

しかしMacBook MB062J/Bを非力に感じ始めているので、こちらも更新できたら嬉しいのですが。
antidevice.png
まずAntiDeviceは、パスの先頭一致で排除と許可が指定できるようになりました。特定メーカー(VID_****)などに限定することができます。
また64ビット版のファイル名をAntiDevice64.exeとし、64ビット版WindowsでAntiDevice.exeが起動された場合にAntiDevice64.exeを自動で起動するようにしました。

deltaend04.png次にDeltaEndは、ATOK 2011対応と、カナキーの指定ができるようになったことなどです。

いずれもホームページ側のWin32コーナーにて。

Shell_NotifyIcon

| コメント(0) | トラックバック(0)
通知領域に入れるようなアプリケーションは常駐型が多いでしょう。

となるとスタートアップか、Vista以降だと管理者権限のログオン時タスクで起動をすることが多いと思います。

Windowsの起動中は色々面倒くさいのですが、Shell_NotifyIconも例外ではありません。

登録に失敗する場合、GetLastError()がERROR_TIMEOUTを返すものだと思っていましたが、タスクからの起動だとタイミングがもっと早いため、ERROR_FILE_NOT_FOUNDが返っていました。

タスクバーの通知領域(実体はツールウィンドウ)が見つからない場合に起こるそうな。ただしその後でTaskbarCreatedのメッセージが来るので、リトライせずともそちらでカバーできます。

何か原因不明のエラーで無限ループになっても困るので、リトライするならこの2つの場合だけにするか、回数制限をするのが良いのでしょう。

DeltaEnd Ver.0.45公開

| コメント(0) | トラックバック(0)
deltaend04.pngBoot CampのCtrl + 1とアイコン再登録、それにATOK 2008のタブ同時確定をメインとしたDeltaEndの刷新版を公開しました。
DeltaEnd 0.45

最近試した色々が盛り込まれ、32ビット版と64ビット版が同期して動作するものになっています。

ATOKのタブ同時確定は今のところ2008専用ですが、新しいのを購入することがあったら更新するでしょう。

実はThinkPad X61tが壊れてMacBook Pro(15.4)を導入しました。手持ちの2008というのがMac+Windows版でして、Lionも出たので新しくしたいのですがいつになるやら。

32/64共同作業

| コメント(0) | トラックバック(0)
64ビット版Windowsにおける一部ユーティリティは、32/64両方のバイナリを動作させなければならない場合があります。

この場合、32ビット版を主体とし、64ビット依存の部分だけ子プロセスを呼んだり、一歩進んでアーキテクチャ依存部と非依存部に分離させ、依存部を子プロセスにしたりするでしょうか。

個人の趣味レベルであれば、同一ソースコードから2つのバイナリを作り、両方とも動作させても構わないでしょう。ただ設定ぐらいは連動させ、可能であればマスター/スレーブとしてそれぞれ振る舞わせ、スレーブ側はUIを表示しないようにしたいところ。

というわけで、平たく言えばATOK2008タブ同時確定やBootcampアイコン再登録をDeltaEndに組み込むため、ファイルマッピングを利用して協調動作させるクラスとそのサンプルアプリを作ってみました。

SyncRun.zip

起動するとプロセスが2つ立ち上がります。マスター側のエディットボックスに入れた数字をスレーブ側に伝えることができ、マスター終了でスレーブも終了しますが、スレーブは単独で終了・再立ち上げ可能です。32ビット版、64ビット版の混在可能、どちらもマスター・スレーブ双方になれます。

このサンプルではアーキテクチャを判別していないので、32ビット版同士/64ビット版同士での動作もできますが、DeltaEndに組み込んだ際にはIsWow64Process APIの戻り値を参照したフラグを入れています。

シングル/マスター/スレーブのモード判別ができ、それに応じてUIを切り替えることができます(もちろんアプリの実装として)。

スレーブが消え失せた場合の処理は少し入れてありますが、マスターが消え失せた場合のスレーブ終了処理はまだ入っていません。タイマーを使って1分毎ぐらいでマスターにWM_NULLをPostMessageするといったあたりでしょうか。

Boot CampとTaskbarCreated

| コメント(0) | トラックバック(0)
4.0は知りませんが、3.xまでのBoot Campはタスクバー再生成の際にアイコンが登録されません。

外部ツールがついでに登録できないかと32ビット版3.2と64ビット版3.3で調べたところ、NOTIFYICONDATAのuIDに0x86を設定することで、他は普通に可能でした。

メッセージを飛ばす相手のウィンドウは、クラス名"Afx:00400000:0"(3.2/32)又は"Afx:000000013FF20000:0"(3.3/64)ですが、2つあるので大きさで判別する必要があります。目的のブツは10x10のものです。

クラス名でなくプロセス名がBootcamp.exe(2.xまではKbdMgr.exe)であるのを確認しても良いでしょう。こちらが参考になります。Bootcamp.exeの在処は、決め打ちでなければプロセス名のフルパスから取るのが正しいですから。

なおアイコンのリソースIDは128、コールバックのメッセージはWM_USER+10です。

exeをLoadLibraryしてLoadIcon(LoadImage)して、Shell_NotifyIconしてFreeLibraryすれば完了です。TerminateProcessして再起動するより行儀の良い再登録が行なえます。
<<前のページへ 1|2|3|4|5|6|7|8

アーカイブ

ウェブページ

Powered by Movable Type 5.2.13

ホームページ