Understanding the Low Fragmentation...

95
페이지 1 / 95 Understanding the Low Fragmentation Heap

Transcript of Understanding the Low Fragmentation...

Page 1: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 1 / 95

Understanding the Low Fragmentation Heap

Page 2: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 2 / 95

목차

UNDERSTANDING THE LOW FRAGMENTATION HEAP ......................................................... 1

INTRODUCTION ....................................................................................................................................................................... 7

Overview ............................................................................................................................................................................... 7

Prior Works .......................................................................................................................................................................... 8

Prerequisites........................................................................................................................................................................ 8

Terminology......................................................................................................................................................................... 9

Notes ....................................................................................................................................................................................... 9

DATA STRUCTURES ............................................................................................................................................................ 10

_HEAP (HeapBase) ........................................................................................................................................................ 10

_HEAP_LIST_LOOKUP (HeapBase->BlocksIndex) ......................................................................................... 11

_LFH_HEAP (HeapBase->FrontEndHeap) ......................................................................................................... 12

_LFH_BLOCK_ZONE (HeapBase->FrontEndHeap->LocalData->CrtZone) ...................................... 13

_HEAP_LOCAL_DATA (HeapBase->FrontEndHeap->LocalData) .......................................................... 14

_HEAP_LOCAL_SEGMENT_INFO (HeapBase->FrontEndHeap->LocalData->SegmentInfo[])14

_HEAP_SUBSEGMENT (HeapBase->FrontEndHeap->LocalData->SegmentInfo[]-

>Hint,ActiveSubsegment,CachedItems) ........................................................................................................... 15

_HEAP_USERDATA_HEADER (HeapBase->FrontEndHeap->LocalData->SegmentInfo[]-

>Hint,ActiveSubsegment,CachedItems->UserBlocks) .............................................................................. 16

_INTERLOCK_SEQ (HeapBase->FrontEndHeap->LocalData->SegmentInfo[]-

>Hint,ActiveSubsegment,CachedItems->AggregateExchg) .................................................................. 16

_HEAP_ENTRY (Chunk Header) .............................................................................................................................. 17

Overview ............................................................................................................................................................................ 18

ARCHITECTURE ..................................................................................................................................................................... 19

FreeLists .............................................................................................................................................................................. 19

ALGORITHMS ......................................................................................................................................................................... 21

Allocation ........................................................................................................................................................................... 21

Back-end Allocation ..................................................................................................................................................... 23

RtlpAllocateHeap ................................................................................................................................................................................. 23

Overview .................................................................................................................................................................................................. 30

Front-end Allocation ................................................................................................................................................... 31

RtlpLowFragHeapAllocFromContext .......................................................................................................................................... 31

Page 3: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 3 / 95

Overview .................................................................................................................................................................................................. 41

Example .................................................................................................................................................................................................... 41

Freeing ................................................................................................................................................................................. 45

Back-end Freeing ................................................................................................................................................................................. 46

RtlFreeHeap ............................................................................................................................................................................................ 46

Overview .................................................................................................................................................................................................. 53

Front-end Freeing ......................................................................................................................................................... 54

RtlpLowFragHeapFree ....................................................................................................................................................................... 54

Overview .................................................................................................................................................................................................. 58

Example .................................................................................................................................................................................................... 59

SECURITYY MECHANISMS .............................................................................................................................................. 62

Heap Randomization ................................................................................................................................................... 62

Comments............................................................................................................................................................................................... 63

Header Encoding/Decoding .................................................................................................................................... 63

Comments............................................................................................................................................................................................... 64

Death of bitmap flipping .......................................................................................................................................... 65

Safe Linking ...................................................................................................................................................................... 66

Comments............................................................................................................................................................................................... 67

TACTICS ................................................................................................................................................................................... 68

Heap Determinism ........................................................................................................................................................ 68

Activation the LFH .............................................................................................................................................................................. 68

Defragmentation ................................................................................................................................................................................. 69

Adjacent Data ....................................................................................................................................................................................... 70

Seeding Data ......................................................................................................................................................................................... 72

Exploitation ....................................................................................................................................................................... 75

Ben Hawkes #1 ..................................................................................................................................................................................... 76

FreeEntryOffset Overwrite............................................................................................................................................................... 80

Observations .................................................................................................................................................................... 88

SubSegment Overwrite .................................................................................................................................................................... 88

Example .................................................................................................................................................................................................... 91

Issues ......................................................................................................................................................................................................... 92

CONCLUSION ......................................................................................................................................................................... 94

BIBLIOGRAPHY .................................................................................................................................................................... 95

Page 4: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 4 / 95

표 목차

표 1. _HEAP VIA WINDBG .......................................................................................................................................................... 10

표 2. _HEAP_LIST_LOOKUP VIA WINDBG ............................................................................................................................ 12

표 3. _LFH_HEAP VIA WINDBG ................................................................................................................................................ 13

표 4. _LFH_BLOCK_ZONE VIA WINDBG ................................................................................................................................ 13

표 5. _HEAP_LOCAL_DATA VIA WINDBG ............................................................................................................................. 14

표 6. _HEAP_LOCAL_SEGMENT_INFO VIA WINDBG ....................................................................................................... 14

표 7. _HEAP_SUBSEGMENT VIA WINDBG ........................................................................................................................... 15

표 8. _HEAP_USERDATA_HEADER VIA WINDBG ............................................................................................................... 16

표 9. _INTERLOCK_SEQ VIA WINDBG ................................................................................................................................... 16

표 10. _HEAP_ENTRY VIA WINDBG ........................................................................................................................................ 17

표 11. RTLALLOCATEHEAP BLOCKSINDEX SEARCH ................................................................................................................. 22

표 12. RTLALLOCATEHEAP HEAP MANAGER SELECTOR .......................................................................................................... 22

표 13. RTLPALLOCATEHEAP SIZE ROUNDING AND HEAP MAINTENANCE ............................................................................ 23

표 14. RTLALLOCATEHEAP LFH HEURISTIC ............................................................................................................................. 25

표 15. RTLPALLOCATEHEAP LISTHINT ALLOCATION ................................................................................................................ 26

표 16. RTLPALLOCATEHEAP HEAP->FREELISTS ALLOCATION ............................................................................................... 27

표 17. LFH SUBSEGMENT ALLOCATION ................................................................................................................................... 31

표 18. LFH USERBLOCKS ALLOCATION SIZE ALGORITHM ...................................................................................................... 34

표 19. RTLPALLOCATEUSERBLOCK WITHOUT CACHING .......................................................................................................... 35

표 20. LFH PRE-SUBSEGMENT INITIALIZATION SETUP ........................................................................................................... 35

표 21. RTLPLOWFRAGHEAPALLOCATEFROMZONE ................................................................................................................. 36

표 22. RTLPSUBSEGMENTINITIALIZE .......................................................................................................................................... 38

표 23. ACTIVESUBSEGMENT ASSIGNMENT ............................................................................................................................... 40

표 24. RTLFREEHEAP .................................................................................................................................................................... 45

표 25. RTLPFREEHEAP BLOCKSINDEX SEARCH ......................................................................................................................... 46

표 26. RTLPFREEHEAP LISTHINT RETRIEVAL ............................................................................................................................. 47

표 27. RTLPFREEHEAP LFH COUNTER DECREMENT ................................................................................................................ 47

표 28. RTLPFREEHEAP CHUNK COALESCING AND HEADER REASSIGNMENT ....................................................................... 48

표 29. RTLPFREEHEAP INSERTION POINT SEARCH .................................................................................................................. 49

표 30. SAFE LINK-IN ..................................................................................................................................................................... 51

표 31. RTLPLOWFRAGHEAPFREE SUBSEGMENT ACQUISITION .............................................................................................. 54

Page 5: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 5 / 95

표 32. RTLPLOWFRAGHEAPFREE OFFSETANDDEPTH / SEQUENCE UPDATE ....................................................................... 55

표 33. RTLPLOWFRAGHEAPFREE EPILOG .................................................................................................................................. 57

표 34. RTLCREATEHEAP RANDOMIZATION ............................................................................................................................... 62

표 35. HEAP HEADER ENCODING ............................................................................................................................................... 64

표 36. HEAP HEADER DECODING ............................................................................................................................................... 64

표 37. DEATH OF BITMAP FLIPPING .......................................................................................................................................... 65

표 38. DEATH OF BITMAP FLIPPING 2 ...................................................................................................................................... 66

표 39. SAFE LINKING .................................................................................................................................................................... 66

표 40. ENABLE THE LFH FOR A SPECIFIC SIZE ......................................................................................................................... 68

표 41. LFH CHUNK OVERFLOW ................................................................................................................................................. 70

표 42. LFH CHUNK OVERFLOW RESULT .................................................................................................................................... 71

표 43. CHUNK REUSE ................................................................................................................................................................... 71

표 44. DATA SEEDING ................................................................................................................................................................... 72

표 45. DATA SEEDING RESULTS ................................................................................................................................................... 73

표 46. USE-AFTER-FREE CONTRIVED EXAMPLE(SOTORIV 2007)........................................................................................... 75

표 47. CHUNK HEADER RELOCATION ......................................................................................................................................... 76

표 48. C++ CONTRIVED EXAMPLE ............................................................................................................................................ 77

표 49. TRY/CATCH FOR LFH ALLOCATION ............................................................................................................................... 80

표 50. FREEENTRYOFFSET OVERWRITE EXAMPLE ..................................................................................................................... 80

표 51. SUBSEGMENT ACQUISITION ............................................................................................................................................ 88

표 52. SUBSEGMENT OVERWRITE .............................................................................................................................................. 91

표 53. SUBSEGMENT VALIDATION .............................................................................................................................................. 92

Page 6: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 6 / 95

그림 목차

그림 1. DATA STRUCTURE OVERVIEW ........................................................................................................................................ 18

그림 2. WINDOWS XP FREELIST RELATIONSHIPS ................................................................................................................... 19

그림 3. NEW FREELIST RELATIONSHIP ...................................................................................................................................... 21

그림 4. BACK-END ALLOCATION ................................................................................................................................................ 30

그림 5. FRONT-END ALLOCATION ............................................................................................................................................. 41

그림 6. FULL USERBLOCK FOR BUCKET 0X6 ........................................................................................................................... 42

그림 7. USERBLOCK AFTER 1ST ALLOCATION FOR 0X30 BYTES ........................................................................................... 43

그림 8. USERBLOCK AFTER THE 2ND CONSECUTIVE ALLOCATION FOR 0X30 BYTES ......................................................... 44

그림 9. RTLPFREEHEAP OVERVIEW ............................................................................................................................................ 54

그림 10. RTLPLOWFRAGHEAPFREE OVERVIEW ....................................................................................................................... 58

그림 11. USERBLOCK AFTER 3RD CONSECUTIVE ALLOCATION FOR 0X30 BYTES .............................................................. 59

그림 12. FREEING OF THE 1ST CHUNK ALLOCATED FOR 0X30 BYTES ................................................................................. 60

그림 13. FREEING OF THE 2ND CHUNK ALLOCATED FOR 0X30 BYTES ................................................................................ 61

그림 14. HEAPBIN CHUNK ......................................................................................................................................................... 76

그림 15. OVERWRITTEN HEAPBIN CHUNK .............................................................................................................................. 77

그림 16. CHUNK SETUP .............................................................................................................................................................. 78

그림 17. CHUNK OVERWRITE AND FREE ................................................................................................................................... 78

그림 18. USERBLOCK AFTER CHUNKING ................................................................................................................................... 82

그림 19. FREEENTRYOFFSET OVERWRITE ................................................................................................................................. 83

그림 20. SECOND ALLOCATION, SETTING OVERWRITTEN FREEENTRYOFFSET .................................................................... 84

그림 21. MULTIPLE USERBLOCKS .............................................................................................................................................. 85

그림 22. CROSS PAGE OVERWRITE ............................................................................................................................................ 86

그림 23. USERBLOCK RESIDING BEFORE SUBSEGMENT POINTERS IN MEMORY ................................................................ 90

그림 24. OVERWRITE INTO _HEAO_SUBSEGMENT .......................................................................................................... 91

Page 7: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 7 / 95

Introduction

수년 간, Windows heap exploitation은 알고리즘과 데이터 구조체를 더욱 복잡하게 구현한

exploitation 대응책 때문에 어려움이 증가하는 쪽으로 진행되었다. 이런 trend와 커뮤니티 내

포괄적인 heap 지식의 부족으로, 신뢰성 있는 exploitation은 심하게 줄어들었다. Heap

manager 내부 작동의 완벽한 이해를 유지하는 것은 예상치 못한 실패와 정확한 exploitation의

다름을 구분할 수 있게 한다.

Low Fragmentation heap은 Windows Vista에서 소개된 이후로 Windows 운영 시스템의 기본

front-end heap manager가 되었다. 이 새로운 front-end manager는 기존의 Lookaside List

를 대체하는 다른 데이터 구조체 집합 및 알고리즘을 가져왔다. 시스템 역시 back-end memory

management 동작 방법으로 변경되었다. 이 모든 자료는 Windows 7상의 어플리케이션 내 메

모리의 할당과 삭제로 인해 발생되는 영향을 이해하기 위해 반드시 리뷰해야만 한다.

이 문서의 주요 목적은 독자로 하여금 Low Fragmentation heap과 관련되어 새롭게 생성된 로

직과 데이터 구조체에 대하여 익숙하게 하는 것이다. 먼저, 새로운 데이터 구조체와 heap

manager 내에서의 그들의 두 가지 목적에 대한 설명을 통해 분명하고 간결한 기초가 제공될 것

이다. 그리고 이들 데이터 구조체들을 조작하는 근본적인 알고리즘에 관련된 세부적인 설명을 할

것이다. 마지막으로, 새로이 알아낸 지식으로부터 실제 어플리케이션에 적용되는 몇 가지 새로이

고안된 exploitation 기술들이 공개될 것이다.

Overview

이 문서는 네 개의 별도 섹션으로 나누어져 있다. 첫 번째 섹션은 heap manager가 메모리의 추

적을 유지하기 위해 사용하는 핵심 데이터 구조체들의 세부사항이다. 이 데이터 구조체들의 철저

한 이해는 이 문서의 나머지 부분에서 제시된 자료를 이해하기 위해 반드시 필요하다.

두 번째 섹션은 Windows Vista에서 도입되어 Windows7 에도 이어진 새롭게 생성된

Architecture에 대해 설명한다. 이 섹션에서는 Windows XP 코드 기반에서 발전된 데이터 구조

체를 사용하는 방법을 보여준다.

세 번째 섹션은 Windows 7 heap이 사용하는 핵심 알고리즘에 대한 심층 정보를 공개한다. 이

섹션은 front-end 및 back-end 방식 양쪽에서의 heap management subsystem을 설명한다.

이 섹션의 자료를 이해하는 것은 exploit 개발에 도움이 되고 네 번째 섹션을 위한 프레임워크를

제공할 것이다.

이 문서의 네 번째와 마지막 부분은 안정적인 heap 조작, 사용자에 의해 제공된 정보의 씨앗,

그리고 코드 실행을 달성하기 위해 heap 메타 데이터 남용을 생산하는 근본적인 heap

manager를 사용하기 위해 채택될 전술을 보여준다.

Page 8: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 8 / 95

Prior Works

Low Fragmentation heap와 관련하여 이용 가능한 더 많은 정보가 있을 수 있지만, 나는 단지

내가 연구할 때 사용했던 몇 가지의 리소스만을 나열하고자 한다. 이 자료는 이 문서를 이해하기

위한 필수적인 재료로 볼 수 있다고 믿는다. 나는 이 리스트에서 빠진 사람에게 미리 사과한다.

▪ 난 여전히 Ben Hawkes가 수년 전에 밝혀낸 것을 확신한다. 2008년 RuxCon/Blackhat

에서 그의 프리젠테이션은 내 작업에 영감을 지속적으로 주고 있다. (Hawkes 2008).

▪ Nico Waisman은 Windows Vista reversing이라는 굉장한 일을 했고, Immunity

Debugger 플러그인 libheap.py에서 자세한 정보를 제공한다. (Waisman 2008).

▪ Heap에 관한 Brett Moore의 문서 Heaps는 내 생각에 지금껏 고안된 최고의 heap 프

리젠테이션 중의 하나이다. 나는 그것이 훌륭한 heap 작업을 다루는데 영원히 참조될

것이라고 생각한다. (Moore 2007).

▪ Brett Moore는 또한 이 문서 내에 직접 게시된 Windows XP SP2 내 FreeList[0]에서

link-in process를 exploiting 하는데 사용되는 자료를 공개했다. (Moore 2005).

▪ ToorCon 2006에서 Richard Johnson의 프리젠테이션은 Windows Vista에서 새로이 생

성된 Low Fragmentation heap에 관해 설명했다. 이것은 알고리즘과 데이터 구조체에

관한 세부 설명을 제공한 최초의 (그리고 아마도 유일한) 자료이다. (Johnson 2006).

▪ David B. Probert 박사의 프리젠테이션은 Low Fragmentation heap의 성능상의 이익

에 대해 주로 초점을 맞추었지만, 그것은 Windows 7에서 나중에 heap 구현이 변화된

이유를 이해하려 할 때 여전히 꽤 유용하다. (Probet)

▪ Adrian Marinescu는 Blackhat 2006에서 Windows Vista heap 변화에 대해 프리젠테

이션 했다. 그것은 이전 heap manager로부터 전환된 이유를 명확하게 보여준다.

(Marinescu 2006).

