Microsoft MVP성태의 닷넷 이야기
개발 환경 구성: 77. Appinit_Dlls로 구현한 환경 변수 설정 DLL [링크 복사], [링크+제목 복사],
조회: 28687
글쓴 사람
정성태 (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
정성태

1  2  3  4  [5]  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13818정성태11/15/20245260Windows: 272. Windows 11 24H2 - sudo 추가
13817정성태11/14/20244923Linux: 106. eBPF / bpf2go - (BPF_MAP_TYPE_HASH) Map을 이용한 전역 변수 구현
13816정성태11/14/20245386닷넷: 2312. C#, C++ - Windows / Linux 환경의 Thread Name 설정파일 다운로드1
13815정성태11/13/20244809Linux: 105. eBPF - bpf2go에서 전역 변수 설정 방법
13814정성태11/13/20245277닷넷: 2311. C# - Windows / Linux 환경에서 Native Thread ID 가져오기파일 다운로드1
13813정성태11/12/20245028닷넷: 2310. .NET의 Rune 타입과 emoji 표현파일 다운로드1
13812정성태11/11/20245246오류 유형: 933. Active Directory - The forest functional level is not supported.
13811정성태11/11/20244848Linux: 104. Linux - COLUMNS 환경변수가 언제나 80으로 설정되는 환경
13810정성태11/10/20245374Linux: 103. eBPF (bpf2go) - Tracepoint를 이용한 트레이스 (BPF_PROG_TYPE_TRACEPOINT)
13809정성태11/10/20245250Windows: 271. 윈도우 서버 2025 마이그레이션
13808정성태11/9/20245256오류 유형: 932. Linux - 커널 업그레이드 후 "error: bad shim signature" 오류 발생
13807정성태11/9/20244983Linux: 102. Linux - 커널 이미지 파일 서명 (Ubuntu 환경)
13806정성태11/8/20244901Windows: 270. 어댑터 상세 정보(Network Connection Details) 창의 내용이 비어 있는 경우
13805정성태11/8/20244736오류 유형: 931. Active Directory의 adprep 또는 복제가 안 되는 경우
13804정성태11/7/20245366Linux: 101. eBPF 함수의 인자를 다루는 방법
13803정성태11/7/20245319닷넷: 2309. C# - .NET Core에서 바뀐 DateTime.Ticks의 정밀도
13802정성태11/6/20245692Windows: 269. GetSystemTimeAsFileTime과 GetSystemTimePreciseAsFileTime의 차이점파일 다운로드1
13801정성태11/5/20245480Linux: 100. eBPF의 2가지 방식 - libbcc와 libbpf(CO-RE)
13800정성태11/3/20246321닷넷: 2308. C# - ICU 라이브러리를 활용한 문자열의 대소문자 변환 [2]파일 다운로드1
13799정성태11/2/20244906개발 환경 구성: 732. 모바일 웹 브라우저에서 유니코드 문자가 표시되지 않는 경우
13798정성태11/2/20245505개발 환경 구성: 731. 유니코드 - 출력 예시 및 폰트 찾기
13797정성태11/1/20245491C/C++: 185. C++ - 문자열의 대소문자를 변환하는 transform + std::tolower/toupper 방식의 문제점파일 다운로드1
13796정성태10/31/20245381C/C++: 184. C++ - ICU dll을 이용하는 예제 코드 (Windows)파일 다운로드1
13795정성태10/31/20245163Windows: 268. Windows - 리눅스 환경처럼 공백으로 끝나는 프롬프트 만들기
13794정성태10/30/20245261닷넷: 2307. C# - 윈도우에서 한글(및 유니코드)을 포함한 콘솔 프로그램을 컴파일 및 실행하는 방법
13793정성태10/28/20245134C/C++: 183. C++ - 윈도우에서 한글(및 유니코드)을 포함한 콘솔 프로그램을 컴파일 및 실행하는 방법
1  2  3  4  [5]  6  7  8  9  10  11  12  13  14  15  ...