Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)
(시리즈 글이 4개 있습니다.)
디버깅 기술: 115. windbg - 덤프 파일로부터 PID와 환경변수 등의 정보를 구하는 방법
; https://www.sysnet.pe.kr/2/0/11478

.NET Framework: 876. C# - PEB(Process Environment Block)를 통해 로드된 모듈 목록 열람
; https://www.sysnet.pe.kr/2/0/12101

디버깅 기술: 153. C# - PEB를 조작해 로드된 DLL을 숨기는 방법
; https://www.sysnet.pe.kr/2/0/12105

디버깅 기술: 157. C# - PEB.ProcessHeap을 이용해 디버깅 중인지 확인하는 방법
; https://www.sysnet.pe.kr/2/0/12115




C# - PEB.ProcessHeap을 이용해 디버깅 중인지 확인하는 방법

아래의 글에 재미있는 내용이 있군요. ^^

Anti-Reversing Technique: PEB.ProcessHeap
; https://t0rchwo0d.github.io/windows/Windows-Anti-Reversing-Technique-PEB.ProcessHeap/

디버거가 연결된 경우 ProcessHeap의,

0:007> dt _PEB @$peb
ntdll!_PEB
   +0x000 InheritedAddressSpace : 0 ''
   +0x001 ReadImageFileExecOptions : 0 ''
   +0x002 BeingDebugged    : 0x1 '' // IsDebuggerPresent function
   +0x003 BitField         : 0x84 ''
...[생략]...
   +0x028 SubSystemData    : 0x00007ffd`b5cfe120 Void
   +0x030 ProcessHeap      : 0x000001b0`39300000 Void
...[생략]...

Flags와 ForceFlags 값이 달라진다는 것입니다.

0:007> dt _HEAP
ntdll!_HEAP
   +0x000 Segment          : _HEAP_SEGMENT
   +0x000 Entry            : _HEAP_ENTRY
   +0x010 SegmentSignature : Uint4B
   +0x014 SegmentFlags     : Uint4B
   +0x018 SegmentListEntry : _LIST_ENTRY
   +0x028 Heap             : Ptr64 _HEAP
   +0x030 BaseAddress      : Ptr64 Void
   +0x038 NumberOfPages    : Uint4B
   +0x040 FirstEntry       : Ptr64 _HEAP_ENTRY
   +0x048 LastValidEntry   : Ptr64 _HEAP_ENTRY
   +0x050 NumberOfUnCommittedPages : Uint4B
   +0x054 NumberOfUnCommittedRanges : Uint4B
   +0x058 SegmentAllocatorBackTraceIndex : Uint2B
   +0x05a Reserved         : Uint2B
   +0x060 UCRSegmentList   : _LIST_ENTRY
   +0x070 Flags            : Uint4B
   +0x074 ForceFlags       : Uint4B
...[생략]...

사실 ProcessHeap이 가리키는 것은 Default Heap이기 때문에 전체 힙 목록은 예전에 설명한 대로,

windbg와 Win32 API로 알아보는 Windows Heap 정보 분석
; https://www.sysnet.pe.kr/2/0/12068

NumberOfHeaps, ProcessHeaps 필드의 값으로 알 수 있습니다.

0:000> dt _PEB @$peb ProcessHeap
ntdll!_PEB
   +0x030 ProcessHeap : 0x00000255`54510000 Void

0:000> dt _PEB @$peb NumberOfHeaps, ProcessHeaps
ntdll!_PEB
   +0x0e8 NumberOfHeaps  : 2
   +0x0f0 ProcessHeaps   : 0x00007ffb`64a43c40  -> 0x00000255`54510000 Void

0:000> dq /c1 0x00007ffb`64a43c40  L2
00007ffb`64a43c40  00000255`54510000
00007ffb`64a43c48  00000255`54410000

보는 바와 같이 ProcessHeaps의 첫 번째 엔트리가 PEB.ProcessHeap 값입니다. 위와 같은 조작을 KernelStructOffset 라이브러리를 이용해 코딩해 보면 다음과 같습니다.

IntPtr pebAddress = EnvironmentBlockInfo.GetPebAddress(out IntPtr tebAddress);

var pebOffset = DbgOffset.Get("_PEB");
var heapOffset = DbgOffset.Get("_HEAP");

if (pebOffset.TryRead<IntPtr>(pebAddress, "ProcessHeap", out IntPtr processHeapPtr) == false)
{
    return;
}

Console.WriteLine($"Default ProcessHeap: {processHeapPtr.ToInt64():x}");

