-->

PEB, TEB 구조체란 ? + 안티디버깅에 활용하는 방법

TEB(Thread Environment Block)는 쓰레드정보를 담고 있는 구조체이고 PEB(Process Environment Block)은 프로세스 정보를 담고있는 구조체이다. 옛날에 덤프 분석할 때, PEB 정보 확인을 안해서 중요 정보를 놓쳤던? 적이 있다. 어쨌든 덤프 분석할 때도 잘 알아야 하고 안티리버싱에도 많이 사용되고 기타 등등 중요한 구조체들이기 때문에 잘 알아둘 필요가 있어서 여기에 정리한다. +추가되는 정보가 있다면 계속 채워나갈 예정이다.

 

 

EPROCESS, ETHREAD, PEB, TEB 차이

먼저, 같은 프로세스, 스레드 정보를 담고 있는 구조체이지만 EPROCESS, ETHREAD 구조체와 PEB, TEB 구조체가 어떻게 다른지를 알고 넘어가야 한다. EPROCESS, ETHREAD는 커널 레벨에서 프로세스, 스레드를 관리하기 위한 구조체라면, PEB, TEB는 유저레벨에서 프로세스에 대한 정보를 저장하고 있는 구조체이다. 

 

프로세스가 생성되면 커널 메모리에 EPROCESS 구조체가 생성되기 때문에 모든 프로세스는 각각 EPROCESS 구조체를 하나씩 갖게되고 사용하는 모든 스레드 가각에 대한 ETHREAD 구조체를 갖는다. EPROCESS, ETHREAD는 커널 메모리에서만 접근할 수 있어서 유저메모리에 프로세스, 스레드 정보를 저장하고 있는 구조체가 필요했고 그게 PEB, TEB인 것이다. 나중에 풀덤프에서 유저모드로 콘텍스트를 변경하는 방법에 관한 포스팅을 하게되면 그때 EPROCESS나 ETHREAD에 대해 좀더 다루기로 한다.

 

 

PEB 구조체 확인

windbg에서 PEB 구조체 시작 주소 확인은 !peb에서 확인할 수 있다.

 

 

 

 

 

peb 구조체 구조를 확인하려면 dt를 사용하면 된다.

 

 

 

 

 

중요한 정보야 분석 목적?에 따라서 다르겠지만 일반적으로 PEB에서 많이 사용되는 멤버를 살펴보면, 디버깅 여부를 판별할 수 있는 정보나 imagebase, 모듈 리스트, 힙 정보 등이 있다.

 

