저번 TEB 편에 이어 PEB 편을 작성하겠습니다. 이 강좌는 끝내놔야 불만/불평이 없을듯 해서 :)
PEB는 TEB와 달리 프로세스의 정보를 저장하는 구조체입니다. (이름에서 알 수 있죠 :p)
PEB는 아래와 같이 생긴 녀석입니다.
nt!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+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 FastPebLockRoutine : Ptr32 Void
+0x024 FastPebUnlockRoutine : Ptr32 Void
+0x028 EnvironmentUpdateCount : Uint4B
+0x02c KernelCallbackTable : Ptr32 Void
+0x030 SystemReserved : [1] Uint4B
+0x034 AtlThunkSListPtr32 : Uint4B
+0x038 FreeList : Ptr32 _PEB_FREE_BLOCK
+0x03c TlsExpansionCounter : Uint4B
+0x040 TlsBitmap : Ptr32 Void
+0x044 TlsBitmapBits : [2] Uint4B
+0x04c ReadOnlySharedMemoryBase : Ptr32 Void
+0x050 ReadOnlySharedMemoryHeap : 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 Void
+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 ImageProcessAffinityMask : 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 Void
+0x1fc ProcessAssemblyStorageMap : Ptr32 Void
+0x200 SystemDefaultActivationContextData : Ptr32 Void
+0x204 SystemAssemblyStorageMap : Ptr32 Void
+0x208 MinimumStackCommit : Uint4B
( ps: 일부 필드의 Offset은 NT version에 따라 조금씩 달랐습니다. )
PEB는 TEB에서 point됩니다. 따라서 아래 명령을 통해서 그 주소 값을 획득할 수 있습니다.
PPEB peb;
__asm
{
MOV peb, fs:[0x30] // +0x030 ProcessEnvironmentBlock : Ptr32 _PEB
}
혹은 깔끔하게 C언어만으로 구현해야한다면, 메모리에 직접 접근해서 구할 수도 있을것 입니다.
// 0x7FFDF000는 NT OS의 TEB address
PPEB PEB = *(PPEB *)(0x7FFDF030L);
그러나, 메모리를 직접 접근하는 것은 위험 부담이 크므로, (NT Version에 따라 동작하지 않을수도 있으므로 :p)
별도의 구조체를 통해서 구할 수도 있습니다.
PEB의 주소값은 PROCESS_BASIC_INFORMATION 이라는 구조체에 잘 나와있습니다.
typedef struct _PROCESS_BASIC_INFORMATION {
NTSTATUS ExitStatus;
PPEB PebBaseAddress;
ULONG AffinityMask;
LONG BasePriority;
ULONG UniqueProcessId;
ULONG InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;
위 구조체는 ZwQueryInformationProcess() Native API에 의해 구해질 수 있으므로...
typedef enum _PROCESS_INFORMATION_CLASS
{
ProcessBasicInformation = 0L, // PROCESS_BASIC_INFORMATION
/* ... omitted ... */
} PROCESS_INFORMATION_CLASS;
NTSTATUS
NTAPI
ZwQueryInformationProcess(
IN HANDLE ProcessHandle,
IN PROCESS_INFORMATION_CLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength ); // exported by NTDLL.DLL
아래와 같이 coding하면 PEB의 주소값을 안정적으로 구할 수 있습니다. (ASM을 쓰는것보단 느리겠지만요 :p)(참고로 ProcessHandle인자만 바꾸어주면 다른 프로세스의 PEB 주소값을 구할 수 있습니다.)
typedef void *PPEB;
typedef LONG NTSTATUS;
#define NtCurrentProcess() ((HANDLE) -1L)
typedef struct _PROCESS_BASIC_INFORMATION {
NTSTATUS ExitStatus;
PPEB PebBaseAddress;
ULONG AffinityMask;
LONG BasePriority;
ULONG UniqueProcessId;
ULONG InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;
typedef enum _PROCESS_INFORMATION_CLASS
{
ProcessBasicInformation = 0L, // PROCESS_BASIC_INFORMATION
/* ... omitted ... */
} PROCESS_INFORMATION_CLASS;
typedef
NTSTATUS
(NTAPI *
ZQIP)(
IN HANDLE ProcessHandle,
IN PROCESS_INFORMATION_CLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength ); // exported by NTDLL.DLL
PPEB GetPEBAddress(HANDLE hProcess)
{
PROCESS_BASIC_INFORMATION pbi;
ZQIP ZwQueryInformationProcess = NULL;
HMODULE hNtDLL = GetModuleHandle("NTDLL.DLL");
if(!hNtDLL)
{
hNtDLL = LoadLibrary("NTDLL.DLL");
if(!hNtDLL) return NULL;
}
ZwQueryInformationProcess = (ZQIP)GetProcAddress(hNtDLL, "ZwQueryInformationProcess");
if(!ZwQueryInformationProcess) return NULL;
ULONG ret;
if(ZwQueryInformationProcess( hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &ret) >= 0)
{
return pbi.PebBaseAddress;
}
else
{
return NULL;
}
}
/*
Usage:
PPEB peb = GetPEBAddress(NtCurrentProcess());
*/
PEB에서는 쓸만한 필드는 아래 세 녀석인듯 합니다.
+0x002 BeingDebugged : UChar
- 디버거 존재 여부를 반환. (IsDebuggerPresent() API에서 사용)
+0x008 ImageBaseAddress : Ptr32 Void
- 프로세스의 메인 module Base Address저장 (GetModuleHandle/LdrGetDllHandle()에서 사용)
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
- 프로세스의 로드된 DLL 목록 저장 (GetModuleHandle/LdrGetDllHandle()에서 사용)
p.s: 일부 user-land rootkit은 Dll Injection 여부를 숨기기 위해 Ldr 필드를 조작해서 DLL을 숨기기도 합니다.