프로가 되자.

post search result

크랙와 관련된 글 1개를 찾았습니다.

  1. 2008/04/09 IsDebuggerPresent() 구현하기

IsDebuggerPresent() 구현하기

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;
}
이게 되겠네요.
크리에이티브 커먼즈 라이센스
Creative Commons License
2008/04/09 22:15 2008/04/09 22:15

top

About this post

이 글에는 아직 트랙백이 없고, 아직 댓글이 없고, , , , , , , 태그가 달려있으며,
2008/04/09 22:15에 작성되었습니다.

◀ recent : [1] : previous ▶

blog information

프로가 되자.
BLOG main image
빗소리를 먹는 사람.
RSS 2.0Tattertools
최근 글 최근 댓글 최근 트랙백
태그 구름사이트 링크