注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

_

_

 
 
 

日志

 
 

64ビット対応のDLLインジェクション(CreateRemoteThread+LoadLibrary)  

2013-12-01 22:37:21|  分类: 默认分类 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

2012-01-14

64ビット対応のDLLインジェクション(CreateRemoteThread+LoadLibrary) Add Starnanjakkunfslasht

久々にDLLインジェクシCommentsョンを書いていたのですが、64ビットに関する情報が意外に少なく少し苦労しました。せっかくですので、今回把握できた事項を可能な限りまとめたいと思います。突っ込み所満載だと思います、ぜひ、色々教えてください。
【サンプルプログラム
ソース DllInjection-src.zip
バイナリ DllInjection-bin.zip
Windowsで、他のプロセス上でコードを実行する方法の1つにDLLインジェクションという手法があります。これを利用すると、通常のプログラムの枠を超えた機能を実現することが可能になります。
DLLインジェクションは、クラッキング等にも悪用され「危険なもの」というイメージもあるようですが、上手く使用すれば、非常に有用プログラムを作成することができると思います。
[使用例]
?修正ができないプログラムを実行時に修正(メッセージ?タイトル?挙動など)
?他のプロセスの動作(メッセージ監視、トラップなど)
?他のプロセスの権限で動作(SYSTEM権限での動作など)
1 DLLインジェクションの動作イメージ
f:id:spw0022:20120108064921p:image:w360:right
Winddows上で動作する各プログラムは、それぞれ別の(仮想)メモリ空間の中で動作し、通常、相互に干渉することはありません。
プロセスのメモリ空間内には、当該プロセスの独自コードのほか、カーネルコードや必要なDLLがロードされています。そして、このDLLのロードの方法には、次の2種類があります。
(1) プログラム作成時にスタティックにリンク
(2) プログラム実行時にダイナミックにロード
DLLを「プログラム実行時にダイナミックにロード」する場合、Win32APIのLoadLibrary()及びFreeLibrary()を使用して行いますが、この動作を他のプロセスから行うのが、CreateRemoteThread+LoadLibraryによるDLLインジェクションです。図で表現すれば、プロセスAがプロセスBのメモリ空間にtest.dllを挿入するイメージです。
いったん挿入されたtest.dllのコードは、プロセスBのメモリ空間で、プロセスBの権限で動作します。
挿入しただけでは、何も起こりませんので、通常は、プロセスA(挿入元)から意図した動作ができるように、何らかの仕組み(トリガ)を組み込む事になります。
2 自プロセス内でのDLLのダイナミックロード
比較のため、最初に、一般的なDLLのダイナミックロードのコードを示します。これは自メモリ空間にDLLを読み込んで使用する普通のコードです。

HMODULE hDll = ::LoadLibrary( "test.dll" ); //test.dllをロードする
if ( hDll != NULL ){
    //DLLを使用するコードをここに記述する
    ::FreeLibrary( hDll );//test.dllをアンロードする
}

LoadLibrary()が成功した時点で、test.dllは、メモリ空間に展開され、DLL内のコードであるDllMain()が呼び出されます。
先の図で言えば、プロセスBが自分のメモリ空間にtest.dllをロードするコードになります。
3 他プロセスでのDLLのダイナミックロード
他のプロセス上でコードを実行するAPIとして、CreateRemoteThread()があります。このAPIは、ターゲットのプロセス空間で指定したスレッドを実行するもです。

HANDLE CreateRemoteThread(
    HANDLE hProcess,        // 新しいスレッドを稼働させるプロセスを識別するハンドル
    LPSECURITY_ATTRIBUTES lpThreadAttributes,// スレッドのセキュリティ属性へのポインタ
    DWORD dwStackSize,     // 初期のスタックサイズ (バイト数)
    LPTHREAD_START_ROUTINE lpStartAddress,// 新しいスレッドに実行させる関数へのポインタ
    LPVOID lpParameter,   // 新しいスレッドの引数へのポインタ
    DWORD dwCreationFlags,  // 作成フラグ
    LPDWORD lpThreadId      // 取得したスレッド識別子へのポインタ
);

