사용자 도구

사이트 도구


kb:dll

Dynamic Link Library

윈도우즈계의 동적 라이브러리

목록

Load Library 편하게 쓰기

from DLLs the Dynamic Way

struct PDLL
{
    HMODULE hDLL;
 
    PDLL(LPCTSTR filename)
    {
        memset(this, 0, sizeof(PDLL));
        hDLL = LoadLibrary(filename);
    }
 
    ~PDLL()
    {
        if (hDLL != NULL)
            FreeLibrary(hDLL);
    }
};
 
#define DECLARE_DLL_CLASS(CLASSNAME, DLLNAME) \
public: \
CLASSNAME() : PDLL(DLLNAME) {} \
~CLASSNAME() {}
 
#define DECLARE_FUNCTION0(RETVAL, FNAME) \
typedef RETVAL (CALLBACK* PFN_##FNAME)(VOID); \
RETVAL FNAME(VOID) \
{ \
    if (hDLL == NULL) return (RETVAL)0; \
    static PFN_##FNAME s_pfn##FNAME = NULL; \
    if (s_pfn##FNAME == NULL) \
    s_pfn##FNAME = (PFN_##FNAME)GetProcAddress(hDLL, #FNAME); \
    return (s_pfn##FNAME != NULL) ?\
        (s_pfn##FNAME)() : (RETVAL)0; \
}
 
#define DECLARE_FUNCTION1(RETVAL, FNAME, P1) \
typedef RETVAL (CALLBACK* PFN_##FNAME)(P1); \
RETVAL FNAME(P1 _1) \
{ \
    if (hDLL == NULL) return (RETVAL)0; \
    static PFN_##FNAME s_pfn##FNAME = NULL; \
    if (s_pfn##FNAME == NULL) \
    s_pfn##FNAME = (PFN_##FNAME)GetProcAddress(hDLL, #FNAME); \
    return (s_pfn##FNAME != NULL) ?\
        s_pfn##FNAME(_1) : (RETVAL)0; \
}
 
#define DECLARE_FUNCTION2(RETVAL, FNAME, P1, P2) \
typedef RETVAL (CALLBACK* PFN_##FNAME)(P1, P2); \
RETVAL FNAME(P1 _1, P2 _2) \
{ \
    if (hDLL == NULL) return (RETVAL)0; \
    static PFN_##FNAME s_pfn##FNAME = NULL; \
    if (s_pfn##FNAME == NULL) \
    s_pfn##FNAME = (PFN_##FNAME)GetProcAddress(hDLL, #FNAME); \
    return (s_pfn##FNAME != NULL) ?\
        s_pfn##FNAME(_1, _2) : (RETVAL)0; \
}
 
#define DECLARE_FUNCTION3(RETVAL, FNAME, P1, P2, P3) \
typedef RETVAL (CALLBACK* PFN_##FNAME)(P1, P2, P3); \
RETVAL FNAME(P1 _1, P2 _2, P3 _3) \
{ \
    if (hDLL == NULL) return (RETVAL)0; \
    static PFN_##FNAME s_pfn##FNAME = NULL; \
    if (s_pfn##FNAME == NULL) \
    s_pfn##FNAME = (PFN_##FNAME)GetProcAddress(hDLL, #FNAME); \
    return (s_pfn##FNAME != NULL) ?\
        s_pfn##FNAME(_1, _2, _3) : (RETVAL)0; \
}
...
#!cpp
#include "PDLL.h"

class DbgHelp : public PDLL
{
    DECLARE_DLL_CLASS(DbgHelp, "dbghelp.dll")
    DECLARE_FUNCTION0(DWORD, SymGetOptions);
    DECLARE_FUNCTION3(BOOL, SymInitialize, HANDLE, PSTR, BOOL);
};

class Toolhelp32 : public PDLL
{
    DECLARE_DLL_CLASS(Toolhelp32, "tlhelp32.dll");
    DECLARE_FUNCTION2(BOOL, CreateToolhelp32Snapshot, DWORD, DWORD);
};

void DllTest()
{
    DbgHelp dbghelp;
    Toolhelp32 toolhelp;
    DWORD o = dbghelp.SymGetOptions();
    BOOL r1 = dbghelp.SymInitialize(NULL, NULL, FALSE);
    BOOL r2 = toolhelp.CreateToolhelp32Snapshot(0, 0);
}

별로 편하지도 않은가…?

DLL Rebasing

DLL Rebasing이란 DLL이 최초에 로드되는 기본 주소를 오버라이드해주는 일을 말한다.

이런 일이 필요한 이유는 대부분의 DLL이 같은 기본 주소(0x10000000)를 가지는 데서 기인한다. DLL을 로드했을 때, 그 DLL이 들어가야할 주소를 다른 DLL이 이미 차지하고 있는 경우, 커널이 다른 주소를 알아서 찾아주게 되는데, 이 작업이 꽤나 오버헤드를 차지한다는 것이다.

그렇다면 어떤 기준으로 DLL이 들어갈 주소를 정해줄 것인가? 업계 표준(-_-)적인 아이디어는 DLL 파일의 알파벳 명칭을 이용하는 것이다.

DLL 이름 알파벳 첫글자 시작 주소
A-C 0x60000000
D-F 0x61000000
G-I 0x62000000
J-L 0x63000000
M-O 0x64000000
P-R 0x65000000
S-U 0x66000000
V-X 0x67000000
Y-Z 0x68000000

위와 같은 식으로 기본 주소를 정하고, 해당하는 DLL들을 하나하나 매치시켜나가는 것이다. 예를 들어 A1.DLL D1.DLL, D2.DLL이 있다면…

DLL 이름 주소
A1.DLL 0x60000000
D1.DLL 0x61000000
D2.DLL 0x61100000

이런 식으로 주소를 정해준다. 주소는 프로젝트 세팅의 링커탭에 가면 간단히 지정해줄 수 있다. 자신이 만든 DLL이 아니라면, 비쥬얼 스튜디오나 플랫폼 SDK에 따라오는 Rebase.exe 유틸리티를 이용하기 바란다.

2003 버전의 경우는 <code>…\Microsoft Visual Studio .NET 2003\Common7\Tools\Bin</code> 디렉토리에 있다.

이 유틸리티를 사용하는 기본적인 방법은 다음과 같다.

rebase /b 0x60000000 A1.DLL 
rebase /b 0x61000000 D1.DLL 
rebase /b 0x61000000 D1.DLL D2.DLL

마지막 라인 같은 경우, 여러 개의 DLL에다 한꺼번에 주소를 주기 위한 기능인데, 주소가 너무 가까워질 우려가 있으므로 귀찮더라도 수동으로 하나씩 하기 바란다. 좀 더 추가적인 기능들은 자체 매뉴얼을 참고하기 바란다.

Delay Loading DLL

프로그램을 실행할 때, 윈도우즈는 실행 파일의 임포트 영역에 있는 DLL들을 전부 로드하게 된다. 문제는 이 DLL 파일이 존재하지 않는 경우, 무슨무슨 DLL이 없다고 프로그램이 실행되지도 않는다는 점이다. 해당하는 DLL이 없어도 일단 프로그램을 실행하고 난 다음, 어떤 처리를 하고 싶을 때 쓸 수 있는 것이 DllDelayLoading이라는 개념이다.

링크

kb/dll.txt · 마지막으로 수정됨: 2014/11/10 20:54 저자 excel96