Microsoft MVP성태의 닷넷 이야기
개발 환경 구성: 723. C# / Visual C++ - Control Flow Guard (CFG) 활성화 [링크 복사], [링크+제목 복사],
조회: 6765
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일

C# / Visual C++ - Control Flow Guard (CFG) 활성화

CFG라는 기술이 있군요. ^^

Control Flow Guard for platform security
; https://learn.microsoft.com/en-us/windows/win32/secbp/control-flow-guard

기존의 /GS (Buffer Security Check), Data Execution Prevention (DEP), Address Space Layout Randomization (ASLR)로도, 시중에서 흔하게 구할 수 있는 해킹 서적의 예제들이 실습이 안 될 정도로 보안이 강화됐는데요, 그런데 이제는 Indirect Call, (쉬운 예로) 코드상 함수 포인터를 이용한 호출까지도 대상 주소가 적법한지를 체크하는 기능이 추가됐습니다.

이를 구현하기 위해, 컴파일러와 운영체제 양측의 지원이 필요한데요, Visual C++ 컴파일러의 경우 "C/C++ > Code Generation > Control Flow Guard" 옵션을 활성화하면 됩니다. 유의해야 할 점은, Release 모드에서만 켜는 것이 좋은데, Debug 모드에서 키게 되면 다른 옵션(Program Database for Edit And Continue)과의 충돌로 빌드가 안 됩니다.

cl : command line  error D8016: '/ZI' and '/guard:cf' command-line options are incompatible

(EnC 옵션이 필요하지 않다면 Debug에서도 /Zi + /guard:cf 옵션을 사용하면 됩니다.)

구체적으로 어떻게 동작하는지에 대해서는, 문서에 있는 한 장의 그림으로 잘 설명하고 있습니다.

cfg_1.jpg

이로 인해 발생하는 뚜렷한 차이점이라면, cfg가 켜져 있는 경우 대상 함수를 호출하기 전에 미리 (fail-fast) 실패해버린다는 점입니다.




실제로 Visual C++ 컴파일러가 어떻게 대응하는지를 간단하게 살펴보겠습니다. ^^

#include <Windows.h>

typedef BOOL(WINAPI* BEEPPROC)(DWORD, DWORD);

int main()
{
    BEEPPROC pfn = (BEEPPROC)GetProcAddress(LoadLibrary(L"kernel32.dll"), "Beep");
    pfn(1000, 1000);
}

위의 예제를 cfg 옵션 없이 컴파일하면 기계어로 다음과 같은 코드가 생성됩니다.

    11:     pfn(1000, 1000);
00007FF6A3D114E4 BA E8 03 00 00       mov         edx,3E8h  
00007FF6A3D114E9 B9 E8 03 00 00       mov         ecx,3E8h  
00007FF6A3D114EE FF 54 24 20          call        qword ptr [pfn]  
00007FF6A3D114F2 90                   nop  

call [pfn], 즉 indirect call을 그대로 호출하고 있는데요, 이를 cfg 옵션을 켜고 컴파일하면 다음과 같이 변경됩니다.

    11:     pfn(1000, 1000);
00007FF62CC11EC4 48 8B 44 24 20       mov         rax,qword ptr [pfn]  
00007FF62CC11EC9 48 89 44 24 28       mov         qword ptr [rsp+28h],rax  
00007FF62CC11ECE BA E8 03 00 00       mov         edx,3E8h  
00007FF62CC11ED3 B9 E8 03 00 00       mov         ecx,3E8h  
00007FF62CC11ED8 48 8B 44 24 28       mov         rax,qword ptr [rsp+28h]  
00007FF62CC11EDD FF 15 2D 01 01 00    call        qword ptr [__guard_dispatch_icall_fptr (07FF62CC22010h)]  
00007FF62CC11EE3 90                   nop  

(아하... 어쩌다 windbg로 역어셈블 코드를 보면 나오던 __guard_dispatch_icall_fptr이 바로 cfg 옵션으로 추가된 코드였었던 것입니다.)

문제를 재현하기 위해 코드를 다음과 같이 바꾸고 실행하면,