このAPIでは、第4パラメータに「新しいスレッドに実行させる関数へのポインタ」を指定しますが、ここには、
LPTHREAD_START_ROUTINE 型関数ポインタが必要です。

typedef DWORD (__stdcall *LPTHREAD_START_ROUTINE) (
    [in] LPVOID lpThreadParameter
);

しかし、LPTHREAD_START_ROUTINE型は、1つのポインタ型のパラメータを受け取るという意味で、LoadLibrary()やFreeLibrary()と同じですので、代わりにLoadLibrary()等の関数ポインタを指定できるという事になります。
なお、「関数へのポインタ」(メモリ上のアドレス)は、コードを実行するメモリ空間でのアドレス(挿入先のアドレス)である必要がありますが、LoadLibrary()及びFreeLibrary()は、kernel.dllに含まれるAPIであり、Windowsではkernel.dllが、すべてのプロセス空間で同一アドレスにロードされている(※1)ため、「挿入元でアドレスを取得」しても、その値をそのまま「挿入先のプロセス空間でのアドレス」として使用することが可能です。
関数へのポインタの取得は、GetProcAddress()を使用しています。
プロセスDLLをロードする一連のコードは、下記のとおりです。

//事前に挿入先のプロセスのハンドル(hProcess)が取得済みとする

//LoadLibrary()のアドレス取得(他プロセス上でも同一のアドレスとなる)
UIntPtr pAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
//挿入先でLoadLibraryを呼び出す pMemはパラメータのアドレス(後述)

IntPtr bytesout;
IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, pAddr, pMem, 0, out bytesout);
if (hThread != IntPtr.Zero) {
    //挿入したスレッドが終了するまで待機(DllMailから返るまで待つ)
    UInt32 INFINITE = 0xFFFFFFFF;//シグナル状態になるまで待機
    WaitForSingleObject(hThread,INFINITE);

    //スレッドの戻り値(LoadLibrary()の戻り値)を取得する
    if (GetExitCodeThread(hThread, out hDll)) {
        result = true;//成功
    }
    //スレッドハンドルのクローズ
    CloseHandle(hThread);
}

CreateRemoteThread()のあと、挿入したDLLのDllMain()が実行され、そこから返るのを、WaitForSingleObject()で待っています。また、GetExitCodeThread()を使用して、LoadLibrary()の戻り値を取得しています。これは、ロードしたDLLのハンドルであり、FreeLibrary()でDLLをアンロードする時のパラメータとして使用します。
4 LoadLibrary()のパラメータ
LoadLibrary()には、ロードするDLLのパス(文字列)のポインタが必要ですが、これも、挿入先のメモリ空間のアドレスである必要があります。自プロセス内の文字列ポインタをそのまま与えても動作できません。
そこで、このパス文字列を挿入先のプロセスのメモリ空間内に展開して、そのアドレスを取得する作業が必要になります。
この方法は、概ね下記の手順になります。
(1)挿入先のプロセス空間にメモリを確保する VirtualAllocEx()
(2)挿入先のメモリ空間に書き込む WriteProcessMemory()
(3)書き込んだメモリを使用する (Loadribrary()のパラメータbに使用)
(4)確保したメモリの開放 VirtualFreeEx()
また、コードは下記のようなものになります。

//事前に挿入先のプロセスのハンドル(hProcess)が取得済みとする

//パラメータ用の文字列を置くための領域を挿入先に確保する
UInt32 MEM_COMMIT = 0x1000;
UInt32 PAGE_EXECUTE_READWRITE = 0x40;
pMem = (IntPtr)VirtualAllocEx(hProcess, IntPtr.Zero, (uint)len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pMem != IntPtr.Zero) {
    IntPtr bytesout;
    //確保した挿入先の領域にDLL名を書き込む
    WriteProcessMemory(hProcess, pMem, dllName, (UIntPtr)len, out bytesout);
    
    //確保したメモリをここで使用する(挿入先でLoadLibraryを呼び出す)

    //確保したメモリの開放
    VirtualFreeEx(hProcess, pMem, 0, 0x8000);
}

