개발/WIN32-MFC

DLL 시리즈 #7 NONAME을 이용한 CLASS 포인터 반환

-=HaeJuK=- 2024. 9. 12. 15:50
NONAME을 이용한 CLASS 포인터 반환

NONAME을 이용한 CLASS 포인터 반환

1. NONAME을 이용한 CLASS 포인터 반환 개념

NONAME 기술을 이용해 DLL에서 클래스를 반환하는 방법은 함수 이름 대신 Ordinal을 사용하여 클래스를 생성하고, 그 포인터를 반환하는 방식입니다. 이 방법은 DLL 내에서 객체의 포인터를 외부로 안전하게 전달하며, 보안을 강화할 수 있습니다. 특히, 메모리 관리 측면에서 클래스 객체의 할당과 해제를 모두 DLL에서 처리해야 안전하게 사용할 수 있습니다.

2. DLL 클래스 포인터 반환 구조

  1. DLL 내에서 클래스 객체를 생성하고, 그 포인터를 반환합니다.
  2. NONAME 방식으로 함수 이름을 숨기고, 외부에서는 Ordinal 값으로 함수에 접근하여 클래스를 생성합니다.
  3. 객체의 해제는 반드시 DLL 내에서 이루어져야 메모리 충돌을 방지할 수 있습니다.

3. 예제 코드

1) DLL 내 클래스 정의 및 포인터 반환

먼저 DLL 내부에서 클래스를 정의하고, 이 객체의 포인터를 반환하는 함수를 구현합니다.


// MyDLL.cpp
#include <windows.h>
#include <iostream>

// 간단한 클래스 A 정의
class A {
public:
    A() {
        std::cout << "A 생성자 호출됨!" << std::endl;
    }
    ~A() {
        std::cout << "A 소멸자 호출됨!" << std::endl;
    }
    void DoSomething() {
        std::cout << "A::DoSomething 호출됨!" << std::endl;
    }
};

// 클래스 포인터 반환 함수
extern "C" __declspec(dllexport) A* CreateClassA() {
    return new A();
}

// 클래스 포인터 해제 함수
extern "C" __declspec(dllexport) void DeleteClassA(A* pA) {
    delete pA;
}

    

2) DEF 파일 설정 (NONAME 방식으로 export)

DLL의 DEF 파일을 통해 함수 이름을 숨기고 Ordinal 값으로 export합니다.


LIBRARY MyDLL
EXPORTS
    CreateClassA @1 NONAME
    DeleteClassA @2 NONAME

    

3) DLL 호출 코드 (애플리케이션에서 DLL 함수 호출)

애플리케이션에서 GetProcAddress()를 사용하여 Ordinal 값으로 DLL의 함수에 접근하고, 반환된 클래스 포인터를 사용합니다.


// main.cpp
#include <windows.h>
#include <iostream>

// 클래스 A를 가리키는 포인터 타입 정의
class A;
typedef A* (*CreateClassA)();
typedef void (*DeleteClassA)(A*);

int main() {
    // DLL 로드
    HMODULE hModule = LoadLibrary("MyDLL.dll");
    if (!hModule) {
        std::cerr << "DLL 로드 실패" << std::endl;
        return 1;
    }

    // Ordinal 값으로 함수 가져오기 (CreateClassA는 @1, DeleteClassA는 @2)
    CreateClassA createClassA = (CreateClassA)GetProcAddress(hModule, MAKEINTRESOURCE(1));  // @1에 해당하는 함수
    DeleteClassA deleteClassA = (DeleteClassA)GetProcAddress(hModule, MAKEINTRESOURCE(2));  // @2에 해당하는 함수

    if (createClassA && deleteClassA) {
        A* pA = createClassA();  // 클래스 A의 인스턴스 생성
        if (pA) {
            pA->DoSomething();   // 생성된 클래스 A의 메서드 호출
            deleteClassA(pA);    // 클래스 A의 인스턴스 해제
        }
    }

    // DLL 언로드
    FreeLibrary(hModule);
    return 0;
}

    

4. 보안 및 관리 측면

  • NONAME 방식으로 export한 함수는 외부에서 함수 이름을 모르면 접근하기 어렵기 때문에 보안을 강화할 수 있습니다.
  • 메모리 해제는 DLL 내부에서 이루어져야 런타임 라이브러리 간의 충돌을 피하고, 메모리 누수를 방지할 수 있습니다.
  • 함수 이름 대신 Ordinal로 접근하기 때문에 디버깅과 유지보수가 어려울 수 있습니다. 이 기술은 신중하게 사용해야 합니다.

5. 결론

NONAME 기술을 이용해 DLL에서 클래스 포인터를 반환하는 것은 보안을 강화하면서도 안전한 메모리 관리를 가능하게 합니다. 하지만 함수 이름 대신 Ordinal 값을 사용하기 때문에 가독성과 유지보수성이 떨어질 수 있으며, 이러한 점들을 고려하여 사용해야 합니다. 이 방법은 보안을 중시하거나 DLL 크기 최적화를 추구하는 상황에서 유용할 수 있습니다.

반응형