IntPtr processHeapsPtr = pebOffset.GetPointer(pebAddress, "ProcessHeaps").ReadPtr();
if (pebOffset.TryRead<int>(pebAddress, "NumberOfHeaps", out int numberOfHeaps) == false)
{
    return;
}

Console.WriteLine($"Ptr of ProcessHeaps: {processHeapsPtr.ToInt64():x}");
Console.WriteLine($"Number of Heaps: {numberOfHeaps}");

for (int i = 0; i < numberOfHeaps; i++)
{
    IntPtr entryPtr = processHeapsPtr + (IntPtr.Size * i);
    IntPtr heapAddress = entryPtr.ReadPtr();

    Console.WriteLine($"[{i}] Heap: {heapAddress.ToInt64():x}");
}

/* 출력 결과

Default ProcessHeap: 13c0000
Ptr of ProcessHeaps: 7ffdba963c40
Number of Heaps: 6
[0] Heap: 13c0000
[1] Heap: fa0000
[2] Heap: 1730000
[3] Heap: 19b0000
[4] Heap: 3250000
[5] Heap: 3410000
*/

그리고, 각각의 heap에 대한 Flags, ForceFlags는 이렇게 구하면 됩니다.

for (int i = 0; i < numberOfHeaps; i++)
{
    IntPtr entryPtr = processHeapsPtr + (IntPtr.Size * i);
    IntPtr heapAddress = entryPtr.ReadPtr();

    Console.WriteLine($"[{i}] Heap: {heapAddress.ToInt64():x}");

    if (heapOffset.TryRead<int>(heapAddress, "Flags", out int flagsValue) == true)
    {
        Console.WriteLine($"\tFlags: {flagsValue:x}");
    }

    if (heapOffset.TryRead<int>(heapAddress, "ForceFlags", out int forceFlagsValue) == true)
    {
        Console.WriteLine($"\tForceFlags: {forceFlagsValue:x}");
    }
}

코딩이 완료되었으니 이제 테스트를 해봐야겠지요. ^^




우선, Visual Studio에서 F5 키를 눌러 "Start Debugging..."으로 시작하면 다음과 같이 결과가 출력됩니다.

// Visual Studio - F5 실행 시

Default ProcessHeap: d50000
Ptr of ProcessHeaps: 7ffdba963c40

Number of Heaps: 6
[0] Heap: d50000
        Flags: 2
        ForceFlags: 0
[1] Heap: 9d0000
        Flags: 8000
        ForceFlags: 0
[2] Heap: 1060000
        Flags: 1002
        ForceFlags: 0
[3] Heap: 1370000
        Flags: 1002
        ForceFlags: 0
[4] Heap: 2c70000
        Flags: 1002
        ForceFlags: 0
[5] Heap: 2b70000
        Flags: 41002
        ForceFlags: 0

/*
0x2 == HEAP_GROWABLE
0x1000 == (?)
0x8000 == HEAP_PSEUDO_TAG_FLAG
0x40000 == HEAP_CREATE_ENABLE_EXECUTE
*/

저 결과는 디버거 없이 실행했을 때와 동일한 것인데 따라서 Visual Studio는 Heap 구성에 별다른 영향을 끼치지 않고 있습니다. 반면, windbg는 다릅니다. "Open Executable..." 메뉴로 exe를 지정한 뒤 "g" 키를 눌러 실행하면 다음과 같이 플래그들이 구성됩니다.

Default ProcessHeap: 13c0000
Ptr of ProcessHeaps: 7ffdba963c40

Number of Heaps: 6
[0] Heap: 13c0000
        Flags: 40000062
        ForceFlags: 40000060
[1] Heap: fa0000
        Flags: 40008060
        ForceFlags: 40000060
[2] Heap: 1730000
        Flags: 40001062
        ForceFlags: 40000060
[3] Heap: 19b0000
        Flags: 40001062
        ForceFlags: 40000060
[4] Heap: 3250000
        Flags: 40001062
        ForceFlags: 40000060
[5] Heap: 3410000
        Flags: 40001062
        ForceFlags: 40000060

/*
0x2 == HEAP_GROWABLE
0x1000 == (?)
0x60 == 
    0x20 HEAP_TAIL_CHECKING_ENABLED
    0x40 HEAP_FREE_CHECKING_ENABLED
0x40000000 == HEAP_VALIDATE_PARAMETERS_ENABLED
*/

의도한 값이 나오는군요. (참고로, 중간에 attach시키면 소용 없습니다.) 재미있는 것은, 해당 프로세스 명(예제에서는 ConsoleApp1.exe)을 Image File Execution Options 레지스트리에 등록하면,

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\ConsoleApp1.exe