5 64ビットと32ビットの混在
64ビットOSでは、従来の32ビットプログラムもWOW64によりそのまま使用することができるため、両者が混在する状況になっています。(※2)しかし、ここで注意が必要なのは、ロードする側のEXEとロードされる側のDLLは、同一である必要があるということです。32ビットのEXEは、32ビットのDLLしか使用できないのです。
32ビットと64ビットのプログラムでLoadLibrary()を使用して、2種類のDLLをロードした試験結果は下記のとおりです。
f:id:spw0022:20111227232617p:image:w450
Dllインジェクションを行う場合も、挿入先のプロセスに合わせたDLLを用意する必要があるという事になります。(※3)
6 CreateRemoteThread()でERROR_ACCESS_DENIEDが発生する
64ビットOS上では、32ビットプログラムも使用できますが、32ビットプログラムからは、CreateRemoteThread()は、ERROR_ACCESS_DENIEDが発生して使用できません。
したがって、CreateRemoteThread()を使用したDLLインジェクションを行う場合、64ビットプログラムでしか実装できないことになります。
また、64ビットプログラムと32ビットプログラムでは、kernel32.dllが同一アドレスにロードされていないため(※4)、自プロセス内で取得したアドレスをそのまま使用することは、できないことになります。
各種の組み合わせの成否を表にしてみました。
f:id:spw0022:20111227235157p:image:w650
64ビットプログラムから32ビットプログラムのメモリ空間アドレスを取得する方法が、まったく無いわけではありませんが、少しややこしくなるため、ここでは割愛させて頂きます。
7 .NETによる両対応EXEの作成
f:id:spw0022:20120101111911p:image:w360:left
.NETプログラム中間言語であるため、32ビットと64ビットの両方で環境に応じて動作するプログラム(EXE)を作成できます。(プラットフォームターゲットでAnyCPUを選択する)
そして、実行時に64ビットで動作しているか32ビットで動作しているかを確認して、適切なDLLをインジェクションすることで両対応のプログラムとすることができます。
動作状態を確認してDLLを選択するコードは次のようになります。

//64ビットで動作している場合、ポインタは8バイトになる
var dName = (IntPtr.Size == 8)?"64bit.dll":"32bit.dll";
dllInjection.Load(dllName, exeName);//DLLのインジェクション

8 64ビット環境でDLLハンドルが取得できない
LoadLibrary()でロードしたDLLをアンロードするにはFreeLibrary()を使用しますが、この時、DLLハンドルが必要になります。先の例ではGetExitCodeThread()を使用してDLLハンドルを取得しましたが、このコードは64ビットでは問題となります。
64ビット環境では、DLLハンドルが32ビット幅を超えた位置になるのに、GetExitCodeThread()で終了ステータスを受け取るパラメータがDWORD(32ビット)のポインタになっているため、下位32ビットのアドレスしか取得できないためです。
64ビット環境でのDLLハンドルが必要な場合は、アドレスを再計算が必要です。
9 挿入するDLLの作成(動作確認用)
挿入されるコードは、ターゲットからLoadLibraryで読み込むことになりますが、これはネイティブDLLである必要があります。C#でこれを作る事ができないので、この作業はC++となります。
動作確認用にDllMail()が呼び出された時、そのメッセージの種類をポップアップするだけのものになっています。ポップアップしたウインドウをクローズしないとDllMail()を抜けませんので、ロードする側のコードもブロックします。動作確認の為、あえてそういう仕様にしました。

//【試験用に作成した test.dll DllMailが呼び出されるたびにMessageBoxを表示する】
#include "stdafx.h"
void popup(char *caption){
	char tmp[128];
	wsprintf(tmp,"pid=%d",GetCurrentProcessId());
	MessageBox(NULL,caption,tmp,0);
}
BOOL APIENTRY DllMain( HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved){
    switch (ul_reason_for_call){
        case DLL_PROCESS_ATTACH:
	   popup("DLL_PROCESS_ATTACH");
	   break;
        case DLL_THREAD_ATTACH:
	   popup("DLL_THREAD_ATTACH");
	   break;
        case DLL_THREAD_DETACH:
	   popup("DLL_THREAD_DETACH");
	   break;
        case DLL_PROCESS_DETACH:
	   popup("DLL_PROCESS_DETACH");
	   break;
    }
    return TRUE;
}

