none
RegDeleteValueWのDLLインジェクション RRS feed

  • 質問

  • さっそですが、表題の質問です。

    現在、特定の外部アプリ(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
    2015年5月26日 17:29

回答

  • 前回の私からの返信に誤りがありました。
    大変申し訳ありませんでした。
    以下訂正させていただきます。

    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 の監視自体が「笊」になってしまう恐れがあると思います。

     


     
    • 編集済み お馬鹿 2015年5月28日 2:45
    • 回答としてマーク minmin312 2015年5月29日 0:43
    • 回答としてマークされていない minmin312 2015年5月29日 0:44
    • 回答としてマーク minmin312 2015年5月31日 23:41
    2015年5月28日 2:44

すべての返信

  • DetourFunction は一般の関数ではないのですから、ちゃんと出典を明示すべきでしょう。
    ここですかね? (or ここ)

    こちらの説明にもあるとおり、関数の先頭 5 バイトで命令が区切られていることを前提として別のアドレスにコピーし、ジャンプ命令に書き換えているようです。
    その 5 バイトで命令がきちんと区切られていれば確かに動作しますが、その保証はありません。

    -----

    Vista SP2 で確認する限り、以下のようなので、5 バイトだと変なことになるのでしょうね。

    _RegDeleteValueW@8:
    76F73FB6 6A 20                push        20h 
    76F73FB8 68 40 40 F7 76       push        76F74040h

    DLL Injection を扱う以上、何を実現しているコードなのか、そのコードの前提は何か、すべての環境で動くのかをきちんと考慮する必要があります。

    // 朝の短い時間で調べた結果なので間違えている可能性はあります。

    2015年5月26日 21:59
    モデレータ
  • 失礼いたしました。出典先はご指摘のリンク(=ここですかね?)の通りです。

    質問の不備にも関わらず、さっそくの的確なご指摘を頂き、誠にありがとうございます。
    お教え頂いた内容をもとに、取り急ぎ、命令の区切り長さを”手打ち”で修正することで、VISTA上でも動作させることができました。
    (Azulean様のような解析をする技量を持ち合わせていないため、いくつかの区切り長さで試したところ、こちらの環境では、7バイト分の区切り長さで動作するようになりました)
    ところで、「命令の区切り長さは一定ではない」ということはわかりましたが、その長さをプログラムコードから動的に確認して対応させるには、どのような方法が考えられるでしょうか?
    再度の質問で恐縮ですが、引き続きご教示を頂けましたら幸いです。

    2015年5月27日 3:21
  • x86 の命令セットとか、調べていけば分かるかもしませんが、私は調べていないのでご自身で調査、検討してください。

    技量がないことを何度か書かれていますが、これは免罪符になりません。
    DLL Injection という、他のプロセスに影響を与えるものを作る以上、相応の知識、判断力、解析力を培うことは必然です。
    (7 バイトで成功することは私の貼った逆アセンブルコードですぐにわかるレベルであってほしいのは求めすぎではないと思う...)
    2015年5月27日 3:49
    モデレータ
  • 全然関係ないことかもしれませんが。。。
    提示されているコードだと、たぶん Windows 7 x86 (32bit) だと動かないと思いますが、その点については、既に確認されていますでしょうか?
    「7バイト分の区切り長さ」にしても、Windows 7 x86 環境ではたぶん無理だと思います。
    • 編集済み お馬鹿 2015年5月27日 7:29 追記
    2015年5月27日 7:09
  • Azulean様

    ご指摘耳に痛み入ります。

    「逆アセンブルコードですぐわかる」というご指摘は至極当然と思いますが、Azulean様のVista環境では8バイトで、私のVista環境では7バイトということであれば、「すべてのクライアント環境で逆アセンブルコードで調べることはできない」のだから、「プログラムコード上から動的に判断する方法がないか」、という事を知りたかったのです。

    お気を悪くされたようでしたら、申し訳ありません。



    • 編集済み minmin312 2015年5月27日 11:59
    2015年5月27日 11:09
  • お馬鹿様

    ご回答ありがとうございます。

    環境がないので確認はしていませんが、動かない可能性が高いと予想しており、引き続き、「命令の区切り長さ」を動的に取得できる方法について調べていきたいと思っています。


    • 編集済み minmin312 2015年5月27日 11:59
    2015年5月27日 11:13
  • Azulean様のVista環境では8バイトで、私のVista環境では7バイト

    本筋ではありませんが、念のため。
    私の先の投稿("_RegDeleteValueW@8:" を含む投稿)は「7 バイトであることの証明」のつもりでした。認識を修正しておいてください。

    「プログラムコード上から動的に判断する方法がないか」、という事を知りたかったのです。

    その質問であることを理解した上で、自分で学ぶ・調べるべきだと主張させていただきました。
    命令フォーマット、オペコードなどの知識がない状態でやることはいらぬ障害を招くだけであるため。
    (「IA-32 インテル® アーキテクチャー・ソフトウェア・デベロッパーズ・マニュアル、中巻 命令セット・リファレンス」といった資料で述べられている事柄など)

    2015年5月27日 12:51
    モデレータ
  • 前回の私からの返信に誤りがありました。
    大変申し訳ありませんでした。
    以下訂正させていただきます。

    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 の監視自体が「笊」になってしまう恐れがあると思います。

     


     
    • 編集済み お馬鹿 2015年5月28日 2:45
    • 回答としてマーク minmin312 2015年5月29日 0:43
    • 回答としてマークされていない minmin312 2015年5月29日 0:44
    • 回答としてマーク minmin312 2015年5月31日 23:41
    2015年5月28日 2:44
  • Azulean様

    資料のご提示を頂き、ありがとうございます。

    ページ数が膨大なので、まずは斜め読みしながらの作業になると思いますが、折を見て、じっくりと拝見させて頂きたいと思います。


    2015年5月28日 8:57
  • お馬鹿様

    詳細な解析・ご回答を頂き、感謝いたします。

    ご指摘の内容については、私一人の力量では到底わかり得なかった情報と思います。

    Azulean様からの情報と併せて、理解・作業を進めていきたいと思います。

    本当にありがとうございました。


    2015年5月28日 9:04