windbg에서 실행했을지라도 디버거가 없는 것처럼 Flags들의 값이 구성됩니다. 정리하면... windbg를 상당히 제한적으로 감지할 수 있는 방법입니다.




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 1/14/2020]

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

비밀번호

댓글 작성자
 




... [136]  137  138  139  140  141  142  143  144  145  146  147  148  149  150  ...
NoWriterDateCnt.TitleFile(s)
1688정성태6/15/201421302오류 유형: 229. 갤럭시 노트 3 환경에서 Xamarin 앱 배포 충돌
1687정성태6/15/201427874개발 환경 구성: 223. PowerShell로 Visual Studio 빌드 스크립트 작성파일 다운로드1
1686정성태6/12/201425952Windows: 96. 윈도우 8 - 그림 암호를 이용해 로그인 시 지연 현상을 해결하는 방법 [1]
1685정성태6/10/201432513.NET Framework: 443. 자바 8과 C#의 람다(Lambda) 지원에 대한 비교 [12]
1684정성태6/9/201442673.NET Framework: 442. C# - 시스템의 CPU 사용량 및 프로세스(EXE)의 CPU 사용량 알아내는 방법 [5]파일 다운로드1
1683정성태6/2/201422213오류 유형: 228. CLR4 보안 - yield 구문 내에서 SecurityCritical 메서드 사용 불가 [2]파일 다운로드1
1682정성태6/1/201427866.NET Framework: 441. .NET CLR4 보안 모델 - 3. CLR4 보안 모델에서의 APTCA 역할파일 다운로드2
1681정성태6/1/201423358.NET Framework: 440. .NET CLR4 보안 모델 - 2. 샌드박스(Sandbox)을 이용한 보안 [2]파일 다운로드1
1680정성태6/1/201422764.NET Framework: 439. .NET CLR4 보안 모델 - 1. "Security Level 2"란?파일 다운로드1
1679정성태5/31/201421883.NET Framework: 438. .NET CLR2 보안 모델에서의 APTCA 역할파일 다운로드1
1678정성태5/31/201425588개발 환경 구성: 222. 라이브러리 개발자를 위한 보안 권한 테스트 - "Network Service" 계정 권한으로 실행
1677정성태5/30/201421043VS.NET IDE: 87. IIS Express - 웹 응용 프로그램의 .NET 버전에 맞는 CLR이 로드되지 않는 경우파일 다운로드1
1676정성태5/27/201429428Windows: 95. 윈도우 8에서 Hyper-V 유무에 따른 듀얼 부트 설정하는 방법 [1]
1675정성태5/27/201431524Windows: 94. 윈도우 8.1에서 윈도우 체험 지수(Windows Experience Index, WEI) 확인 방법
1674정성태5/24/201425088VS.NET IDE: 86. 하나의 T4 템플릿으로 여러 개의 소스코드 파일을 자동으로 생성하는 방법 [1]파일 다운로드1
1673정성태5/19/201424586.NET Framework: 437. WACOM 태블릿 환경에서 WinForm 실행시 System.ArgumentException 예외 발생
1672정성태5/15/201425212기타: 46. Microsoft의 응용 프로그램을 클라우드로 제공하는 서비스 - Azure RemoteApp 소개 [2]
1671정성태5/15/201425842.NET Framework: 436. XNA Content 리소스의 해제 후 다시 로드해서 사용하면 ObjectDisposedException 예외 발생 [2]
1670정성태5/15/201426046.NET Framework: 435. .NET GC - 하위 세대의 객체를 포함하는 상위 세대의 참조를 추적하기 위한 card-table
1669정성태5/15/201446199Windows: 93. 윈도우 시스템 디스크 용량 확보를 위한 $PatchCache$ 폴더 삭제 [2]
1668정성태5/10/201425171.NET Framework: 434. Microsoft.SqlServer.Types.SqlGeography 형변환 시 null 반환하는 문제
1667정성태5/5/201426164개발 환경 구성: 221. Azure 데이터베이스를 로컬 DB로 이전하는 방법 [2]
1666정성태5/2/201442820기타: 45. 윈도우 계정의 암호를 알아내는 mimikatz 도구 [5]
1665정성태5/1/201426211.NET Framework: 433. C# - 간단한 HyperLogLog 자료 구조 테스트파일 다운로드1
1664정성태4/28/201423646오류 유형: 227. Process Explorer의 프로세스 뷰가 트리 형식으로 보이지 않는 문제
1663정성태4/28/201420027오류 유형: 226. Visual Studio - We were unable to establish the connection because it is configured for user
... [136]  137  138  139  140  141  142  143  144  145  146  147  148  149  150  ...