BEEPPROC pfn = (BEEPPROC)GetProcAddress(LoadLibrary(L"kernel32.dll"), "Beep");
pfn = (BEEPPROC)0x00007FF091AD03A0;
pfn(1000, 1000);

cfg가 활성화된 경우 LdrpDispatchUserCallTarget에서 예외가 발생합니다.

    ntdll.dll!LdrpDispatchUserCallTarget()  Unknown
>    ConsoleApplication1.exe!main() Line 12  C++
    ConsoleApplication1.exe!invoke_main() Line 79   C++
...[생략]...

이때의 이벤트 로그를 보면, "Exception code: 0xc0000409 (STATUS_FAIL_FAST_EXCEPTION)"으로 나옵니다. 반면 cfg가 꺼진 상태라면 call stack이 이렇게 나오는데요,

    00007ff091ad03a0()  Unknown
>    ConsoleApplication1.exe!main() Line 12  C++
    ConsoleApplication1.exe!invoke_main() Line 79   C++

그러니까, 명시된 주소로 call을 했고 그 내부의 기계어 코드가 정상적이지 않을 것이므로 AV(Access Violation) 예외가 발생하게 됩니다. 따라서 이벤트 로그에도 "Exception code: 0xc0000005"로 나옵니다.

위의 경우에는 임의의 주소를 지정했지만, 실제로는 (dumpbin으로 확인할 수 있는) Guard CF Function Table에 등록돼 있지 않은 주소는 모두 invalid 주소로 판정하고 실패하는 것으로 보입니다. 보다 구체적인 정보는 blackhat 문서를 참고하세요.)




참고로 .NET App은 어떻게 하고 있을까요? ^^

우선, dotnet.exe 및 관련 바이너리(clrjt.dll)을 보면 CFG 컴파일러 스위치가 적용된 것을 확인할 수 있습니다.

C:temp> dumpbin /headers /loadconfig "C:\Program Files\dotnet\dotnet.exe"
Microsoft (R) COFF/PE Dumper Version 14.40.33811.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file C:\Program Files\dotnet\dotnet.exe

PE signature found

File Type: EXECUTABLE IMAGE

...[생략]...

OPTIONAL HEADER VALUES
             20B magic # (PE32+)
           14.40 linker version
            DA00 size of code
           14200 size of initialized data
               0 size of uninitialized data
            9790 entry point (0000000140009790)
            1000 base of code
       140000000 image base (0000000140000000 to 0000000140024FFF)
            1000 section alignment
             200 file alignment
            6.00 operating system version
            0.00 image version
            6.00 subsystem version
               0 Win32 version
           25000 size of image
             400 size of headers
           2671E checksum
               3 subsystem (Windows CUI)
            C160 DLL characteristics
                   High Entropy Virtual Addresses
                   Dynamic base
                   NX compatible
                   Control Flow Guard
                   Terminal Server Aware
          180000 size of stack reserve
            1000 size of stack commit
          100000 size of heap reserve
            ...[생략]...

  Section contains the following load config:

            00000140 size
                   0 time date stamp
                0.00 Version
            ...[생략]...
    000000014000F328 Guard CF address of check-function pointer
    000000014000F338 Guard CF address of dispatch-function pointer
    000000014000F420 Guard CF function table
                  1E Guard CF function count
            10417500 Guard Flags
                       CF instrumented
                       FID table present
                       Protect delayload IAT
                       Delayload IAT in its own section
                       Export suppression info present
                       Long jump target table present
                       EH Continuation table present
                0000 Code Integrity Flags
                0000 Code Integrity Catalog
            00000000 Code Integrity Catalog Offset
            00000000 Code Integrity Reserved
    0000000000000000 Guard CF address taken IAT entry table
                   0 Guard CF address taken IAT entry count
    0000000000000000 Guard CF long jump target table
                   0 Guard CF long jump target count
    0000000000000000 Dynamic value relocation table
    0000000000000000 Hybrid metadata pointer
    0000000000000000 Guard RF address of failure-function
    0000000000000000 Guard RF address of failure-function pointer
            00000000 Dynamic value relocation table offset
                0000 Dynamic value relocation table section
                0000 Reserved2
    0000000000000000 Guard RF address of stack pointer verification function pointer
            00000000 Hot patching table offset
                0000 Reserved3
    0000000000000000 Enclave configuration pointer
    0000000140013034 Volatile metadata pointer
    000000014000F3F0 Guard EH continuation table
                   9 Guard EH continuation count
    000000014000F330 Guard XFG address of check-function pointer
    000000014000F340 Guard XFG address of dispatch-function pointer
    000000014000F348 Guard XFG address of dispatch-table-function pointer
    000000014000F350 CastGuard OS determined failure mode
    000000014000F358 Guard memcpy function pointer

            ...[생략]...

