Microsoft MVP성태의 닷넷 이야기
개발 환경 구성: 77. Appinit_Dlls로 구현한 환경 변수 설정 DLL [링크 복사], [링크+제목 복사],
조회: 22973
글쓴 사람
정성태 (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)
13409정성태9/8/20233911닷넷: 2140. C# - Win32 API를 이용한 모니터 전원 끄기
13408정성태9/5/20233875Windows: 251. 임의로 만든 EXE 파일을 포함한 ZIP 파일의 압축을 해제할 때 Windows Defender에 의해 삭제되는 경우
13407정성태9/4/20233606닷넷: 2139. C# - ParallelEnumerable을 이용한 IEnumerable에 대한 병렬 처리
13406정성태9/4/20233587VS.NET IDE: 186. Visual Studio Community 버전의 라이선스
13405정성태9/3/20234013닷넷: 2138. C# - async 메서드 호출 원칙
13404정성태8/29/20233579오류 유형: 876. Windows - 키보드의 등호(=, Equals sign) 키가 눌리지 않는 경우
13403정성태8/21/20233376오류 유형: 875. The following signatures couldn't be verified because the public key is not available: NO_PUBKEY EB3E94ADBE1229CF
13402정성태8/20/20233465닷넷: 2137. ILSpy의 nuget 라이브러리 버전 - ICSharpCode.Decompiler
13401정성태8/19/20233704닷넷: 2136. .NET 5+ 환경에서 P/Invoke의 성능을 높이기 위한 SuppressGCTransition 특성 [1]
13400정성태8/10/20233535오류 유형: 874. 파이썬 - pymssql을 윈도우 환경에서 설치 불가
13399정성태8/9/20233491닷넷: 2135. C# - 지역 변수로 이해하는 메서드 매개변수의 값/참조 전달
13398정성태8/3/20234329스크립트: 55. 파이썬 - pyodbc를 이용한 SQL Server 연결 사용법
13397정성태7/23/20233821닷넷: 2134. C# - 문자열 연결 시 string.Create를 이용한 GC 할당 최소화
13396정성태7/22/20233583스크립트: 54. 파이썬 pystack 소개 - 메모리 덤프로부터 콜 스택 열거
13395정성태7/20/20233477개발 환경 구성: 685. 로컬에서 개발 중인 ASP.NET Core/5+ 웹 사이트에 대해 localhost 이외의 호스트 이름으로 접근하는 방법
13394정성태7/16/20233437오류 유형: 873. Oracle.ManagedDataAccess.Client - 쿼리 수행 시 System.InvalidOperationException
13393정성태7/16/20233610닷넷: 2133. C# - Oracle 데이터베이스의 Sleep 쿼리 실행하는 방법
13392정성태7/16/20233505오류 유형: 872. Oracle - ORA-01031: insufficient privileges
13391정성태7/14/20233541닷넷: 2132. C# - sealed 클래스의 메서드를 callback 호출했을 때 인라인 처리가 될까요?
13390정성태7/12/20233481스크립트: 53. 파이썬 - localhost 호출 시의 hang 현상
13389정성태7/5/20233526개발 환경 구성: 684. IIS Express로 호스팅하는 웹을 WSL 환경에서 접근하는 방법
13388정성태7/3/20233653오류 유형: 871. 윈도우 탐색기에서 열리지 않는 zip 파일 - The Compressed (zipped) Folder '[...].zip' is invalid. [1]파일 다운로드1
13387정성태6/28/20233721오류 유형: 870. _mysql - Commands out of sync; you can't run this command now
13386정성태6/27/20233778Linux: 61. docker - 원격 제어를 위한 TCP 바인딩 추가
13385정성태6/27/20233979Linux: 60. Linux - 외부에서의 접속을 허용하기 위한 TCP 포트 여는 방법
13384정성태6/26/20233707.NET Framework: 2131. C# - Source Generator로 해결하는 enum 박싱 문제파일 다운로드1
1  2  3  4  5  6  7  8  [9]  10  11  12  13  14  15  ...