▪ 마지막으로, Lionel d’Hauenens(http://www.laboskopia.com)의 Symbol Type

Viewer는 Windows 7 heap manager에 의해 사용되는 데이터 구조체를 분석할 때 매

우 중요한 툴이었다. 그것이 없었다면 적절한 구조체를 찾는데 많은 시간이 소비되었을

것이다.

Prerequisites

달리 명시되지 않는 한 이 문서에서 사용된 모든 의사코드와 구조체는 32bit Windows 7

ntdll.dll version 6.1.7600.16385에서 추출되었다. 구조체 정의는 Symbol Type Viewer tool과

Windbg를 사용하여 Microsoft Symbol Server의 라이브러리로부터 제공되었다.

이 코드의 의사코드 표현은heap 관리 알고리즘에 가장 일반적으로 사용되도록 초점을 맞추어 제

공하기 위해 간결하게 많이 수정되었다. 만약 당신이 코드의 틀린 곳을 발견했다면

[email protected] 으로 연락하라. I wil give you dollar.

Page 9: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 9 / 95

Terminology

많은 논문들이 Windows heap에 관하여 작성되어 왔지만 불행하게도 나는 모든 자료들에 걸쳐

사용되는 다양한 용어를 보았다. 이 문서에서 사용되는 용어는 다른 사람들에 의해 사용된 것과

다들 수도 있지만 나는 이 문서 전체의 일관성을 유지하기 위해 그것들을 정의하고 싶다.

Block 또는 blocks는 8바이트의 연속된 메모리를 참조한다. 이것은 그들의 크기를 참조할 때

heap chunk header들에 의해 사용되는 측정 단위이다. Chunk는 block 또는 byte로 측정될 수

있는 메모리의 연속된 부분이다.

HeapBase는 Windows Debugging Symbols에 의해 정의된 _HEAP 구조체 포인터를 위한 가명

이다. 이 문서 전체에 걸쳐서 object는 HeapBase로부터 일정한 offset에 있다고 정의한다. 또한

Low Fragmentation Heap은 어떤 장소에서 LFH로 단축된 형태로 표현된다.

BlocksIndex는 _heap_LIST_LOOKUP 구조체의 또 다른 이름이다. 이 두 용어는 서로 교환하

여 사용될 수 있다. 0x400(1024) 바이트 하위의 chunk들을 유지하는 리스트를 관리하는

BlocksIndex 구조체는 첫 번째 BlocksIndex로써 참조된다. 반면에 0x400-0x4000(16k) 범위

의 chunk들을 유지하는 리스트를 관리하는 구조체는 두 번째 BlocksIndex로써 참조된다. 16k

보다 크고 DeCommitThreshold 와 0xFE00 block 보다 아래인 chunk들은 FreeList[0]와 동일

한 구조체(이 문서의 나중에 논의됨) 내에서 관리된다.

전용 FreeList들을 가지는 개념은 사라졌다. 용어 ListHint 또는 FreeList는 특정 위치에서

Heap->FreeLists 로 가리키는 리스트들을 참조할 때 사용된다. 이것은 Architecture 섹션 내에

서 더욱 자세히 논의될 것이다.

마지막으로 HeapBin, Bin 또는 UserBlock 용어는 특정 크기를 위한 Low Fragmentation heap

로부터 할당된 메모리를 참조할 때 사용왼다.

나는 대부분의 이 호출이 HeapBucket이라는 것을 알지만, Microsoft의 debugging symbols가

크기를 지정하지만 메모리 억제를 위해서 사용되지는 않는 0X4 바이트 데이터 구조체를 위해 사

용한 이름인 _HEAP_BUCKET과의 혼동을 제거하기 위해서 해당 용어 사용을 자제할 것이다.

Notes

이 문서는 John McDonald와 내가 Blackhat USA 2009를 위해 완료된 작업에 대한 후속적인

지식의 역할을 하기 위한 것이다. Doubly linked list, Lookaside list, 등의 내부적인 작동에 관

한 모든 정보는 Practical Windows XP/2003 Exploitation 이라는 제목의 문서에서 찾을 수 있

다. (McDonald/Valasek 2009).

Page 10: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 10 / 95

Data Structures

이런 데이터 구조체들은 ntdll.dll version 6.1.7600.16385를 위한 Windows Debugging

Symbols으로부터 추출되었다. 그들은 주로 HeapAlloc(), HeapFree(), malloc(), free() 같은

추상 함수 호출을 통해 가상 메모리에 원할한 접근을 사용자에게 제공하는 manager 내에서 메

모리의 추적을 유지하는데 사용된다.

_HEAP (HeapBase)

각 heap은 HeapBase로 불리는 핵심 구조체로 시작하여 생성된다. HeapBase는 많은 중요한 값

들과 heap manager에 의해 사용되는 구조체들을 가리키는 포인터들을 포함하고 있다. 이것은

모든 heap의 심장부이며 신뢰성 있는 할당 및 해제 오퍼레이션을 제공하기 위해 자신의 무결성

을 유지해야만 한다. 만약 당신이 Windows XP 코드 기반에서 사용되는 HeapBase에 익숙하다

면 이것은 매우 비슷하게 보일 것이지만 아직 굵게 표현된 항목 중의 일부는 조금 더 설명이 필

요할 것이다. 아래는 32bit Windows 7 Service Pack 0 내의 _HEAP 구조체의 내용을 보여준다.

표 1. _HEAP via windbg

0:001> dt _HEAP

ntdll!_HEAP

+0x000 Entry : _HEAP_ENTRY

+0x008 SegmentSignature : Uint4B

+0x00c SegmentFlags : Uint4B

+0x010 SegmentListEntry : _LIST_ENTRY

+0x018 Heap : Ptr32 _HEAP

+0x01c BaseAddress : Ptr32 Void

+0x020 NumberOfPages : Uint4B

+0x024 FirstEntry : Ptr32 _HEAP_ENTRY

+0x028 LastValidEntry : Ptr32 _HEAP_ENTRY

+0x02c NumberOfUnCommittedPages : Uint4B

+0x030 NumberOfUnCommittedRanges : Uint4B

+0x034 SegmentAllocatorBackTraceIndex : Uint2B

+0x036 Reserved : Uint2B

+0x038 UCRSegmentList : _LIST_ENTRY

+0x040 Flags : Uint4B

+0x044 ForceFlags : Uint4B 8

+0x048 CompatibilityFlags : Uint4B

+0x04c EncodeFlagMask : Uint4B

+0x050 Encoding : _HEAP_ENTRY

+0x058 PointerKey : Uint4B

+0x05c Interceptor : Uint4B

+0x060 VirtualMemoryThreshold : Uint4B

+0x064 Signature : Uint4B

+0x068 SegmentReserve : Uint4B

Page 11: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 11 / 95

+0x06c SegmentCommit : Uint4B

+0x070 DeCommitFreeBlockThreshold : Uint4B

+0x074 DeCommitTotalFreeThreshold : Uint4B

+0x078 TotalFreeSize : Uint4B

+0x07c MaximumAllocationSize : Uint4B

+0x080 ProcessHeapsListIndex : Uint2B

+0x082 HeaderValidateLength : Uint2B

+0x084 HeaderValidateCopy : Ptr32 Void

+0x088 NextAvailableTagIndex : Uint2B

+0x08a MaximumTagIndex : Uint2B

+0x08c TagEntries : Ptr32 _HEAP_TAG_ENTRY

+0x090 UCRList : _LIST_ENTRY

+0x098 AlignRound : Uint4B

+0x09c AlignMask : Uint4B

+0x0a0 VirtualAllocdBlocks : _LIST_ENTRY

+0x0a8 SegmentList : _LIST_ENTRY

+0x0b0 AllocatorBackTraceIndex : Uint2B

+0x0b4 NonDedicatedListLength :

▪ EncodeFlagMask – 만약 heap chunk header가 인코딩 되었는지 확인하는데 사용되

는 값. 이 값은 RtlCreateHeap() 내 RtlpCreateHeapEncoding()에 의해 0x100000으

로 초기화 되어있다.

▪ Encoding – 예측 가능한 meta-data corruption을 금지하기 위한 chunk headers를

인코딩 하기 위해 XOR 오퍼레이션에 사용된다.

▪ BlocksIndex – 다양한 목적을 위해 사용되는 _HEAP_LIST_LOOKUP 구조체이다. 이

것의 중요성으로 인해 이 문서의 뒷 부분에서 더 자세히 논의될 것이다.

▪ FreeLists – 이 heap을 위한 모든 free chunk 포인터를 포함하는 특별 연결 리스트.

그것은 heap cache로 생각되지만, 모든 크기의 chunk를 위한 것이다. (그리고 하나의

연관된 비트맵이 아니다).

▪ FrontEndHeapType – 0x0으로 초기화 되는 정수이며, 이후 LFH의 사용을 가리키는

0ㅌ2 값으로 할당된다. 참고: Windows 7은 실제로 Lookaside Lists 사용을 지원하지

않는다.

▪ FrontEndHeap – front-end heap에 연관된 포인터. NULL 값이거나 Windows 7이 실

행될 때 _LFH_HEAP 구조체를 가리킨다

_HEAP_LIST_LOOKUP (HeapBase->BlocksIndex)

_HEAP_LIST_LOOKUP 구조체를 이해하는 것은 Windows 7 heap management의 굳건한 기

초를 다지기 위한 가장 중요한 작업 중의 하나이다. 이것은 back-end manager와 front-end

manager에 의해 사용되는 할당과 해제의 머릿돌이다. 정상적인 조건 하에서 RtlCreateHeap()

내에서 초기화되는 첫 번째 _HEAP_LIST_LOOKUP 구조체는 HeapBase로부터 +0x150 오프셋

Page 12: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 12 / 95

거리에 위치한다.

표 2. _HEAP_LIST_LOOKUP via windbg

0:001> dt _HEAP_LIST_LOOKUP

ntdll!_HEAP_LIST_LOOKUP

+0x000 ExtendedLookup : Ptr32 _HEAP_LIST_LOOKUP

+0x004 ArraySize : Uint4B

+0x008 ExtraItem : Uint4B

+0x00c ItemCount : Uint4B

+0x010 OutOfRangeItems : Uint4B

+0x014 BaseIndex : Uint4B

+0x018 ListHead : Ptr32 _LIST_ENTRY

+0x01c ListsInUseUlong : Ptr32 Uint4B

+0x020 ListHints : Ptr32 Ptr32 _LIST_ENTRY

▪ ExtendedLookup – 다음 _HEAP_LIST_LOOKUP 구조체를 가리키는 포인터. 만약

더 이상의 ExtendedLookup이 없을 경우 NULL이다.

▪ ArraySize – 최상위 block 크기이며 이 구조체는 추적된다.다시 말해 특정 ListHint

내에 해당 구조체가 저장된다. Windows 7은 현재 0x80과 0x800의 두 가지 크기만을

사용하고 있다.

▪ OutOfRangeItems – 이 4바이트의 값은 FreeList[0]과 비슷한 구조체 내 아이템들

의 숫자를 카운트한다. 각 _HEAP_LIST_LOOKUP은 ListHint[ArraySize-BaseIndex-1]

내 ArraySize -1 보다 큰 free chunk를 추적한다.

▪ BaseIndex – 각 _HEAP_LIST_LOOKUP이 특정 크기에 대해 지정되어 있기 때문에

ListHint 배열 속의 연관된 오프셋을 찾는데 사용된다. 예를 들어, 첫 번째 BlocksIndex

의 BaseIndex는 그것이 0x0-0x80 사이의 chunk들의 리스트를 관리하기 때문에 0x0

이다. 반면에 두 번째 BlocksIndex는 0x80의 BaseIndex를 가진다.

▪ ListHead – HeapBase->FreeLists와 동일한 위치를 가리키는 포인터이며, heap이 사

용가능한 모든 free chunk들의 연결 리스트이다.

▪ ListsInUseUlong – 공식적으로 FreeListInUseBitmap으로 알려진 이 4바이트 정수

는 ListHints가 가진 사용가능한 chunk들을 결정하는데 사용하는 최적화이다.

▪ ListHints – 이들 링크들은 메모리의 free chunk들의 포인터를 제공하는 FreeLists로

알려져 있는 반면에 또 다른 목적도 제공한다.

_LFH_HEAP (HeapBase->FrontEndHeap)

Low Fragmentation heap은 이 데이터 구조체에 의해 관리된다. 활성화 되면, 그것은 이전에

사용된 chunk들의 cache들을 유지하는 한편 heap manager가 관리 가능한 크기가 얼마인지

알려준다. BlocksIndex는 길이 0x800 블록들을 초과하는 chunk들의 추적이 가능한 반면에,

LFH는 오직 16k 보다 작은 chunk들에게 사용된다.

Page 13: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 13 / 95

표 3. _LFH_HEAP via windbg

0:001> dt _LFH_HEAP

ntdll!_LFH_HEAP

+0x000 Lock : _RTL_CRITICAL_SECTION

+0x018 SubSegmentZones : _LIST_ENTRY

+0x020 ZoneBlockSize : Uint4B

+0x024 Heap : Ptr32 Void

+0x028 SegmentChange : Uint4B

+0x02c SegmentCreate : Uint4B

+0x030 SegmentInsertInFree : Uint4B

+0x034 SegmentDelete : Uint4B

+0x038 CacheAllocs : Uint4B

+0x03c CacheFrees : Uint4B

+0x040 SizeInCache : Uint4B

+0x048 RunInfo : _HEAP_BUCKET_RUN_INFO

+0x050 UserBlockCache : [12] _USER_MEMORY_CACHE_ENTRY

+0x110 Buckets : [128] _HEAP_BUCKET

+0x310 LocalData : [1] _HEAP_LOCAL_DATA

▪ Heap – 이 LFH의 부모 heap 포인터

▪ UserBlockCache – 이것은 철저하게 논의되지는 않지만, 미래의 할당을 위해 이전에

사용된 메모리 chunk들의 추적을 유지하는 UserBlockCache 배열을 언급할 가치가 있

다.

▪ Buckets – 색인과 크기의 트랙을 유지하기 위한 목적으로만 사용되는 0x04 바이트 데

이터 구조체들의 배열이다. 이것은 왜 Bin이라는 용어가 특정 Bucket을 위한 요청을 수

행하는데 사용된 메모리의 영역을 설명하는데 사용하는지의 이유이다.

▪ LocalData – 이것은 각 SubSegment에 관한 정보를 유지하는 대형 데이터 구조체에

대한 포인터이다. 자세한 내용은 _HEAP_LOCAL_DATA를 참고하라.

_LFH_BLOCK_ZONE (HeapBase->FrontEndHeap->LocalData->CrtZone)

이 데이터 구조체는 서비스 할당 요청들에 사용된 메모리의 위치를 추적하는데 사용된다. 이런

포인터들은 LFH에 의해 제공되는 첫 번째 요청에 의해 설치되거나 포인터 리스트가 소진된 후에

설치된다.

표 4. _LFH_BLOCK_ZONE via windbg

0:000> dt _LFH_BLOCK_ZONE

ntdll!_LFH_BLOCK_ZONE

+0x000 ListEntry : _LIST_ENTRY

+0x008 FreePointer : Ptr32 Void

Page 14: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 14 / 95

+0x00c Limit : Ptr32 Void

▪ ListEntry – LFH_BLOCK_ZONE 구조체의 연결 리스트.

▪ FreePointer – _HEAP_SUBSEGMENT에 의해 사용될 수 있는 메모리의 포인터를 유

지한다.

▪ Limit – 리스트 내 마지막 _LFH_BLOCK_ZONE 구조체. 이 값에 도달하거나 초과하면,

back-end heap은 더 많은 _LFH_BLOCK_ZONE 구조체를 생성하기 위해 사용된다.

_HEAP_LOCAL_DATA (HeapBase->FrontEndHeap->LocalData)

Low Fragmentation heap에 _HEAP_LOCAL_SEGMENT_INFO 인스턴스를 제공하는 주요 구조

체.

표 5. _HEAP_LOCAL_DATA via windbg

0:000> dt _HEAP_LOCAL_DATA

ntdll!_HEAP_LOCAL_DATA

+0x000 DeletedSubSegments : _SLIST_HEADER

+0x008 CrtZone : Ptr32 _LFH_BLOCK_ZONE

+0x00c LowFragHeap : Ptr32 _LFH_HEAP

+0x010 Sequence : Uint4B

+0x018 SegmentInfo : [128] _HEAP_LOCAL_SEGMENT_INFO

▪ LowFragHeap – 이 구조체와 연관된 Low Fragmentation heap.

▪ SegmentInfo – 이 LFH를 위해 사용가능한 모든 크기를 나타내는

_HEAP_LOCAL_SEGMENT_INFO 구조체의 배열. 자세한 것은

_HEAP_LOCAL_SEGMENT_INFO를 참고하라.

_HEAP_LOCAL_SEGMENT_INFO (HeapBase->FrontEndHeap->LocalData->SegmentInfo[])

사용된 _HEAP_LOCAL_SEGMENT_INFO 구조체를 결정하는 제공될 수 있는 요청의 크기. 이

구조체는 메모리의 할당 및 해제의 가장 효츌적인 방법을 결정할 때 사용되는 heap 알고리즘의

정보를 유지한다. _HEAP_LOCAL_DATA 내에 이 구조체들이 오직 128개만 있지만, 16k 하위의

8바이트로 정렬된 모든 사이즈는 상응하는 _HEAP_LOCAL_SEGMENT_INFO를 가진다. 각

Bucket이 지정된 구조체를 가짐을 보장하기 위해 특정 알고리즘은 상대적으로 인덱스를 계산한

다.

표 6. _HEAP_LOCAL_SEGMENT_INFO via windbg

0:000> dt _HEAP_LOCAL_SEGMENT_INFO

ntdll!_HEAP_LOCAL_SEGMENT_INFO

+0x000 Hint : Ptr32 _HEAP_SUBSEGMENT

Page 15: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 15 / 95

+0x004 ActiveSubsegment : Ptr32 _HEAP_SUBSEGMENT

+0x008 CachedItems : [16] Ptr32 _HEAP_SUBSEGMENT

+0x048 SListHeader : _SLIST_HEADER

+0x050 Counters : _HEAP_BUCKET_COUNTERS

+0x058 LocalData : Ptr32 _HEAP_LOCAL_DATA

+0x05c LastOpSequence : Uint4B

+0x060 BucketIndex : Uint2B

+0x062 LastUsed : Uint2B

▪ Hint – 이 SubSegment는 LFH가 관리하는 chunk가 해제될 때에만 설정된다. 만약

chunk가 해제되지 않을 경우, 이 값은 항상 NULL 이다.

▪ ActiveSubsegment – 대부분의 메모리 요청들에 사용되는 SubSegment. NULL로

초기화되는 반면에, 특정 크기를 위한 첫 번째 할당 시에 설정된다.

▪ LocalData – 이 구조체에 연관된 _HEAP_LOCAL_DATA 구조체.

▪ BucketIndex – 각 SegmentInfo 객체는 특정 Bucket 크기(또는 인덱스)에 관련되어

있다.

_HEAP_SUBSEGMENT (HeapBase->FrontEndHeap->LocalData->SegmentInfo[]-

>Hint,ActiveSubsegment,CachedItems)

절절한 구조체가 특정 _HEAP_BUCKET를 위해 식별되고 난 후에, front-end manager가 해제

또는 할당을 수행한다. LFH가 heap manager 내부의 heap manager로 생각되기 때문에, 사용

가능한 메모리가 얼마나 되는지, 어떻게 배포되었는지 추적하는데_HEAP_SUBSEGMENT가 사용

된다는 것이 중요하다.

표 7. _HEAP_SUBSEGMENT via windbg

0:000> dt _HEAP_SUBSEGMENT

ntdll!_HEAP_SUBSEGMENT

+0x000 LocalInfo : Ptr32 _HEAP_LOCAL_SEGMENT_INFO 13

+0x004 UserBlocks : Ptr32 _HEAP_USERDATA_HEADER

+0x008 AggregateExchg : _INTERLOCK_SEQ

+0x010 BlockSize : Uint2B

+0x012 Flags : Uint2B

+0x014 BlockCount : Uint2B

+0x016 SizeIndex : UChar

+0x017 AffinityIndex : UChar

+0x010 Alignment : [2] Uint4B

+0x018 SFreeListEntry : _SINGLE_LIST_ENTRY

+0x01c Lock : Uint4B

▪ LocalInfo – 이 구조체와 관련된 _HEAP_LOCAL_SEGMENT_INFO 구조체.

▪ UserBlocks – n개의 chunk들로 분할된 대형 메모리의 chunk를 유지하는

Page 16: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 16 / 95

SubSegment와 결합된 _HEAP_USERDATA_HEADER 구조체

▪ AggregateExchg – 현재 Offset과 Depth를 추적하는데 사용되는

_INTERLOCK_SEQ 구조체

▪ SizeIndex – 이 SubSegment를 위한 _HEAP_BUCKET SizeIndex.

_HEAP_USERDATA_HEADER (HeapBase->FrontEndHeap->LocalData->SegmentInfo[]-

>Hint,ActiveSubsegment,CachedItems->UserBlocks)

이 헤더는 Low Fragmentation heap을 위한 모든 요청을 서비스하는데 사용되는 UserBlock

chunk 앞에 있다. SubSegment를 위치하기 위해 모든 로직이 수행된 후 이 구조체는 커밋된

메모리가 실제로 조작된 곳이 어디인지를 나타낸다.

표 8. _HEAP_USERDATA_HEADER via windbg

0:000> dt _HEAP_USERDATA_HEADER

ntdll!_HEAP_USERDATA_HEADER

+0x000 SFreeListEntry : _SINGLE_LIST_ENTRY

+0x000 SubSegment : Ptr32 _HEAP_SUBSEGMENT

+0x004 Reserved : Ptr32 Void

+0x008 SizeIndex : Uint4B

+0x00c Signature : Uint4

_INTERLOCK_SEQ (HeapBase->FrontEndHeap->LocalData->SegmentInfo[]-

>Hint,ActiveSubsegment,CachedItems->AggregateExchg)

UserBlock chunk들을 분할하는 방식으로 인해 다음 chunk를 해제하거나 할당하기 위해 현재

오프셋을 얻는 방법이 있을 필요가 있다. 이 프로세스는 _INTERLOCK_SEQ 데이터 구조체에 의

해 처리된다.

표 9. _INTERLOCK_SEQ via windbg

0:000> dt _INTERLOCK_SEQ

ntdll!_INTERLOCK_SEQ

+0x000 Depth : Uint2B

+0x002 FreeEntryOffset : Uint2B

+0x000 OffsetAndDepth : Uint4B

+0x004 Sequence : Uint4B

+0x000 Exchg : Int8B

▪ Depth – 얼마나 많은 chunk가 UserBlock 내에 남아있는지 추적하는 카운터. 이 숫자

는 해제 시 증가하고 할당 시에 감소한다. 그 값은 HeapBucket 크기로 나누어진

UserBlock의 크기로 초기화 된다.

Page 17: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 17 / 95

▪ FreeEntryOffset – 이 2바이트의 정수는 _HEAP_USERDATA_HEADER의 주소에 추

가될 때 해제되거나 할당된 메모리의 다음 위치에 대한 포인터 내 결과 값을 유지한다.

이 값은 블록(0x8 바이트 chunk)로 표현되며 sizeof(_HEAP_USERDATA_HEADER)는

0X10과 같도록 0x2로 초기화 된다.[0x2 * 0x8 == 0x10].

▪ OffsetAndDepth – Depth와 FreeEntryOffset은 양쪽 모두 2바이트이기 때문에, 그

들은 이 단일 4바이트 값에 조합되어 사용될 수 있다.

_HEAP_ENTRY (Chunk Header)

Heap chunk header로 알려진 _HEAP_ENTRY는 heap(심지어 UserBlocks 내부의 chunk들 마

저도) 내 모든 메모리 chunk 전에 저장된 8바이트의 값이다. 그것은 새로운 버전의 Windows에

소개된 보안 및 헤더 검증 내 수정 때문에 Windows XP code 기반 이후 꽤 많이 변경되었다.

표 10. _HEAP_ENTRY via windbg

_HEAP_ENTRY

ntdll!_HEAP_ENTRY

+0x000 Size : Uint2B

+0x002 Flags : UChar

+0x003 SmallTagIndex : Uchar

+0x000 SubSegmentCode : Ptr32 Void

+0x004 PreviousSize : Uint2B

+0x006 SegmentOffset : Uchar

+0x006 LFHFlags : Uchar

+0x007 UnusedBytes : Uchar

+0x000 FunctionIndex : Uint2B

+0x002 ContextValue : Uint2B

+0x000 InterceptorValue : Uint4B

+0x004 UnusedBytesLength : Uint2B

+0x006 EntryOffset : Uchar

+0x007 ExtendedBlockSignature : Uchar

▪ Size – chunk의 블록 내 사이즈. 이것은 _HEAP_ENTRY 자신을 포함한다.

▪ Flags – 이 heap chunk의 상태를 나타내는 플래그. 예를 들어 FREE 또는 BUSY.

▪ SmallTagIndex – 이 값은 _HEAP_ENTRY의 처음 세 바이트들의 턖된 체크섬 값을

유지한다.

▪ UnusedBytes/ExtendedBlockSignature – 사용되지 않는 바이트 또는 LFH에 의

해 관리되는 chunk의 상태를 나타내는 바이트를 유지하는데 사용되는 값.

Page 18: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 18 / 95

Overview

그림 1. Data structure overview

Page 19: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 19 / 95

Architecture

Windows 7 heap manager는 Windows XP 시절부터 꽤 많이 변경되었다. 그래서 몇 가지 조정

된 구조를 살펴보고 갈 필요가 있다. 특히 FreeList 작업은 그들 내에 데이터가 저장되는 벙법과

함께 다시 설계되어 설명이 필요하다.

FreeLists

우리가 핵심 알고리즘에 관해 설명하기 전에, 현재와 이전의 FreeList 구조체를 점검해야만 한다.

이것은 FreeLists 오퍼레이션과 데이터 저장이 Windows XP 코드 기반 이후에 변경되었기 때문

이다. 이것은 John McDonald과 내가 쓴 이전의 문서에 정의된 FreeList 구조체의 개요이다.

1024 바이트 하위의 각각 가능한 크기에 대한 별도의 리스트는 총 128개의 free 리스트

를 제공한다. (heap block은 8의 배수 크기이다). 각 이중 연결 free 리스트는 heap의

base에서 배열 내에 위치한 sentinel head node를 가진다. 각 head node는 순방향 연결

(FLink), 역방향 연결(Blink) 두 개의 포인터를 포함한다. FreeList[1]은 사용하지 않으며

FreeList[2]에서 FreeList[127]를 전용 free 리스트라고 한다. 이 전용 리스트들을 위해 리

스트 내 모든 free block들은 배열의 인덱스 * 8에 대응하는 같은 크기를 가진다. 1024와

같거나 큰 크기의 모든 block들은 FreeList[0] 하나에 유지된다. (이 슬롯은 크기 0의 free

block들이 존재하지 않기 때문에 사용 가능하다.). 이 리스트의 free block들은 가장 작은

free block에서 가장 큰 block으로 정렬된다. 그래서 FreeList[0].Flink는 가장 작은 free

block(크기 >= 1024)을 가리키고, FreeList[0].Blink는 가장 큰 free block(크기 >= 1024)을

가리킨다.

그림 2. Windows XP FreeList relationships

Page 20: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 20 / 95

front-end manager가 동작하도록 LFH가 변경되었기 때문에, 이 방법은 back-end manager

에도 적용되었다. 이제 하나의 전용 FreeList가 없기 때문에, 대신 각 BlocksIndex는 NULL로

초기화 된 자체 ListHints를 유지한다.

BlocksIndex 구조체는 FreeLists 구조체 내부를 가리키는 ListHints로써 참조되는 포인터를 유

지한다. 예전 FreeLists와 매우 유사하게 오직 0번째 포지션만 설정되는 연결리스트는

0x400(1024) 바이트들 보다 큰 chunk를 위해 사용되지 않는 대신에 조건이 필요하다. 만약

BlocksIndex->ExtendedLookup이 없는 경우 BlocksIndex->ArraySize -1보다 크거나 같은

크기의 모든 chunk들은 FreeList[ArraySize-BaseIndex-1] 내 오름차순으로 저장된다.

FreeLists는 ListHints 포인터로부터의 계산된 오프셋 지점에 sentinel node를 포함함에도 불구

하고 FreeLists는 대부분 유사하게 끝에 존재한다. Flink 포인터는 계속 FreeList 내 사용 가능한

다음 chunk를 가리킬 뿐만 아니라 더 큰 FreeLists로 확장된다. (it can span into larger

Freelists as well). 이것은 Heap.FreeLists가 특정 heap을 위해 이용 가능한 모든 free chunk

를 횡단(traverse) 할 수 있도록 한다.

Sentinel node 내 Blink도 변경되었는데, 이중 목적을 제공한다. 만약 LFH가 Bucket에 대해 활

성화되어 있지 않다면, sentinel Blink는 할당 heuristic 내에서 사용되는 카운터를 유지한다.

(the sentinel Blink will hold a counter used in an allocation heuristic.). 그렇지 않을 경우,

LFH는 _HEAP_BUCKET + 1(ListHint[ArraySize–BaseIndex -1]의 경우를 제외하고)의 주소를

포함한다.

아래의 그림은 이런 새로운 구조가 어떻게 서로 상호작용 하는지를 보여주는 드문드문 존재하는

heap의 예이다. 그것은 크기가 1024바이트 하위의 chunk들을 추적하도록 설계된 단일

BlocksIndex를 포함한다. 이 heap과 관련된 다섯 개의 chunk가 있으며 그들은 다양한 방법으

로 접근할 수 있다.

예를 들어, 만약 0x30(48) 바이트의 할당 요청이 오면 heap은 ListHint[0x6]을 사용하려 할 것

이다. 그림에서 볼 수 있듯이 0x30 크기를 위해 사용 가능한 chunk는 오직 세 개 밖에 없음에

도 불구하고, 0x30 크기를 위한 마지막 free chunk 내 Flink는 FreeList[0x7]를 포함하는 엔트

리를 가리킨다. FreeList[0x7]은 하나의 엔트리를 가지지만 FreeList[0x6] 같이 마지막 chunk

는 크기 경계를 걸쳐 더 큰 chunk를 가리킨다(its last chunk points across the size boundary

into a larger chunk).

이것은 리스트가 끝나는 방법을 변경한다(This changes the way that list termination is done).

대신 리스트 내 마지막 node는 전용 FreeList 상의 자신의 sentinel node를 가리키는데 이것은

HeapBase로부터 +0xC4 지점의 FreeLists 엔트리를 가리킨다.

참고: _HEAP_LIST_LOOKUP 구조체가 RtlCreateHeap() 또는 RtlpExtendListLookup을 통해

Page 21: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 21 / 95

초기화 되면, ListHead는 Heap.FreeLists[HeapBase로부터 0xC4 지점]을 가리키도록 설정된

다. 이것은 엔트리들을 확인하고 메모리 내 동일 지역을 가리키게 한다(This makes both

entries identical and pointing to the same region in memory).

그림 3. New FreeList relationship

Algorithms

지식의 기본 기초가 heap 결정론과 공격을 완전히 이해하기 위해 반드시 마련되어야만 한다. 이

핵심 정보 없이는 어떤 것도 오직 pray-after-free 밖에 없다(Without this core information

one can only pray-after-free). 이 섹션은 back-end와 front-end로 나누어진 할당과 해제의

두 파트 내에서 핵심 알고리즘을 허물어뜨리는 것이다. 이것은 front 또는 back end에 의해 수

행된 메모리 조작이 다른 메모리의 상태에 영향을 미칠 수 있기 때문에 필요한 것이다.

Allocation

호출 어플리케션에서 서비스 요청을 시도할 때 RtlAllocateHeap() 에서 할당이 시작한다. 이 함

Page 22: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 22 / 95

수는 희망 할당 량을 8바이트로 정렬하는 것으로 시작한다. 그리고 ListHints 내 인덱스를 획득

한다. 만약 특정 인덱스가 없다면, BlocksIndex->ArraySize-1이 사용된다.

표 11. RtlAllocateHeap BlocksIndex Search

if(Size == 0x0)

Size = 0x1;

//ensure that this number is 8-byte aligned

int RoundSize = Round(Size);

int BlockSize = Size / 8;

//get the HeapListLookup, which determines if we should use the LFH

_HEAP_LIST_LOOKUP *BlocksIndex = (_HEAP_LIST_LOOKUP*)heap->BlocksIndex;

//loop through the HeapListLookup structures to determine which one to use

while(BlocksSize >= BlocksIndex->ArraySize)

{

if(BlocksIndex->ExtendedLookup == NULL)

{

BlocksSize = BlocksIndex->ArraySize - 1;

break;

}

BlocksIndex = BlocksIndex->ExtendedLookup;

}

검색이 BlocksIndex->ArraySize – 1의 ListHint 인덱스를 반환하는 조건이 존재한다. 만약 이

조건이 발생하는 경우 그 때 back-end 할당자가 NULL 값인 FreeList를 사용한다. 이것은

back-end 할당자가 Heap->FreeLists 사용을 시도하게 하는 원인이 된다. 만약 FreeLists가 충

분한 크기의 chunk를 포함하고 있지 않을 경우, heap은 RtlpExtendHeap()를 통해 확장된다.

특정 인덱스가 성공적으로 획득되었다면, heap manager는 요청된 크기를 위한 FreeList 사용

을 시도한다. 그런 다음 만약 Low Fragmentation heap 이 Bucket에 대해 활성화되어 있는지

확인하기 위해 FreeList->Blink를 보도록 진행된다. 그렇지 않을 경우 manager는 back-end를

기본 값으로 사용한다.

표 12. RtlAllocateHeap heap manager selector

//get the appropriate freelist to use based on size

int FreeListIndex = BlockSize - HeapListLookup->BaseIndex;

_LIST_ENTRY *FreeList = &HeapListLookup->ListHints[FreeListIndex];

if(FreeList)

{

Page 23: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 23 / 95

//check FreeList[index]->Blink to see if the heap bucket

//context has been populated via RtlpGetLFHContext()

//RtlpGetLFHContext() stores the HeapBucket

//context + 1 in the Blink

_HEAP_BUCKET *HeapBucket = FreeList->Blink;

if(HeapBucket & 1)

{

RetChunk = RtlpLowFragHeapAllocFromContext(HeapBucket-1, aBytes);

if(RetChunk && heap->Flags == HEAP_ZERO_MEMORY)

memset(RetChunk, 0, RoundSize);

}

}

//if the front-end allocator did not succeed, use the back-end

if(!RetChunk)

{

RetChunk = RtlpAllocateHeap(heap, Flags | 2, Size, RoundSize, FreeList);

}

Back-end Allocation

Back-end 할당자는 할당 내 마지막 라인이다. 만약 그것이 실패할 경우, 메모리에 대한 요청이

발생하지 않고 NULL을 리턴할 것이다. Front-end에 의해 제공될 수 없는 메모리 요청 서비스의

책임과 함께, back-end 역시 활성화된 heuristic를 기반으로 front-end 할당자를 활성화한다

(Along with the responsibilities of servicing memory requests unable to be serviced by

the front-end, the back-end also activates the front-end allocator based on activation

heuristics). 이것은 Window XP code 기반에서 동작하는 heap cache heuristic 방식과 매우

동일하다고 생각할 수 있다.

RtlpAllocateHeap

_HEAP 구조체, 할당 크기, 원하는 ListHint(FreeList)는 RtlpAllocateHeap()로 전달되는 인자의

일부이다. RtlAllocateHeap과 동일하게, 비즈니스 로직의 처음 순서는 요청된 크기를 가장 가까

운 8바이트로 정렬된 값으로 반올림하고 만약 Flag가 HEAP_NO_SERIALIZE 비트가 설정되었는

지를 확인하는 것이다. 만약 이 비트가 설정되면, LFH는 활성화 되지 않는다.

(http://msdn.microsoft.com/en-us/library/aa366599%28v=VS.85%29.aspx)

표 13. RtlpAllocateHeap size rounding and Heap maintenance

int RoundSize;

//if the FreeList isn't NULL, the rounding has already

//been preformed

if(FreeList)

Page 24: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 24 / 95

{

RoundSize = RoundSize;

}

else

{

int MinSize = 1;

if(Size)

MinSize = Size;

//rounds to the nearest 8-byte aligned number

RoundSize = Round(MinSize);

}

int SizeInBlocks = RoundSize / 8;

if(SizeInBlocks < 2)

{

//RoundSize += sizeof(_HEAP_ENTRY)

RoundSize = RoundSize + 8;

SizeInBlocks = 2;

}

//if NOT HEAP_NO_SERIALIZE, use locking mechanisms

//LFH CANNOT be enabled if this path isn't taken

if(!(Flags & HEAP_NO_SERIALIZE))

{

//setup locking mechanisms here

//if we have certain compaitibilityflags

//(either set below, or otherwise, then

//we will call 'RtlpPerformHeapMaintenance'

//which will activate the LFH and

//setup an ExtendedListLookup as well

if(Heap->CompatibilityFlags & 0x60000000)

RtlpPerformHeapMaintenance(Heap);

}

참고: 당신은 다음 코드에서 어떻게 CampatibilityFlags가 설정되는지 볼 수 있다. 이것은 Low

Fragmenttion heap이 활성화 되는 방법이다. LFH는 기본적으로 front-end manager임에도 불

구하고 특정 heuristic이 실행(triggered)되기 전까지 실제로 어떤 memory management 처리

도 하지 않는다.

이 시점에서 LFH는 비록 서비스 요청을 대기하고 있다 할지라도, back-end 할당자는 이 할당을

계속한다. 가상 메모리 요청을 조절하기 위한 코드를 벗어나면서 RtlpAllocateHeap()이

FreeList 인자가 NULL이 아닌지를 확인하려는 시도를 볼 수 있다. 유효한 FreeList 인자를 기다

Page 25: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 25 / 95

리는 동안, back-end manager는 LFH가 이후 할당을 위해 사용되어야 하는지를 판단하기 위해

사용되는 heuristic을 적용한다.

표 14. RtlAllocateHeap LFH Heuristic

if(FreeList != NULL)

{

//if this freelist doesn't hold a _HEAP_BUCKET

//update the counters and attempt to get the LFH context

if(!(FreeList->Blink & 1))

{

//add a certain amount to the blink

FreeList->Blink += 0x10002;

//if the counter has ran more than 0x10 times OR

//we haven't successfully entered the critical section OR

//we've been through 0x1000 iterations

//then attempt to set the Compatibility flags

//(which, in turn, will call RtlpPerformHeapMaintenance())

if(WORD)FreeList->Blink > 0x20 || FreeList->Blink > 0x10000000)

{

//if the FrontEndHeapType is LFH (0x2) assign it

int FrontEndHeap;

if(Heap->FrontEndHeapType == 0x2)

FrontEndHeap = Heap->FrontEndHeap;

else FrontEndHeap = NULL;

//this function gets a _HEAP_BUCKET

//stored in _LFH_HEAP->Bucket[BucketSize]

//if the LFH hasn't been activated yet, it will return NULL

char *LFHContext = RtlpGetLFHContext(FrontEndHeap, Size);

//if the context isn't set AND

//we've seen 0x10+ allocations, set the flags

if(LFHContext == NULL)

{

if((WORD)FreeList->Blink > 0x20)

{

//RtlpPerformHeapMaintenance heurstic

if(Heap->FrontEndHeapType == NULL)

Heap->CompatibilityFlags |= 0x20000000;

}

} else

{

//save the _HEAP_BUCKET in the Blink

//+1 == _HEAP_BUCKET

Page 26: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 26 / 95

FreeList->Blink = LFHContext + 1;

}

}

}

}

참고: 이것이 내가 front와 back end manager가 서로 밀접한 관련이 있다고 언급한 이유이다.

보시다시피 ListHint는 _HEAP_BUCKET의 주소를 저장하기 위해 사용되고 차례로 manager가

LFH를 사용해야 하는지 결정하는데 사용된다. 이 두 가지의 사용은 지금 당장은 혼란스럽겠지

만, front-end 할당과 해제 알고리즘을 논의한 후에 명확해질 것이다.

이제 LFH 활성화 플래그가 설정되고 할당자는 back-end 상의 과정을 계속 진행한다. FreeList

는 채워져 있는지를 확인한 후 safe unlink 검사가 수행된다. 이것은 unlink 시에 4바이트 덮어

쓰기를 방지하기 위해서 FreeList 값의 무결성 유지를 보장한다.

ListsInUseUlong(FreeListInUseBitmap)은 다음으로 상황에 맞추어 업데이트 된다. 마지막으로

리스트로부터 unlink된 chunk는 BUSY 로 표시된 업데이트된 헤더를 가지고 리턴된다.

표 15. RtlpAllocateHeap ListHint allocation

//attempt to use the Flink

if(FreeList != NULL && FreeList->Flink != NULL)

{

int SizeToUse;

//saved values

_HEAP_ENTRY *Blink = FreeList->Blink;

_HEAP_ENTRY *Flink = FreeList->Flink;

//get the heap chunk header by subtracting 8

_HEAP_ENTRY *ChunkToUseHeader = Flink - 8;

//decode the header if applicable

if(Heap->EncodeFlagMask)

DecodeAndValidateChecksum(ChunkToUseHeader);

//ensure safe unlinking before acquiring this chunk for use

if(Blink->Flink != Flink->Blink || Blink->Flink != FreeList)

{

RtlpLogHeapFailure();

//XXX RtlNtStatusToDosError and return

}

//decrement the total heap size

Heap->TotalFreeSize -= ChunkToUseHeader->Size;

Page 27: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 27 / 95

//iterate through the BlocksIndex structures

//If no sufficient BlocksIndex is found, use BlocksIndex->ArraySize - 1

_HEAP_LIST_LOOKUP *BlocksIndex = Heap->BlocksIndex;

if(BlocksIndex)

{

int ListHintIndex = GetListHintIndex(BlocksIndex, Size);

int RelativeOffset = ListHintIndex - BlocksIndex->BaseIndex;

//if there are more of the same size

//don't update the bitmap

if(Flink->Flink != BlocksIndex->ListHead && Flink.Size == Flink->Flink.Size)

{

BlocksIndex->ListHints[FreeListOffset] = Flink->Flink;

}

else

{

BlocksIndex->ListHints[FreeListOffset] = NULL;

BlocksIndex->ListsInUseUlong[RelativeOffset >> 5] &= ~(1 << RelativeOffset & 0x1F);

}

}

//unlink the current chunk to be allocated

Blink->Flink = Flink;

Flink->Blink = Blink;

}

참고: 나중에 얘기될테지만, bitmap의 업데이트가 더 이상 XOR 오퍼레이션을 사용하지 않고 대

신 bitwise AND를 사용한다는다는 것을 알아야 한다. 이것은 1byte FreeListInUseBitmap

flipping attack을 방지한다(McDonald/Valasek 2009).

만약 ListHint가 어플리케이션에게 메모리 요청을 제공할 수 없다면 back-end heap manager가

Heap->FreeLists 사용을 진행한다. FreeLists는 heap을 위한 모든 chunk들을 포함한다. 만약

충분한 크기의 chunk가 발견되면 그것은 필요할 경우 분할되어 사용자에게 리턴된다. 그렇지 않

으면, heap은 RtlpExtendHeap()을 통해 확장될 것이다.

표 16. RtlpAllocateHeap Heap->FreeLists allocation

//attempt to use the FreeLists

_LIST_ENTRY *HeapFreeLists = &Heap->FreeLists;

_HEAP_LIST_LOOKUP *BlocksIndex = Heap->BlocksIndex;

_LIST_ENTRY *ChunkToUse;

//find an appropriate chunk on the FreeLists

_HEAP_LIST_LOOKUP *CurrBlocksIndex = BlocksIndex;

Page 28: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 28 / 95

while(1)

{

//if we've ran out of structures

//abort and we'll extend the heap

if(CurrBlocksIndex == NULL)

{

ChunkToUse = CurrBlocksIndex->ListHead;

break;

}

//remember that ListHead and HeapFreeLists

//point to the same location

CurrListHead = CurrBlocksIndex->ListHead;

//if we've came upon an empty FreeList extend the heap

if(CurrListHead == CurrListHead->Blink)

{

ChunkToUse = CurrListHead;

break;

}

_HEAP_ENTRY *BlinkHeader = (CurrListHead->Blink - 8);

//if the chunk is encoded decode it

if(Heap->EncodeFlagMask && Heap->EncodeFlagMask & BlinkHeader)

{

DecodeHeader(BlinkHeader);

}

//if the chunk can't be serviced by the

//largest chunk extend the heap

if(SizeInBlocks > BlinkHeader->Size)

{

ChunkToUse = CurrListHead;

break;

}

_HEAP_ENTRY *FlinkHeader = CurrListHead->Flink-8;

//if the chunk is encoded decode it

if(Heap->EncodeFlagMask && Heap->EncodeFlagMask & FlinkHeader)

{

DecodeHeader(FlinkHeader);

}

Page 29: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 29 / 95

//if the first chunk is sufficient use it

//otherwise loop through the rest

if(FlinkHeader->Size >= SizeInBlocks)

{

ChunkToUse = CurrListHead->Flink;

break;

}

else

{

//loop through all the BlocksIndex->ListHints

//looking for a sufficiently sized chunk

//then update the bitmap accordingly

}

//look at the next blocks index

CurrBlocksIndex = CurrBlocksIndex->ExtendedLookup;

}

Page 30: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 30 / 95

Overview

그림 4. Back-end allocation

Page 31: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 31 / 95

Front-end Allocation

이제 우리는 Low Fragmentation heap이 back-end 내에서 heuristic을 통해 활성화 되는 방법

을 보여줄 것이고 우리는 front-end manager가 사용하는 할당 알고리즘을 다룰 것이다. LFH는

성능 및 안전성을 염두에 두고 설계되었다(Marinescu 2006). 이런 새로이 원하는 혜택은 주로

front-end 할당자가 어떻게 동작하는지 이해하는 능력인 리버스 엔지니어에 대해 큰 비용이 되

었다(These newly desired benefits come at a large cost to reverse engineers; mainly the

ability to understand how the front-end allocator works). 이 섹션에서 나는 Low

Fragmentation heap를 사용할 때 일반적으로 할당이 발생하는 방법을 설명하려고 한다.

RtlpLowFragHeapAllocFromContext

이전에서 보았듯이, RtlpLowFragHeapAllocFromContext()는 ListHint의 Blink가 0x1로 설정된

zero bit를 가질 경우에 호출된다. Bitwise 오퍼레이션은 만약 blink가 HeapBucket을 포함하고

있다면 LFH로부터의 요청을 제공할 준비가 되어있음을 가리킨다고 결정한다.

할당은 manager가 사용할 모든 핵심 데이터 구조체를 요청하는 것으로 시작한다. 이것은

_HEAP_LOCAL_DATA, _HEAP_LOCAL_SEGMENT_INFO 그리고 _HEAP_SUBSEGMENT를 포

함한다[이 구조체들 간의 관계는 그림 1. Data structure overview 에서 볼 수 있다].

할당자는 먼저 Hint SubSegment를 사용하려고 시도한다. 실패를 기다리는 동안, 할당자는 다음

으로 ActiveSubsegment를 사용하려고 시도한다. 만약 ActiveSubsegment가 실패하면 할당자

는 LFH를 진행하기 위해 적절한 데이터 구조체를 설정해야만 한다[중복을 방지하기 위해, 아래

의 코드는 Hint SubSegment에서만 사용되는 의사코드를 보여주지만, 로직은

ActiveSubsegment에도 마찬가지로 적용될 수 있다].

_INTERLOCK_SEQ 구조체는 현재 Depth, Offset 그리고 Sequence를 얻기 위한 쿼리를 한다.

이 정보는 현재 free chunk에 대한 포인터를 얻을 뿐 아니라 이용 가능한 다음 chunk에 대한

Offset을 계산하는데도 사용된다. 루프 로직은 중요한 데이터의 업데이트가 오퍼레이션 사이에

어떤 변화 없이 atomic fashion으로 이루어짐을 보장한다.

표 17. LFH SubSegment allocation

int LocalDataIndex = 0;

//uses the SizeIndex of the _HEAP_BUCKET to

//get the address of the LFH for this bucket

_LFH_HEAP *LFH = GetLFHFromBucket(HeapBucket);

//figure this out yourself :)

if(aHeapBucket->Affinity == 1)

Page 32: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 32 / 95

{

AllocateAndUpdateLocalDataIndex();

}

//get the LocalData and LocalSegmentInfo

//structures based on Affinity and SizeIndex

_HEAP_LOCAL_DATA *HeapLocalData = LFH->LocalData[LocalDataIndex];

_HEAP_LOCAL_SEGMENT_INFO *HeapLocalSegmentInfo = HeapLocalData->SegmentInfo[HeapBucket->SizeIndex];

//try to use the 'Hint' SubSegment first

//otherwise this would be 'ActiveSubsegment'

_HEAP_SUBSEGMENT *SubSeg = HeapLocalSegmentInfo->Hint;

_HEAP_SUBSEGMENT *SubSeg_Saved = HeapLocalSegmentInfo->Hint;

if(SubSeg)

{

while(1)

{

//get the current AggregateExchange information

_INTERLOCK_SEQ *AggrExchg = SubSeg->AggregateExchg;

int Offset = AggrExchg->FreeEntryOffset;

int Depth = AggrExchg->Depth;

int Sequence = AggrExchg->Sequence;

//store the old values, to ensure atomic swapping

_INTERLOCK_SEQ AggrExchg_Saved;

AggrExchg_Saved.OffsetAndDepth = AggrExchg.OffsetAndDepth;

AggrExchg_Saved.Sequence = Sequence;

//continue only if this is a valid SubSegment

_HEAP_USERDATA_HEADER *UserBlocks = SubSeg->UserBlocks;

if(!Depth || !UserBlocks || SubSeg->LocalInfo != HeapLocalSegmentInfo)

{

break;

}

//this gets the offset from the AggregateExchg

//(block size) and creates a byte offset

int ByteOffset = Offset * 8;

LFHChunk = UserBlocks + ByteOffset;

//the next offset is store in the 1st 2-bytes

//of the userdata (this can probably be abused ;))

short NextOffset = UserBlocks + ByteOffset + sizeof(_HEAP_ENTRY));

//store the updated offset, depth and sequence

Page 33: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 33 / 95

//new_offset = current_offset += BucketSize

//new_depth = current_depth--

//new_sequence = depends on current depth

_INTERLOCK_SEQ AggrExchg_New;

AggrExchg_New.Offset = NextOffset;

AggrExchg_New.Depth = Depth--;

if(AggrExchg_New.Depth == -1)

AggrExchg_New.Sequence = Sequence--;

else

AggrExchg_New.Sequence = Sequence;

//i.e InterlockedCompareExchange

if(AtomicSwap(AggrExchg, AggrExchg_New))

{

UpdateHeaders(LFHChunk);

return LFHChunk;

}

else

{

UpdateAffinity();

SubSeg = SubSeg_Saved;

}

}

}