c:\temp> dumpbin /headers /loadconfig "C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.5\clrjit.dll"
Microsoft (R) COFF/PE Dumper Version 14.40.33811.0
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.5\clrjit.dll

PE signature found

File Type: DLL

            ...[생략]...

OPTIONAL HEADER VALUES
             20B magic # (PE32+)
            ...[생략]...
               2 subsystem (Windows GUI)
            4160 DLL characteristics
                   High Entropy Virtual Addresses
                   Dynamic base
                   NX compatible
                   Control Flow Guard
          100000 size of stack reserve
            ...[생략]...

        00000001 extended DLL characteristics
                   CET compatible

  Section contains the following load config:

            00000140 size
            ...[생략]...
    0000000000000000 Edit List
    000000018019F040 Security Cookie
    0000000180166470 Guard CF address of check-function pointer
    0000000180166480 Guard CF address of dispatch-function pointer
    0000000180166550 Guard CF function table
                  E7 Guard CF function count
            10417500 Guard Flags
                       CF instrumented
                       FID table present
                       Protect delayload IAT
                       Delayload IAT in its own section
                       Export suppression info present
                       Long jump target table present
                       EH Continuation table present
                0000 Code Integrity Flags
                0000 Code Integrity Catalog
            ...[생략]...

하지만, JIT 컴파일이 된 코드에는 CFG가 적용되지 않은 듯합니다.

namespace ConsoleApp1;

internal class Program
{
    static unsafe void Main(string[] args)
    {
        delegate* unmanaged[Stdcall]<int, bool, int> sleepExFunc = (delegate* unmanaged[Stdcall]<int, bool, int>)0x00007FF091AD03A0;
        sleepExFunc(2000, false);
    }
}

위의 코드를 실행해 보면, 0x00007FF091AD03A0 주소에서 AV(0xc0000005) 예외가 발생합니다.

물론, 닷넷 코드에서 저렇게 native 주소를 직접 호출하는 코드를 넣는 경우는 거의 없을 것이므로, CFG 여부에 따른 차이가 크진 않을 것입니다.




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







[최초 등록일: ]
[최종 수정일: 9/19/2024]

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

비밀번호

댓글 작성자
 



2024-09-17 07시42분
The case of the fail-fast crashes coming from the power management system
; https://devblogs.microsoft.com/oldnewthing/20240913-00/?p=110257

---------------------------

A quick introduction to return address protection technologies (Intel Control-flow Enforcement Technology (CET))
; https://devblogs.microsoft.com/oldnewthing/20241015-00/?p=110374

Effects of classic return address tricks on hardware-assisted return address protection
; https://devblogs.microsoft.com/oldnewthing/20241016-00/?p=110378
정성태

