前回は lib.exe をつかって libcmt.lib から printf.obj を抽出してから、printf() を検出しましたが、libcmt.lib を読み込んで直接 printf() を検出してみましょう。
Hello.txt
Hello
World.txt
World!
ar.txt
!<arch> Hello.txt/ 1243824130 500 513 100000 5 ` Hello World.txt/ 1243824143 500 513 100000 6 ` world!
#define IMAGE_ARCHIVE_START_SIZE 8 #define IMAGE_ARCHIVE_START "!<arch>\n"アーカイブメンバヘッダは WinNT.H に以下のように定義されています。先述したように全てのメンバは '\0' で終了しない文字列で表現され、残りは空白で埋められます。
typedef struct _IMAGE_ARCHIVE_MEMBER_HEADER { BYTE Name[16]; BYTE Date[12]; BYTE UserID[6]; BYTE GroupID[6]; BYTE Mode[8]; BYTE Size[10]; BYTE EndHeader[2]; } IMAGE_ARCHIVE_MEMBER_HEADER, *PIMAGE_ARCHIVE_MEMBER_HEADER;
Name | アーカイブメンバの名前。アーカイブメンバがファイルの場合はファイル名になります。終了文字として '/' が使われます。ただし、Microsoft のツールで扱うライブラリファイルでは '/' で始まる名前は特殊な意味を持ちます(後述)。 |
---|---|
Date | アーカイブメンバのタイムスタンプを10進文字列で表現したもの。 |
UserID | Microsoft のツールで使われるライブラリファイルでは使用しません。 |
GroupID | Microsoft のツールで使われるライブラリファイルでは使用しません。 |
Mode | Microsoft のツールで使われるライブラリファイルでは使用しません。 |
Size | アーカイブメンバのサイズを10進文字列で表現したもの。 |
EndHeader | ヘッダの終了を表すマジックストリングで "`\n" でなければいけません。 |
ar のアーカイブファイルはその構造上、目的とするシンボルを探し出すのに適していません。目的のシンボルを含むメンバを最初から順に探しだすなんてことは馬鹿げています。そのため、アーカイブした obj ファイルに含まれる外部定義シンボルの名前のテーブルと、その外部定義シンボルがどのメンバ(obj ファイル)に含まれているかを素早く調べられるデータを持っています。
そのデータは互換性を維持するための古い形式と、現在使われている新しい形式の2種類があり、どちらも "/ " という名前で必ずライブラリファイルの1番目(古い形式)と2番目(新しい形式)のメンバとして存在します。これらは第1リンカメンバ(First Linker Member)、第2リンカメンバ(Second Linker Member)と呼ばれています。
また、アーカイブした obj ファイルの名前が15文字を越える場合は名前が "// " のロング名メンバと呼ばれるデータを持ち、そこに実際のファイル名が格納されます。その場合、15文字を超える名前をもつメンバの名前は '/' で始まり、ロング名メンバ内のオフセットを10進表記にした文字列が格納されます。例えば "/0 " はロング名メンバのデータの先頭に実際の名前が格納されていることを示します。なお、ロング名メンバに格納されている名前は '/' ではなく '\0' で終了しているので取り出しが簡単です。
図1は第2リンカメンバの構造と各フィールドの使われ方を示しています(名前や数値は適当です)。
LibFile クラスは指定した外部定義シンボルを持つ obj ファイルをライブラリファイルから探しだすためのクラスです。宣言は以下のようになっています。
LibFile.h
1 : #ifndef __LIB_FILE_H_INCLUDED__ 2 : #define __LIB_FILE_H_INCLUDED__ 3 : #include <PshPack1.h> 4 : 5 : 6 : //! アーカイブメンバヘッダ(+ データの先頭) 7 : struct ArchiveMemberHeader 8 : { 9 : BYTE name[16]; //!< 名前。 10 : BYTE date[12]; //!< タイムスタンプ。 11 : BYTE user_id[6]; //!< 使用しない。 12 : BYTE group_id[6]; //!< 使用しない。 13 : BYTE mode[8]; //!< 使用しない。 14 : BYTE size[10]; //!< メンバのサイズ。 15 : BYTE end_header[2]; //!< ヘッダのエンドマーカー。 16 : BYTE member_data[1]; //!< メンバのデータの先頭。 17 : 18 : //! end_header と size に有効な値が入っているか調べる。 19 : bool IsValid() const; 20 : //! 名前を取得する。 21 : std::string GetName() const; 22 : //! メンバのサイズを取得する。 23 : size_t GetSize() const; 24 : }; 25 : 26 : 27 : //! lib ファイルを扱うクラス。 28 : class LibFile 29 : { 30 : //=========================================================================== 31 : // 公開メソッド 32 : //=========================================================================== 33 : public: 34 : //! コンストラクタ。既存の lib ファイルを開く。 35 : explicit LibFile(const wchar_t* i_file_path); 36 : 37 : //! 正しく開けたかどうか確認する。 38 : bool IsOpened() const; 39 : 40 : //! i_index 番目(0-based)のメンバヘッダへのポインタを取得する。 41 : const ArchiveMemberHeader* GetMemberHeaderPtr(int i_index) const; 42 : //! シンボルを含むアーカイブメンバのヘッダへのポインタを取得する。 43 : const ArchiveMemberHeader* FindSymbol(const std::string& i_symbol_name); 44 : 45 : private: 46 : //! 有効なポインタかどうか調べる。 47 : bool IsValidPtr(const void* i_ptr, size_t i_size) const; 48 : 49 : //! lib ファイルかどうか調べる。 50 : static bool IsLibFile(const BYTE* i_ptr, size_t i_size); 51 : 52 : //! 第2リンカメンバからリンカメンバを除くメンバ数を取得する。 53 : static size_t GetMemberLength(const BYTE* i_second_linker_member_p); 54 : //! 第2リンカメンバからメンバへのオフセット配列へのポインタを取得する。 55 : static const DWORD* GetMemberOffsets(const BYTE* i_second_linker_member_p); 56 : //! 第2リンカメンバからシンボル数を取得する。 57 : static size_t GetSymbolLength(const BYTE* i_second_linker_member_p); 58 : //! 第2リンカメンバからシンボルのインデックスをメンバオフセットのイン 59 : // デックスに変換する配列へのポインタを取得する。 60 : static const WORD* GetOffsetIndices(const BYTE* i_second_linker_member_p); 61 : //! 第2リンカメンバからシンボル名テーブルへのポインタを取得する。 62 : static const char* GetSymbolNames(const BYTE* i_second_linker_member_p); 63 : 64 : private: 65 : bool m_is_opened; 66 : std::vector<BYTE> m_lib_data; 67 : size_t m_member_length; 68 : const DWORD* m_member_offsets_p; 69 : size_t m_symbol_length; 70 : const WORD* m_offset_index_from_symbol_index_p; 71 : std::vector<std::string> m_symbol_names; 72 : }; 73 : 74 : 75 : #include <PopPack.h> 76 : #endif
LibFile.cpp
30 : //! end_header と size に有効な値が入っているか調べる。 31 : bool ArchiveMemberHeader::IsValid() const 32 : { 33 : return memcmp(end_header, "`\n", sizeof(end_header)) == 0 && GetSize() != 0; 34 : }
LibFile.cpp
40 : //! 名前を取得する。 41 : std::string ArchiveMemberHeader::GetName() const 42 : { 43 : char* p = reinterpret_cast<char*>(_alloca(sizeof(name) + 1)); 44 : 45 : // '\0' で終端する文字列としてコピー 46 : memcpy(memset(p, '\0', sizeof(name) + 1), name, sizeof(name)); 47 : // 両端の空白を除去 48 : StrTrimA(p, " "); 49 : 50 : // '/' で始まっておらず、最後の文字が '/' の場合は '/' を除去。 51 : if( p[0] != '/' && p[strlen(p) - 1] == '/' ) 52 : p[strlen(p) - 1] = '\0'; 53 : 54 : return std::string(p); 55 : }
LibFile.cpp
61 : //! メンバのサイズを取得する。 62 : size_t ArchiveMemberHeader::GetSize() const 63 : { 64 : char* p = reinterpret_cast<char*>(_alloca(sizeof(size) + 1)); 65 : 66 : // '\0' で終端する文字列としてコピー 67 : memcpy(memset(p, '\0', sizeof(size) + 1), size, sizeof(size)); 68 : // 両端の空白を除去 69 : StrTrimA(p, " "); 70 : 71 : return atoi(p); 72 : }
LibFile.cpp
11 : //! 第2リンカメンバのメンバ数とオフセット配列。 12 : struct MemberOffsets 13 : { 14 : DWORD member_length; 15 : DWORD offsets[1]; 16 : }; 17 : 18 : //! 第2リンカメンバのシンボル数、メンバインデックスの配列。 19 : struct OffsetIndexFromSymbolIndex 20 : { 21 : DWORD symbol_length; 22 : WORD offset_index_from_symbol_index[1]; 23 : };
LibFile.cpp
303 : //! 第2リンカメンバからリンカメンバを除くメンバ数を取得する。 304 : size_t LibFile::GetMemberLength(const BYTE* i_second_linker_member_p) 305 : { 306 : return reinterpret_cast<const MemberOffsets*>(i_second_linker_member_p)->member_length; 307 : }
LibFile.cpp
313 : //! 第2リンカメンバからメンバへのオフセット配列へのポインタを取得する。 314 : const DWORD* LibFile::GetMemberOffsets(const BYTE* i_second_linker_member_p) 315 : { 316 : return reinterpret_cast<const MemberOffsets*>(i_second_linker_member_p)->offsets; 317 : }
LibFile.cpp
323 : //! 第2リンカメンバからシンボル数を取得する。 324 : size_t LibFile::GetSymbolLength(const BYTE* i_second_linker_member_p) 325 : { 326 : size_t member_length = GetMemberLength(i_second_linker_member_p); 327 : const DWORD* member_offsets = GetMemberOffsets(i_second_linker_member_p); 328 : 329 : return reinterpret_cast<const OffsetIndexFromSymbolIndex*>( 330 : &member_offsets[member_length] 331 : )->symbol_length; 332 : }
LibFile.cpp
338 : //! 第2リンカメンバからシンボルのインデックスをメンバオフセットのイン 339 : // デックスに変換する配列へのポインタを取得する。 340 : const WORD* LibFile::GetOffsetIndices(const BYTE* i_second_linker_member_p) 341 : { 342 : size_t member_length = GetMemberLength(i_second_linker_member_p); 343 : const DWORD* member_offsets = GetMemberOffsets(i_second_linker_member_p); 344 : 345 : return reinterpret_cast<const OffsetIndexFromSymbolIndex*>( 346 : &member_offsets[member_length] 347 : )->offset_index_from_symbol_index; 348 : }
LibFile.cpp
354 : //! 第2リンカメンバからシンボル名テーブルへのポインタを取得する。 355 : const char* LibFile::GetSymbolNames(const BYTE* i_second_linker_member_p) 356 : { 357 : size_t symbol_length = GetSymbolLength(i_second_linker_member_p); 358 : const WORD* offset_indices = GetOffsetIndices(i_second_linker_member_p); 359 : 360 : return reinterpret_cast<const char*>(&offset_indices[symbol_length]); 361 : }
LibFile.cpp
139 : //! コンストラクタ。lib ファイルを読み込む。 140 : LibFile::LibFile(const wchar_t* i_file_path) 141 : : m_is_opened(false), 142 : m_lib_data(), 143 : m_member_length(0), 144 : m_member_offsets_p(NULL), 145 : m_symbol_length(0), 146 : m_offset_index_from_symbol_index_p(NULL), 147 : m_symbol_names() 148 : { 149 : do 150 : { 151 : if( LoadFile(i_file_path, m_lib_data) == false ) 152 : { 153 : _RPTF1(_CRT_WARN, "%S を開けませんでした。\n", i_file_path); 154 : break; 155 : } 156 : 157 : if( IsLibFile(&m_lib_data[0], m_lib_data.size()) == false ) 158 : { 159 : _RPTF1(_CRT_WARN, "%S はライブラリファイルではありません。\n", i_file_path); 160 : break; 161 : } 162 : 163 : // 第2リンカメンバのメンバヘッダへのポインタを取得。 164 : const ArchiveMemberHeader* header_p = GetMemberHeaderPtr(1); 165 : if( header_p == NULL || header_p->GetName() != "/" ) 166 : { 167 : _RPTF0(_CRT_WARN, "第2リンカメンバを含んでいません。\n"); 168 : break; 169 : } 170 : 171 : // 第2リンカメンバをアクセスしやすい形で保持しておく。 172 : m_member_length = GetMemberLength(header_p->member_data); 173 : m_member_offsets_p = GetMemberOffsets(header_p->member_data); 174 : m_symbol_length = GetSymbolLength(header_p->member_data); 175 : m_offset_index_from_symbol_index_p = GetOffsetIndices(header_p->member_data); 176 : 177 : // シンボル名テーブルを std::vector<std::string> に変換 178 : m_symbol_names.reserve(m_symbol_length); 179 : const char* p = GetSymbolNames(header_p->member_data); 180 : for(size_t i = 0; i < m_symbol_length; i++) 181 : { 182 : m_symbol_names.push_back(p); 183 : p += strlen(p) + 1; 184 : } 185 : 186 : m_is_opened = true; 187 : } 188 : while( 0 ); 189 : }
LibFile.cpp
242 : //! シンボルを含むアーカイブメンバのヘッダへのポインタを取得する。 243 : const ArchiveMemberHeader* LibFile::FindSymbol(const std::string& i_symbol_name) 244 : { 245 : const ArchiveMemberHeader* retval = NULL; 246 : 247 : if( m_is_opened ) 248 : { 249 : std::vector<std::string>::iterator itr = std::lower_bound( 250 : m_symbol_names.begin(), 251 : m_symbol_names.end(), 252 : i_symbol_name 253 : ); 254 : if( itr != m_symbol_names.end() && *itr == i_symbol_name ) 255 : { 256 : int symbol_index = itr - m_symbol_names.begin(); 257 : int offset_index = m_offset_index_from_symbol_index_p[symbol_index] - 1; 258 : int member_offset = m_member_offsets_p[offset_index]; 259 : 260 : retval = reinterpret_cast<const ArchiveMemberHeader*>( 261 : &m_lib_data[0] + member_offset 262 : ); 263 : if( IsValidPtr(retval, sizeof(*retval)) == false 264 : || retval->IsValid() == false ) 265 : { 266 : retval = NULL; 267 : } 268 : } 269 : } 270 : 271 : return retval; 272 : }
前回は libcmt.lib から抽出した printf.obj を読み込むようになっていましたが、libcmt.lib を読み込んで printf.obj のメンバを読み込むように修正します。
hookdll.cpp 修正前
112 : // obj ファイルを開く 113 : ObjFile obj_file(L"printf.obj");
hookdll.cpp 修正後
113 : // ライブラリファイルを開く 114 : LibFile lib_file(L"libcmt.lib"); 115 : if( lib_file.IsOpened() == false ) 116 : { 117 : Error(L"libcmt.lib ファイルのオープンに失敗しました。\n"); 118 : break; 119 : } 120 : 121 : // _printf を探す。 122 : const ArchiveMemberHeader* header_p = lib_file.FindSymbol("_printf"); 123 : if( header_p == NULL ) 124 : { 125 : Error(L"_printf シンボルが見つかりませんでした。\n"); 126 : break; 127 : } 128 : 129 : // obj ファイルを開く 130 : ObjFile obj_file(header_p->member_data, header_p->GetSize());