トップ回答者
RegDeleteValueWのDLLインジェクション

質問
-
さっそですが、表題の質問です。
現在、特定の外部アプリ(32bit)をDLLインジェクションで制御・監視するツールを作成しています。
Windows7(64bit)、Windows8.1(64bit)で正常に動作するところまで目処がついたのですが、WindowsVISTA(32bit)において、外部アプリが不正終了してしまいます。
DLLインジェクションは、複数のAPI関数に適用していますが、引数として文字列が与えられている場合にフリーズしてしまうようです。
例えば、RegDeleteValueW 関数などがそれにあたります。
もう少し具体的に言うと、DLLインジェクションによる差し替え用関数(ここでは p_RegDeleteValueW としました)の呼び出しまでは問題ないのですが、その中で、元々の関数 RegDeleteValueW に制御を渡そうとした途端、フリーズが発生します。
ここ数日、原因を調査していますが、技量不足のため解決に至りません。
詳しい方のお力添えをお願いしたく、どうぞよろしくお願いいたします。
(以下、コードが長いので、抜粋して表示させて頂きました。)
//■■■■■■■■■■■■■■■■■■■■■■■■■■■■ // DLLインジェクションによって、 // [RegDeleteValueW] の呼び出しを // [p_RegDeleteValueW] に差し替える //■■■■■■■■■■■■■■■■■■■■■■■■■■■■ //-------------------------------------------------------- // p_RegDeleteValueW を定義 //-------------------------------------------------------- typedef LONG (WINAPI * t_RegDeleteValueW)(HKEY, LPCWSTR); t_RegDeleteValueW s_RegDeleteValueW = NULL; LONG WINAPI p_RegDeleteValueW(HKEY hKey, LPCWSTR lpValueName){ MessageBox(NULL , lpValueName , TEXT("呼出開始") , MB_OK); //<-これは表示される LONG ret = s_RegDeleteValueW(hKey, lpValueName); //<-ここでフリーズ MessageBox(NULL , lpValueName , TEXT("呼出終了") , MB_OK); //<-ここまで到達しない!! return ret; } //-------------------------------------------------------- // DLLインジェクションをセット //-------------------------------------------------------- BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){ switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: s_RegDeleteValueW = (t_RegDeleteValueW)DetourFunction((PBYTE)GetProcAddress(GetModuleHandle(TEXT("advapi32.dll")), "RegDeleteValueW"), (PBYTE)p_RegDeleteValueW); hCommunicateThread = CreateThread(NULL, 0, CommunicateThreadFunc, NULL, 0, &dwComThreadId); } return TRUE; }
- 編集済み minmin312 2015年5月26日 20:21
回答
-
前回の私からの返信に誤りがありました。
大変申し訳ありませんでした。
以下訂正させていただきます。Windows 7 以降では、おそらくx86 / x64 共に提示されているコードで動くと思います。
ただ RegDeleteValueW() API は、Windows Vista までは advapi32.dll にその実体が実装されていましたが、私が調べた限りでは Windows 7 以降、kernel32.dll 内に移動されたようですので、この点を考慮する必要があると思います。以下、Windows 7 x86 環境で採取したデバッグ情報を示します。
(アドレス長は異なりますが、呼出し手順としては x64 も同じだと思います。)-------------------------------------------- ;; advapi32.dll 内 RegDeleteValueW() API のエントリ ポイントを確認 1: kd> x advapi32!RegDeleteValueW 77cbcede ADVAPI32!RegDeleteValueW (<no parameter info>) ;; advapi32.dll 内 RegDeleteValueW() API の処理を確認 ;; (下記逆アセンブルは、エントリ ポイントの5バイト分前からを表示) 1: kd> u 77cbcede-5 ADVAPI32!RegDeleteValueWStub+0x8: 77cbced9 90 nop 77cbceda 90 nop 77cbcedb 90 nop 77cbcedc 90 nop 77cbcedd 90 nop ADVAPI32!RegDeleteValueW: 77cbcede ff25b414cb77 jmp dword ptr [ADVAPI32!_imp__RegDeleteValueW (77cb14b4)] 77cbcee4 90 nop 77cbcee5 90 nop ;; ADVAPI32!_imp__RegDeleteValueW が示すアドレスを確認 ;; (_imp__RegDeleteValueW は、advapi32.dll モジュールの IAT 内に存在。) 1: kd> dds 77cb14b4 l1 77cb14b4 76d4bc53 kernel32!RegDeleteValueW ;; kernel32.dll 内 RegDeleteValueW() API のエントリ ポイントを確認 1: kd> uf kernel32!RegDeleteValueW kernel32!RegDeleteValueW: 76d4bc53 6a20 push 20h 76d4bc55 68f0bcd476 push offset kernel32!BaseReleaseProcessExePath+0xfba (76d4bcf0) 76d4bc5a e859100100 call kernel32!_SEH_prolog4 (76d5ccb8) 76d4bc5f 33db xor ebx,ebx 76d4bc61 895de0 mov dword ptr [ebp-20h],ebx 76d4bc64 895de4 mov dword ptr [ebp-1Ch],ebx 76d4bc67 e82e120100 call kernel32!RegKrnGetGlobalState (76d5ce9a) 76d4bc6c 8b7864 mov edi,dword ptr [eax+64h] 76d4bc6f 8b7508 mov esi,dword ptr [ebp+8] 76d4bc72 81fe04000080 cmp esi,80000004h 76d4bc78 0f843ff30300 je kernel32!RegDeleteValueW+0x27 (76d8afbd) --------------------------------------------
つまり Windows 7 以降では、advapi32.lib をリンクしなくても、kernel32.lib をリンクしていれば RegDeleteValueW() API は成功することなります。
言い換えれば、advapi32.dll 内の RegDeleteValueW() ルーチンを経由しなくても、この関数を成功させることができるので、その場合、提示されているコードでは、RegDeleteValueW() API コールの監視を取りこぼすことになると思います。上記では、あくまでも RegDeleteValueW() API に特化して説明しましたが、このように実装されているモジュールが変更されている API は他にも多数あります。
従って、そのような API がどのくらいあるかをきちんと調べない限り、API の監視自体が「笊」になってしまう恐れがあると思います。
すべての返信
-
DetourFunction は一般の関数ではないのですから、ちゃんと出典を明示すべきでしょう。
ここですかね? (or ここ)こちらの説明にもあるとおり、関数の先頭 5 バイトで命令が区切られていることを前提として別のアドレスにコピーし、ジャンプ命令に書き換えているようです。
その 5 バイトで命令がきちんと区切られていれば確かに動作しますが、その保証はありません。-----
Vista SP2 で確認する限り、以下のようなので、5 バイトだと変なことになるのでしょうね。
_RegDeleteValueW@8:
76F73FB6 6A 20 push 20h
76F73FB8 68 40 40 F7 76 push 76F74040hDLL Injection を扱う以上、何を実現しているコードなのか、そのコードの前提は何か、すべての環境で動くのかをきちんと考慮する必要があります。
// 朝の短い時間で調べた結果なので間違えている可能性はあります。
- 編集済み AzuleanMVP, Moderator 2015年5月26日 22:51
-
失礼いたしました。出典先はご指摘のリンク(=ここですかね?)の通りです。
質問の不備にも関わらず、さっそくの的確なご指摘を頂き、誠にありがとうございます。
お教え頂いた内容をもとに、取り急ぎ、命令の区切り長さを”手打ち”で修正することで、VISTA上でも動作させることができました。
(Azulean様のような解析をする技量を持ち合わせていないため、いくつかの区切り長さで試したところ、こちらの環境では、7バイト分の区切り長さで動作するようになりました)
ところで、「命令の区切り長さは一定ではない」ということはわかりましたが、その長さをプログラムコードから動的に確認して対応させるには、どのような方法が考えられるでしょうか?
再度の質問で恐縮ですが、引き続きご教示を頂けましたら幸いです。 -
Azulean様のVista環境では8バイトで、私のVista環境では7バイト
本筋ではありませんが、念のため。
私の先の投稿("_RegDeleteValueW@8:" を含む投稿)は「7 バイトであることの証明」のつもりでした。認識を修正しておいてください。「プログラムコード上から動的に判断する方法がないか」、という事を知りたかったのです。
その質問であることを理解した上で、自分で学ぶ・調べるべきだと主張させていただきました。
命令フォーマット、オペコードなどの知識がない状態でやることはいらぬ障害を招くだけであるため。
(「IA-32 インテル® アーキテクチャー・ソフトウェア・デベロッパーズ・マニュアル、中巻 命令セット・リファレンス」といった資料で述べられている事柄など)- 編集済み AzuleanMVP, Moderator 2015年5月27日 22:42
-
前回の私からの返信に誤りがありました。
大変申し訳ありませんでした。
以下訂正させていただきます。Windows 7 以降では、おそらくx86 / x64 共に提示されているコードで動くと思います。
ただ RegDeleteValueW() API は、Windows Vista までは advapi32.dll にその実体が実装されていましたが、私が調べた限りでは Windows 7 以降、kernel32.dll 内に移動されたようですので、この点を考慮する必要があると思います。以下、Windows 7 x86 環境で採取したデバッグ情報を示します。
(アドレス長は異なりますが、呼出し手順としては x64 も同じだと思います。)-------------------------------------------- ;; advapi32.dll 内 RegDeleteValueW() API のエントリ ポイントを確認 1: kd> x advapi32!RegDeleteValueW 77cbcede ADVAPI32!RegDeleteValueW (<no parameter info>) ;; advapi32.dll 内 RegDeleteValueW() API の処理を確認 ;; (下記逆アセンブルは、エントリ ポイントの5バイト分前からを表示) 1: kd> u 77cbcede-5 ADVAPI32!RegDeleteValueWStub+0x8: 77cbced9 90 nop 77cbceda 90 nop 77cbcedb 90 nop 77cbcedc 90 nop 77cbcedd 90 nop ADVAPI32!RegDeleteValueW: 77cbcede ff25b414cb77 jmp dword ptr [ADVAPI32!_imp__RegDeleteValueW (77cb14b4)] 77cbcee4 90 nop 77cbcee5 90 nop ;; ADVAPI32!_imp__RegDeleteValueW が示すアドレスを確認 ;; (_imp__RegDeleteValueW は、advapi32.dll モジュールの IAT 内に存在。) 1: kd> dds 77cb14b4 l1 77cb14b4 76d4bc53 kernel32!RegDeleteValueW ;; kernel32.dll 内 RegDeleteValueW() API のエントリ ポイントを確認 1: kd> uf kernel32!RegDeleteValueW kernel32!RegDeleteValueW: 76d4bc53 6a20 push 20h 76d4bc55 68f0bcd476 push offset kernel32!BaseReleaseProcessExePath+0xfba (76d4bcf0) 76d4bc5a e859100100 call kernel32!_SEH_prolog4 (76d5ccb8) 76d4bc5f 33db xor ebx,ebx 76d4bc61 895de0 mov dword ptr [ebp-20h],ebx 76d4bc64 895de4 mov dword ptr [ebp-1Ch],ebx 76d4bc67 e82e120100 call kernel32!RegKrnGetGlobalState (76d5ce9a) 76d4bc6c 8b7864 mov edi,dword ptr [eax+64h] 76d4bc6f 8b7508 mov esi,dword ptr [ebp+8] 76d4bc72 81fe04000080 cmp esi,80000004h 76d4bc78 0f843ff30300 je kernel32!RegDeleteValueW+0x27 (76d8afbd) --------------------------------------------
つまり Windows 7 以降では、advapi32.lib をリンクしなくても、kernel32.lib をリンクしていれば RegDeleteValueW() API は成功することなります。
言い換えれば、advapi32.dll 内の RegDeleteValueW() ルーチンを経由しなくても、この関数を成功させることができるので、その場合、提示されているコードでは、RegDeleteValueW() API コールの監視を取りこぼすことになると思います。上記では、あくまでも RegDeleteValueW() API に特化して説明しましたが、このように実装されているモジュールが変更されている API は他にも多数あります。
従って、そのような API がどのくらいあるかをきちんと調べない限り、API の監視自体が「笊」になってしまう恐れがあると思います。