참고: 서식의 문제로 인해 제거되었지만, RtlpLowFragHeapAllocFromContext() 내의 모든 코

드는 try/catch 블록으로 싸여있음을 이해해야 한다. 이것은 만약 LFH에 어떤 실패가 있을 경

우, NULL이 반환되도록 하고 back-end 할당자가 메모리 요청 서비스를 시도하도록 한다.

만약 Hint와 Active SubSegment가 실패하면, 초기화 랙 이나 invalid로 인해,

RtlpLowFragHeapAllocFromContext()는 메모리(back-end를 사용하는) 할당하고 HeapBin에

대한 큰 메모리 chunk를 나눔으로써 새로운 SubSegment를 획득하여야만 한다. 이 단계가 완

료되면, 위의 코드는 ActiveSubsegment를 통해 요청을 제공한다.

실패한 두 SubSegment의 이벤트 내 요청을 서비스하기 위해 메모리의 새로운 chunk는 front-

end heap을 위해 할당되어야만 한다. 요청되는 메모리의 크기는 임의적이 아니지만 요청되는

chunk의 크기와 heap이 이용가능한 메모리의 총 크기를 기준으로 한다. 아래의 의사코드는

Magic Formula로써 내가 참조하는 어떤 것이다(The following pseudo-code is something I

refer to as the Magic Formula). 그것은 back-end로부터 얼마나 많은 메모리 요청이 있었는지,

