LoginSignup
1
2

More than 5 years have passed since last update.

PEファイルのバイナリ構造(8) 遅延インポート関数の列挙

Posted at

遅延インポート関数

前回はインポート関数の列挙を解説しました。

このインポート関数は「静的」にインポートされる関数の一覧であり、
その関数を持つモジュールは起動する際同時に読み込まれます。

これに対して「動的」にインポートされる関数があり、
「遅延インポート」と呼ばれています。

IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT

セクションに関してはもうお馴染みの手順で、
配列の添え字だけ「IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT」として確認します。

IMAGE_DATA_DIRECTORY& tData = tNT.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT];
DWORD nOffset = tData.VirtualAddress;

ImgDelayDescr

インポートの時と同じ手順でセクションまで確認後、ImgDelayDescrのポインタへとキャストします。

ImpDelayDescrを使う際には「delayimp.h」をインクルードしておきます。

ImgDelayDescr* pDelay = (ImgDelayDescr*)&(pFile[nOffset - delta]);

ImgDelayDescr は以下の様に定義されています。

typedef DWORD RVA;
typedef struct ImgDelayDescr {  
    DWORD        grAttrs;        // attributes  
    RVA          rvaDLLName;     // RVA to dll name  
    RVA          rvaHmod;        // RVA of module handle  
    RVA          rvaIAT;         // RVA of the IAT  
    RVA          rvaINT;         // RVA of the INT  
    RVA          rvaBoundIAT;    // RVA of the optional bound IAT  
    RVA          rvaUnloadIAT;   // RVA of optional copy of original IAT  
    DWORD        dwTimeStamp;    // 0 if not bound,  
                                 // O.W. date/time stamp of DLL bound to (Old BIND)  
} ImgDelayDescr, * PImgDelayDescr;  

後は構造体の配列として定義されていますので、
rvaDLLNameが0になるまでループします。

rvaIATとrvaINTに関しては、インポートの時に解説した物と同じになりますので、
これを踏まえて列挙すると以下の様な形になります。

for (int i = 0; pDelay[i].rvaDLLName != 0; ++i)
{
    ImgDelayDescr& tDelay = pDelay[i];
    LPCSTR szOrg = (char*)&(pFile[tDelay.rvaDLLName - delta]);
    _tprintf(TEXT("\n[%s]"), (LPCTSTR)CA2T(szOrg));
    if (b64 == false)
    {
        IMAGE_THUNK_DATA32* pINT = (IMAGE_THUNK_DATA32*)&(pFile[tDelay.rvaINT - delta]);
        IMAGE_THUNK_DATA32* pIAT = (IMAGE_THUNK_DATA32*)&(pFile[tDelay.rvaIAT - delta]);
        for (int j = 0; pINT[j].u1.AddressOfData != 0 && pIAT[j].u1.Function != 0; ++j)
        {
            if (IMAGE_SNAP_BY_ORDINAL32(pINT[j].u1.Ordinal))
            {
                _tprintf(TEXT("\n\t(NONAME)[%d]"), (int)IMAGE_ORDINAL32(pINT[j].u1.Ordinal));
            }
            else
            {
                IMAGE_IMPORT_BY_NAME& tName = (*(IMAGE_IMPORT_BY_NAME*)&(pFile[pINT[j].u1.AddressOfData - delta]));
                _tprintf(TEXT("\n\t%s[%d]"), (LPCTSTR)CA2T(tName.Name), tName.Hint);
            }
        }
    }
    else
    {
        IMAGE_THUNK_DATA64* pINT = (IMAGE_THUNK_DATA64*)&(pFile[tDelay.rvaINT - delta]);
        IMAGE_THUNK_DATA64* pIAT = (IMAGE_THUNK_DATA64*)&(pFile[tDelay.rvaIAT - delta]);
        for (int j = 0; pINT[j].u1.AddressOfData != 0 && pIAT[j].u1.Function != 0; ++j)
        {
            if (IMAGE_SNAP_BY_ORDINAL64(pINT[j].u1.Ordinal))
            {
                _tprintf(TEXT("\n\t(NONAME)[%d]"), (int)IMAGE_ORDINAL64(pINT[j].u1.Ordinal));
            }
            else
            {
                IMAGE_IMPORT_BY_NAME& tName = (*(IMAGE_IMPORT_BY_NAME*)&(pFile[pINT[j].u1.AddressOfData - delta]));
                _tprintf(TEXT("\n\t%s[%d]"), (LPCTSTR)CA2T(tName.Name), tName.Hint);
            }
        }
    }
}

問題点

基本上記のコードで正常に動作するのですが、一部デバッグモジュールを通すと
モジュール名(rvaDLLName)が正常な文字列として取得できません。

こちらに関しては現在確認中です。
#やり方が悪いのか、理解が足りてないのか…

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2