... 76  77  78  79  80  81  82  [83]  84  85  86  87  88  89  90  ...
NoWriterDateCnt.TitleFile(s)
11856정성태3/25/201919509VS.NET IDE: 134. 마이크로소프트의 CoreCLR 프로파일러 리눅스 예제를 Visual Studio F5 원격 디버깅하는 방법 [1]파일 다운로드1
11855정성태3/25/201921622개발 환경 구성: 436. 페이스북 HTTPS 인증을 localhost에서 테스트하는 방법
11854정성태3/25/201917338VS.NET IDE: 133. IIS Express로 호스팅하는 사이트를 https로 접근하는 방법
11853정성태3/24/201920031개발 환경 구성: 435. 존재하지 않는 IP 주소에 대한 Dns.GetHostByAddress/gethostbyaddr/GetNameInfoW 실행이 느리다면? - 두 번째 이야기 [1]
11852정성태3/20/201919418개발 환경 구성: 434. 존재하지 않는 IP 주소에 대한 Dns.GetHostByAddress/gethostbyaddr/GetNameInfoW 실행이 느리다면?파일 다운로드1
11851정성태3/19/201923205Linux: 8. C# - 리눅스 환경에서 DllImport 대신 라이브러리 동적 로드 처리 [2]
11850정성태3/18/201922079.NET Framework: 813. C# async 메서드에서 out/ref/in 유형의 인자를 사용하지 못하는 이유
11849정성태3/18/201921547.NET Framework: 812. pscp.exe 기능을 C#으로 제어하는 방법파일 다운로드1
11848정성태3/17/201918182스크립트: 14. 윈도우 CMD - 파일이 변경된 경우 파일명을 변경해 복사하고 싶다면?
11847정성태3/17/201922705Linux: 7. 리눅스 C/C++ - 공유 라이브러리 동적 로딩 후 export 함수 사용 방법파일 다운로드1
11846정성태3/15/201921341Linux: 6. getenv, setenv가 언어/운영체제마다 호환이 안 되는 문제
11845정성태3/15/201921550Linux: 5. Linux 응용 프로그램의 (C++) so 의존성 줄이기(ReleaseMinDependency) [3]
11844정성태3/14/201922838개발 환경 구성: 434. Visual Studio 2019 - 리눅스 프로젝트를 이용한 공유/실행(so/out) 프로그램 개발 환경 설정 [1]파일 다운로드1
11843정성태3/14/201917793기타: 75. MSDN 웹 사이트를 기본으로 영문 페이지로 열고 싶다면?
11842정성태3/13/201916217개발 환경 구성: 433. 마이크로소프트의 CoreCLR 프로파일러 예제를 Visual Studio CMake로 빌드하는 방법 [1]파일 다운로드1
11841정성태3/13/201916515VS.NET IDE: 132. Visual Studio 2019 - CMake의 컴파일러를 기본 g++에서 clang++로 변경
11840정성태3/13/201918087오류 유형: 526. 윈도우 10 Ubuntu App 환경에서는 USB 외장 하드 접근 불가
11839정성태3/12/201922035디버깅 기술: 124. .NET Core 웹 앱을 호스팅하는 Azure App Services의 프로세스 메모리 덤프 및 windbg 분석 개요 [3]
11838정성태3/7/201925597.NET Framework: 811. (번역글) .NET Internals Cookbook Part 1 - Exceptions, filters and corrupted processes [1]파일 다운로드1
11837정성태3/6/201939616기타: 74. 도서: 시작하세요! C# 7.3 프로그래밍 [10]
11836정성태3/5/201923093오류 유형: 525. Visual Studio 2019 Preview 4/RC - C# 8.0 Missing compiler required member 'System.Range..ctor' [1]
11835정성태3/5/201921657.NET Framework: 810. C# 8.0의 Index/Range 연산자를 .NET Framework에서 사용하는 방법 및 비동기 스트림의 컴파일 방법 [3]파일 다운로드1
11834정성태3/4/201920516개발 환경 구성: 432. Visual Studio 없이 최신 C# (8.0) 컴파일러를 사용하는 방법
11833정성태3/4/201920995개발 환경 구성: 431. Visual Studio 2019 - CMake를 이용한 공유/실행(so/out) 리눅스 프로젝트 설정파일 다운로드1
11832정성태3/4/201916937오류 유형: 524. Visual Studio CMake - rsync: connection unexpectedly closed
11831정성태3/4/201916741오류 유형: 523. Visual Studio 2019 - 새 창으로 뜬 윈도우를 닫을 때 비정상 종료
... 76  77  78  79  80  81  82  [83]  84  85  86  87  88  89  90  ...