그래서 특정 HeapBucket을 위해 UserBlock이 얼마나 많이 분할되었는지를 계산한다.

Page 34: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 34 / 95

표 18. LFH UserBlocks allocation size algorithm

int TotalBlocks = HeapLocalSegmentInfo->Counters->TotalBlocks;

if(MaxRunLenReached)

TotalBlocks = TotalBlocks / 16;

int BucketAffinity = HeapBucket->Affinity & 1;

int BucketBytesSize = RtlpBucketBlockSizes[HeapBucket->SizeIndex];

int StartIndex = 7; if(BucketBytesSize < 256) BucketAffinity--;

if ( dword_77F97594 > RtlpHeapMaxAffinity >> 1) )

BucketAffinity++;

int BlockMultiplier = 4 - BucketAffinity;

if(TotalBlocks < (1 << BlockMultiplier))

TotalBlocks = 1 << BlockMultiplier;

if(TotalBlocks > 1024)

TotalBlocks = 1024;

//used to calculate cache index and size to allocate

int TotalBlockSize = TotalBlocks * (BucketBytesSize + sizeof(_HEAP_ENTRY)) + 0x18;

if(TotalBlockSize > 0x78000)

TotalBlockSize = 0x78000;

//calculate the cahce index

//upon a cache miss, this index will determine

//the amount of memory to be allocated

if(TotalBlockSize >= 0x80)

{

do

{

StartIndex++;

}while(TotalBlockSize >> StartIndex);

}

//we will @ most, only allocate 40 pages (0x1000 bytes per page)

if((unsigned)StartIndex > 0x12)

StartIndex = 0x12;

int UserBlockCacheIndex = StartIndex;

Page 35: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 35 / 95

if(HeapBucket->Affinity & 6)

UserBlockCacheIndex = 0x12;

//allocate space for a _HEAP_USERDATA_HEADER along with room

//for ((1 << UserBlockCacheIndex) / BucketBytesSize) heap chunks

void *pUserData = RtlpAllocateUserBlock(LFH, UserBlockCacheIndex, BucketByteSize + 8);

_HEAP_USERDATA_HEADER *UserData = (_HEAP_USERDATA_HEADER*)pUserData;

if(!pUserData)

return 0;

위의 코드에서 UserBlockCacheIndex 값은 캐쉬된 아이템들의 배열 내 인덱스로 사용된다. 만

약 캐쉬 미스가 있다면, 이 동일한 값은 얼마나 많은 메모리가 UserBlocks chunk를 위해 할당

되었는지를 계산하는데 사용된다. 이 UserBlocks chunk는 나중에 BucketSize chunk들로 분할

된다. 캐쉬된 아이템을 사용하지 않을 때, RtlpAllocateUserBlock이 기본적으로 어떻게

RtlpAllocateHeap에 대한 wrapper가 되는지 알아보자.

표 19. RtlpAllocateUserBlock without caching

int AllocAmount = 1 << UserBlockCacheIndex;

if(AllocAmount > 0x78000)

AllocAmount = 0x78000;

UserBlock = RtlAllocateHeap(LFH->Heap, 0x800000, AllocAmount - 8);

if(UserBlock)

{

LFH->CacheAllocs++;

//Assign the _HEAP_USERDATA_HEADER->SizeIndex

*(UserBlock+8) = UserBlockCacheIndex;

}

return UserBlock;

메모리가 할당되었음에도 불구하고, LFH에 의해 사용될 준비가 되지는 않는다. 먼저

_HEAP_SUBSEGMENT와 결합되어야 한다. 이 SubSegment는 이전에 삭제된 하나로부터 획득

되거나 _LFH_BLOCK_ZONE 리스트로부터 검색된 주소로 생성된다.

표 20. LFH Pre-SubSegment initialization setup

int UserDataBytesSize = 1 << UserData->AvailableBlocks;

if(UserDataBytesSize > 0x78000)

UserDataBytesSize = 0x78000;

Page 36: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 36 / 95

int UserDataAllocSize = UserDataBytesSize - 8;

//Increment SegmentCreate to denote a new SubSegment created

InterlockedExchangeAdd(&LFH->SegmentCreate, 1);

DeletedSubSegment = ExInterlockedPopEntrySList(HeapLocalData);

_HEAP_SUBSEGMENT *NewSubSegment = NULL;

if (DeletedSubSegment)

{

// if there are any deleted subsegments, use them

NewSubSegment = (_HEAP_SUBSEGMENT *)(DeletedSubSegment - 0x18);

}

else

{

NewSubSegment = RtlpLowFragHeapAllocateFromZone(LFH, LocalDataIndex);

//return failure use back-end

if(!NewSubSegment)

return 0;

}

//this function will setup the _HEAP_SUBEMENT structure

//and chunk out the data in 'UserData' to be of HeapBucket->SizeIndex chunks

RtlpSubSegmentInitialize(LFH, NewSubSegment, UserBlock, RtlpBucketBlockSizes[HeapBucket->SizeIndex],

UserDataAllocSize,HeapBucket);

//each UserBlock starts with the same sig

UserBlock->Signature = 0xF0E0D0C0;

RtlpLowFragHeapAllocateFromZone()은 _HEAP_SUBSEGMENT의 포인터를 찾거나 나중에

주소 추적을 위한 여러 _LFH_BLOCK_ZONE 구조체를 생성하는 두 가지 목적의 함수로써 제공

된다.

함수는 먼저 SubSegment에 의해 사용되는 주소를 유지하는 유효한 _LFH_BLOCK_ZONE가 있

는지 확인한다. 만약 없거나 설계된 한도를 초과했을 경우 함수는 새로운 _LFH_BLOCK_ZONE

오브젝트를 저장할 수 있는 0x3F8(1016) 바이트의 메모리를 할당한다. 아래의 코드는

RtlpLowFragHeapAllocateFromZone()의 일반적인 작동 방법을 보여준다.

표 21. RtlpLowFragHeapAllocateFromZone

_LFH_BLOCK_ZONE *CrtZone = LFH->LocalData[LocalDataIndex]->CrtZone;

_LFH_BLOCK_ZONE *CrtZoneFlink = NULL;

while(1)

Page 37: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 37 / 95

{

while(1)

{

while(1)

{

//Flink == NULL => create initial zones

CrtZoneFlink = CrtZone->ListEntry->Flink;

if(!CrtZone-Flink)

break;

void *FreePointer = CrtZoneFlink->FreePointer;

//This will increment it to the next SubSegment

void *FreePointer_New = FreePointer + LFH->ZoneBlockSize;

//if we've exceeded the limit

//create more zones

if(FreePointer_New >= CrtZoneFlink->Limit)

break;

//InterlockedCompareExchange

//loop if this fails

if(CompareExchange( &CrtZoneFlink->FreePointer, FreePointer_New))

return FreePointer

}

if (CrtZoneFlink == CrtZone->ListEntry.Flink )

break;

}

//this will effectively give us 31 _LFH_BLOCK_ZONE

//structures to use for keeping track of userdata

void *NewLFHBlockZone = RtlAllocateHeap(LFH->Heap, 0x800000u, 0x3F8u);

if(!NewLFHBlockZone)

return 0;

//if the CrtZone's ListEntry is empty

if (CrtZoneFlink == CrtZone->ListEntry.Flink )

{

//link in the newly created structure

//into the LFH->SubSegmentZones

LinkInBlockZone(LFH, NewLFHBlockZone);

//points to the end of the allocation

NewLFHBlockZone->Limit = NewLFHBlockZone + 0x3F8;

Page 38: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 38 / 95

//sizeof(_LFH_BLOCK_ZONE) == 0x10

char *AlignedZone = RoundAlign(NewLFHBlockZone + 0x10);

NewLFHBlockZone->FreePointer = AlignedZone;

CrtZone->ListEntry.Flink = NewLFHBlockZone;

continue;

}

//if we failed, free the data

RtlFreeHeap(LFH->Heap, 0x800000, NewLFHBlockZone);

}

위의 코드는 이해하기 조금 어려운 것처럼 보일 수 있지만, 몇 가지 설계 목적을 염두에 둔 것이

다. 대부분의 내부 루프는 멀티 스레드를 처리할 때 race condition을 피하기 위해 FreePointer

의 atomic swapping을 보장한다. 바깥 쪽 대부분의 루프는 이 함수가 소진되는 상황에서 새로

운 BlockZone들을 생성함을 보증한다.

RtlpLowFragHeapAllocateFromZone()으로부터 주소가 획득된 후 SubSegment는

RtlpSubSegmentInitialize() 내에서 초기화 된다. 이름에서도 알 수 있듯이, 다양한 인수를 받

아서 새롭게 생성된 SubSegment(NewSubSegment), 최근에 할당된 메모리(UserBlock), 사용

가능한 메모리의 양(UserDataAllocSize) 그리고 생성된 chunk들의 크기(HeapBucket /

BucketBytesSize)와 같은 _HEAP_SUBSEGMENT를 초기화 할 책임이 있다.

RtlpSubSegmentInitialize()는 먼저 HeapBucket 크기에 기반한 LocalSegmentInfo와

LocalData 구조체를 가져온다. 특정 선호도 상태가 보장되어 있는지 확인한 후, 다음 진행은 이

UserBlock이 사용할 수 있을 거라고 생각되는 chunk들의 양을 정확하게 계산하는 것이다. 만들

chunk의 개수가 결정되면, 큰 메모리의 chunk를 통해 각 chunk의 header에 대한 쓰기가 반복

된다. 마지막으로 _INTERLOCK_SEQ는 NumberOfChunks 및 0x2와 동일한 FreeEntryOffset

의 Depth를 가지고 설정 초기값을 가진다(Finally the _INTERLOCK_SEQ has its initial values

set to have the Depth as the NumberOfChunks and the FreeEntryOffset equal to 0x2).

표 22. RtlpSubSegmentInitialize

void *UserBlockData = UserBlock + sizeof(_HEAP_USERDATA_HEADER);

_HEAP_LOCAL_SEGMENT_INFO *LocalSegmentInfo = LFH->LocalData[NewSubSegment->AffinityIndex]-

>SegmentInfo[HeapBucket->SizeIndex];

_HEAP_LOCAL_DATA *LocalData = LFH->LocalData[NewSubSegment->AffinityIndex]->Segmentinfo[HeapBucket-

>SizeIndex]->LocalData;

Page 39: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 39 / 95

if (!((HeapBucket->Affinity >> 1) & 3))

{

int TotalBucketByteSize = BucketByteSize + sizeof(_HEAP_ENTRY);

int BucketBlockSize = TotalBucketByteSize / 8;

//sizeof(_HEAP_USERDATA_HEADER) == 0x10

int NumberOfChunks = (UserDataAllocSize - 0x10) / TotalBucketByteSize;

//skip past the header, so we can start chunking

void *pUserData = UserBlock + sizeof(_HEAP_USERDATA_HEADER);

//assign the SubSegment

UserBlock->SubSegment = NewSubSegment;

//sizeof(_HEAP_USERDATA_HEADER) == 0x10 (2 blocks)

int SegmentOffset = 2;

_INTERLOCK_SEQ AggrExchg_New;

AggrExchg_New.FreeEntryOffset = 2;

if(NumberOfChunks)

{

int NumberOfChunksItor = NumberOfChunks;

do

{

SegmentOffset += BucketBlockSize;

pUserData = UserBlockData;

UserBlockData += BucketByteSize;

//next FreeEntryOffset

*(WORD*)(pUserData + 8) = SegmentOffset;

//Set _HEAP_ENTRY.LFHFlags

*(BYTE*)(pUserData + 6) = 0x0;

//Set _HEAP_ENTRY.UnusedBytes

*(BYTE*)(pUserData + 7) = 0x80;

EncodeDWORD(LFH, pUserData);

} while(NumberOfChunksItor--);

}

//-1 indicates last chunk

//in the UserBlock (_HEAP_USERDATA_HEADER)

*(WORD*)(pUserData + 8) = -1;

Page 40: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 40 / 95

//Sets all the values for this subsegment

InitSubSegment(NewSubSegment);

//updates the bucket counter to reflect

//NumberOfChunk as total blocks and

//sets the SubSegmentCount

UpdateBucketCounters(LocalSegmentInfo);

//will be atomically assigned to the

//NewSubSegment's _INTERLOCK_SEQ

AggrExchg_New.Depth = NumberOfChunks;

AggrExchg_New.Sequence = AggrExchg_Saved.Sequence + 1;

//InterlockedCompareExchange64

AtomicSwap(&NewSubSegment->AggregateExchg, AggrExchg_New);

}

마지막으로 UserBlocks가 할당된 후에, SubSegment가 할당되고 SubSegment가 초기화 되며,

LFH는 막 초기화 된 것의 ActiveSubsegment를 설정할 수 있다. 그것은 결국 일부 locking

mechanism을 오퍼레이트 하며 atomic하게 ActiveSubsegment를 할당하게 된다. 마지막으로

실행은 표 17에서 보여준 지점에서 재개된다.

표 23. ActiveSubsegment assignment

//now used for LFH allocation for a specific bucket size

AtomicSwap(&HeapLocalSegmentInfo->ActiveSegment, NewSubSegment);

Page 41: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 41 / 95

Overview

그림 5. Front-end allocation

Example

할당 프로세스를 완전히 이해하는 가장 좋은 방법은 예제이다. LFH가 활성화 되었고 front-end

할당자에 의해 실현된 첫 번째 할당 요청중이라고 가정하자. 요청은 0x28(40) 바이트를 받을

것이고 후에 0x30(48)바이트(또는 0x6 블록)가 될 헤더를 위한 공간을 추가할 것이다. 또한 우

리가 _HEAP_LOCAL_DATA 구조체 내에 위치한 segmentinfo[0x6]으로부터

ActiveSubsegment를 사용한다고 가정하자.

중요: LFH->LocalData[0]->SegmentInfo[0x6]->ActiveSubsegment->UserBlocks

Page 42: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 42 / 95

위에 표시한 Magic Formula를 바탕으로 우리는 0x30바이트(Depth인)에 대해 0x2A chunk가

될 것이라 추측할 수 있다. _HEAP_USERDATA_HEADER은 0x10 바이트이기 때문에 초기화 오

프셋은 0x2로 설정된다.

UserBlock 내 각 chunk가 처음 4바이트는 인코딩되어 있고, 사용자가 쓰기 가능한 데이터의 n

바이트가 호출 프로세스에게 리턴되는 8바이트 헤더를 포함할 것이다. 사용자가 쓰기 가능한 데

이터의 처음 2바이트는 _INTERLOCK_SEQ에 할당되는 다음 오프셋을 유지한다.

각 오프셋은 UserBlock chunk의 시작부분부터 계산되고 block들의 측면에서 참조된다. 다음 이

용가능한 chunk의 오프셋 바이트는 UserBlocks + FreeEntryOffset * 0x8이 될 것이다.

그림 6. Full UserBlock for Bucket 0x6

초기 할당이 이루어진 후에 Depth와 오프셋은 UserBlock 내 다음 이용가능한 chunk를 반영하

도록 업데이트 된다. 메모리가 실제로 이동하지는 않는 반면, 단지 다른 인덱스들과 그에 따른

diagram은 하나가 할당된 후 이용가능한 메모리를 표시한다. Chunk(NextOffset) 내 저장된 이

전의 값을 유지하는 오프셋과 Depth는 0x1 만큼 감소된다. 우리가 사용한 하나의 블록이 표시

되고 0x29가 남아있다.

Page 43: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 43 / 95

그림 7. UserBlock after 1st allocation for 0x30 bytes

두 번째 할당 후에 오프셋 UserBlock + 0xE가 다음 free chunk가 될 것이다. 그 후 Userblock

+ 0x14는 그 다음 free chunk가 될 것이고 그 다음도 계속 그렇게 될 것이다. Depth가 0이 될

때까지 Offset 증가와 Depth 감소를 유지한다. Depth가 0이 되면 또 다른 UserBlock을 위한

더 많은 메모리를 할당할 필요가 있음을 나타낸다.

Page 44: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 44 / 95

그림 8. UserBlock after the 2nd consecutive allocation for 0x30 bytes

Page 45: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 45 / 95

Freeing