GUIを持たないサービスなどに、このDLLを挿入すると、ポップアップウインドウのOKボタンが押せないためDllMain()から戻ることができません。同プロセスはロックし再起動が必要になりますのでご注意ください。
10 動作確認
サンプルに含まれるDllImjectionTest.exeを起動すると、「Load」と「Unload」のボタンがあるウインドウが表示されます。notepad.exeメモ帳」を起動した状態で「Load」ボタンを押すと、notepadのプロセスIDと「DLL_PROCESS_ATTACH」が表示されたメッセージボックスがポップアップします。また「Unload」ボタンを押すと、「DLL_PROCESS_DETTACH」が表示されます。
「Load」だけ押して、dllがロードされた状態で「メモ帳」を閉じると、自動的にdllが開放されるので「DLL_PROCESS_DETACH」がポップアップします。
f:id:spw0022:20120102183141p:image:w360:left
Windows7及びVistaで実行する場合、ユーザーアカウント制御 (UAC; User Account Control)が有効になっているとOpenProcess()でACCESS_DENYEDになります。なお、UACの設定変更を反映するには、Windows再起動が必要ですので、動作確認の場合はご注意ください。
(※1)kernal.dllは、すべてのプロセスで同一アドレスにロードされている
下記は、ListDlls.exeを使用して、現在ロードされているDLLを列挙した結果です。
winlogon.exe及びnotepad.exeは、共にkernel.dllを0x00000000771c0000からロードしています。(64bit Vistaで確認)

C:\>ListDlls winlogon
ListDLLs v3.1 - List loaded DLLs
Copyright (C) 1997-2011 Mark Russinovich
Sysinternals - www.sysinternals.com

------------------------------------------------------------------------------
winlogon.exe pid: 680
Command line: winlogon.exe

Base Size Path
0x00000000ffb30000 0x67000 C:\Windows\system32\winlogon.exe
0x0000000077650000 0x186000 C:\Windows\system32\ntdll.dll
0x00000000771c0000 0x12d000 C:\Windows\system32\kernel32.dll
0x00000000fe9b0000 0x108000 C:\Windows\system32\ADVAPI32.dll
0x00000000fe7f0000 0x143000 C:\Windows\system32\RPCRT4.dll
>ListDlls notepad
ListDLLs v3.1 - List loaded DLLs
Copyright (C) 1997-2011 Mark Russinovich
Sysinternals - www.sysinternals.com

------------------------------------------------------------------------------
notepad.exe pid: 4264
Command line: "C:\Windows\system32\NOTEPAD.EXE" C:\work\32.txt

Base Size Path
0x00000000ffe10000 0x2f000 C:\Windows\system32\NOTEPAD.EXE
0x0000000077650000 0x186000 C:\Windows\system32\ntdll.dll
0x00000000771c0000 0x12d000 C:\Windows\system32\kernel32.dll
0x00000000fe9b0000 0x108000 C:\Windows\system32\ADVAPI32.dll
0x00000000fe7f0000 0x143000 C:\Windows\system32\RPCRT4.dll

(※2)64ビットと32ビットで動作しているプロセスを確認する
f:id:spw0022:20111228084043p:image:w360:left
タスクマネージャを使用すると、32ビットで動作中のプロセスには、「*32」が表示されるため、どちらで動作しているかを簡単に確認することができます。
図では、notepad.exe winlogon.exeなどが64ビットで、POWERPNT.EXE、TrueCrypt.exeなどが32ビットで動作していることが分かります。
(※3)DLLの32ビットと64ビットの違い
DLLが32ビットで作成されているか、64ビットで確認されているかは、ヘッダ情報を見ることで確認できます。
64ビットでコンパイルしたDLL
c:\>dumpbin /HEADERS 64.dll
Microsoft (R) COFF/PE Dumper Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.