0:005> dt _peb
ntdll!_PEB
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar  // 디버깅당하고 있는지 확인 (IsDebuggerPresent)
   +0x003 BitField         : UChar
   +0x003 ImageUsesLargePages : Pos 0, 1 Bit
   +0x003 IsProtectedProcess : Pos 1, 1 Bit
   +0x003 IsLegacyProcess  : Pos 2, 1 Bit
   +0x003 IsImageDynamicallyRelocated : Pos 3, 1 Bit
   +0x003 SkipPatchingUser32Forwarders : Pos 4, 1 Bit
   +0x003 SpareBits        : Pos 5, 3 Bits
   +0x004 Mutant           : Ptr32 Void
   +0x008 ImageBaseAddress : Ptr32 Void  // 프로세스의 imagebase
   +0x00c Ldr              : Ptr32 _PEB_LDR_DATA  // 프로세스에 로드된 모듈 리스트
   +0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
   +0x014 SubSystemData    : Ptr32 Void
   +0x018 ProcessHeap      : Ptr32 Void  // 프로세스의 힙 정보
   +0x01c FastPebLock      : Ptr32 _RTL_CRITICAL_SECTION
   +0x020 AtlThunkSListPtr : Ptr32 Void
   +0x024 IFEOKey          : Ptr32 Void
   +0x028 CrossProcessFlags : Uint4B
   +0x028 ProcessInJob     : Pos 0, 1 Bit
   +0x028 ProcessInitializing : Pos 1, 1 Bit
   +0x028 ProcessUsingVEH  : Pos 2, 1 Bit
   +0x028 ProcessUsingVCH  : Pos 3, 1 Bit
   +0x028 ProcessUsingFTH  : Pos 4, 1 Bit
   +0x028 ReservedBits0    : Pos 5, 27 Bits
   +0x02c KernelCallbackTable : Ptr32 Void
   +0x02c UserSharedInfoPtr : Ptr32 Void
   +0x030 SystemReserved   : [1] Uint4B
   +0x034 AtlThunkSListPtr32 : Uint4B
   +0x038 ApiSetMap        : Ptr32 Void
   +0x03c TlsExpansionCounter : Uint4B
   +0x040 TlsBitmap        : Ptr32 Void
   +0x044 TlsBitmapBits    : [2] Uint4B
   +0x04c ReadOnlySharedMemoryBase : Ptr32 Void
   +0x050 HotpatchInformation : Ptr32 Void
   +0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
   +0x058 AnsiCodePageData : Ptr32 Void
   +0x05c OemCodePageData  : Ptr32 Void
   +0x060 UnicodeCaseTableData : Ptr32 Void
   +0x064 NumberOfProcessors : Uint4B
   +0x068 NtGlobalFlag     : Uint4B
   +0x070 CriticalSectionTimeout : _LARGE_INTEGER
   +0x078 HeapSegmentReserve : Uint4B
   +0x07c HeapSegmentCommit : Uint4B
   +0x080 HeapDeCommitTotalFreeThreshold : Uint4B
   +0x084 HeapDeCommitFreeBlockThreshold : Uint4B
   +0x088 NumberOfHeaps    : Uint4B
   +0x08c MaximumNumberOfHeaps : Uint4B
   +0x090 ProcessHeaps     : Ptr32 Ptr32 Void
   +0x094 GdiSharedHandleTable : Ptr32 Void
   +0x098 ProcessStarterHelper : Ptr32 Void
   +0x09c GdiDCAttributeList : Uint4B
   +0x0a0 LoaderLock       : Ptr32 _RTL_CRITICAL_SECTION
   +0x0a4 OSMajorVersion   : Uint4B
   +0x0a8 OSMinorVersion   : Uint4B
   +0x0ac OSBuildNumber    : Uint2B
   +0x0ae OSCSDVersion     : Uint2B
   +0x0b0 OSPlatformId     : Uint4B
   +0x0b4 ImageSubsystem   : Uint4B
   +0x0b8 ImageSubsystemMajorVersion : Uint4B
   +0x0bc ImageSubsystemMinorVersion : Uint4B
   +0x0c0 ActiveProcessAffinityMask : Uint4B
   +0x0c4 GdiHandleBuffer  : [34] Uint4B
   +0x14c PostProcessInitRoutine : Ptr32     void 
   +0x150 TlsExpansionBitmap : Ptr32 Void
   +0x154 TlsExpansionBitmapBits : [32] Uint4B
   +0x1d4 SessionId        : Uint4B
   +0x1d8 AppCompatFlags   : _ULARGE_INTEGER
   +0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
   +0x1e8 pShimData        : Ptr32 Void
   +0x1ec AppCompatInfo    : Ptr32 Void
   +0x1f0 CSDVersion       : _UNICODE_STRING
   +0x1f8 ActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
   +0x1fc ProcessAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
   +0x200 SystemDefaultActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
   +0x204 SystemAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
   +0x208 MinimumStackCommit : Uint4B
   +0x20c FlsCallback      : Ptr32 _FLS_CALLBACK_INFO
   +0x210 FlsListHead      : _LIST_ENTRY
   +0x218 FlsBitmap        : Ptr32 Void
   +0x21c FlsBitmapBits    : [4] Uint4B
   +0x22c FlsHighIndex     : Uint4B
   +0x230 WerRegistrationData : Ptr32 Void
   +0x234 WerShipAssertPtr : Ptr32 Void
   +0x238 pContextData     : Ptr32 Void
   +0x23c pImageHeaderHash : Ptr32 Void
   +0x240 TracingFlags     : Uint4B
   +0x240 HeapTracingEnabled : Pos 0, 1 Bit
   +0x240 CritSecTracingEnabled : Pos 1, 1 Bit
   +0x240 SpareTracingBits : Pos 2, 30 Bits

 

 

