리소스 풀어 헤치기

@codemaru · July 31, 2011 · 6 min read

디버그뷰나 프로세스 익스플로러같은 프로그램을 보면 실행 파일 하나로 구성되어 있는데 프로그램을 실행하면 드라이버를 로딩하는 것을 볼 수 있습니다. 어떻게 하는 걸까요? 답은 간단한데요. 해당 드라이버 파일을 리소스에 포함시켜 놓고 런타임에 풀어서 사용하는 방식입니다. 리소스에 추가하는 방법은 간단합니다. Visual Studio 리소스 뷰에서 오른쪽 클릭하시고 임포트 하시면 됩니다. sys 파일 같은건 안보이시죠. *.*로 필터링한다음 추가하시면 됩니다. 그러면 어떤 타입을 추가할지 물어볼텐데요. 아무렇게나 이름을 지어 주시면 됩니다. Binary 이런 식으로요. 자 그렇다면 이제 이렇게 추가된 리소스 파일을 런타임에 어떻게 조작하는지를 알아보도록 합시다.

조작하는 API는 소 심플합니다. FindResource, LoadResource, SizeOfResource, LockResource라는 API가 사용됩니다. 각각의 함수 사용 방법을 알아보도록 합시다.

HRSRC FindResource(HMODULE hModule, LPCTSTR lpName, LPCTSTR lpType);

FindResource 함수입니다. 이 함수는 우리가 분리해 내려고 하는 리소스를 찾는데 사용됩니다. hModule에는 리소스를 찾을 DLL의 모듈 핸들을 넣어주면 됩니다. NULL을 전달하면 현재 프로세스의 실행 파일 모듈에서 찾습니다. lpName에는 리소스 이름을 전달합니다. MAKEINTRESOURCE를 사용해서 리소스 ID를 문자열로 변환한 것을 대입해 주면 됩니다. 리소스 ID가 IDR_BINARY1이라면 MAKEINTRESOURCE(IDR_BINARY1)을 전달하면 되겠죠. lpType에는 리소스 타입을 넣어줍니다. 리소스 타입이 Binary라면 “Binary”를 전달하면 됩니다. 이렇게 넣어주면 리소스를 찾아서 해당 리소스의 핸들을 넘겨줍니다. 실패하면 NULL이 리턴되겠죠.

DWORD SizeofResource(HMODULE hModule, HRSRC hResInfo);

러소스의 크기를 구할 때 사용하는 함수입니다. hModule에는 모듈 핸들을 hResInfo에는 FindResource를 통해서 찾은 리소스 핸들을 넣어줍니다. 성공한 경우에는 리소스 크기를, 실패한 경우에는 0을 리턴합니다.

HGLOBAL LoadResource(HMODULE hModule, HRSRC hResInfo);

리소스를 로드하는 함수입니다. hModule에는 모듈 핸들을 hResInfo에는 FindResource로 찾은 리소스 핸들을 전달하면 되겠습니다. 해당 리소스의 포인터가 리턴됩니다.

LPVOID LockResource(HGLOBAL hResData);

마지막 함수입니다. LoadResource 한 메모리의 실제 포인터를 반환합니다. hResData에는 LoadResource에서 반환한 포인터를 넣어줍니다. 그럼 실제 포인터가 리턴됩니다.

종합해서 함수 하나로 만들어 봅시다. GetResourceInfo라는 함수입니다. module에서 type, name의 리소스를 찾아서 해당 리소스의 포인터를 data에 크기를 rsize에 있는지 없는지를 리턴 값으로 리턴해 주는 함수입니다.

BOOL WINAPI GetResourceInfo(HMODULE module
                            , LPCTSTR type
                            , LPCTSTR name
                            , PVOID *data
                            , SIZE_T *rsize)
{
    DWORD size;
    HGLOBAL mem;
    PVOID ptr;
    HRSRC src;

    src = FindResourceW(module, name, type);
    if(!src)
        return FALSE;

    size = SizeofResource(module, src);
    if(!size)
        return FALSE;

    mem = LoadResource(module, src);
    if(!mem)
        return FALSE;

    ptr = LockResource(mem);
    if(!ptr)
        return FALSE;

    __try
    {
        if(data)
            *data = ptr;

        if(rsize)
            *rsize = size;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        return FALSE;
    }

    return TRUE;
}

여기까지만 설명하고 마칠까 하는데 아마 똑똑한 분들이라면 먼가 찜찜한 기분이 드실 거예요. 화장실가서 볼 일 보고 뭔가 정리하지 않고 나온 느낌이겠죠. 실컷 로딩하고 락하고 했는데 그것들을 해제하는 작업에 대해서는 하나도 설명하지 않았으니 말입니다. UnlockResource, FreeResource와 같은 함수들이 있긴 한데, 그것들은 사용하지 않아도 됩니다. 왜냐하면 여기서 사용하는 리소스 핸들을 구하고 하는 함수들은 로드된 모듈에서 포인터를 참조해 오는 일이거든요. 여러분이 획득한 모든 포인터 내지는 리소스는 hModule이 FreeLibrary 되는 시점에 같이 사라집니다. 따라서 별도로 해제할 필요가 없는 셈이죠. 참고로 그래도 나는 해제하겠다고 UnlockResource, FreeResource를 호출한들 실제 해당 함수에서 하는 일은 return 밖에는 없답니다.

@codemaru
돌아보니 좋은 날도 있었고, 나쁜 날도 있었다. 그런 나의 모든 소소한 일상과 배움을 기록한다. 여기에 기록된 모든 내용은 한 개인의 관점이고 의견이다. 내가 속한 조직과는 1도 상관이 없다.
(C) 2001 YoungJin Shin, 0일째 운영 중