이제 Windows 7에서 메모리가 획득되는 방법의 기본적인 이해를 할 차례이고, 나는 그것이 릴

리즈되는 방법에 대해 논의할 것이다. 사용 중인 chunck는 어플리케이션에 의해 결국 해제되고

heap manager로 다시 돌아가게 된다. 이 프로세스는 heap, flags, 그리고 해제된 chunk를 인

자로 받는 RtlFreeHeap()에서 시작된다. 이 함수의 첫 번째 순서는 이 chunk가 실제로 해제 가

능한지 확인하고 heap manager의 어느 부분이 해당 chunk를 방출할 책임이 있는지를 결정하

는 chunk의 헤더를 점검하는 것이다.

표 24. RtlFreeHeap

ChunkHeader = NULL;

//it will not operate on NULL

if(ChunkToFree == NULL)

return;

//ensure the chunk is 8-byte aligned

if(!(ChunkToFree & 7))

{

//subtract the sizeof(_HEAP_ENTRY)

ChunkHeader = ChunkToFree - 0x8;

//use the index to find the size

if(ChunkHeader->UnusedBytes == 0x5)

ChunkHeader -= 0x8 * (BYTE)ChunkToFreeHeader->SegmentOffset;

}

else

{

RtlpLogHeapFailure();

return;

}

//position 0x7 in the header denotes

//whether the chunk was allocated via

//the front-end or the back-end (non-encoded ;) )

if(ChunkHeader->UnusedBytes & 0x80)

RtlpLowFragHeapFree(Heap, ChunkToFree);

else

RtlpFreeHeap(Heap, Flags | 2, ChunkHeader, ChunkToFree);

return;

Page 46: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 46 / 95

Back-end Freeing

back-end manager는 크기 또는 Low Fragmentation heap의 부족으로 인해 front-end heap

에 의해 처리되지 않는 모든 메모리들을 처리할 책임이 있다. 0xFE00 블록들 전체에 대한 모든

할당들은 VirtualAlloc()/VirtualFree()에 의해 처리되는 반면에, 0x800 블록들 보다 큰 모든 메

모리 관리는 front-end에 의해 제공될 수 없는 특정 메모리로 back-end에 의해 처리된다.

RtlFreeHeap

RtlpFreeHeap()은 _HEAP, Flags, ChunkHeader, 그리고 ChunkToFree를 인자로 받는다. 이

함수는 먼저 만약 chunk header가 인코딩 되어 있다면 디코딩을 시도한다. 그리고

BlocksIndex 내 적절한 ListHint 탐색을 진행한다. 만약 충분한 인덱스가 발견되지 않는다면,

ListHint로 blocksIndex->ArraySize-1을 사용한다.

표 25. RtlpFreeHeap BlocksIndex search

if(Heap->EncodeFlagMask)

DecodeAndValidateChecksum(ChunkHeader);

int ChunkSize = ChunkHeader->Size;

_HEAP_LIST_LOOKUP *BlocksIndex = Heap->BlocksIndex;

while(1)

{

//if the chunk will fit in this BlocksIndex, break out

if(ChunkSize < BlocksIndex->ArraySize)

break;

//if the chunk is too big for this blocksindex and there is NOT

//and extended lookup, then free onto FreeList[BlocksIndex->ArraySize-1]

if(!BlocksIndex->ExtendedLookup)

{

ChunkSize = BlocksIndex->ArraySize - 1;

break;

}

//next item in the linked list

BlocksIndex = BlocksIndex->ExtendedLookup;

}

참고: ListHint Index를 찾아 리턴하는 과정은 BlocksIndexSearch()이라고도 한다. 이 함수는

_HEAP_LIST_LOOKUP과 ChunkSize를 입력으로 받는다. 후보자를 찾을 때까지 BlocksIndex

인자를 업데이트 하는 연결 리스트를 순회하다가 마지막으로 FreeListIndex를 반환한다.

Page 47: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 47 / 95

이제 _HEAP_LIST_LOOKUP이 발견되었다. 이제 이것으로 함수는 특정 ListHint를 사용하려고

시도할 수 있다. ListHint는 ListHints[0x6] 같은 특정 값이 되거나 또는 만약 해제하려는 chunk

가 BlocksIndex가 관리하는 것보다 클 경우 ListHint는 ListHints[BlocksIndex->ArraySize-

BaseIndex-1] 내에 위치할 수 있다.(이것은 FreeList[0] 유형 리스트로 간주할 수 있다.)

표 26. RtlpFreeHeap ListHint retrieval

//attempt to locate a freelist

_LIST_ENTRY *ListHint = NULL;

//if the chunk can be managed by specific BlocksIndex

if(ChunkSize < (BlocksIndex->ArraySize - 1) || BlocksIndex->ExtendedLookup != 0x0 && ChunkSize == (BlocksIndex-

>ArraySize - 1))

{

//get the offset into the ListHints

int BaseIndex = BlocksIndex->BaseIndex;

int FreeListIndex = RelativeSize(BlocksIndex, ChunkSize - BaseIndex);

//acquire a freelist

ListHint = BlocksIndex->ListHints[FreeListIndex];

}

만약 ListHint가 위치되고 blink에 HeapBucket이 포함되어 있지 않으면 back-end manager가

LFH heuristic 내에서 사용되는 값을 업데이트 한다. Chunk는 heap에 다시 삽입되게 되고,

counter로부터 0x2를 뺀다. 이것은 실제로 지정된 Bucket의 LFH가 활성화 됨을 의미하고, 당

신은 최소한 0x11의 연속적인 할당을 참조해야 한다(This means that to actually enable the

LFH for a given Bucket, you must see at least 0x11 consecutive allocations).

예를 들어, 만약 0x10 요청을 Bucket[0x6]이 받은 경우, 이 chunk의 0x2가 방출되어 heap으

로 돌려지고, 같은 사이즈 0x2를 할당한 다음, LFH는 Bucket[0x6]에 대해 활성화 되지 않는다.

임계값은 activation heuristic이 heap maintenance를 수행하기 전에 충족되어야 한다.

표 27. RtlpFreeHeap LFH counter decrement

if(ListHint != NULL)

{

int FreeListBlink = ListHint->Blink;

//if the blink is not populated with a HeapBucket

//decrement the counter, as we just freed a chunk

if( !(BYTE)FreeListBlink & 1)

{

if(FreeListBlink >= 2)

Page 48: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 48 / 95

ListHint->Blink = FreeListBlink - 2;

}

}

LFH activation을 위한 counter를 업데이트 한 후 RtlpFreeHeap()는 만약 heap이 허용할 경우

chunk를 합치려고 시도한다. Chunk 합치기는 해제될 메모리 조각들에 인접한 두 개의 chunk를

바라보게 하는 중요한 과정이다. 이것은 서로의 다음에 위치하는 수 많은 작은 free chunk들이

존재하는 것을 피하기 위해 수행된다(Low Fragmentation heap이 직접적으로 이 문제를 해결한

다). RtlpCoalesceFreeBlocks()가 항상 호출되지만, chunk 합치기는 인접한 chunk가 FREE로

표시된 경우에만 일어난다.

인접한 블록들의 합치기는 새로운 전체 블록 사이즈가 Heap->DeCommitThreshold를 넘지 않

으며 가상 메모리에 의해 처리될 필요가 없도록 보장하는 검사와 함께 완료된다. 마지막으로 알

고리즘의 이 부분은 chunk를 FREE로 표시하고 사용되지 않는 바이트들을 제로로 한다.

표 28. RtlpFreeHeap Chunk coalescing and header reassignment

//unless the heap says otherwise, coalesce the adjacent free blocks

int ChunkSize = ChunkHeader->Size;

if( !(Heap->Flags & 0x80) )

{

//combine the adjacent blocks

ChunkHeader = RtlpCoalesceFreeBlocks(Heap, ChunkHeader, &ChunkSize, 0x0);

}

//reassign the ChunkSize if neccessary

ChunkSize = ChunkHeader->Size;

//if the coalesced chunk is bigger than the

//decommit threshold for this heap, decommit the memory

if(ChunkHeader->Size > DeCommitThreshold || ChunkHeader->Size + TotalFreeBlocks > DeCommitThreshold)

{

RtlpDeCommitFreeBlock(Heap, ChunkHeader, ChunkSize, 0x0);

return;

}

if(ChunkSize > 0xFE00)

{

RtlpInsertFreeBlock(Heap, ChunkHeader, ChunkSize);

UpdateTagEntry(ChunkHeader);

return;

}

Page 49: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 49 / 95

//mark the chunk as FREE

ChunkToFreeHeader->Flags = 0x0;

ChunkToFreeHeader->UnusedBytes = 0x0;

Free chunk는 FreeLists 또는 최소한 ListHints[ArraySize-BaseIndex-1]에 유지된 FreeList[0]

유형의 구조체 상의 특정 위치에 존재해야만 한다. 이 과정의 첫 단계는 삽입 위치를 위해

_HEAP_LIST_LOOKUP을 순회하는 것이다. 그런 다음 만약 기억난다면, Heap->FreeLists와 동

일한 포인터인 listHead를 순회한다. 이 포인터는 가장 작은 free chunk로부터 시작하고 링크는

더 큰 chunk 쪽을 향한다.

루프는 이 heap이 이용 가능한 모든 _HEAP_LIST_LOOKUP 구조체를 통한 반복을 설정한다.

알고리즘은 다음에 ListHead를 붙잡고 몇 가지의 초기 검증을 할 것이다. 첫 번째로 리스트가

비어있는지를 검사한다. 만약 리스트가 비어있다면, 루프는 종료되고 실행이 계속된다. 두 번째

검사는 free 될 chunk가 실제로 리스트에 들어가도록 보장하는 것이다. 이 작업은 리스트

(ListHead->Blink) 내 마지막 아이템이 free될 chunk보다 큰 크기를 가지는 것을 보장하여 수

행된다.

끝으로, ListHead 상의 첫 번째 아이템이 삽입되기 전에 삽입이 가능한지 결정하는 검사를 한다.

만약 삽입할 수 없다면, FreeLists는 새로 free된 chunk가 FreeListIndex 위치가 시작되는 곳으

로 연결될 수 있는지 결정하기 위한 순회를 한다. [FreeLists 에 대한 자세한 내용은 그림 3을

보라.]. 당신은 이제 왜 이들 아이템들이 실제로 sentinel node로의 링크에 의해 종료되는 지정

된 리스트들이 아니라 전체 FreeLists 내 위치에 대한 포인터이기 때문에 ListHints로 언급되는

지 알 수 있을 것이다.

표 29. RtlpFreeHeap Insertion point search

//FreeList will determine where, if anywhere there is space

BlocksIndex = Heap->BlocksIndex;

_LIST_ENTRY *InsertList = Heap->FreeLists;

//attempt to find where to insert this item

//on the ListHead list for a particular BlocksIndex

if(BlocksIndex)

{

//find a BlocksIndex for storage

int FreeListIndex = BlocksIndexSearch(BlocksIndex, ChunkSize);

while(BlocksIndex != NULL)

{

_HEAP_ENTRY *ListHead = BlocksIndex->ListHead;

//if the ListHead is empty

Page 50: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 50 / 95

//our insert list will be the sentinel node

if(ListHead == ListHead->Blink)

{

InsertList = ListHead;

break;

}

//if the chunk is larger than the largest

//entry, we'll insert it after

if(ChunkSize > ListHead->Blink.Size)

{

InsertList = ListHead;

break;

}

//pick the insertion point behind the 1st

//chunk larger than the ChunkToFree

_LIST_ENTRY *NextChunk = ListHead->Flink;

if(NextChunk.Size > ChunkSize)

{

InsertList = NextChunk;

break;

}

NextChunk = BlocksIndex->ListHints[FreeListIndex];

while(NextChunk != ListHead)

{

//there is actually some decoding done here

if(NextChunk.Size > ChunkSize)

{

InsertList = NextChunk;

break;

}

NextChunk = NextChunk->Flink;

}

//if we've found an insertion

//spot terminate the loop

if(InsertList != Heap->FreeLists)

break;

BlocksIndex = BlocksIndex->ExtendedLookup;

}

}

Page 51: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 51 / 95

참고: 크기를 얻기 위해 chunk header의 디코딩은 빠졌다. 이 인코딩 되지 않은 header의

dereference 하는 실수를 하지 말라.

정확한 지점에 삽입하여 RtlpFreeHeap()은 chunk가 올바른 위치에 링크되었는지를 확인한다.

Chunk 삽입의 마지막 위치가 결정되면, 해당 위치는 FreeList에 안전하게 연결된다. 내가 알기

로 이 기능은 새로운 것이고 Brett Moore’s의 Insertion Attack(Moore 2005)를 직접적으로 해

결한다. 끝으로 chunk가 적절한 FreeList 상에 위치되면, ListsInUseUlong이 이에 맞게 업데이

트 된다.

표 30. Safe link-in

while(InsertList != Heap->FreeLists)

{

if(InsertList.Size > ChunkSize)

break;

InsertList = InsertList->Flink;

}

//R.I.P FreeList Insertion Attack

if(InsertList->Blink->Flink == InsertList)

{

ChunkToFree->Flink = InsertList;

ChunkToFree->Blink = InsertList->Blink;

InsertList->Blink->Flink = ChunkToFree;

InsertList->Blink = ChunkToFree

}

else

{

RtlpLogHeapFailure();

}

BlocksIndex = Heap->BlocksIndex;

if(BlocksIndex)

{

FreeListIndex = BlocksIndexSearch(BlocksIndex, ChunkSize);

int RelSize = ChunkSize - BlocksIndex->BaseIndex;

FreeListIndex = RelativeSize(BlocksIndex, RelSize);

_LIST_ENTRY *FreeListToUse = BlocksIndex->ListHints[FreeListIndex];

if(ChunkSize >= FreeListToUse.Size)

{

Page 52: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 52 / 95

BlocksIndex->ListHints[FreeListIndex] = ChunkToFree;

}

//bitwise OR instead of the XP XOR (R.I.P Bitmap flipping (hi nico))

if(!FreeListToUse)

{

int UlongIndex = Chunkize - BlocksIndex->BaseIndex >> 5;

int Shifter = ChunkSize - BlocksIndex->BaseIndex & 1F;

BlocksIndex->ListsInUseUlong[UlongIndex] |= 1 << Shifter;

}

EncodeHeader(ChunkHeader);

}

참고: listInUseUlong은 이전에 사용하던 XOR 대신 bitwise OR을 사용한다는 것을 명심하라.

이것은 populated list는 항상 populated로 표시되고 empty list는 오직 populated로 표시되도

록 보장한다.

힌트: 만약 RtlpLogHeapFailure()가 실행을 종료하지 않으면 어떤 일이 일어날까? (Flink/Blink

는 업데이트 된 적이 없을 경우…)

Page 53: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 53 / 95

Overview

Page 54: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 54 / 95

그림 9. RtlpFreeHeap overview

Front-end Freeing

Front-end 할당은 Low Fragmentation heap에 의해 조절된다. 이것은 처음부터 사용되지 않는

반면에, 특정 heuristic이 발생(trigger)될 때 사용되는 유일한 heap manager이다. LFH는 메모

리 단편화를 방지하고 연결 리스트 구조체 내 1024 바이트 이하의 chunk들의 트랙을 유지하던

예전 front-end manger Lookaside List로부터 크게 달라졌으며 특정 크기의 빈번한 사용을 지

원하도록 설계되었다. BlocksIndex 구조체는 크기가 16k보다 큰 chunk들을 추적할 수 있지만

LFH는 오직 16k보다 작은 chunk들에만 사용된다.

RtlpLowFragHeapFree

RtlpLowFragHeapFree()는 _HEAP 구조체와 free 될 chunk에 대한 포인터를 두 개의 인자로

가진다. 함수는 먼저 ChunkToFree header 내 특정 플래그가 설정되었는지를 체크한다. 만약 해

당 플래그가 0x5일 경우 조정은 header의 위치를 변경하도록 되어 있다. 그 다음으로 메모리

추적에 필요한 모든 멤버에 접근하는 연관된 SubSegment를 찾는다. 또한 최근에 free된

chunk를 반영하도록 header 내 몇몇 값을 재설정 한다.

표 31. RtlpLowFragHeapFree Subsegment acquisition

//hi ben hawkes :)

_HEAP_ENTRY *ChunkHeader = ChunkToFree - sizeof(_HEAP_ENTRY);

if(ChunkHeader->UnusedBytes == 0x5)

ChunkHeader -= 8 * (BYTE)ChunkHeader->SegmentOffset;

_HEAP_ENTRY *ChunkHeader_Saved = ChunkHeader;

//gets the subsegment based off

//the LFHKey, Heap and ChunkHeader

_HEAP_SUBSEGMENT SubSegment = GetSubSegment(Heap, ChunkToFree);

_HEAP_USERDATA_HEADER *UserBlocks = SubSegment->UserBlocks;

//Set flags to 0x80 for LFH_FREE (offset 0x7)

ChunkHeader->UnusedBytes = 0x80;

//Set SegmentOffset or LFHFlags (offset 0x6)

ChunkHeader->SegmentOffset = 0x0;

Ben Hawkes는 ChunkHeader 포인터를 수정하는데 사용되는 chunk header를 덮어써서

semi-control 된 메모리의 해제를 일어나게 하는 방법을 설명했다. 당신은 ChunkHeader –

Page 55: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 55 / 95

(0x8 * 0xFF)으로부터 선택된 메모리 주소 공간을 가질 것이다.

적절한 chunk header가 위치되면, 이제 함수는 새로운 오프셋을 계산할 필요가 있게 된다. 오프

셋은 SubSegment(위에서 계산된)와 관련된 UserBlock으로 쓰는데 사용된다. 일부 초기 검사

는 SubSegment가 실제로 chunk를 해제하기 전에 경계를 넘어서지 않을 것을 보장하기 위해

만들어졌다. 만약 이 조건이 만들어지지 않을 경우, 값은 SubSegment가 필요한 몇 가지 수정

을 수행하도록(간결함을 위해 제외함) 나타내기 위해 설정된다.

다음 시도는 SubSegment로부터 _INTERLOCK_SEQ를 얻고 현재 Offset, Depth, Sequence를

얻기 위해 만든 것이다. 다음 오프셋은 해제될 chunk 앞쪽의 인접한 chunk로부터 수집된다.

RtlpLowFragHeapAllocFromContext() 에서 본 바와 같이, 이것은 chunk 데이터의 처음 2바이

트에 저장된다. Chunk는 사용 가능한 bin으로 바로 삽입되었기 때문에 Depth는 하나 증가한다.

예전 값과 새 값에 대한 atomically swap 시도는 성공할 경우 루프가 끝나고 실패할 경우 루프

가 계속 된다. 이것은 고수준의 멀티 스레드 환경에서 동작하도록 설계되었으며 LFH 내에서 대

부분 전형적인 동작이다.

표 32. RtlpLowFragHeapFree OffsetAndDepth / Sequence update

while(1)

{

_INTERLOCK_SEQ AggrExchg;

AggrExchg.OffsetAndDepth = SubSegment->AggregateExchg.OffsetAndDepth;

AggrExchg.Sequence = SubSegment->AggregateExchg.Sequence;

int Sequence_New = AggrExchg.Sequence + 1;

if(AggrExchg.OffsetAndDepth >= -1)

Sequence_New--;

int Depth = SubSegment->AggrExchg.Depth;

_HEAP_LOCAL_SEGMENT_INFO *LocalSegmentInfo = SubSegment->LocalInfo;

unsigned int Sequence = SubSegment->LocalInfo->LocalData->Sequence;

unsigned int LastOpSequence = LocalSegmentInfo->LastOpSequence;

//setup the new INTERLOCK_SEQ

_INTERLOCK_SEQ AggrExchg_New;

AggrExchg_New.Sequence = Sequence_New;

if(Depth != SubSegment->BlockCount || LocalSegmentInfo->Counters.SubSegmentCounts == 1)

{

if(Sequence >= LastOpSequence && (Sequence - LastOpSequence) < 0x20)

Page 56: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 56 / 95

{

//set the FreeEntry Offset of ChunkToFree

*(WORD)(ChunkHeader + 8) = AggrExchg.FreeEntryOffset;

//Subtract the size of the block being freed

//from the current offset; which will give

//you the next free chunk

int NewOffset = AggrExchg.FreeEntryOffset - (ChunkHeader - UserBlocks) / 8;

AggrExchg_New.FreeEntryOffset = NewOffset;

//increase depth because we're freeing

AggrExchg_New.Depth = Depth + 1;

//set the Hint in the subsegment

Sequence = 1;

SubSegment->LocalInfo->Hint = SubSegment;

}

else

{

Sequence = 3;

AggrExchg_New.Depth = -1; //last entry

AggrExchg_New.FreeEntryOffset = 0; //no offset

}

}

else

{

Sequence = 3;

AggrExchg_New.Depth = -1; //last entry

AggrExchg_New.FreeEntryOffset = 0; //no offset

}

//_InterlockedCompareExchange64

if(AtomicSwap(&SubSegment->AggregateExchg, AggrExchg_New, AggrExchg))

break;

//if something has changed since swapping, try again

ChunkHeader = ChunkHeader_Saved;

}

참고: SubSegment->Hint는 할당자 내에서 나중에 사용되도록 예약되어 있는 곳이라고 볼 수

있다.

마지막으로 검사는 Sequence 값이 0x3으로 설정되어 있는지 확인하도록 만들어져 있다. 만약

0x3으로 설정되어 있다면, 이것은 SubSegment가 몇 가지 오퍼레이션을 수행할 필요가 있다는

Page 57: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 57 / 95

