タイトル/名前 | 更新者 | 更新日 |
---|---|---|
日記/2010/06/16/VirtualAlloc(), VirtualProtect()のメモ | msakamoto-sf | 2010-06-16 16:08:43 |
日記/2010/06/16/CUnit(GoogleCode版)1.1.1 with VC++2008 Express Edition | msakamoto-sf | 2010-06-16 10:37:13 |
日記/2010/06/12/PEファイルをFileMappingでロードした場合のRW設定で疑問 | msakamoto-sf | 2010-06-12 21:41:10 |
日記/2010/06/10/PEファイルのDLL遅延ロード情報を表示するPython | msakamoto-sf | 2010-06-10 14:13:52 |
日記/2010/06/10/PEファイルの事前バインド情報を表示するPython | msakamoto-sf | 2010-06-10 12:04:58 |
C言語系/memos/VC++/06, DLLの事前バインド(BindImageEx()) | msakamoto-sf | 2010-06-09 21:31:04 |
日記/2010/06/09/PEファイルのインポート情報を表示するPython(一区切り) | msakamoto-sf | 2010-06-09 21:11:37 |
日記/2010/06/09/PEファイルのエクスポート情報を表示するPython | msakamoto-sf | 2010-06-09 18:59:16 |
日記/2010/06/05/城ヶ島 | msakamoto-sf | 2010-06-07 11:02:00 |
C言語系/memos/VC++/08, DLLの遅延読み込み(delay loading) | msakamoto-sf | 2010-06-04 13:27:03 |
VirtualAlloc()で20KB分(0x5000)確保した後、4KB毎にVirtualProtect()で保護モードを変更してみるサンプル。
va01.cpp:
#include <windows.h> #include <stdio.h> void PrintErrorMsg(DWORD err) { LPTSTR lpMsgBuf; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); printf("%s\n", lpMsgBuf); LocalFree(lpMsgBuf); } void DumpMemoryInfo(void *addr) { MEMORY_BASIC_INFORMATION mbi; size_t res_sz = 0; res_sz = VirtualQuery(addr, &mbi, sizeof(mbi)); if (0 == res_sz) { PrintErrorMsg(GetLastError()); fprintf(stderr, "VirtualQuery() failed.\n"); exit(-1); } printf("[MBI at 0x%08X]:\n", addr); printf("\tmbi.BaseAddress = 0x%08X\n", mbi.BaseAddress); printf("\tmbi.AllocationBase = 0x%08X\n", mbi.AllocationBase); printf("\tmbi.AllocationProtect = 0x%08X\n", mbi.AllocationProtect); printf("\tmbi.RegionSize = 0x%08X\n", mbi.RegionSize); printf("\tmbi.State = 0x%08X\n", mbi.State); printf("\tmbi.Protect = 0x%08X\n", mbi.Protect); printf("\tmbi.Type = 0x%08X\n", mbi.Type); } char msg[] = "ABCD"; int main(int argc, char *argv[]) { void *p1 = NULL; DWORD dw1 = 0; size_t init_sz = 0x5000; p1 = VirtualAlloc(NULL, init_sz, MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); if (NULL == p1) { PrintErrorMsg(GetLastError()); fprintf(stderr, "VirtualAlloc() failed.\n"); return -1; } printf("p1 = 0x%08X\n", p1); dw1 = (DWORD)p1; DumpMemoryInfo((void*)(dw1 + 123)); DWORD oldp = 0; DWORD start = dw1; if (!VirtualProtect((void*)(start), 0x1000, PAGE_READONLY, &oldp)) { PrintErrorMsg(GetLastError()); fprintf(stderr, "VirtualProtect() failed.\n"); return -1; } printf("------[1]\n"); DumpMemoryInfo((void*)(start)); start += 0x1000; if (!VirtualProtect((void*)(start), 0x1000, PAGE_EXECUTE_READ, &oldp)) { PrintErrorMsg(GetLastError()); fprintf(stderr, "VirtualProtect() failed.\n"); return -1; } printf("------[2]\n"); DumpMemoryInfo((void*)(start)); start += 0x1000; if (!VirtualProtect((void*)(start), 0x1000, PAGE_READWRITE, &oldp)) { PrintErrorMsg(GetLastError()); fprintf(stderr, "VirtualProtect() failed.\n"); return -1; } printf("------[3]\n"); DumpMemoryInfo((void*)(start)); start += 0x1000; if (!VirtualProtect((void*)(start), 0x1000, PAGE_READONLY, &oldp)) { PrintErrorMsg(GetLastError()); fprintf(stderr, "VirtualProtect() failed.\n"); return -1; } printf("------[4]\n"); DumpMemoryInfo((void*)(start)); return 0; }
実行結果:
p1 = 0x003A0000 [MBI at 0x003A007B]: mbi.BaseAddress = 0x003A0000 mbi.AllocationBase = 0x003A0000 mbi.AllocationProtect = 0x00000004 mbi.RegionSize = 0x00005000 mbi.State = 0x00001000 mbi.Protect = 0x00000004 mbi.Type = 0x00020000 ------[1] [MBI at 0x003A0000]: mbi.BaseAddress = 0x003A0000 mbi.AllocationBase = 0x003A0000 mbi.AllocationProtect = 0x00000004 mbi.RegionSize = 0x00001000 mbi.State = 0x00001000 mbi.Protect = 0x00000002 mbi.Type = 0x00020000 ------[2] [MBI at 0x003A1000]: mbi.BaseAddress = 0x003A1000 mbi.AllocationBase = 0x003A0000 mbi.AllocationProtect = 0x00000004 mbi.RegionSize = 0x00001000 mbi.State = 0x00001000 mbi.Protect = 0x00000020 mbi.Type = 0x00020000 ------[3] [MBI at 0x003A2000]: mbi.BaseAddress = 0x003A2000 mbi.AllocationBase = 0x003A0000 mbi.AllocationProtect = 0x00000004 mbi.RegionSize = 0x00003000 mbi.State = 0x00001000 mbi.Protect = 0x00000004 mbi.Type = 0x00020000 ------[4] [MBI at 0x003A3000]: mbi.BaseAddress = 0x003A3000 mbi.AllocationBase = 0x003A0000 mbi.AllocationProtect = 0x00000004 mbi.RegionSize = 0x00001000 mbi.State = 0x00001000 mbi.Protect = 0x00000002 mbi.Type = 0x00020000
VirtualAlloc()で確保したメモリ中を、0x1000サイズの領域毎にプロテクトモードを変更出来ている。
CUnitの本家はSourceForgeの方だが、2006年3月の2.1.0でリリースが止まっている。
本家の方で対応してくれない追加要望に対し、独自対応するためのコピープロジェクトが Google Code の方のCUnitになる。まずはcruisecontrolに対応する為のjunitライクなXML出力に対応している。
というわけで、GoogleCode版のCUnitをVC++2008 Express Editionでコンパイルしてみた。
といっても特にメモしておくような注意点は無い。
今回使用したのはc-unit-1.1.1で、tar.gzを展開したフォルダの中に"vc8"というフォルダがある。この中にVC++2005用のソリューション・プロジェクトファイルが入っている。まだVC++2008 = vc9用のファイルは用意されていないが、VC++2008による自動変換で問題なく利用出来る。
"CUnit.sln" をVC++2008で開けば自動的に変換される。あとはソリューションをビルドすればよい。
注意点らしき注意点と言えば、"BasicTest"プロジェクトが最初ビルドに失敗する。ただし、一旦"libcunit"をビルドした後、再度ビルドすれば成功する。
ドキュメントは Google Code のWikiを見てもよいし、ソリューション中の"BasicTest"や"AutomatedTest"プロジェクトなどがそのままサンプルコードになっているのでそれをコピペしても良し。Web上で検索すれば解説記事も豊富に見つかるので、CUnitの使い方自体で困ることはない。
先日試作したPythonでDLLをロードして実行可能にするスクリプトでは、FileMappingは使わずにダイレクトにメモリ上にDLLのファイルデータをコピーした。
現実のOSによるロードでは、FileMappingを使ってロードしているはず。
でふと疑問に思ったのが、IATや再配置情報など実行時に書き換える部分。
これはプロセス毎に変わる可能性があるし、環境によって変動する。ので、ファイル内容を書き換えるわけにはいかない。
では、どういうフラグや属性を使ってFileMappingしてるのか?が・・・謎に思えてきた。
そこだけCopy on Writeみたいな仕組みにしてるのかな?
これも調べておきたいな。
日記/2010/06/09/PEファイルのインポート情報を表示するPython(一区切り), 日記/2010/06/10/PEファイルの事前バインド情報を表示するPython のバリエーションの最後、DLLの遅延ロード情報を表示するPython。
遅延ロード情報はDataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT]の示すRVAに、ImgDelayDescr構造体の配列として記録されている。
ImgDelayDescr構造体はSDKの提供する"delayimp.h"で定義されている(遅延ロードはOS自身の機能ではなく、SDKのリンカとスタブコードにより実現されている点に注意)。
日記/2010/06/09/PEファイルのインポート情報を表示するPython(一区切り) の続きで、事前バインド情報を表示するようにしてみた。
事前バインド情報はDLL一つにつきIMAGE_BOUND_IMPORT_DESCRIPTOR構造体一つ分として記録される。事前に計算されたアドレス値はIAT(FirstThunk)に書き込まれる。IMAGE_BOUND_IMPORT_DESCRIPTOR構造体ではバインドされたDLL名やタイムスタンプ、転送(forward)シンボルの有無が記録されている。
DLLの事前バインドとは、DLLがロードされた時のエクスポートシンボルのアドレスを事前に計算しておき、IATに予め書き込んでおく仕組み。
多数のDLLをロードするEXEでは、DLLのロードと再配置+IATの書き換えで起動時間が遅くなる場合がある。DLLを事前バインドしておくことで、IATの書き換えをスキップでき起動時間の短縮に効果がある。
ただしDLLのバージョンはもとよりOSのバージョン・環境が異なれば当然、ロードされるアドレスも変わる。開発環境で事前バインドしても、実際に動作するクライアント環境ではバインドされたアドレスが無効となる可能性がある。そのため通常はインストール時に、つまりクライアント上で事前バインドを行う。
開発環境で事前バインドを試す場合は、VisualStudio付属ツールの"BIND.exe"か、"EDITBIN.exe"を使う。VSのバージョンによっては"BIND.exe"が無い場合もあるかも知れない。VC++2008ExpressEditionでは"EDITBIN.exe"が提供されている。あるいはクライアント上でのインストール時に事前バインドを行いたい場合は、イメージヘルプAPI(imagehlp.dll)のBindImageEx()APIを使う。
本記事ではBindImageEx()APIを使って事前バインドを試み、その影響をdumpbinなどで確認してみる。
最後にSDK提供の"EDITBIN /BIND(:PATH=)"オプションを使って事前バインドを試みる。
参考:
対象:Visual C++ 2008 Express Edition
> cl Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. > link Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved.
なお動作確認は Windows XP SP3 上で行っている。
日記/2010/05/31/PEファイルのインポート情報を表示するPython(作りかけ) の続きというか一区切り。
インポート情報にも、Import Name Table(INT) と Import Address Table (IAT)、バインドされたインポート情報、遅延ロードされるインポート情報がある。
この内、IATとINTについて表示するPythonスクリプトがひとまず動いたので、載せておく。
バインドされたインポート情報や遅延ロードについては見るDataDirectoryが異なる為、まだ取得出来ていない。
日記/2010/05/31/PEファイルのインポート情報を表示するPython(作りかけ) では未対応だった序数インポート情報を表示出来るように解決した。
日記/2010/05/31/PEファイルのインポート情報を表示するPython(作りかけ) から少し寄り道して、エクスポート情報をダンプしてみる。
エクスポート情報は、インポート情報よりも単純な作りになっている。
なおエクスポートシンボルが別のDLLに転送(forwarding)される場合は、AddressOfFunctionsの該当シンボルのRVAが実行コードのアドレスではなく、IMAGE_EXPORT_DIRECTORY構造体と同じセクション内のRVAを指し、"モジュール名.シンボル名"で転送先のDLL名とシンボル名を指定するようになっている。
LoadLibrary(), GetProcAddress()は面倒くさくて使いたくない、でもDLLのロードは関数が呼ばれるその時まで遅らせたい・・・
そんな要望に応えるのが、VC6よりサポートされたDLLの遅延読み込み(delay loading)である。
呼ぶ側のコードは暗黙的リンク(implicit-link)の書き方のまま、リンカオプションに"/DELAYLOAD:(DLLファイル名)"を付けるだけで自動的にLoadLibrary()+GetProcAddress()を行うスタブが生成される。最初に呼ばれるのはスタブコードだが、LoadLibrary()+GetProcAddress()成功時にはスタブ内部で自動的にIATを書き換えてくれるため、二回目以降は直接DLLを呼ぶようになる(=二度LoadLibrary()が呼ばれることは無い)。
DLLが見つからなかった場合どうする? or スタブの挙動をカスタマイズしたい or 遅延読み込みのタイミングを自分で決めたい・・・なども、フックやヘルパー関数が用意されているため開発者により調整可能となっている。
今回は自作DLLを用意し、遅延読み込みを試してみる。
参考資料:
対象:Visual C++ 2008 Express Edition
> cl Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. > link Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved.
なお動作確認は Windows XP SP3 上で行っている。