Windows API 중에 IsDebuggerPresent()가 있습니다.
무엇인고 하니.. 만약 현재 프로세스에 debugger가 물려있는지의 여부(즉 내가 debugee 상태라면)를 알려줍니다.
프로그램 크랙할 때
BOOL bIsDebugger;
bIsDebugger = IsDebuggerPresent();
if(bIsDebugger == TRUE)
{
// error!
}
와 같은 루틴을 찾아서 if문을 patch하는 방식을 주로 이용하는데요..
이것을 직접 구현하는 방법을 알아보도록 하겠습니다.
Windows에서는 Thread Environment Block라는 블록이 있습니다.
말 그대로 Thread에 관련된 환경값이 있는데요, 이 중 Thread가 속한 Process Environment Block를 구할 수 있는 field가 있습니다.
TEB는 FS:[0x18]로 구하고, PEB는 아래에 나오는 구조체의 필드로부터 메모리 주소값을 얻어와서 구합니다.
왜 FS:[0x18]이냐... ring3에서는 fs레지스터가 teb를 가리키고 있게 구성 되어 있다고 합니다.
그렇다면 우선 Winternl.h에 선언되어 있는 PEB와 TEB의 구조를 보겠습니다. (VS 2003입니다)
재미있는건 PEB를 IsDebuggerPresent(), CheckRemoteDebuggerPresent() 대신 BeingDebugged 필드를 이용하여도 된다고 하네요.
//
// Instead of using the BeingDebugged field, use the Win32 APIs
// IsDebuggerPresent, CheckRemoteDebuggerPresent
// Instead of using the SessionId field, use the Win32 APIs
// GetCurrentProcessId and ProcessIdToSessionId
// Sample x86 assembly code that gets the SessionId (subject to change
// between Windows releases, use the Win32 APIs to make your application
// resilient to changes)
// mov eax,fs:[00000018]
// mov eax,[eax+0x30]
// mov eax,[eax+0x1d4]
//
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[229];
PVOID Reserved3[59];
ULONG SessionId;
} PEB, *PPEB;
//
// Instead of using the Tls fields, use the Win32 TLS APIs
// TlsAlloc, TlsGetValue, TlsSetValue, TlsFree
//
// Instead of using the ReservedForOle field, use the COM API
// CoGetContextToken
//
typedef struct _TEB {
BYTE Reserved1[1952];
PVOID Reserved2[412];
PVOID TlsSlots[64];
BYTE Reserved3[8];
PVOID Reserved4[26];
PVOID ReservedForOle; // Windows 2000 only
PVOID Reserved5[4];
PVOID TlsExpansionSlots;
} TEB;
typedef TEB *PTEB;
역시 MS......... PEB와 TEB의 구조를 다 Reserved로 바꿔놓았네요.
아래는 reserved를 없애고 실제 데이터가 의미하는 바를 나타낸 구조체 입니다.
typedef struct _CLIENT_ID
{
HANDLE UniqueProcess;
HANDLE UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
typedef struct _RTL_USER_PROCESS_PARAMETERS
{
BYTE Reserved1[16];
PVOID Reserved2[10];
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
typedef struct _PEB_LDR_DATA
{
BYTE Reserved1[8];
PVOID Reserved2[3];
LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
typedef struct _PEB_FREE_BLOCK
{
_PEB_FREE_BLOCK *Next;
ULONG Size;
} PEB_FREE_BLOCK, *PPEB_FREE_BLOCK;
typedef void (* PPEBLOCKROUTINE)(PVOID PebLock);
typedef void **PPVOID;
typedef struct _PEB {
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;
BOOLEAN BeingDebugged;
BOOLEAN Spare;
HANDLE Mutant;
PVOID ImageBaseAddress;
PPEB_LDR_DATA LoaderData;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID SubSystemData;
PVOID ProcessHeap;
PVOID FastPebLock;
PPEBLOCKROUTINE FastPebLockRoutine;
PPEBLOCKROUTINE FastPebUnlockRoutine;
ULONG EnvironmentUpdateCount;
PPVOID KernelCallbackTable;
PVOID EventLogSection;
PVOID EventLog;
PPEB_FREE_BLOCK FreeList;
ULONG TlsExpansionCounter;
PVOID TlsBitmap;
ULONG TlsBitmapBits[0x2];
PVOID ReadOnlySharedMemoryBase;
PVOID ReadOnlySharedMemoryHeap;
PPVOID ReadOnlyStaticServerData;
PVOID AnsiCodePageData;
PVOID OemCodePageData;
PVOID UnicodeCaseTableData;
ULONG NumberOfProcessors;
ULONG NtGlobalFlag;
BYTE Spare2[0x4];
LARGE_INTEGER CriticalSectionTimeout;
ULONG HeapSegmentReserve;
ULONG HeapSegmentCommit;
ULONG HeapDeCommitTotalFreeThreshold;
ULONG HeapDeCommitFreeBlockThreshold;
ULONG NumberOfHeaps;
ULONG MaximumNumberOfHeaps;
PPVOID *ProcessHeaps;
PVOID GdiSharedHandleTable;
PVOID ProcessStarterHelper;
PVOID GdiDCAttributeList;
PVOID LoaderLock;
ULONG OSMajorVersion;
ULONG OSMinorVersion;
ULONG OSBuildNumber;
ULONG OSPlatformId;
ULONG ImageSubSystem;
ULONG ImageSubSystemMajorVersion;
ULONG ImageSubSystemMinorVersion;
ULONG GdiHandleBuffer[0x22];
ULONG PostProcessInitRoutine;
ULONG TlsExpansionBitmap;
BYTE TlsExpansionBitmapBits[0x80];
ULONG SessionId;
} PEB, *PPEB;
typedef struct _TEB
{
NT_TIB Tib;
PVOID EnvironmentPointer;
CLIENT_ID Cid;
PVOID ActiveRpcInfo;
PVOID ThreadLocalStoragePointer;
PPEB Peb;
ULONG LastErrorValue;
ULONG CountOfOwnedCriticalSections;
PVOID CsrClientThread;
PVOID Win32ThreadInfo;
ULONG Win32ClientInfo[0x1F];
PVOID WOW32Reserved;
ULONG CurrentLocale;
ULONG FpSoftwareStatusRegister;
PVOID SystemReserved1[0x36];
PVOID Spare1;
ULONG ExceptionCode;
ULONG SpareBytes1[0x28];
PVOID SystemReserved2[0xA];
ULONG GdiRgn;
ULONG GdiPen;
ULONG GdiBrush;
CLIENT_ID RealClientId;
PVOID GdiCachedProcessHandle;
ULONG GdiClientPID;
ULONG GdiClientTID;
PVOID GdiThreadLocaleInfo;
PVOID UserReserved[5];
PVOID GlDispatchTable[0x118];
ULONG GlReserved1[0x1A];
PVOID GlReserved2;
PVOID GlSectionInfo;
PVOID GlSection;
PVOID GlTable;
PVOID GlCurrentRC;
PVOID GlContext;
NTSTATUS LastStatusValue;
UNICODE_STRING StaticUnicodeString;
WCHAR StaticUnicodeBuffer[0x105];
PVOID DeallocationStack;
PVOID TlsSlots[0x40];
LIST_ENTRY TlsLinks;
PVOID Vdm;
PVOID ReservedForNtRpc;
PVOID DbgSsReserved[0x2];
ULONG HardErrorDisabled;
PVOID Instrumentation[0x10];
PVOID WinSockData;
ULONG GdiBatchCount;
ULONG Spare2;
ULONG Spare3;
ULONG Spare4;
PVOID ReservedForOle;
ULONG WaitingOnLoaderLock;
PVOID StackCommit;
PVOID StackCommitMax;
PVOID StackReserved;
} TEB, *PTEB;
필드들이 참 많죠; 이 중 Winternl.h에 등록되어 있는 것도 있다고 합니다만, platform sdk를 업그레이드 안해서인지 VS 2003에는 구조체들이 없더라구요.
어쨌건간에.. FS:[0x18]에 가보면 진짜로(?) TEB가 저장되어 있습니다. VC에서 TEB를 구하기 위해서는 다음과 같이 작성합니다
DWORD dwTEBAddress;
__asm
{
push eax
mov eax, fs:[0x18]
mov dwTEBAddress, eax
pop eax
}
이게 뭔말인지 모르신다면..
HANDLE hThread;
BOOL bRet;
DWORD dwTEBAddress;
CONTEXT context;
LDT_ENTRY des;
hThread = GetCurrentThread();
// segment 정보만 얻어옴
context.ContextFlags = CONTEXT_SEGMENTS;
bRet = GetThreadContext(hThread, &context);
if(bRet == FALSE)
{
// 오류 처리
}
// FS descriptor entry계산
bRet = GetThreadSelectorEntry(hThread, context.SegFs, &des);
dwTEBAddress = ((des.HighWord.Bytes.BaseHi << 8 | des.HighWord.Bytes.BaseMid) << 16 | des.BaseLow);
요렇게 구하셔도 됩니다.
이렇게 구하셨다면.. 이제 TEB에서 PEB를 구해야 합니다.
TEB *pTEB; PEB *pPEB; BOOL bIsDebugger; pTEB = (TEB *)dwTEBAddress; pPEB = pTEB->Peb; bIsDebugger = pPEB->BeingDebugged;이제 bIsDebugger를 확인 하면 땡입니다. 위와 같이 하는게 귀찮으시다(?) 하면.. PEB의 offset을 직접 계산하셔도 됩니다. 즉..
BOOL bIsDebugger; bIsDebugger = (char *)*((void **)((char *)dwTEBAddress + 0x30)) + 0x02;에 접근 하셔도 됩니다. 이걸 이용해서 함수로 만들어 보자면..
DWORD __declspec(naked) MyIsDebuggerPresent()
{
__asm
{
// ecx 백업
push ecx
// TEB 주소 계산
mov eax, fs:[0x18]
// PEB 주소 계산
mov ecx, dword ptr [eax + 0x30]
// PEB->BeingDebugged 값을 가져오기 전 eax 초기화
xor eax, eax
// PEB->BeingDebugged 필드 값을 al에 저장(al = 8bit)
mov al, byte ptr [ecx + 0x02]
// ecx 복구
pop ecx
// // return eax
retn
}
}
와 같이 되겠습니다.
C 버전으로 보길 원하신다면..
DWORD MyIsDebuggerPresent()
{
HANDLE hThread;
BOOL bRet;
DWORD dwTEBAddress;
CONTEXT context;
LDT_ENTRY des;
TEB *pTEB;
PEB *pPEB;
hThread = GetCurrentThread();
// segment 정보만 얻어옴
context.ContextFlags = CONTEXT_SEGMENTS;
bRet = GetThreadContext(hThread, &context);
if(bRet == FALSE)
{
return 0;
}
// FS descriptor entry계산
bRet = GetThreadSelectorEntry(hThread, context.SegFs, &des);
dwTEBAddress = ((des.HighWord.Bytes.BaseHi << 8 | des.HighWord.Bytes.BaseMid) << 16 | des.BaseLow);
pTEB = (TEB *)dwTEBAddress;
pPEB = pTEB->Peb;
return pPEB->BeingDebugged;
}
이게 되겠네요.


Attribution/Share Alike 2.0 license