것을 의미하고 UserBlocks chunk는 해제될 수 있다.(back-end manager를 통해). 만약 이 경

우가 아니라면, 0x1이 호출한 함수로 리턴된다.

표 33. RtlpLowFragHeapFree Epilog

//if there are cached items handle them

UpdateCache(SubSegment);

//if we've freed every item in the list

//update the subsegment and free the UserBlock

if(Sequence == 3)

{

PerformSubSegmentMaintenance(SubSegment);

RtlpFreeUserBlock(LFH, SubSegment->UserBlocks);

}

return 1;

참고: PerformSubSegmentMaintenance()는 실제 함수가 아니라 해제 또는 향후 사용을 위해

SubSegment를 준비하는 복잡한 연속적인 명령들을 가리키는 가명이다.

Page 58: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 58 / 95

Overview

그림 10. RtlpLowFragHeapFree overview

Page 59: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 59 / 95

Example

이 문서의 할당 섹션 내 예제를 계속하여 우리는 0x30 바이트를 위한 3번째 연속 할당을 할 것

이다. 이것은 0x27 chunk들이 남겨졌고(0x30 조각) 다음 chunk에 대한 현재 오프셋은

UserBlock로부터 0x14임을 의미한다. 이것은 다음과 같다.

그림 11. UserBlock after 3rd consecutive allocation for 0x30 bytes

LFH로부터 할당된 메모리가 해제될 때 정확히 어떤 일이 일어나는가? 이전에 언급한 바와 같이,

메모리는 실제로 어디론가 옮겨지는 것이 아니라 UserBlocks 내에 다음 free location에 대한

인덱스로 사용되는 오프셋이 업데이트 된다.

Userblocks로부터 할당된 첫 번째 chunk가 해제되었다고 가정하자. 첫 번째 chunk의 위치에

대한 Offset을 업데이트하고 depth를 0x1만큼 증가시켜야 할 필요가 있다. 새로운 오프셋은

chunk header의 주소에서 UserBlock의 주소를 뺀 다음 0x8로 나눔으로써 계산된다. 즉, 새로

운 오프셋이 UserBlock(blocks 내)으로부터 상대적인 위치로부터 파생된다. 다음 그림은 초기

메모리의 chunk가 해제된 후의 UserBlock 상태이다.

Page 60: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 60 / 95

그림 12. Freeing of the 1st chunk allocated for 0x30 bytes

이제 할당된 두 번째 chunk가 해제되었다고 상상해보자.(다른 할당이나 해제가 일어나기 전에).

새로운 오프셋은 두 번째 chunk가 첫 번째 이후에 해제되었기 때문에 0x8일 것이다. 오프셋

0x2에 free chunk가 있다 할지라도 다음 chunk는 오프셋 0x8에 상주하는 할당에 사용된다. 이

것은 전체 주소 대신 포인터의 부분을 업데이트 하는 연결 리스트로 간주할 수 있다.

Page 61: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 61 / 95

그림 13. Freeing of the 2nd chunk allocated for 0x30 bytes

Page 62: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 62 / 95

Securityy Mechanisms

Windows XP SP2에 소개된 보안 매커니즘의 대부분이 Windows에 그대로 있지만 Windows

7(Windows Vista code 기반)에서 추가된 몇 가지가 더 있다. 이 섹션에서 우리는 이런 보안 매

커니즘의 일부들을 논의할 것이다. 이들이 어떻게 구현되었으며 각 매커니즘에 대한 생각을 마지

막으로 다룰 것이다. 내 생각에 Windows Vista code 기반에서 소개된 보호 로직의 전체는 시간

이 갈수록 가장 익스플로잇 하기 어려운(hardest-to-exploit) Window heap을 제공하는 것 같다.

Heap Randomization

Heap randomization의 목적은 HeapBase가 예측되지 않은 주소를 가지도록 보장하는 것이다.

Heap이 생성될 때 마다 생성되는 랜덤 값은 메모리 주소를 예측하는 것을 방지하기 위한 시도로

base address에 더해진다.

HeapBase에 추가될 64k로 정렬된 랜덤 값의 생성은 RtlCreateHeap()에서 일어난다. 이 값은

어플리케이션이 실행될 때마다 다양한 주소를 생성하려고 시도한다. Randomization은

HeapCreate()에 전달된 인자로부터 계산된 heap의 최대 크기에 의존한다. 다음은 HeapBase를

무작위하게 하려고 시도하는 RtlCreateHeap()로부터의 코드 조각이다.

표 34. RtlCreateHeap randomization

int BaseAddress = Zero;

int RandPad = Zero;

//get page aligned size to use as a random pad

int RandPad = (RtlpHeapGenerateRandomValue64() & 0x1F) << 0x10;

//if maxsize + pad wraps, null out the randpad

int TotalMaxSize = MaximumSize + RandPad;

if(TotalMaxSize < MaximumSize)

{

TotalMaxSize = MaximumSize;

RandPad = Zero;

}

//0x2000 = MEM_RESERVE

//0x40 = PAGE_EXECUTE_READWRITE

//0x04 = PAGE_READWRITE

//this will reserve the memory at the baseaddress

//but NOT actually commit any memory at this point

int Opts = 0x4;

if(Options & 0x40000)

Opts = 0x40;

Page 63: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 63 / 95

if(NtAllocateVirtualmemory(-1, &BaseAddress, 0x0, &TotalMaxSize, 0x2000, Opts))

return 0;

Heap = (_HEAP*)BaseAddress;

//adjust the heap pointer by randpad if possible

if(RandPad != Zero)

{

if(RtlpSecMemFreeVirtualMemory(-1, &BaseAddress, &RandPad, 0x8000) >= 0 )

{

Heap = (_HEAP*)RandPad + BaseAddress;

MaximumSize = TotalSize - RandPad;

}

}

Comments

랜덤 값이 64k로 정렬될(엔트로피의 5비트) 것이라는 사실 때문에 randomization을 사용할 때

heap의 base address가 제한된 숫자일 수 있다. 엔트로피의 부족을 기반으로 하는 heap

address 추측이 실용적이지는 않지만 확실히 불가능한 것은 아니다.

또 다른 좀 더 가능성 높은 시나리오는 만약 RandPad + MaximumSize를 wrap 하면 RandPad

가 NULL이 된다는 것이다(Another less likely scenario stems from the fact that if RandPad

+ MaximumSize wraps, the RandPad will be NULL). 이것은 random heap 생성을 효과적으

로 피할 수 있다. 이것이 매우 가능성 높은 주장이라는 두 가지 이유가 있다. 먼저 HeapCreate()

로 전달되는 인자들의 조절이 부족하다는 것이다. 나는 애플리케이션 내에서 이것이 일어나는 것

으로 확신하지만, 흔한 일은 아니다. 두 번째 이유는 wrap 하기 충분히 큰 MaximumSize를 가

져오는 것은 대부분 NtAllocateVirtualmemory()가 NULL을 반환하는 원인이 되어 결과는 전체

적인 실패가 된다.

Header Encoding/Decoding

Windows Vista 이전 chunk가 손상되지 않았음을 보장하는 유일한 방법은 chunk header 내에

위치하는 1바이트 쿠키를 확인하는 것이었다. 이것은 쿠키는 brute force될 수도 있지만 더 중요

한 것은 cookie 앞에 header data가 있다는 사실로 인해 가장 강력한 방법이 아님이 명백해졌

다 (McDonald/Valasek 2009).

이것은 heap chunk header를 인코딩의 출현을 이끌었다. Heap은 이제 각 _HEAP_ENTRY의

첫 번째 4바이트를 인코딩한다. 이것은 size, flags, checksum 오버플로우가 원하는 효과를 방

지한다. 인코딩은 첫 번째 3바이트를 서로 XOR 하고 SmallTagIndex 값으로 저장한다. 다음으

로 첫 번재 4바이트들은 Heap->Encoding(RtlCreateHeap()에서 만들어지는 랜덤한 값)과

Page 64: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 64 / 95

XOR 된다.

표 35. Heap header encoding

EncodeHeader(_HEAP_ENTRY *Header, _HEAP *Heap)

{

if(Heap->EncodeFlagMask)

{

Header->SmallTagIndex = (BYTE)Header ^ (Byte)Header+1 ^ (Byte)Header+2;

(DWORD)Header ^= Heap->Encoding;

}

}

Chunk의 decoding은 encoding과 아주 비슷하지만, decoding이 실제로 수행되기 전에 몇 개의

추가적인 검사를 한다. decoding되는 chunk 헤더가 heap chunk가 encoding 되지 않았다는 가

정 하에 처음에 encoding되었는지를 확인한다.

표 36. Heap header decoding

DecodeHeader(_HEAP_ENTRY *Header, _HEAP *Heap)

{

if(Heap->EncodeFlagMask && (Header & Heap->EncodeFlagMask))

{

(DWORD)Header ^= Heap->Encoding;

}

}

Comments

Chunk header의 처음 4바이트를 encoding 하는 것은 정보 유출의 사용 없이 size, flags,

checksum 필드를 성공적으로 덮어쓰기가 불가능하다(Hawkes 2008). 그러나 이것은 header

내 다른 정보에 대한 overwriting을 방지하지는 못한다. 만약 header가 덮어 쓰여질 수 있고 값

을 check-sum 하기 전에 이용된다면, 잠재적으로 실행 흐름을 변경하는데 사용될 수 있다. 우리

는 이후의 섹션에서 이 문제에 대해 자세히 이야기 할 것이다.

다른 가능한 우회 방법은 chunk가 encoding 되지 않았다고 결정되도록 하는 heap manager

내 값을 덮어쓰도록 처리한다. 이것은 몇 가지 방법으로 수행될 수 있다. 최초의 방법은 가능성

이 좀 적은데, Heap->EncodeFlagMask(0x100000로 초기화 되는)을 NULL이 되도록 하는 것

이다. 향후 decoding이나 encoding 작업이 수행되지 않는다. 이 방법은 유명한 heap 불안정으

로 인해 몇 가지 단점을 가지고 있다. 새로운 heap은 전반적으로 요구되는 효과(_HEAP_ENTRY

header의 encoding 되지 않은 덮어쓰여짐)를 얻도록 생성될 수 밖에 없다.

Page 65: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 65 / 95

더 많은 가능성이 있는 시나리오인 두 번째 방법은 chunk header의 처음 4바이트를 덮어써서

Heap->EncodeFlagMask과 bitwise AND가 false를 반환하도록 하여 size, flags, checksum의

제한된 컨트롤을 제공토록 하는 것이다. 이것은 할당 루틴 내에서 checksum 확인이 실행도록

함으로써 FreeLists 내 headers를 덮어쓸 때만 유용하다.

마지막으로, 공격자는 chunk header의 마지막 4바이트를 목표로 할 수 있다. 우리는 이 필드들

이 chunk header의 상태를 결정하는데 사용되는 것을 이전에 보았다. 예를 들어 chunk header

의 offset 0x7은 chunk가 LFH 또는 back-end로부터 왔는지 결정하는데 사용된다.

Death of bitmap flipping

Heap에 대한 Brett Moors의 Heaps에서는 heap이 실제로 그렇지 않을 때 채워지거나 비워져있

다고 믿도록 Freelist를 속이는 방법에 대한 논의가 있었다. 이것은 bitmap flipping로 언급되었

다(Moore 2008). 이 취약점 공격은 2009년에 작성된 고도의 기술과 비판적인 혹평을 받은 문

서로 인해 새로운 버전의 윈도우에서는 직접적으로 해결되었다(McDonald/Valasek 2009)[이것

은 100% 거짓이다].

Windows XP 코드 기반에서 XOR 연산자는 bitmap을 업데이트 하는데 사용되었다. 만약

update 작업에 도달하기 위한 logic이 도달하고 FreeList가 비어있다면, 연산자는 bitmap 내 현

재 bit를 자신과 XOR하고 따라서 그 값은 반대(역순)가 된다.

표 37. Death of Bitmap Flipping

// if we unlinked from a dedicated free list and emptied it,clear the bitmap

if (reqsize < 0x80 && nextchunk == prevchunk)

{

size = SIZE(chunk);

BitMask = 1 << (size & 7);

// note that this is an xor

FreeListsInUseBitmap[size >> 3] ^= vBitMask;

}

이 기술의 문제는 만약 chunk의 크기가 손상되었을 경우 변경되지 말아야 할 때 전용 FreeList

의 상태가 변경될 수 있다는 것이다. 그 결과 HeapBase 내 중요한 데이터의 덮어쓸 수 있게 된

다.

빈 리스트를 표시하도록 bitmap을 업데이트 할 때 bitwise AND가 사용되고 빈 리스트가 비어있

는 상태를 유지하도록 보장하며 populated list만 empty로 표시할 수 있도록 한다(When

updating the bitmap to display an empty list, bitwise AND is used, ensuring that an

empty list will stay empty and a populated list can only be marked as empty). 리스트를

Page 66: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 66 / 95

populated로 표시하는 것과 동일하게 ListsInUseUlong를 수정하기 위해 bitwise OR을 사용한

다. 그 방법은 empty list가 populated가 될 수 있지만 populated list는 unpopulated될 수 없

도록 free하는 것이다(That way, an empty list can become populated but a populated list

being freed to can’t become unpopulated).

이러한 모든 변화들의 꼭대기에, 전용 Freelists의 개념은 사라졌기 때문에, empty list로부터의

할당 시도는 오직 충분한 크기의 chunk를 위한 FreeList 구조체를 탐색하는 것 밖에 없게 되었

다.

표 38. Death of Bitmap Flipping 2

//HeapAlloc

size = SIZE(chunk);

BitMask = 1 << (Size & 0x1F);

BlocksIndex->ListInUseUlong[Size >> 5] &= ~BitMask;

//HeapFree

size = SIZE(chunk);

BitMask = 1 << (Size & 0x1F);

BlocksIndex->ListInUseUlong[Size >> 5] |= BitMask;

Safe Linking

Safe unlinking은 FreeList로부터 chunk를 unlinking 할 때(또는 2개의 해제된 chunk들을 병

합할 때) 일어나는 4바이트 overwrite를방지하기 위해 Windows XP SP2에서 소개되었다. 이것

은 이후에 일반적인 heap exploitation 정도로 금지되었다.

리스트로부터 아이템을 unlinking하는 것이 더 이상 임의의 주소를 overwrite하는데 사용될 수

없지만, 덮어쓰길 원하는 주소를 가리키도록 FreeList[0]에서 entry의 blink를 덮어쓸 수 있다

(Moore 2005). 그런 식으로 만약 chunk가 병합된 entry 전에 삽입되었다면, blink가 이제 막

해제된 chunk의 주소를 가리키게 될 것이다.

Back-end manager 내 새로운 검사는 free chunk를 link 하기 전에 chunk의 blink를 검사한다.

우리는 RtlpFreeHeap()가 어떻게 동작하는지 설명할 때 이 코드를 위에서 보았지만 다시 검토

해보자. 만약 FreeList의 Blink->Flink가 자신을 가리키지 않는다면, 손상된 것으로 간주하고

link-in 프로세스가 수행되지 않는다.

표 39. Safe Linking

if(InsertList->Blink->Flink == InsertList)

{

ChunkToFree->Flink = InsertList;

Page 67: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 67 / 95

ChunkToFree->Blink = InsertList->Blink;

InsertList->Blink->Flink = ChunkToFree;

InsertList->Blink = ChunkToFree

}

else

{

RtlpLogHeapFailure();

}

Comments

이 장치가 손상된 Blink를 통해 포인터의 overwrting을 금지하는 동안, 만약 프로세스가

RtlpLogHeapFailure()를 끝내지 않는다면 문제는 여전히 존재하게 된다. 코드는 ListHints의

flink와 blink의 실제적인 업데이트 없이 직접 chunk를 ListHints 내 적절한 위치 내로 삽입한다

(The code directly after this inserts the chunk into its appropriate spot in the ListHints

without actually updating its flink and blink). 이것은 flink와 blink는 완전히 사용자에 의해

관리되는 것을 뜻한다.

Page 68: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 68 / 95

Tactics

Heap Determinism

수년간 연구가들은 실행 흐름을 변경하기 위한 heap meta-data의 손상에 중점을 두어왔다. 이

사소한 작업 하나가 점점 더 어렵게 되었다. 일반적인 4바이트 덮어쓰기 공격은 사라지고, heap

headers는 이제 무결성을 보장하기 위해 임의의 값으로 encoding괴고 FreeList 삽입 공격과 같

은 오래된 속임수는 기본적으로 죽어버렸다.

지금 그 어느 때보다 공격은 유리한 상황을 설정하기 위한 시도를 할 때 높은 정확도를 가지고

있어야만 한다. Heap에 대한 이야기를 할 때 우리는 heap 조작이라고 부른다. Chunk size를 넘

어서는 조작, 할당과 해제는 현대의 Windows heap exploitation에서는 정말 다르다.

이 섹션에서 나는 Low Fragmentation heap을 사용하도록 결정된 상태에서 heap을 얻기 위한

시도를 할 때 일어나는 몇 가지 일반적인 시나리오를 논의할 것이다. 할당된 X가 어디에 위치할

것인가? 무엇이 할당된 X와 인접할 것인가? 만약 chunk X가 해제되면 어떤 일이 일어날 것인가?

Activation the LFH

알아야 할 정보 중에서 가장 기본적인 부분 중의 하나는 특정 Bucket에 대한 LFH를 활성화 하

는 방법이다. Windows 7에서 LFH는 오직 front-end heap manager 이지만 그것이 기본적으로

특정 chunk 크기에 대한 모든 할당을 처리한다는 의미는 아니다. 위의 코드에서도 보았듯이,

LFH는 back-end 내에서 heuristic을 통해 활성화 되어야만 한다. 만약 당신이 강제로 할당이 일

반적으로 일어나도록 할 수 있다면 특정 크기에 대해 LFH를 활성화 할 수 있을 것이다. LFH는

적어도 같은 크기의 적어도 0x12번(만약 LFH가 이전에 활성화 되었다면 0x11번) 연속적인 할

당에 의해 활성화 할 수 있다.

표 40. Enable the LFH for a specific size

//0x10 => Heap->CompatibilityFlags |= 0x20000000;

//0x11 => RtlpPerformHeapMaintenance(Heap);

//0x11 => FreeList->Blink = LFHContext + 1;

for(i = 0; i < 0x12; i++)

{

printf("Allocation 0x%02x for 0x%02x bytes\n", i, SIZE);

allocb[i] = HeapAlloc(pHeap, 0x0, SIZE);

}

//now that the _HEAP_BUCKET is in the

//ListHint->Blink, the LFH will be used

printf("Allocation 0x%02x for 0x%02x bytes\n", i++, SIZE);

printf("\tFirst serviced by the LFH\n");

Page 69: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 69 / 95

allocb[i] = HeapAlloc(pHeap, 0x0, SIZE);

당신도 볼 수 있듯이, DOM object를 할당하는 것 같이 만약 당신이 할당을 조절할 수 있는 능력

이 있다면 특정 크기에 대한 LFH의 활성화 및 사용은 매우 사소한 것이다. 이제 front-end

manager는 특정 크기에 사용될 것이고, 단계는 좀 더 높은 단계의 결정을 제공하는 것을 취할

것이다.

Defragmentation

우리가 LFH 내 인접하게 chunk들을 위치시키는 것에 대해 이야기 하기 전에, fragmentation의

주제가 논의되어야만 한다. 대부분의 어플리케이션들의 잦은 할당/해제를 하는 루틴으로 인해

UserBlock chunk는 fragment 된다. 이것은 몇 가지 defragmentation은 당신이 덮어쓰는

chunk가 당신이 덮어쓰는 chunk에 인접하도록 보장하기 위해 위치되어야 함을 의미한다.

다음 예제에서 우리는 chunk들이 서로 직접 인접하게 위치해야 한다는 전제 조건을 가진다. 아

직 어떤 홀도 없기 때문에 새로운 SubSegment를 다룰 때 꽤 사소한 반면에, 사용되는

SubSegment를 사용할 때의 경우는 사소하지 않다. 아래의 그림은 하나의 할당이 세 개의 인접

한 object들을 취하지 않는 것을 보여준다. 이런 홀은 이런 일이 일어나는 것이 충분해야만 한다.

이 작업을 수행하는 가장 쉬운 방법은 어플리케이션들의 현재 메모리 레이아웃에 대한 몇 가지

이해하고 몇 가지 할당을 하는 것이다.

이 시나리오에서, 우리는 오직 우리가 모든 홀들을 채울 때까지 세 개의 할당만을 만드는 것이

필요로 한다. 그러나 이것은 확실히 현실적인 예제는 아니다. 공격자는 자신이 채워야 할 홀이

얼마나 많이 있는지 정확하게 알 수 없고, 아마도 UserBlocks(Depth == 0x0인)을 완전히 소모

하기 위해 충분한 할당을 만드는 것을 필요로 할 것이다. 이것은 heap manager가 어떤 홀도 포

함하지 않는 새로운 SubSegment를 생성하도록 강제할 것이다. (참고: Alex/Matt에게 감사).

Page 70: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 70 / 95

Adjacent Data

Heap 기반 버퍼 오버플로우를 공격하기 위한 시도를 할 때 가장 어려운 작업 중의 하나는 잘 알

려진 상태로 heap을 조작하는 것이다. 당신은 덮어쓰고자 하는 chunk가 직접적으로 덮어쓰여질

chunk에 근접하기를 원할 것이다. Back-end heap에서 일반적으로 병합은 메모리가 해제될 때

공격을 시도할 때 신뢰할 수 없는 것을 이끌어 내는 문제를 가진다.

