Microsoft MVP성태의 닷넷 이야기
개발 환경 구성: 77. Appinit_Dlls로 구현한 환경 변수 설정 DLL [링크 복사], [링크+제목 복사],
조회: 28838
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 5개 있습니다.)
Appinit_Dlls로 구현한 환경 변수 설정 DLL


환경 변수(Environment variables) 값으로 프로그램의 동작을 제어하는 경우가 종종 있는데요.

윈도우 환경에서는 기본적으로 "사용자 계정 단위"와 "시스템 전역 단위"로 환경 변수를 설정해주는 UI를 제공합니다.

appinit_dll_env_set_1.png

문제는, "사용자 계정 단위"는 상황에 따라서 고려할 사항들이 많아서 때론 부적합한 면이 있고, "시스템 전역 단위"는 자칫 원하는 응용 프로그램을 제외한 다른 프로그램에도 영향을 주기 때문에 문제가 됩니다. 영향을 주는 정도가 아니고, dll 로딩에 연관된 동작이라면 해당 모듈이 잠기는 상황까지 발생하기 때문에 차후의 업그레이드나 설치 제거를 위해서 컴퓨터를 재부팅해야 하는 점이 다소 심각하긴 합니다.

즉, 특정 응용 프로그램에만 "환경 변수"를 적용하게 하고 싶은데 - 예를 들어, COM+ dllhost.exe에만 설정하고 싶다거나 - 기존의 방법으로는 적당한 것이 없다는 것!




그래서, 고민을 한 결과 Appinit_Dlls 레지스트리를 이용하여 설정을 이용해 보는 것이 어떨까 하는 생각이 들었습니다. 코드도 무지 간단합니다.

BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    wchar_t fileName[MAX_PATH];
    if (GetModuleFileName(NULL, fileName, MAX_PATH) == 0)  // Kernel32.dll
    {
        return FALSE;
    }

    wstring fileNamePart = GetFileNamePart(fileName); // 전체 파일 경로에서 파일명만을 소문자로 반환 (코드 생략)
    if (wcscmp(fileNamePart.c_str(), L"w3wp.exe") != 0)
    {
        return FALSE;
    }

    SetEnvironmentVariable(L"TEST", "1");  // Kernel32.dll

    return FALSE;
}

모든 메서드가 Kernel32.dll에서 제공되므로 부작용도 없고, 결정적으로 마지막에 무조건 "FALSE"를 반환하기 때문에 말 그대로 환경 변수만 정확하게 설정하고 해당 exe 프로세스 공간에서 제거되어 모듈이 잠기는 현상까지도 걱정하지 않아도 됩니다. 간단하죠! ^^ "AppInit"이라는 목적에 정확하게 부합하는 DLL입니다.




위의 구현이 한 가지 아쉬운 점이라면, 코드는 간단한 반면 실제 적용하는 과정이 다소 복잡하다는 것입니다. Appinit_Dlls 레지스트리에 등록된 DLL은 모든 프로세스에 올라온다는 점에 착안하여 악성 코드들의 심심치 않은 응용 대상이 된다는 것인데, 이 때문에 마이크로소프트는 Vista부터 기본적으로 Appinit_Dlls 기능을 비활성화 해놓고 있으며, Windows 7부터는 서명된 DLL만 활성화되도록 하는 옵션을 추가했습니다. (그런데, 서명된 DLL 사용의 기본값은 Windows 7에서 0, Windows Server 2008 R2에서 1입니다.)

이 때문에, 위와 같이 환경 변수만을 설정하는 정상적인 프로그램조차도 모든 윈도우 운영체제에 적용하려면 다음과 같은 사항들을 고려해야 합니다.

  • 설치 시에, "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" 경로의 "Appinit_Dlls" 값에 자신이 만든 DLL 경로 등록
  • 설치 시에, "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" 경로의 "LoadAppInit_DLLs" 값을 숫자 1로 설정
  • 설치 시에, "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" 경로의 "RequireSignedAppInit_DLLs" 값을 숫자 0으로 설정

그런데, 여기서 "RequireSignedAppInit_DLLs" 값을 0으로 설정해서 비활성화 하는 것은 왠지 방법이 아닌 것 같습니다. 왜냐하면 악성 코드들의 통로를 열어주는 것이 되므로 이 방법보다는 차라리 우리가 만든 DLL을 서명하는 것이 더 바람직합니다. (사실, 이 부분에 맞지 않는 부분이 있긴 합니다. 어차피 Appinit_Dlls 레지스트리 값에 "쓰기" 권한을 가진 악성 코드라면, 당연히 RequireSignedAppInit_DLLs도 0으로 설정할 것이기 때문입니다.)

이런저런 이유로 해당 DLL을 서명해 주는 것이 좋을 텐데... 익숙하게 알고 있던 ActiveX 서명과는 방법이 좀 다릅니다. 이에 대한 자세한 문서는 다음의 경로에서 구할 수 있습니다.

