Development/Windows

#5 AMSI - Regsvr32로 AMSI Provider 등록하기

geunyeong 2021. 11. 11. 23:15

Table of Contents

    Abstract

    Regsvr32는 Windows 레지스트리에 DLL을 등록하거나 해제하는 데 사용되는 유틸리티다. Regsvr32는 등록하려는 DLL을 로드한 후 해당 DLL의 DllRegisterServer 함수를 호출함으로써 등록하고, DllUnregisterServer를 호출함으로써 해제한다. 본 글에서는 이러한 Regsvr32를 통해 AMSI Provider를 레지스트리에 등록하는 방법을 서술한다.

    How to register using regsvr32

    Regsvr32

    Regsvr32는 Windows 레지스트리에 DLL이나 ActiveX 컨트롤 같은 OLE 컨트롤을 등록하거나 해제하는 커맨드 라인 유틸리티다. Windows XP부터 존재했으며 %systemroot%/System32 폴더에 위치한다. 64비트 Windows의 32비트 Regsvr32는 %systemroot%/SysWoW64에 존재한다. Regsvr32 사용법은 다음과 같다.

    > regsvr32 [/u] [/s] [/n] [/i[:cmdline]] <DLL Name>
    /U - 서버 등록 해제
    /s - 메시지 표시 안함
    /n - DllRegisterServer 호출 안함. 이 옵션은 /i 파라메터를 필요로 함.
    /i:<cmdline> - DllInstall을 호출하는 데 필요한 명령줄을 입력. /u 옵션과 함께 사용하면 DllUninstall이 호출됨.

    Regsvr32를 실행하면 Regsvr32는 DLL을 로드한 후 DllRegisterServer라는 함수를 호출한다. 그러므로 DllRegisterServer라는 함수를 DLL에 작성하고, 해당 함수에서는 자기 자신을 레지스트리 경로에 등록하는 작업을 하면 된다.

    Write DllRegisterServer/DllUnregisterServer

    Create a module define file

    먼저 모듈 정의 파일을 생성한다. AMSI DLL 프로젝트의 리소스 파일 필터를 우클릭하고 새 항목을 추가한다.

     

    새 항목 추가

     

    좌측 메뉴에서 Visual C++/코드로 가면 모듈 정의 파일( .def) 항목이 있다. 선택 후 이름을 적절히 지어주고 추가를 누른다.

     

    모듈 정의 파일

     

    모듈 정의 파일에 아래와 같이 내용을 작성한다.

     

    모듈 정의 파일 내용

    Write DllRegisterServer/DllUnregisterServer

    그 후 cpp 파일을 만들고 아래와 같이 내용을 작성하면 된다. 코드 내용을 보면 알 수 있듯이 DllRegisterServer에서는 자기 자신의 경로를 AMSI 레지스트리에 등록하는 작업을 하고, DllUnregisterServer에서는 AMSI 레지스트리에서 자기 자신을 지우는 일을 한다.

    단 아래 코드는 32비트 AMSI DLL의 경우 WoW64 레지스트리 경로에만 자기 자신을 등록하기 때문에 64비트 AMSI Client들은 AMSI 기능을 이용하지 못하고, 64비트 AMSI DLL의 경우에는 WoW64 레지스트리 경로에 자기 자신을 등록하지 않기 때문에 32비트 AMSI Client들은 AMSI 기능을 이용하지 못한다. 그러므로 WoW64 환경에서 32/64를 모두 지원해야 한다면 그에 따른 레지스트리 등록 코드를 작성해야 한다.

    ////////////////////////////////////////////////////
    // 
    // register.cpp of amsiprovider
    // 
    // Code from https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/AmsiProvider/AmsiProvider.cpp
    // 
    ////////////////////////////////////////////////////
    
    #include <strsafe.h>
    
    #include "CAmsiProvider.h"
    #include "register.h"
    
    using namespace Microsoft::WRL;
    
    #pragma region COM server boilerplate
    
    STDAPI DllCanUnloadNow(void) {
    	return Module<InProc>::GetModule().Terminate() ? S_OK : S_FALSE;
    }
    
    STDAPI DllGetClassObject(
    	_In_ REFCLSID ref_clsid,
    	_In_ REFIID ref_iid,
    	_Outptr_ LPVOID FAR* cls_obj
    )
    {
    	return Module<InProc>::GetModule().GetClassObject(ref_clsid, ref_iid, cls_obj);
    }
    
    #pragma endregion
    
    CoCreatableClass(CAmsiProvider);
    
    #pragma region Install / uninstall
    
    HRESULT SetKeyStringValue(
    	_In_     HKEY   key, 
        _In_opt_ PCWSTR subkey, 
        _In_opt_ PCWSTR value, 
        _In_     PCWSTR data
    )
    {
        LONG status = RegSetKeyValue(key, 
                                     subkey, 
                                     value, 
                                     REG_SZ, 
                                     data, 
                                     (wcslen(data) + 1) * sizeof(wchar_t));
        return HRESULT_FROM_WIN32(status);
    }
    
    STDAPI DllRegisterServer()
    {
        wchar_t module_path[MAX_PATH];
        if (GetModuleFileName(g_current_module, 
                              module_path, 
                              ARRAYSIZE(module_path)) >= ARRAYSIZE(module_path)
        )
        {
            return E_UNEXPECTED;
        }
    
        wchar_t clsid[40];
        if (StringFromGUID2(__uuidof(CAmsiProvider), clsid, ARRAYSIZE(clsid)) == 0)
        {
            return E_UNEXPECTED;
        }
    
        //
        // 레지스트리에 AMSI 구성 정보 등록
        //
        wchar_t registry_subkey[200];
        HRESULT hr = StringCchPrintf(registry_subkey, 
                                     ARRAYSIZE(registry_subkey), 
                                     L"Software\\Classes\\CLSID\\%ls", 
                                     clsid);
        if (FAILED(hr)) return hr;
    
        hr = SetKeyStringValue(HKEY_LOCAL_MACHINE, 
                               registry_subkey, 
                               nullptr, 
                               L"SampleAmsiProvider");
        if (FAILED(hr)) return hr;
    
        hr = StringCchPrintf(registry_subkey, 
                             ARRAYSIZE(registry_subkey), 
                             L"Software\\Classes\\CLSID\\%ls\\InProcServer32", 
                             clsid);
        if (FAILED(hr)) return hr;
    
        hr = SetKeyStringValue(HKEY_LOCAL_MACHINE, 
                               registry_subkey, 
                               nullptr, 
                               module_path);
        if (FAILED(hr)) return hr;
    
        hr = SetKeyStringValue(HKEY_LOCAL_MACHINE, 
                               registry_subkey, 
                               L"ThreadingModel", 
                               L"Both");
        if (FAILED(hr)) return hr;
    
        //
        // 레지스트리에 AMSI 프로필 정보 등록
        //
        hr = StringCchPrintf(registry_subkey, 
                             ARRAYSIZE(registry_subkey), 
                             L"Software\\Microsoft\\AMSI\\Providers\\%ls", 
                             clsid);
        if (FAILED(hr)) return hr;
    
        hr = SetKeyStringValue(HKEY_LOCAL_MACHINE, 
                               registry_subkey, 
                               nullptr, 
                               L"AMSI Provider Example");
        if (FAILED(hr)) return hr;
    
        return S_OK;
    }
    
    STDAPI DllUnregisterServer()
    {
        wchar_t clsid[40];
        if (StringFromGUID2(__uuidof(CAmsiProvider), clsid, ARRAYSIZE(clsid)) == 0)
        {
            return E_UNEXPECTED;
        }
    
        wchar_t registry_subkey[200];
        HRESULT hr = StringCchPrintf(registry_subkey, 
                                     ARRAYSIZE(registry_subkey), 
                                     L"Software\\Microsoft\\AMSI\\Providers\\%ls", 
                                     clsid);
        if (FAILED(hr)) return hr;
    
        LONG status = RegDeleteTree(HKEY_LOCAL_MACHINE, registry_subkey);
        if (status != NO_ERROR && status != ERROR_PATH_NOT_FOUND) 
            return HRESULT_FROM_WIN32(status);
    
        hr = StringCchPrintf(registry_subkey, 
                             ARRAYSIZE(registry_subkey), 
                             L"Software\\Classes\\CLSID\\%ls", 
                             clsid);
        if (FAILED(hr)) return hr;
    
        status = RegDeleteTree(HKEY_LOCAL_MACHINE, registry_subkey);
        if (status != NO_ERROR && status != ERROR_PATH_NOT_FOUND) 
            return HRESULT_FROM_WIN32(status);
    
        return S_OK;
    }
    
    #pragma endregion

    Run regsvr32

    빌드 하고 regsvr32을 실행한다. 별다른 옵션 없이 DLL 이름을 넘겨주면 DllRegisterServer를 호출하며 DLL 설치가 진행된다.

    > regsvr32 <DLL Name>

     

    regsvr32 실행

     

    레지스트리를 확인해보면 AMSI DLL이 잘 등록된 것을 볼 수 있다.

     

    AMSI Provider 프로필 레지스트리
    AMSI 구성 정보 레지스트리

     

    반대로 등록 해제하고 싶다면 regsvr32에 /u 옵션을 붙이면 된다.

    > regsvr32 /u <DLL Name>

     

    regsvr32 /u 실행
    AMSI Provider 프로필 레지스트리에서 사라진 모습
    AMSI 구성 정보 레지스트리가 사라진 모습

    Close

    이상으로 총 5편에 걸쳐 AMSI를 만드는 법에 대해 알아봤다. AMSI는 현재 대부분의 AV 프로그램에서 사용하고 있는 악성 스크립트 스캔 기능인 만큼 보안 프로그램을 제작하는 사람이거나 샌드박스를 구성하는데 관심있는 사람이라면 공부해볼만한 내용이다.

    Github

    AMSI Example 코드 깃허브

    https://github.com/geun-yeong/amsi-example/tree/main

    'Development > Windows' 카테고리의 다른 글

    #1 MiniFilter - 개요  (0) 2021.11.20
    Windows I/O Manager  (0) 2021.11.19
    #4 AMSI - AMSI Provider 등록하기  (0) 2021.11.09
    #3 AMSI - AMSI Provider 작성하기  (0) 2021.11.08
    #2 AMSI - AMSI 스캔 기능 사용하기  (0) 2021.11.06