참고: 실제적인 Windows XP/2003 heap exploitation을 위한 데모를 하는 동안 사용되는 heap

overflow 취약점을 시도할 때 이것이 가장 어려운 작업이었다. 어플리케이션의 다중 스레드된

환경은 합병 chunk들은 신뢰할 수 없어서 우리 heap 조작을 효과가 없도록 만들었다.

LFH는 chunk들이 모두 동일한 크기를 가지기 때문에 합병을 하지 않는다. 따라서 UserBlock로

부터의 그들의 오프셋에 상대적으로 인덱싱된다. 이것은 서로 다른 사소한 옆에 같은 크기의

chunk들을 위치시키도록 만든다. (This makes placing chunks of the same size next to each

other trivial.). 만약 overflow가 가능하다면 BUSY와 FREE chunk들은 UserBlocks의 현재 상

태에 의존하여 overwrite될 수 있다.

당신이 alloc3 안의 정보를 덮어쓰길 원하고, alloc3 앞에 오버플로우가 일어날 만큼 길게 당신의

어플리케이션이 쓴다고 해보면, 당신은 alloc3 내 데이터를 덮어쓸 수 있을 것이다.

표 41. LFH Chunk overflow

EnableLFH(SIZE);

NormalizeLFH(SIZE);

alloc1 = HeapAlloc(pHeap, 0x0, SIZE);

alloc2 = HeapAlloc(pHeap, 0x0, SIZE);

memset(alloc2, 0x42, SIZE);

*(alloc2 + SIZE-1) = '\0';

alloc3 = HeapAlloc(pHeap, 0x0, SIZE);

memset(alloc3, 0x43, SIZE);

Page 71: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 71 / 95

*(alloc3 + SIZE-1) = '\0';

printf("alloc2 => %s\n", alloc2);

printf("alloc3 => %s\n", alloc3);

memset(alloc1, 0x41, SIZE * 3);

printf("Post overflow..\n");

printf("alloc2 => %s\n", alloc2);

printf("alloc3 => %s\n", alloc3);

표 42. LFH Chunk overflow result

Result:

alloc2 => BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

alloc3 => CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

Post overflow..

alloc2 => AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCCCCC

CCCCCCCCC

alloc3 => AAAAAAAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCC

Alloc3 안에 선물된 데이터의 일부분이 이제는 alloc1에 쓰여진 데이터로 덮어 쓰여졌음을 당신

도 볼 수 있을 것이다. 언급되어야 할 또 다른 부작용은 overflow가 일어난 후 alloc2의 문자 길

이이다. 이 길이는 null terminator가 덮어 쓰여졌을 때부터 실제 alloc2와 alloc3가 결합된 것

이다. 코드 실행 확보에 도움이 되는 null terminator를 덮어쓰는 실제 환경의 예제는 Peter

Vreugdenhil의 놀라운 문서(Vreugdenhil 2010)를 참조하라.

그러나 만약 alloc3 내 data가 사용되기 전에 alloc2가 사용되거나 alloc2의 header 검증이 있

다면 어떻게 될 것인가? 당신은 오버플로우 될 chunk 앞에 오버플로우 될 수 있는 chunk를 직

접 배치해야 할 필요가 있을 것이다. (You will need to position the chunk that can be

overflowed directly before the chunk that is being overflowed into.). 이것은 사소한 것처럼

보일 수 있지만, fragmentation은 할당과 해제를 조절할 수 있는 능력과 함께 고려되어야만 한

다.

표 43. Chunk reuse

alloc1 = HeapAlloc(pHeap, 0x0, SIZE);

alloc2 = HeapAlloc(pHeap, 0x0, SIZE);

alloc3 = HeapAlloc(pHeap, 0x0, SIZE);

HeapFree(pHeap, 0x0, alloc2);

Page 72: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 72 / 95

//overflow-able chunk just like alloc1 could reside in same position as alloc2

alloc4 = HeapAlloc(pHeap, 0x0, SIZE);

memcpy(alloc4, src, user_controlled_size)

참고: 비록 chunk들을 인접하도록 주문하는 heap 조작이 LFH에서 쉬울 수 있지만, 이것은 큰

단점을 가지고 있다. 다른 크기의 두 개의 chunk들을 위치시키는 능력은 훨씬 더 복잡하다.(인

접하는 메모리에 여러 SubSegment을 포함하여). 이것은 만약 할당 크기를 조절하는 능력이

완전히 제어되지 않는다면 exploitation 과정을 방해할 수 있다.

Seeding Data

집필 당시 use-after-free 취약점은 매우 인기가 있었다. 이들 취약점들에 대한 대부분의

exploit 들은 코드에 heap 내 seed data에 대한 여러 할당 메소드들을(자바스크립트 문자열,

DOM object instances, 등) 이용한 시도를 포함하고 있었다. Nico Waisman은 object data에

관한 이해의 부족으로 인해 이 기술을 pray-after-free라고 불렀다.(Waisman 2010).

우리는 LFH가 어떻게 BucketSize chunk들로 분할된 큰 UserBlocks 내에 메모리를 저장하는지

를 이미 알고 있다. 우리는 UserBlock 내 이들 chunk들 각자가 할당과 해제를 제어하기 위한

능력에 따라 서로 인접할 수 있다는 것 또한 알고 있다. 이 지식을 사용하여 각 chunk의 내용은

사용자가 쓰기 가능한 메모리에 데이터를 쓰는 것의 seed가 될 수 있다.[이것은

HEAP_ZERO_MEMORY flag에 따라 HeapAlloc()의 호출 내에 설정되지 않을 수도 있다.].

(http://msdn.microsoft.com/en-us/library/aa366597%28VS.85%29.aspx).

아래의 예제는 어떻게 메모리가 LFH 상의 chunk들에게 복사하고 연속적으로 해제하며 많은 원

본 데이터의 손실 없이 다시 할당되는지를 보여준다.

표 44. Data seeding

EnableLFH(SIZE);

NormalizeLFH(SIZE);

for(i = 0; i < 0x10; i++)

{

printf("Allocation 0x%02x for 0x%02x bytes => ", i, SIZE);

allocb[i] = HeapAlloc(pHeap, 0x0, SIZE);

memset(allocb[i], 0x41 + i, SIZE);

for(j=0; j<12; j++)

printf("%.2X", allocb[i][j]); printf("\n");

}

printf("Freeing all chunks!\n");

Page 73: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 73 / 95

for(i = 0; i < 0x10; i++)

{

HeapFree(pHeap, 0x0, allocb[i]);

}

printf("Allocating again\n");

for(i = 0; i < 0x10; i++)

{

printf("Allocation 0x%02x for 0x%02x bytes => ", i, SIZE);

allocb[i] = HeapAlloc(pHeap, 0x0, SIZE);

for(j=0; j<12; j++)

printf("%.2X", allocb[i][j]);

printf("\n");

}

표 45. Data seeding results

Result:

Allocation 0x00 for 0x28 bytes => 41414141 41414141 41414141

Allocation 0x01 for 0x28 bytes => 42424242 42424242 42424242

Allocation 0x02 for 0x28 bytes => 43434343 43434343 43434343

Allocation 0x03 for 0x28 bytes => 44444444 44444444 44444444

Allocation 0x04 for 0x28 bytes => 45454545 45454545 45454545

Allocation 0x05 for 0x28 bytes => 46464646 46464646 46464646

Allocation 0x06 for 0x28 bytes => 47474747 47474747 47474747

Allocation 0x07 for 0x28 bytes => 48484848 48484848 48484848

Allocation 0x08 for 0x28 bytes => 49494949 49494949 49494949

Allocation 0x09 for 0x28 bytes => 4A4A4A4A 4A4A4A4A 4A4A4A4A

Allocation 0x0a for 0x28 bytes => 4B4B4B4B 4B4B4B4B 4B4B4B4B

Allocation 0x0b for 0x28 bytes => 4C4C4C4C 4C4C4C4C 4C4C4C4C

Allocation 0x0c for 0x28 bytes => 4D4D4D4D 4D4D4D4D 4D4D4D4D

Allocation 0x0d for 0x28 bytes => 4E4E4E4E 4E4E4E4E 4E4E4E4E

Allocation 0x0e for 0x28 bytes => 4F4F4F4F 4F4F4F4F 4F4F4F4F

Allocation 0x0f for 0x28 bytes => 50505050 50505050 50505050

Freeing all chunks!

Allocating again

Allocation 0x00 for 0x28 bytes => 56005050 50505050 50505050

Allocation 0x01 for 0x28 bytes => 50004F4F 4F4F4F4F 4F4F4F4F

Allocation 0x02 for 0x28 bytes => 4A004E4E 4E4E4E4E 4E4E4E4E

Allocation 0x03 for 0x28 bytes => 44004D4D 4D4D4D4D 4D4D4D4D

Allocation 0x04 for 0x28 bytes => 3E004C4C 4C4C4C4C 4C4C4C4C

Allocation 0x05 for 0x28 bytes => 38004B4B 4B4B4B4B 4B4B4B4B

Allocation 0x06 for 0x28 bytes => 32004A4A 4A4A4A4A 4A4A4A4A

Page 74: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 74 / 95

Allocation 0x07 for 0x28 bytes => 2C004949 49494949 49494949 65

Allocation 0x08 for 0x28 bytes => 26004848 48484848 48484848

Allocation 0x09 for 0x28 bytes => 20004747 47474747 47474747

Allocation 0x0a for 0x28 bytes => 1A004646 46464646 46464646

Allocation 0x0b for 0x28 bytes => 14004545 45454545 45454545

Allocation 0x0c for 0x28 bytes => 0E004444 44444444 44444444

Allocation 0x0d for 0x28 bytes => 08004343 43434343 43434343

Allocation 0x0e for 0x28 bytes => 02004242 42424242 42424242

Allocation 0x0f for 0x28 bytes => 62004141 41414141 41414141

참고: 만약 HeapBin 내 모든 chunk들이 해제되었다면, 전체 Bin은 스스로 해제된다. 이것은 전

체 plunger 효과가 Lookaside List 같은 것에 대해서는 불가능 하다는 것을 의미한다. (This

means that a full plunger effect isn’t possible like it was with the Lookaside List). Jay-Z

는 너무 작고 연속적으로 해제된 HeapBin의 변경을 최소화 하는 여러 해제들을 수행하기 전

chunk들의 큰 양을 할당하는 것을 제안하였다.

첫 번째 할당 출력은 0x28(_HEAP_ENTRY를 위한 공간을 추가한 후에는 0x30) 크기에 대한

LFH 내 각 chunk들에 쓰여진 메모리를 보여준다. 모든 chunk들은 1단계에서와 같은 방식으로

해제된 후 할당된다. 이 시점에 당신이 memset에 대한 호출을 볼 수는 없지만, chunk들 내

data는 눈에 띄게 비슷하다.

이것은 어떤 chunk들도 합병되지 않을 뿐만 아니라 그들로부터 data가 삭제되지도 않기 때문이

다. 변화를 보인 정보의 유일한 조각은 data의 처음 2바이트다. 이것은 Algorithm 섹션에서 이

야기 한 FreeEntryOffset에 저장된다. FreeEntryOffset은 heap manager에 의해 나중에 사용

될 chunk header 바로 다음 2바이트에 저장된다.

이것은 chunk가 그들이 원래 UserBlock을 늘였을 때로부터 반대의 순서로 되어있음을 주목해야

한다. 메모리가 실제 스스로 거꾸로 된 것이 아니라 FreeEntryOffset이 모든 HeapFree() 후에

다시 인덱스 되었다는 것이다. 그래서 할당의 크기와 안정된 data를 아는 것으로 인해 달성되는

것은 무엇인가? Use-after-free 취약점은 알려진 크기의 할당을 할 수 있는 능력 활용을 위한 완

벽한 후보이다.

Object가 0x30의 크기를 가지고 오프셋 0x0에 vtable을 가진다고 가정하자. 이 object는 사

용되고 garbage collected 된 다음 메모리가 해제된 후 잘못 사용된다. 이것은 그것이 해제된

후 사용되기 전 data의 overwrite 할 수 있는 기회를 남겨준다. 어떤 주소의 제어권을 공격자에

게 주는 것은 vtable로 사용된다.

크기를 알려져 있고 해제된 object가 사용되기 전에 할당을 제어할 수 있기 때문에, 이것은

object의 vtable을 위해 사용된 주소에 대해 완벽한 제어권을 우리에게 준다. 이러한 제어를 하

는 것이 우리에게 제공된 주소와 해당하는 payload로 실행의 흐름을 변경할 수 있는 능력을 준

다.

Page 75: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 75 / 95

표 46. Use-after-free contrived example(Sotoriv 2007)

//LFH is enabled

//0x30 byte object

var obj = new FakeObj();

//.

//.

//.

//obj goes out of scope & garbage collected

//.

//.

//.

var heap = new heapLib.ie();

//this will allocate the same location

//in memeory as obj

heap.alloc("A" * 0x30, "overwrite");

//vtable == 0x41414141

obj.DoStuff();

참고: 이것은 단순한 예제이지만, 더 정확한 exploitation 결과를 가져올 heap manager의 지식

을 알 수 있는 방법을 보여준다. 만약 당신이 Blackhat USA 2010 전에 이 문서를 읽고 있다면

Nico Waisman가 이야기한 Aleatory Persistent Threat에 참석할 것을 강력히 추천한다. 만약

동일한 제목의 그의 논문을 읽기 위해 많은 시간이 걸리지 않을 것이다. 이것은 이 논문에서 논

의될 주제의 실용적인 어플리케이션을 제공할 것이다.

Exploitation

Heap exploitation가 유명하게 된 이후 safe-unlinking으로부터 chunk headers의 encoding까

지 많은 보호 매커니즘이 생성되었다. Heap exploitation은 지속적으로 어려움이 증가하고 있다.

Meta-data 손상은 이제 직접적으로 코드 실행을 손상시키는 방법을 사용하는 것보다 heap에 있

는 어플리케이션 data를 overwrite 하기 위해 일반적으로 사용된다. 그리고 말했다시피 그것이

불가능한 것으로 간주되어서는 안된다. Meta-data 손상은 비록 어렵지만 여전히 코드 실행을 얻

기 위해 사용된다.

이 섹션에서 나는 heap meta-data를 exploit 하기 위한 새로운 기술과 함께 이전에 사용된 세

부적인 기술들에 대해 설명할 것이다. 이 meta-data 손상은 쉽지는 않지만, 이것은 코드 실행을

달성하기 위해 data를 어떻게 조절하는지를 보여줄 것이고, 몇 가지 전제 조건을 충족되어 제공

Page 76: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 76 / 95

된다.

Ben Hawkes #1

Ben Hawkes의 RuxCon 2008 논문은 Windows Vista의 메모리 관리의 훌륭한 개요를 줄 뿐만

아니라 heap corruption을 통해 코드 실행을 이끌어내는 새로운 기술도 보여준다. 나는 이 문서

를, 특히 이 섹션을 위해 미리 위에서 언급한 논문을 읽을 것을 권장한다. 그는 여러 기술들, 그

중에서도 자신의 heap HANDLE payload를 보여주었지만, 나는 이 문서에서 오직 하나의 기술만

을 설명할 것이다.

Hawkes 박사는 특히 LFH에 의해 관리되는 UserBlock 내 chunk들의 손상에 관해 논의했다.

RtlpLowFragHeapFree() 내 chunk가 해제될 때 RtlpLowFragHeapFree()는 _HEAP_ENTRY

가 0x5와 같은 UnusedBytes(오프셋 0x7)을 가지고 있는지 체크한다. 이 경우 함수는 다른

chunk header에 대한 인덱스로써 SegmentOffset(오프셋 0x6)을 사용한다.

표 47. Chunk header relocation

_HEAP_ENTRY *ChunkHeader = ChunkToFree - sizeof(_HEAP_ENTRY);

if(ChunkHeader->UnusedBytes == 0x5)

ChunkHeader -= 8 * (BYTE)ChunkHeader->SegmentOffset;

LFH에서 일반적인 chunk는 아래와 같은 header 를 가진다.

그림 14. HeapBin chunk

참고: NextOffset은 실제로 분리된 필드가 아니지만 사용자가 쓰기 가능한 데이터의 처음 2바이

트 내에 존재한다.

만약 당신이 overflow 가능한 chunk가 해제되기 전에 직접 위치시킬 수 있다면, UnusedBytes

는 0x5의 값으로 할당될 수 있고 SegOffset은 당신이 원하는 1바이트 값이 될 수 있다. 이것은

당신에게 선택 가능한 반대 방향의 0xFF * 8바이트를 준다. (This gives you 0xFF * 8 bytes of

negative direction to choose from).

Page 77: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 77 / 95

그림 15. Overwritten HeapBin chunk

ChunkHeader는 덮어쓰여진 SegOffset을 기반으로 하는 값이 재할당 될 것이지만,

RtlpLowFragHeapFree()가 그것을 해제해야만 하므로 올바른 _HEAP_ENTRY이어야만 한다. 이

것은 처음에는 쓸모가 없어 보이지만 예제 코드를 한번 보도록 하자.

이것은 소스로부터 입력을 가져오고 object들을 생성하려는 시도를 하며 나중에 사용하기 위해

메모리를 할당하는 매우 단순하고 인위적인(범위를 무시한) 예제이다. 읽을 수 있는 메모리의 양

의 계산착오로 인해 사소한 buffer overflow가 있다. 나는 당신에게 어떻게 이 overflow가

application data의 overwriting으로 이어질 수 있는지 보여줄 것이다.

표 48. C++ contrived example

class Paser

{

public:

Parser();

virtual void DoThings();

~Parser();

private:

int Items;

int Values;

int Stuff;

int Things;

};

void *buffer, *output;

int action, copy_size = 0x40;

Parser *p;

while(1)

{

Page 78: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 78 / 95

action = ReadInt();

if(action == 0x1)

p = new Parser();

else if(action == 0x2)

buffer = malloc(sizeof(Parser));

else if(action == 0xFF)

break;

action = ReadInt();

if(action == 0x3)

p.DoThings();

else if(action == 0x4)

ReadBytes(buffer, copy_size);

else if(action == 0x5)

free(buffer);

else if(action == 0x6)

delete p;

else if(action == 0x7)

ReadBytes(buffer, 0x10);

}

첫 번째 순서는 sizeof(Parser); 를 위한 LFH를 활성화 하는 것이다. 그리고 Parser object가

당신이 overflow할 수 있는 할당 뒤에 위치해 있도록 메모리를 설정한다. Overflow된 chunk

header가 할당 후에 RtlpLowFragHeapAllocFromContext() 내에서 재할당 되지 않도록 또 다

른 할당이 만들어 져야 한다. 이것은 사용자가 조절 가능한 값으로 인접한 header를 덮어쓸 수

있도록 한다.

그림 16. Chunk setup

Alloc2내 header를 Parser object를 가리키도록 chunk header를 조절할 값으로 덮어쓴 후에,

Alloc2를 해제하면 Alloc2는 parser object의 메모리 위치를 실제 다음으로 사용 가능한 chunk

로 만들 것이다.

그림 17. Chunk overwrite and free

Page 79: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 79 / 95

이제 우리의 다음 할당은 vtable을 덮어쓴 후 우리의 지배 하에 있을 parser object의 주소를

받게 될 것이다. 이것은 세 번째 할당과 이후 p.DoThings()를 호출을 필요로 한다.

Step-by-Step

1. sizeof(Parser)에 대한 LFH를 활성화

2. Parser object를 할당

3. Overflow될 chunk 메모리를 할당(Alloc1)

4. 덮어 쓰여질 chunk 메모리 할당(Alloc2)

5. Alloc1에서 Alloc2로 chunk header를 0x5와 동일한 UnusedBytes와 parser object를

가리키는 block들의 크기와 동일한 SegOffset로 overwrite

6. Alloc2를 해제

7. Chunk 메모리를 할당하고 원하는 내용을 작성. 이 시나리오에서 당신은 parser object의

vtable을 가리키도록 덮어쓴다고 가정함(Alloc3)

8. Parser object에 대한 호출을 trigger(예제에서는 p.DoThings())

Prerequisites

특정 크기의 할당을 제어

특정 크기를 위한 LFH를 활성화할 수 있는 능력

Chunk가 overflow되기 전에 적법한 LFH chunk를 배치

적어도 8바이트를 overflow 하고 메모리의 인접한 조각의 chunk header를 수정

덮어 쓰여진 chunk들을 해제할 수 있는 능력

Methodology

1. LFH 활성화

2. Heap의 정상화

3. Alloc1

4. Alloc2

5. Alloc3

6. Alloc3를 overwrite(적어도 8바이트)

7. Alloc3 해제(Alloc1을 가리키도록 header를 수정)

8. Alloc4(실제로 Alloc1을 가리킴)

9. Data 쓰기(Alloc1 내 메모리를 오염)

10. Alloc1을 사용

Page 80: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 80 / 95

FreeEntryOffset Overwrite

이 섹션은 이 논문에 대해 연구하는 동안 고안한 새로운 기술을 시작한다. 이전 섹션에서 우리는

UserBlock 내 chunk들을 현재 오프셋과 다음으로 사용 가능한 chunk의 오프셋에 대한 추적을

유지함으로써 조절할 수 있는 방법에 관해 논의를 했다. 현재 오프셋은 _INTERLOCK_SEQ 구조

체 내에서 유지된다. 이 방법으로 LFH는 다음 free chunk를 어디서 얻을 수 있는지 알 수 있다.

이 방법의 주요 문제점은 다음 FreeEntryOffset이 사용자가 쓰기 가능한 데이터의 처음 2바이트

에 저장된다는 점이다. 이것은 할당자가 조절될 수 있는 chunk의 크기를 기반으로 값을 얻어야

만 한다는 것을 의미한다. 다음으로 이것은 다음 반복을 위해 저장된다. LFH 내 각 chunk는 다

른 것들과 직접 인접할 수 있고 chunk header는 할당을 확인하지 않기 때문에,

FreeEntryOffset은 새로운 offset으로 덮어쓸 수 있게 된다. 이 새로운 offset은 나중에 할당이

부분적으로 임의인 위치를 가리키도록 한다.

표 49. Try/Catch for LFH allocation

try

{

//the next offset is stored in the 1st 2-bytes of userdata

short NextOffset = UserBlocks + BlockOffset + sizeof(_HEAP_ENTRY));

_INTERLOCK_SEQ AggrExchg_New;

AggrExchg_New.Offset = NextOffset;

}

