개발/WIN32-MFC

DLL 시리즈 #5 DLL EXPORT 취약점(후킹)

-=HaeJuK=- 2024. 9. 12. 15:38
DLL EXPORT 문제점 (후킹에 대한 위험)

DLL EXPORT의 문제점 (후킹에 대한 위험)

1. 후킹(Hooking)이란?

후킹은 운영 체제나 프로그램의 API 호출 흐름을 가로채는 기술로, 특정 함수 호출이나 메시지 처리를 가로채어 조작하거나 다른 기능을 수행하도록 할 수 있습니다. 후킹은 프로그램의 정상적인 흐름을 변경해 악성 코드를 실행하거나 데이터를 탈취하는 등 여러 가지 보안 위협을 일으킬 수 있습니다.

  • API 후킹: 프로그램이 호출하는 API 주소를 변경해 공격자가 원하는 코드가 실행되도록 하는 방법.
  • DLL 후킹: DLL의 함수 호출을 가로채는 방식으로, 프로그램이 DLL 내 특정 함수를 호출할 때 이를 가로채 공격자의 코드를 실행하도록 유도.

2. DLL EXPORT와 후킹의 관계

DLL에서 __declspec(dllexport)로 외부에 노출된 함수는 프로그램이 사용하기 쉽도록 만들어져 있지만, 후킹 공격의 대상이 될 수 있습니다. 특히, Import Address Table (IAT) 후킹Inline 후킹이 주요한 공격 방법입니다.

IAT 후킹 예제

아래는 IAT 후킹을 통해 DLL의 원래 함수 대신 공격자가 정의한 악성 함수를 실행하도록 만드는 간단한 예제입니다.


// 원래 DLL 함수 (MyDLL.cpp)
extern "C" __declspec(dllexport) int Add(int a, int b) {
    return a + b;
}

// 프로그램에서 사용하는 코드 (main.cpp)
#include <iostream>
#include <windows.h>

typedef int (*AddFunc)(int, int);

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

    AddFunc Add = (AddFunc)GetProcAddress(hDll, "Add");
    if (Add == NULL) {
        std::cerr << "함수 주소 로드 실패" << std::endl;
        FreeLibrary(hDll);
        return 1;
    }

    int result = Add(2, 3);  // 정상적인 Add 함수 호출
    std::cout << "Add(2, 3) = " << result << std::endl;

    FreeLibrary(hDll);
    return 0;
}

    

악성 코드로 IAT 후킹

IAT 후킹은 Import Address Table을 조작하여, 프로그램이 정상적으로 호출하는 DLL 함수를 가로채는 방식입니다. 아래는 Add 함수를 후킹해, 원래 함수 대신 악성 코드를 실행하게 만드는 예시입니다.


// 악성 후킹 함수 (MaliciousHook.cpp)
#include <iostream>

int HookedAdd(int a, int b) {
    std::cout << "악성 코드 실행됨!" << std::endl;
    return 0;  // 실제 덧셈 대신 잘못된 값을 반환
}

// IAT 후킹 (IATHook.cpp)
#include <windows.h>
#include <iostream>

void HookIAT() {
    HMODULE hModule = GetModuleHandle(NULL);
    PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER)hModule;
    PIMAGE_NT_HEADERS pNTHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDOSHeader + pDOSHeader->e_lfanew);

    DWORD_PTR importTableRVA = pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
    PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)pDOSHeader + importTableRVA);

    while (pImportDescriptor->Name != NULL) {
        const char* dllName = (const char*)((DWORD_PTR)pDOSHeader + pImportDescriptor->Name);

        if (_stricmp(dllName, "MyDLL.dll") == 0) {  // MyDLL을 찾음
            PIMAGE_THUNK_DATA pThunkData = (PIMAGE_THUNK_DATA)((DWORD_PTR)pDOSHeader + pImportDescriptor->FirstThunk);

            while (pThunkData->u1.Function != NULL) {
                FARPROC* ppfn = (FARPROC*)&pThunkData->u1.Function;
                if ((DWORD_PTR)*ppfn == (DWORD_PTR)GetProcAddress(GetModuleHandle("MyDLL.dll"), "Add")) {
                    DWORD oldProtect;
                    VirtualProtect(ppfn, sizeof(DWORD_PTR), PAGE_EXECUTE_READWRITE, &oldProtect);
                    *ppfn = (FARPROC)HookedAdd;  // Add 함수를 HookedAdd로 변경
                    VirtualProtect(ppfn, sizeof(DWORD_PTR), oldProtect, &oldProtect);
                    std::cout << "IAT 후킹 성공!" << std::endl;
                    return;
                }
                pThunkData++;
            }
        }
        pImportDescriptor++;
    }
}

int main() {
    HookIAT();  // IAT 후킹 실행
    // 이제 프로그램이 Add를 호출하면 HookedAdd가 호출됨
}

    

후킹 방지 방법

  • 디지털 서명: DLL에 디지털 서명을 적용하여, 신뢰할 수 있는 출처에서 배포된 DLL만 로드되도록 해야 합니다.
  • ASLR(Address Space Layout Randomization): ASLR을 사용하여 메모리 주소를 무작위화해, 공격자가 DLL 함수 주소를 예측하지 못하게 할 수 있습니다.
  • Safe DLL Search Mode: Safe DLL Search Mode를 활성화하여 시스템 디렉터리에서 먼저 DLL을 찾도록 설정하면 악성 DLL 로드를 방지할 수 있습니다.

3. 결론

DLL EXPORT 함수는 프로그램 간의 상호작용을 용이하게 하지만, 후킹과 같은 보안 취약점에 노출될 수 있습니다. 이를 방지하기 위해서는 디지털 서명, ASLR, Safe DLL Search Mode 등 다양한 보안 기법을 적용해야 합니다.

반응형