Dump of file 64.dll
PE signature found
File Type: DLL

FILE HEADER VALUES
8664 machine (x64)
7 number of sections
4EF94A96 time date stamp Tue Dec 27 13:33:26 2011
0 file pointer to symbol table
0 number of symbols
F0 size of optional header
2022 characteristics
Executable
Application can handle large (>2GB) addresses
DLL

32ビットでコンパイルしたDLL
c:\>dumpbin /HEADERS 32.dll
Microsoft (R) COFF/PE Dumper Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file 32.dll

PE signature found
File Type: DLL
FILE HEADER VALUES
14C machine (x86)
7 number of sections
4EF947E4 time date stamp Tue Dec 27 13:21:56 2011
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
2102 characteristics
Executable
32 bit word machine
DLL

(※4) 64ビットと32ビットでは、kernal.dllがロードされているアドレスが違う
32ビットで動作しているPOWERPNT.EXEやTrueCrypt.exeは、共に、0x0000000075230000 からkernel.dllをロードしているが、これは、先の64ビットのnotepad.exe等とは違うアドレスでとなっている。
c:\>listdlls POWERPNT.EXE
ListDLLs v3.1 - List loaded DLLs
Copyright (C) 1997-2011 Mark Russinovich
Sysinternals - www.sysinternals.com

------------------------------------------------------------------------------
POWERPNT.EXE pid: 5072
Command line:

Base Size Path
0x000000002dc30000 0x212000 C:\Program Files (x86)\Microsoft Office\Office14\POWERPNT.EXE
0x00000000774f0000 0x186000 C:\Windows\system32\ntdll.dll
0x0000000075140000 0x45000 C:\Windows\system32\wow64.dll
0x0000000074ae0000 0x4e000 C:\Windows\system32\wow64win.dll
0x0000000075130000 0x9000 C:\Windows\system32\wow64cpu.dll
0x000000002dc30000 0x212000 C:\Program Files (x86)\Microsoft Office\Office14\POWERPNT.EXE
0x00000000776b0000 0x160000 C:\Windows\SysWOW64\ntdll.dll
0x0000000075230000 0x110000 C:\Windows\syswow64\kernel32.dll
0x000000005a160000 0x924000 C:\Program Files (x86)\Microsoft Office\Office14\ppcore.dll
c:\>listdlls truecrypt.exe
ListDLLs v3.1 - List loaded DLLs
Copyright (C) 1997-2011 Mark Russinovich
Sysinternals - www.sysinternals.com

------------------------------------------------------------------------------
TrueCrypt.exe pid: 3796
Command line: "C:\Program Files\TrueCrypt\TrueCrypt.exe"

Base Size Path
0x0000000000400000 0x19d000 C:\Program Files\TrueCrypt\TrueCrypt.exe
0x00000000774f0000 0x186000 C:\Windows\system32\ntdll.dll
0x0000000075140000 0x45000 C:\Windows\system32\wow64.dll
0x0000000074ae0000 0x4e000 C:\Windows\system32\wow64win.dll
0x0000000075130000 0x9000 C:\Windows\system32\wow64cpu.dll
0x0000000000400000 0x19d000 C:\Program Files\TrueCrypt\TrueCrypt.exe
0x00000000776b0000 0x160000 C:\Windows\SysWOW64\ntdll.dll
0x0000000075230000 0x110000 C:\Windows\syswow64\kernel32.dll
0x0000000074a50000 0x85000 C:\Windows\WinSxS\x86_microsoft.windows.common-con
trols_6595b64144ccf1df_5.82.6002.18305_none_88f3a38569c2c436\COMCTL32.dll

参考資料
別のプロセスにコードを割り込ませる3つの方法
DLL Injection and function interception tutorial
Advanced Windows 第5版 上 (マイクロソフト公式解説書)
ListDLLs 作成者: Mark Russinovich
  评论这张
 
阅读(1137)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017