AppInit DLLs in Windows 7 and Windows Server 2008 R2
; https://learn.microsoft.com/en-us/windows/win32/win7appqual/appinit-dlls-in-windows-7-and-windows-server-2008-r2

AppInit_Win7.docx
; http://download.microsoft.com/download/7/E/7/7E7662CF-CBEA-470B-A97E-CE7CE0D98DC2/AppInit_Win7.docx

대강 정리해 보면, 다음과 같은 순서로 진행해야 합니다.

  1. DLL 빌드 시에 "/integritycheck" 옵션을 준다.
  2. 교차 인증서(cross-certificate)를 같이 사용해서 서명한다.

우선 1번은, Linker 옵션 설정 창에서 다음과 같이 설정해 주는 것으로 해결할 수 있습니다. (속성 페이지를 열은 김에, Debug/Release 모두 설정해 주는 것이 좋겠지요! ^^)

appinit_dll_env_set_2.png

실수로 Debug에만 설정하고 나중에 배포할 때 Release로 빌드했다가 누락되는 경우가 있을 수도 있는데 배포된 PC에서 정상적으로 빌드된 경우인지 확인하려면 dumpbin을 이용하면 됩니다.

C:\temp>dumpbin /headers test.dll
Microsoft (R) COFF/PE Dumper Version 10.00.30319.01
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file test.dll

PE signature found

File Type: DLL

FILE HEADER VALUES
             ...[생락]...

OPTIONAL HEADER VALUES
             10B magic # (PE32)
             ...[생략]...
               2 subsystem (Windows GUI)
             1C0 DLL characteristics
                   Dynamic base
                   Check integrity
                   NX compatible
          100000 size of stack reserve
             ...[생략]...

"/integritycheck" 옵션이 적용된 경우라면, 위와 같이 "DLL characteristics"에서 "Check integrity" 항목을 발견할 수 있습니다.

그다음, 교차 인증서를 다운로드해야 하는데요.

Cross-Certificates for Kernel Mode Code Signing
; https://learn.microsoft.com/en-us/windows-hardware/drivers/install/cross-certificates-for-kernel-mode-code-signing

위의 경로에서 소프트웨어 코드 서명 인증서를 발급받은 인증 기관에 해당하는 교차 인증서를 다운로드합니다. 현재 아래의 인증기관에 대한 교차 인증서만을 제공해 주고 있는데요.

  • Baltimore CyberTrust Root
  • Equifax Secure Certificate Authority
  • GTE CyberTrust Global Root
  • GlobalSign Root CA
  • GeoTrust Global CA
  • VeriSign Class 3 Public Primary Certification Authority

의미인 즉, 위의 인증 기관 이외에서 발급받은 인증서로는 Appinit_Dlls 목적으로 사용될 DLL 서명용으로는 부적합하다는 것입니다.

이렇게 해서 교차인증서를 다운로드했으면 (여기서는 Verisign에 해당하는 "MSCV-VSClass3.cer" 파일을 다운로드했다고 가정하고.) 다음과 같은 인자와 함께 최종적으로 서명하는 것이 가능합니다.

Signtool sign /v /ph /ac MSCV-VSClass3.cer /s my /n sysnet.pe.kr /t http://timestamp.verisign.com/scripts/timestamp.dll test.dll

만약, Team Build에서 위의 과정을 추가하고 싶다면 "/s my" 옵션의 계정에 종속된 위치보다는 "LOCAL MACHINE"에 설정된 인증서를 지정하고 싶을 텐데요. 그런 경우에는 "/sm" 옵션으로 주면 됩니다.

Signtool sign /v /ph /ac MSCV-VSClass3.cer /sm /n sysnet.pe.kr /t http://timestamp.verisign.com/scripts/timestamp.dll test.dll

만약, 위의 과정 중에 하나라도 부적합하다면 해당 DLL은 프로세스에 올라오지 않습니다.




참고로, "/integritycheck"만 있고 서명을 하지 않은 경우에는 모든 exe를 실행할 때마다 다음과 같은 오류 메시지가 발생합니다.

appinit_dll_env_set_3.png

"
... is either not designed to run on Windows or it contains an error. Try installing the program again using the original installation media or contract your system administrator or the software vendor for support.
"



다행히, 해당 응용 프로그램이 실행은 됩니다.



[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 11/22/2022]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2010-11-01 10시47분
Reading another process’s environment
; (broken) http://blogs.msdn.com/b/matt_pietrek/archive/2004/08/25/220330.aspx