catch

{

return 0;

}

이 지식과 힘께 적어도 0x9(0xA를 선호한다)바이트를 덮어쓰고, NextOffset의 값을 감염할 수

있고, 직접 나중에 만든 할당에 영향을 미칠 수 있다. 절반의 확률인 예제를 보자.

표 50. FreeEntryOffset overwrite example

class Dollar

{

public:

Dollar();

virtual void INeedDollar();

private:

int Job = 0;

int Dollars = 0;

Page 81: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 81 / 95

unsigned int Booze = 0xFFFFFFFF

};

int first_len, second_len, total_len;

char *dollar1, *dollar2, *dollar3, *data1, *data2;

char *statement = "I need dollar";

first_len = ReadInt();

second_len = ReadInt();

data = alloc(first_len);

ReadBytes(data, first_len);

//int wrap

total_len = first_len + second_len;

//we can write more data than this

//allocation can hold

dollar1 = alloc(total_len);

memcpy(dollar1, data, first_len);

//this will store the tainted FreeEntryOffset

//in the _INTERLOCK_SEQ

dollar2 = alloc(total_len);

memcpy(dollar2, statement, strlen(statement));

//although not on the same UserBlock, it will be

//allocated on an adjacent page in memory

Dollar dollars[0x20];

for(i = 0; i < 0x20; i++)

{

dollars[i] = new Dollar();

}

//this will give us 0xFFFF * 0x8 bytes of

//forward range when making an allocation, more

//than enough room to overwrite any of the Dollar objects

dollar3 = alloc(first_len);

memcpy(dollar3, data, first_len);

//one or more of these can be overwritten

//with data copied into dollar3

for(i = 0; i < 0x20; i++)

{

dollars[i].INeedDollar();

}

Page 82: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 82 / 95

이 단순한 함수의 가장 처음에서 따라오는 잠재적인 buffer overflow에 의해 명백한 integer

wrap이 있지만 이전 exploitation 방법들과는 반대로, 우리는 단순히 몇 개의 meta data를 덮

어써서 코드 실행을 얻을 수 없다. 그러나 dollar2의 data 내에 저장된 FreeEntryOffset을 덮어

씀으로 인해 우리는 dollar3에 대해 리턴되는 주소를 제어할 수 있다.

LFH가 Bucket[0x6](0x30 바이트)를 위해 활성화 되었다고 가정하고 역시 dollar1은

Bucket*0x6을 위해 할당될 첫 번째 chunk 라고 가정하자. (이것은 단지 단순화하기 위해서이다.

실제로는 특정 크기의 LFH로부터 첫 번째 할당이 될 필요는 없다.). UserBlock의 상태는 아래와

같다.

그림 18. Userblock after chunking

dollar1이 할당된 후에 이이서 다음 free heap chunk로 overflow 되고, FreeEntryOffset은 다

음 chunk의 오프셋으로 업데이트 된다(0x0008).

Page 83: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 83 / 95

그림 19. FreeEntryOffset Overwrite

이 시점에서 다음 오프셋은 우리가 조절한 값으로 덮어 쓰여지지만 이 오프셋을 사용하기 위해

당신은 나중에 사용될 _INTERLOCK_SEQ 내 값을 저장하는 다른 할당을 호출해야 할 필요가

있다. 이 예제에서 이 할당은 dollar2를 위한 메모리를 할당할 때 일어난다.

Page 84: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 84 / 95

그림 20. Second allocation, setting overwritten FreeEntryOffset

이제 우리는 0x1501의 FreeEntryOffset을 가진다. 이 값은 이 UserBlock을 위해 할당된 페이

지에 쓰여지기 대문에 인접한 메모리를 덮어쓸 필요가 있다. (이것은 항상 그런 것은 아니다.).

만약 이 메모리 페이지 내 덮어쓸 수 있는 완벽한 data가 있다면, 이제 덮어 쓰여진 오프셋은

0x0000-0xFFFF 에서 모든 값이 될 수 있다.

이 예제에서 인접한 메모리는 0x20 Dollar objects의 배열을 제작할 때 생성된다. (우리는 이들

이 0x40 바이트 넓이를 가진다고 가정한다.). 한번 Bucket[0x8](0x40 바이트)을 위한 LFH가

활성화되면, 당신은 다음과 유사하게 보이는 전반적인 메모리 레이아웃을 가지게 된다.

Page 85: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 85 / 95

그림 21. Multiple UserBlocks

이 시점에서 함수 포인터나 vtable들을 덮어쓰기 위한 매우 유리한 상황이 있다. 0x30 바이트들

을 위한 다음 할당은 다음 free entry가 현재 UserBlock[0x5157800]의 주소를 가져와서

UserBlocks[0x0E](chunk 내 0x6 block들의 2개 항목이다) 내 현재 오프셋을 더하고 마지막으

로 FreeEntryOffset*8[0x1501]을 더하여 계산되기 때문에 0x5162006 메모리 주소를 반환한

다.

NextChunk = UserBlock + Depth_IntoUserBlock + (FreeEntryOffset * 8)

NextChunk = 0x5157800 + 0x0E + (0x1501 * 8)

NextChunk = 0x5162016

이것은 우리가 dollar3를 할당하고 데이터의 0x30 바이트를 쓰면, 우리는 Dollar 배열 내

object를 덮어쓸 수 있다는 것을 의미한다. 이 덮어 쓰여진 오프셋은 배열 내 특정 object를 가

리키도록 조정될 수 있다. 비록 이 예제가 단순하고 heap 내 application data를 공격했지만,

이것은 여전히 n바이트를 덮어쓰는 방법을 여러 가지로 변화시켜 사용할 수 있다.

Page 86: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 86 / 95

그림 22. Cross page overwrite

Step-by-Step

1. LFH 활성화

2. 사용할 크기의 chunk 할당(dollar1)

3. 다음에 할당되어야 할 바로 인접한 free chunk 안에 적어도 0x9(0xA 권장) 바이트로

dollar1 overflow

4. 사용할 크기의 chunk 할당(dollar2)[이것은 _INTERLOCK_SEQ 내 덮어 쓰여진

FreeEntryOffset를 저장]

5. 덮어 쓰여질 object를 할당. 이 할당은 FreeEntryOffset에 사용할 수 있는 범위(0x08

* 0xFFFF가 최대) 내에 있어야 할 필요가 있다. 이 경우에 Dollar object들은 인접한

메모리 페이지에 있다.

6. 사용할 크기의 chunk 할당(dollar3)[이것은 덮어 쓰여진 FreeEntryOffset를 기반으로

당신이 선택하는 주소를 반환]

7. 중요한 object를 덮어쓰도록 n바이트를 씀

8. 덮어 쓰여진 함수 포인터/vtable을 호출

Page 87: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 87 / 95

Prerequisites

특정 Bucket을 위한 LFH를 사용하는 능력

특정 Bucket을 위한 할당 제어

적어도 0x9바이트를 덮어씀(0xA를 권장)

Overwrite를 위한 인접한 free block

최대 범위(0xFFFF * 0x8) 내 overwrite를 위한 object

Overwrite 된 object를 trigger 하는 방법

Methodology

1. LFH 활성화

2. Heap의 일반화

3. Alloc1

4. 인접한 chunk의 FreeEntryOffset을 overwrite

5. Alloc2

6. Alloc3

7. Alloc3에 data 쓰기(관심있는 object를 overwrite)

8. Trigger

Page 88: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 88 / 95

Observations

이 섹션에서 제시된 자료를 exploitation section 속에 넣는 것이 더 쉬운 것일 수 있지만 나는

Observations 아래 두는 것이 낫다고 생각했다. 이것의 숨은 이유는 코드 실행을 사용하기 위해

이 기술을 사용하려고 시도할 때 신뢰성이 부족하기 때문이다. 마지막 이유로 내가 하기 원하는

것은 Sinan Erin이 딸기 푸딩을 씌우는 것 처럼 exploitation 섹션 내 흥미로운 아이템을 배치하

는 것이다.

SubSegment Overwrite

우리가 Allocation 섹션에서 보았듯이 LFH는 메모리를 할당할 때 SubSegment를 사용하려고

시도한다. 만약 어떤 SubSegment도 사용할 수 없다면, LFH는 UserBlock를 위한 공간을 할당

하고 이후에 SubSegment 구하는 진행을 한다.

표 51. SubSegment acquisition

HEAP_SUBSEGMENT *SubSeg = HeapLocalSegmentInfo->ActiveSubsegment;

if(SubSeg)

{

while(1)

{

.

.

.

//checks to ensure valid subsegment

_HEAP_USERDATA_HEADER *UserBlocks = SubSeg->UserBlocks;

if(!Depth || !UserBlocks || SubSeg->LocalInfo != HeapLocalSegmentInfo)

{

break;

}

.

.

.

}

}

.

.

.

_HEAP_USERDATA_HEADER *UserData = RtlpAllocateUserBlock(lfh, UserBlockCacheIndex, BucketByteSize + 8);

DeletedSubSegment = ExInterlockedPopEntrySList(HeapLocalData);

Page 89: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 89 / 95

if (DeletedSubSegment)

{

// if there are any deleted subsegments, use them

NewSubSegment = (_HEAP_SUBSEGMENT *)(DeletedSubSegment - 0x18);

}

else

{

_HEAP_SUBSEGMENT *NewSubSegment = RtlpLowFragHeapAllocateFromZone(LFH, LocalDataIndex);

//return failure use back-end

if(!NewSubSegment)

return 0;

}

//this function will setup the _HEAP_SUBEMENT structure

//and chunk out the data in 'UserData' to be of HeapBucket->SizeIndex chunks

RtlpSubSegmentInitialize(LFH, NewSubSegment, UserBlock, RtlpBucketBlockSizes[HeapBucket->SizeIndex],

UserDataAllocSize,HeapBucket);

.

.

.

_HEAP_SUBSEGMENT가 설치되지 않거나(예를 들어, 특정 Bucket에 대한 LFH로부터 첫 번째

할당일 경우) 또는 만약 모든 SubSegments가 사용되고 있을 때 다음 실행 흐름이 일반적으로

발생한다. 이 상황은 다음과 같은 메모리 레이아웃을 제공한다.

Page 90: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 90 / 95

그림 23. UserBlock residing before SubSegment pointers in memory

보시다시피, 만약 당신이 UserBlock chunk를 SubSegments를 위해 할당된 메모리 앞에 위치

시킨다면 이후 _HEAP_SUBSEGMENT 구조체에 의해 사용되는 포인터가 overflow 된다.

Richard Johnson의 _LFH_BLOCK_ZONE 내 FreePointer가 절반 정도 임의의 위치에

_HEAP_SUBSEGMENT 구조체를 작성하는데 노출될 수 있다는 이론 제시에도 불구하고 나는 다

른 생각이 있다. 당신은 UserBlock 포인터를 포함하는 SubSegment를 overwrite 할 수 있다.

그리고 subsequent 할당을 만드는 것을 진행하면 사용자에게 제공된 포인터에 n 바이트 쓸 수

있도록 제공한다.

Page 91: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 91 / 95

그림 24. Overwrite into _HEAO_SUBSEGMENT

Example

다음 예제는 어떻게 LFH Bin이 특정 크기에 대해 할당되고 그 후 subsequent 할당이

_HEAP_SUBSEGMENT를 overwrite하고 나중의 할당을 위해 사용된 오염된 값이 얻어지는 지

를 보여준다. 0x200 크기의 overwrite는 특정한 값이 아니라 모든 _LFH_BLOCK_ZONE 항목들

이 overwrite 될 수 있음을 보여주기 위해 사용된 것임을 명심하라.

표 52. SubSegment overwrite

//turn on the LFH

for(i = 0; i < 0x12; i++)

allocb[i] = HeapAlloc(pHeap, 0x0, SIZE);

//first allocation for SIZE in LFH

alloc1 = HeapAlloc(pHeap, 0x0, SIZE);

//get closer in virtual memory

Page 92: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 92 / 95

//to make overwrite easier

for(i = 0; i < 0x27; i++)

alloc2 = HeapAlloc(pHeap, 0x0, SIZE);

//overwrite the UserBlocks pointer for this SubSegment

alloc3 = HeapAlloc(pHeap, 0x0, SIZE);

memset(alloc3, 0x41, 0x200);

//this allocation will use the tainted UserBlocks

alloc4 = HeapAlloc(pHeap, 0x0, SIZE);

Issues

이 기술을 유지하는 데 다시 두 가지의 장애물이 있다. 첫 번째는 특정 방법으로 heap을 조절하

거나 못하느냐에 달려있다. 예제에서 본 것처럼, UserBlock chunk는 SubSegment 포인터들을

(_LFH_BLOCK_ZONEs) 저장하기 위해 사용된 메모리의 앞에 할당되어야 할 필요가 있다. LFH

Bin을 활성화시키는 것은 사소한 반면, SubSegment 포인터들을 위해 사용된 chunk 앞의 연속

적인 메모리 내로 위치시키는 것은 현실에서는 더욱 어려울 것이다. 이것이 얼마나 어려운 것인

지는 Internet Explorer을 한번 보아야 할 필요가 있다.

두 번째 작업은 SubSegment 무결성을 보장하기 위해 할당 루틴이 실행되었는지에 대한 검사를

피하는 것이다. 이 검사는 요청된 크기를 위한 _HEAP_LOCAL_SEGMENT_INFO 구조체가

_HEAP_SUBSEGMEMT 내 현재 저장되어 있는 것과 일치하는지 확인한다.

표 53. SubSegment validation

HEAP_LOCAL_SEGMENT_INFO *HeapLocalSegmentInfo = HeapLocalData->SegmentInfo[HeapBucket->SizeIndex];

_HEAP_SUBSEGMENT *SubSeg = HeapLocalSegmentInfo->ActiveSubsegment;

.

.

if(!Depth || !UserBlocks || SubSeg->LocalInfo != HeapLocalSegmentInfo)

{

break;

}

Depth와 UserBlock들 조건은 쉽게 도용할 수 있는 반면에, LocalInfo 구조체가 Low

Fragmentation heap 내 저장된 것처럼 동일한 포인터인지를 확인하는 검사는 조금 더 복잡하다.

노출된 chunk 주소를 사용하는 것도 이 문제를 해결하는 하나의 가능한 해결책이지만 더 오래

application을 실행하는 것은 신뢰성 있게 주소를 예측하는 것이 더욱 어렵다. 가장 쉬운 방법은

HeapBase로부터 FrontEndHeap 포인터의 주소를 획득하는 것이다. 이것은 _LFH_HEAP 구조

체에 대한 포인터를 제공하고 적절한 _HEAP_LOCAL_SEGMENT_INFO 항목의 주소는 요청된

Page 93: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 93 / 95

chunk의 크기에 의해 유추될 수 있다.

전체적으로 이 기술은 heap meta-data와 overwrite을 위한 주소에 전적으로 의존하는 n바이트

를 쓸 수 있는 상황을 매우 쉽게 제공한다. 불행하게도 이 문서를 작성하는 시점에서 신뢰할 수

있는 기술은 아직 발견되지 않았다. 이것이 불가능을 뜻하는 것이 아니라 나의 큰 실패 때문에

포기하게 된 것을 의미한다.

Page 94: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 94 / 95

Conclusion

Windows 메모리 관리의 많은 측면이 Windows XP 코드 기반 이후에 변경되었다. 이런 변화들

의 대부분은 Windows Vista에서 시작되어 Windows 7로 이어졌다. 사용되는 data 구조체들은

더욱 복잡해졌고 멀티 스레드를 통해 자주 요청되는 메모리를 위해 더 나은 지원을 제공하지만

과거 heap 구현들과 비슷한 사용법으로 인해 일부는 친숙하다.

이 새로운 data 구조체들은 이전과는 다른 방식으로 사용된다. 전용 FreeLists의 개념은 보다 빠

른 기술을 위해 사장되었다. 이들 새로운 기술들은 front-end manager를 활성화 시키기 위해

back-end manager에 대한 수단을 제공한다. 이제 Lookaside List는 없어지고 오직 Low

Fragmentation heap만 지원한다. 새로운 구조체는 UserBlock로써 참조되고 이제 특정 임계값

이 충족된 후에는 하나의 큰 연속된 메모리 조각 내에 HeapBuck을 위한 모든 chunk들을 유지

한다. 이것은 잦은 할당과 해제들을 위한 더욱 효과적인 메모리 접근을 제공한다.

비록 encoding된 header, anti-bitmap flipping 그리고 safe linking 같은 많은 보안 매커니즘

이 추가되었지만, 실제적으로 heap을 조작을 더욱 신뢰성이 있게 만들어 주는 몇 가지 시나리오

가 발생되었다. 이제 같은 크기의 메모리는 다음 각 메모리들과 인접하여 위치할 수 있고,

overwrite는 좀 더 예측이 가능하며 data seeding은 할당과 해제를 간단하게 조절하여 구할 수

있다.

새로이 생성된 모든 data 구조체들과 알고리즘들은 새로운 복잡도를 가진다. 이 문서에서 Ben

Hawkes와 내가 이전에 보여준 것처럼 이 복잡성은 실행 흐름을 조절하기 위해 사용될 수 있다.

새로운 오프셋들과 포인터들은 heap의 상태를 수정하고 신뢰성 있는 실행의 수단을 좀 더 제공

하는 간단한 overflow 시나리오들 내에서 사용될 수 있다.

마지막으로 meta-data overwrite는 실행 흐름 내 변경으로 이어질 수 있지만, 그들은 예전만큼

유용하지는 않다. Heap exploitation은 시간이 갈수록 더욱 복잡해졌고, 할당/해제를 하는 것이

무엇인지, 그것이 어디에서 일어나는지에 대해 이해하는 능력은 당신이 overwrite 하는 것(DEP

와 ASLR은 말할 나위 없이) 보다 더욱 중요해지고 있다. Front와 back-end manager에 대해

깊게 논의하는 것이 불필요한 것처럼 보일 수 있지만, heap을 유능하게 조절하기 위해 당신은

그것이 어떻게 동작하는지를 정확하게 이해해야만 한다.

- Chris Valasek 2010

@nudehaberdasher

[email protected]

Page 95: Understanding the Low Fragmentation Heappds24.egloos.com/pds/201205/09/51/Understanding_the_Low... · 2012-05-09 · heap chunk header들에 의해 사용되는 측정 단위이다.

페이지 95 / 95

Bibliography

Hawkes, Ben. 2008. Attacking the Vista Heap. Ruxcon 2008 / Blackhat USA 2008, http://www.lateralsecurity.com/downloads/hawkes_ruxcon-nov-2008.pdf http://www.blackhat.com/presentations/bh-usa-08/Hawkes/BH_US_08_Hawkes_Attacking_Vista_Heap.ppt Immunity Inc. Immunity Debugger heap library source code. Immunity Inc. http://debugger.immunityinc.com/update/Documentation/ref/Libs.libheap-pysrc.html Johnson, Richard. 2006. Windows Vista: Exploitation Countermeasures. Toorcon 8, http://rjohnson.uninformed.org/Presentations/200703%20EuSecWest%20-%20Windows%20Vista%20Exploitation%20Countermeasures/rjohnson%20-%20Windows%20Vista%20Exploitation%20Countermeasures.ppt McDonald/Valasek. 2009. Practical Windows XP/2003 Heap Exploitation. Blackhat USA 2009, http://www.blackhat.com/presentations/bh-usa-09/MCDONALD/BHUSA09-McDonald-WindowsHeap-PAPER.pdf Marinescu, Adrian. 2006. Windows Vista Heap Management Enhancements. Blackhat USA 2006 http://www.blackhat.com/presentations/bh-usa-06/BH-US-06-Marinescu.pdf Moore, Brett. 2005. Exploiting Freelist[0] on XP Service Pack 2. Security-Assessment.com White Paper, http://www.insomniasec.com/publications/Exploiting_Freelist%5B0%5D_On_XPSP2.zip Moore, Brett. 2008. Heaps About Heaps. SyScan 2008, http://www.insomniasec.com/publications/Heaps_About_Heaps.ppt Probert, David B. (PhD), http://www.i.u-tokyo.ac.jp/edu/training/ss/lecture/new-documents/Lectures/16-UserModeHeap/UserModeHeapManager.ppt Sotirov, Alexander. 2007. Heap Feng Shui in JavaScript. Black Hat Europe 2007, http://www.blackhat.com/presentations/bh-europe-07/Sotirov/Presentation/bh-eu-07-sotirov-apr19.pdf Vreugdenhil, Peter. 2010. Windows7-InternetExplorer8. Pwn2Own 2010, http://vreugdenhilresearch.nl/Pwn2Own-2010-Windows7-InternetExplorer8.pdf Waisman, Nico. 2010. (A)leatory (P)ersitent (T)hreat, http://eticanicomana.blogspot.com/2010/03/aleatory-persitent-threat.html