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

... [16]  17  18  19  20  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13233정성태1/28/20237121오류 유형: 840. C# - WebClient로 https 호출 시 "The request was aborted: Could not create SSL/TLS secure channel" 예외 발생
13232정성태1/27/20234890스크립트: 43. uwsgi의 --processes와 --threads 옵션
13231정성태1/27/20233844오류 유형: 839. python - TypeError: '...' object is not callable
13230정성태1/26/20234191개발 환경 구성: 660. WSL 2 내부로부터 호스트 측의 네트워크로 UDP 데이터가 1개의 패킷으로만 제한되는 문제
13229정성태1/25/20235205.NET Framework: 2090. C# - UDP Datagram의 최대 크기
13228정성태1/24/20235305.NET Framework: 2089. C# - WMI 논리 디스크가 속한 물리 디스크의 정보를 얻는 방법 [2]파일 다운로드1
13227정성태1/23/20235000개발 환경 구성: 659. Windows - IP MTU 값을 바꿀 수 있을까요? [1]
13226정성태1/23/20234717.NET Framework: 2088. .NET 5부터 지원하는 GetRawSocketOption 사용 시 주의할 점
13225정성태1/21/20233922개발 환경 구성: 658. Windows에서 실행 중인 소켓 서버를 다른 PC 또는 WSL에서 접속할 수 없는 경우
13224정성태1/21/20234307Windows: 221. Windows - Private/Public/Domain이 아닌 네트워크 어댑터 단위로 방화벽을 on/off하는 방법
13223정성태1/20/20234503오류 유형: 838. RDP 연결 오류 - The two computers couldn't connect in the amount of time allotted
13222정성태1/20/20234193개발 환경 구성: 657. WSL - DockerDesktop.vhdx 파일 위치를 옮기는 방법
13221정성태1/19/20234379Linux: 57. C# - 리눅스 프로세스 메모리 정보파일 다운로드1
13220정성태1/19/20234474오류 유형: 837. NETSDK1045 The current .NET SDK does not support targeting .NET ...
13219정성태1/18/20234027Windows: 220. 네트워크의 인터넷 접속 가능 여부에 대한 판단 기준
13218정성태1/17/20233969VS.NET IDE: 178. Visual Studio 17.5 (Preview 2) - 포트 터널링을 이용한 웹 응용 프로그램의 외부 접근 허용
13217정성태1/13/20234561디버깅 기술: 185. windbg - 64비트 운영체제에서 작업 관리자로 뜬 32비트 프로세스의 덤프를 sos로 디버깅하는 방법
13216정성태1/12/20234805디버깅 기술: 184. windbg - 32비트 프로세스의 메모리 덤프인 경우 !peb 명령어로 나타나지 않는 환경 변수
13215정성태1/11/20236396Linux: 56. 리눅스 - /proc/pid/stat 정보를 이용해 프로세스의 CPU 사용량 구하는 방법 [1]
13214정성태1/10/20235947.NET Framework: 2087. .NET 6부터 SourceGenerator와 통합된 System.Text.Json [1]파일 다운로드1
13213정성태1/9/20235458오류 유형: 836. docker 이미지 빌드 시 "RUN apt install ..." 명령어가 실패하는 이유
13212정성태1/8/20235204기타: 85. 단정도/배정도 부동 소수점의 정밀도(Precision)에 따른 형변환 손실
13211정성태1/6/20235234웹: 42. (https가 아닌) http 다운로드를 막는 웹 브라우저
13210정성태1/5/20234328Windows: 219. 윈도우 x64의 경우 0x00000000`7ffe0000 아래의 주소는 왜 사용하지 않을까요?
13209정성태1/4/20234241Windows: 218. 왜 윈도우에서 가상 메모리 공간은 64KB 정렬이 된 걸까요?
13208정성태1/3/20234213.NET Framework: 2086. C# - Windows 운영체제의 2MB Large 페이지 크기 할당 방법파일 다운로드1
... [16]  17  18  19  20  21  22  23  24  25  26  27  28  29  30  ...