Read Environment Strings of Remote Process
; http://www.codeproject.com/KB/threads/ReadProcEnv.aspx?display=Print
kevin25
2010-11-01 10시48분
kevin25
2012-04-28 11시35분
AppInit DLL의 DllMain에서 return FALSE;를 하는 것에 대한 주의가 요구됩니다. 최근 Windows 2008 R2가 설치된 환경에서 특정 32비트 응용 프로그램들이 return FALSE;인 경우 비정상 종료하는 문제가 발생했습니다. (R2 가 설치된 모든 컴퓨터가 그랬던 것도 아니고, 모든 32비트 응용 프로그램들이 비정상 종료한 것은 아닙니다.)

또한, DllMain 내에서 사용되는 버퍼들의 합이 일정 크기를 넘어서는 것도 문제가 될 수 있습니다. 너무 크게 잡았을 때, Visual Studio 등의 프로그램을 종료시킬 때, 비정상 종료 대화상자가 뜨는 것을 경험하기도 했습니다.
정성태
2014-09-04 06시52분
언제 한번 테스트 해봐야겠군요. Profiler 로드가 먼저일지, WinAppXRT.dll 로드가 먼저일지? ^^

.NET 어플리케이션과 APPX_PROCESS 환경변수, 그리고 WinAPPXRT.DLL
; http://blog.naver.com/gloryo/220110897359
정성태
2015-06-18 12시44분
동적 연결 라이브러리 동작 원리
; http://www.jiniya.net/wp/archives/10447
정성태

... 46  [47]  48  49  50  51  52  53  54  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12765정성태8/9/202114077Java: 31. Cannot load JDBC driver class 'org.mysql.jdbc.Driver'
12764정성태8/9/202152327Java: 30. XML document from ServletContext resource [/WEB-INF/applicationContext.xml] is invalid
12763정성태8/9/202116116Java: 29. java.lang.NullPointerException - com.mysql.jdbc.ConnectionImpl.getServerCharset
12762정성태8/8/202119404Java: 28. IntelliJ - Unable to open debugger port 오류
12761정성태8/8/202116079Java: 27. IntelliJ - java: package javax.inject does not exist [2]
12760정성태8/8/202112861개발 환경 구성: 594. 전용 "Command Prompt for ..." 단축 아이콘 만들기
12759정성태8/8/202117477Java: 26. IntelliJ + Spring Framework + 새로운 Controller 추가 [2]파일 다운로드1
12758정성태8/7/202116877오류 유형: 751. Error assembling WAR: webxml attribute is required (or pre-existing WEB-INF/web.xml if executing in update mode)
12757정성태8/7/202117498Java: 25. IntelliJ + Spring Framework 프로젝트 생성
12756정성태8/6/202115753.NET Framework: 1084. C# - .NET Core Web API 단위 테스트 방법 [1]파일 다운로드1
12755정성태8/5/202115822개발 환경 구성: 593. MSTest - 단위 테스트에 static/instance 유형의 private 멤버 접근 방법파일 다운로드1
12754정성태8/5/202116220오류 유형: 750. manage.py - Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
12753정성태8/5/202117144오류 유형: 749. PyCharm - Error: Django is not importable in this environment
12752정성태8/4/202113990개발 환경 구성: 592. JetBrains의 IDE(예를 들어, PyCharm)에서 Visual Studio 키보드 매핑 적용
12751정성태8/4/202116908개발 환경 구성: 591. Windows 10 WSL2 환경에서 docker-compose 빌드하는 방법
12750정성태8/3/202113940디버깅 기술: 181. windbg - 콜 스택의 "Call Site" 오프셋 값이 가리키는 위치
12749정성태8/2/202113381개발 환경 구성: 590. Visual Studio 2017부터 단위 테스트에 DataRow 특성 지원
12748정성태8/2/202114399개발 환경 구성: 589. Azure Active Directory - tenant의 관리자(admin) 계정 로그인 방법
12747정성태8/1/202114677오류 유형: 748. 오류 기록 - MICROSOFT GRAPH – HOW TO IMPLEMENT IAUTHENTICATIONPROVIDER파일 다운로드1
12746정성태7/31/202119183개발 환경 구성: 588. 네트워크 장비 환경을 시뮬레이션하는 Packet Tracer 프로그램 소개
12745정성태7/31/202114950개발 환경 구성: 587. Azure Active Directory - tenant의 관리자 계정 로그인 방법
12744정성태7/30/202115327개발 환경 구성: 586. Azure Active Directory에 연결된 App 목록을 확인하는 방법?
12743정성태7/30/202116551.NET Framework: 1083. Azure Active Directory - 외부 Token Cache 저장소를 사용하는 방법파일 다운로드1
12742정성태7/30/202114577개발 환경 구성: 585. Azure AD 인증을 위한 사용자 인증 유형
12741정성태7/29/202116083.NET Framework: 1082. Azure Active Directory - Microsoft Graph API 호출 방법파일 다운로드1
12740정성태7/29/202114605오류 유형: 747. SharePoint - InvalidOperationException 0x80131509
... 46  [47]  48  49  50  51  52  53  54  55  56  57  58  59  60  ...