DLL+EXEからDLLの使い方を解析する[前編]

はじめに

イメージ・マジックの安藤です。
先日から季節が急速に真冬の様相を呈していますね。風邪やインフルエンザが流行りだす時期なので皆様におかれましても十分ご用心ください。ちなみに、私は去年の大晦日から今年の三が日にかけてインフルエンザを発症するという何とも言えない新年スタートを切っているので気を付けます。
今回は題名の通りDLLの解析をしたという話です。

DLLとは

多分この説明要らないんじゃないかなぁと思いつつ書いておきます。
DLLとは、プログラムのある機能を再利用できるようにプログラム本体と切り分けたライブラリのことを指します。Windowsならば.dll、Linux系では.soという拡張子が使われます。
なお、今回の話はWindowsの.dllで、一般的な、Cで記述されたDLLという話です。

コンパイルを通すのに必要なもの

DLLの利用方法には暗黙的リンクと明示的リンクが存在します。今回は完全に個人的にDLLを使いたいだけなので暗黙的リンクでコンパイルするものとします。
以下が必要になります。

  • インポートライブラリ(.lib)
  • ヘッダファイル(.h)
更にメタ的に言うとこうなります。

  • エクスポートされている関数名
  • 関数の引数と型

初期状態

DLLがどんな状態で使われていたのかを少しだけ書いておきます。
DLLとDLLの機能をラップしたEXEがあり、EXEに引数を渡して実行してあげるとDLLの機能が使えるという構成になっていました。最終的にはこのEXEを介さずにDLLの機能を呼び出すのがゴールです。

インポートライブラリを用意する

第一の関門として、インポートライブラリが存在していません。ただ、これに関してはVisual Studioを使えばDLLから同等のものを用意することができます。
まず、以下のコマンドを実行します。

dumpbin /exports hoge.dll


C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools>dumpbin /exports VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64\c1.dll
Microsoft (R) COFF/PE Dumper Version 14.16.27024.1
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64\c1.dll

File Type: DLL

  Section contains the following exports for c1.dll

    00000000 characteristics
    FFFFFFFF time date stamp
        0.00 version
           1 ordinal base
           3 number of functions
           3 number of names

    ordinal hint RVA      name

          1    0 000DDDB0 AbortCompilerPass
          2    1 000DDE50 CloseTypeServerPDB
          3    2 000055FC InvokeCompilerPassW

  Summary

       32000 .data
       17000 .pdata
       F3000 .rdata
        7000 .reloc
        1000 .rsrc
      138000 .text

このコマンドでDLLからエクスポートされている関数の一覧がテーブル上に表示されます。MSVCに存在する適当なDLLを表示してみます。 ここでordinal~nameの列に注目すると関数名が分かります。この関数名を以下のように羅列し、拡張子を.defで保存します。これはモジュール定義ファイルと呼ばれるものです。

EXPORTS
AbortCompilerPass
CloseTypeServerPDB
InvokeCompilerPassW

作成したモジュール定義ファイルは以下のコマンドでインポートライブラリに変換できます。なお、64bitアーキテクチャを想定しています。

lib /DEF:hoge.def /MACHINE:X64 /out:hoge.lib

ヘッダファイルを用意する

ヘッダファイルでは以下のように書くことでDLLの関数を宣言できます。

#define DLLImport __declspec(dllimport)

#ifdef __cplusplus
extern "C"{
#endif

    DLLImport void funcName1();
    DLLImport int funcName2(int arg);

#ifdef __cplusplus
}
#endif


以上を見るとわかりますが、関数の戻り値と引数がわからないとDLLの機能は使えないことが分かります。

以降、今回のケースで関数定義をどう解析したかは次回の私の番で書いていきたいと思います。