참고로, 구조체를 실제 값과 같이 보려면 dt 뒤에 위에서 확인한 peb 주소를 같이 넘겨주면 된다.

 

 

 

 

 

TEB 구조체에서 PEB 접근하기

teb 구조체도 peb와 마찬가지로 확인할 수 있다. 사실, teb는 내 지식선상에서는 peb 구조체의 주소를 구하기 위해 많이 쓰이는 것 같다. teb+0x30에 ProcessEnvironmentBlock 멤버변수가 있고 여기서 peb 구조체 시작 주소를 확인할 수 있다.

 

 

 

 

 

TEB를 사용해 PEB에 접근하는 방법에는 ntdll 함수인 NtcurrentTeb API를 사용하거나 FS 레지스터를 사용하는 방법 등이 있다. FS 레지스터는 (유저모드에서는) teb 구조체를 가리키는 데에 사용되는 레지스터이기 때문에 FS 레지스터를 사용하면 peb 구조체의 시작 주소를 구할 수 있게 된다.

 

아래 어셈을 보면, fs:[30]으로 peb 주소를 구하고 원하는 peb 정보를 얻고 있다. 참고로 fs:[0]은 TEB 구조체의 시작 주소를 담고있다.

 

 

 

 

 

PEB를 활용한 안티디버깅

사실 내가 이 포스팅을 쓰기 시작한 진짜 이유는 안티리버싱 때문이다. peb의 몇가지 멤버를 활용하면 안티디버깅을 위한 정보를 사용할 수 있다. 아래 열거한 방식보다 더 많치만 일단 2개만 알아보고 나중에 추가하도록 한다.

 

PEB.BeingDebugged (peb+0x2)

대표적이면서도 가장 기초적이어서 공부목적으로밖에 사용하지 않는? IsDebuggerPresent 함수는 내부 코드를 살펴보면 peb+0x2 위치의 BeingDebugged 값을 확인하는 방식으로 디버깅 여부를 판단한다. 이 값이 0x0이면 정상이고 0x1이면 디버깅 중인 상태로 판단한다.

 

PEB.ProcessHeap (peb+0x18)

peb의 ProcessHeap은(peb+0x18) HEAP 구조체의 시작 주소를 갖고 있고 PEB.ProcessHeap+0xC는 Flags를 +0x10은 ForceFlags 값을 갖고있다. 이 Flag 값을 사용하면 디버깅 중인지 여부를 판단할 수 있게된다. PEB.ProcessHeap + 0xC 값은 0x2일 경우 정상이고 0x0인 경우 디버깅 중인 상태로 판단한다. 

 

 

 

 

 

PEB.ProcessHeap + 0x10 값은 0x0일 경우 정상이고 그 외의 값인 경우 디버깅 중인 상태로 판단한다. 

 

 

 

 

PEB.Ldr (peb+0xc)

peb의 Ldr은(peb+0xc)는 _PEB_LDR_DATA 구조체를 가리키는데 Ldr을 사용한 안티디버깅 방법은 디버깅 중인 프로세스의 사용되지 않는 힙은 0xEEFEEEFE 혹은 0xABABABAB로 채워지는 특성을 이용한 방법이다. _PEB_LDR_DATA 구조체는 힙메모리 안에 있기 때문에 Ldr주소에서부터 메모리를 검사하면 디버깅 여부를 탐지할 수 있게된다.

 

 

 

 

댓글

Designed by JB FACTORY