스레드는 스레드마다 자신의 정보를 담고 있는 Thread Environmnet Block이란 것을 가지고 있다.
운영체제마다 다르고 버전마다 조금씩 다르며, 그 구조체에는 그냥 보면 너무 숨겨져 있어서 커널 디버거로 열어봐야 하는데 열어보며 너어어무 길다.
오프셋 0 | NtTib : _NT_TIB
이 TEB의 첫번째 멤버인 _NT_TIB이다. 참고로 TIB는 Thread Information Block이다.
구조체가 너무 복잡하진 않다. 첫번째 ExceptionList는 나중에 윈도우 운영체제에서 예외 이벤트를 처리하는 SEH 메커니즘때 사용된다 정도만 알아두자.
맨 밑의 Self는 자기 자신을 가리키는 포인터다. 정확히는 자기 자신의 시작을 가리키는것이지.
근데 NT_tIB가 TEB의 첫번째였잖아. 그니까 _NT_TIB를 가리키는 것은 결국 TEB구조체를 가리키는 포인터이기도 하다.
오프셋 30 | ProcessEnvironmentBlock : Ptr32_PEB
이따가 설명할 PEB 구조체를 가리키는 포인터다.
유저모드에선 Ntdll.NtCurrentTeb( )라는 api를 이용해서 접근할 있다. 현재 스레드의 TEB 구조체 주소를 리턴해주는 API거든.
이 TEB의 주소는 FS 세그먼트 레지스터의 주소랑 일치한다.
이 레지스터는 현재 스레드의 TEB 주소를 지시한다.
32비트 체계에서 주소는 XX XX XX XX 4바이트였지. 즉 32비트.
근데 이 레지스터는 16비트, 2바이트뿐이다. 이것만 갖고 어케 주소를 가리키냐
정확히는 실제 TEB 주소를 갖고 있는 Segment Descriptor Table의 인덱스를 가리킨다.
요런 감성. 세그먼트 메모리의 시작 주소에 TEB 위치한 감성
그래서 FS 세그먼트 레지스터는 세그먼트 셀렉터라고도 한다.
FS:[0x18] = FS에서 TEB.NtTib.Self를 가리킨다 = TIB의 주소 = TEB의 주소 = 세그먼트 메모리의 시작 주소
FS:[0x30] = FS에서 TEB.ProcessEnvironmentBlock을 가리킨다 = PEB의 주소
PEB는 안티 디버깅할 때 자주 참조된다.
FS:[0] = FS에서 TEB.NtTib.ExceptionList를 가리킨다 = SEH 주소
구조적 예외 핸들러인 SEH는 안티디버깅에 많이 사용된다.
PEB도 똑같다. 프로세스 정보를 담는 구조체인데 프로세스다보니 TEB보단 크기가 크고, 대부분 문서화되어 있다.
오프셋 2 | BeingDebugged : UChar
현재 프로세스가 디버깅 당하는지 판단해서 디버깅 중이면 1, 아니면 0을 리턴한다.
안티디버깅할 때 쓸 수 있겠지
오프셋 8 | ImageBaseAddress : Ptr32 Void
프로세스의 ImageBasefmf 표시한다. GetModuleHandle( )은 이 이미지베이스를 얻어내는 API야.
오프셋 c | Ldr : Ptr32 _PEB_LDR_DATA
프로세스에 로딩된 DLL 모듈의 로딩 베이스 주소를 직접 구할 수 있게 해준다.
이 멤버가 가리키는 _PEB_LDR_DATA 구조체 중에 _LIST_ENTRY 타입의 멤버들은 양방향 연결 리스트 메커니즘을 제공한다.
너무 어려우니까 생략
오프셋 18 | ProcessHeap : Ptr32 Void
오프셋 68 | NtGlobalFlag : Uint4B
둘다 안티디버깅에 활용된다. 디버깅 중이라면 이 멤버들은 특정한 값을 가지게 된다.
따로 API를 쓰지 않고 TEB를 접근하는 방법에서 이용한다.
FS:[0x30] = FS에서 TEB.ProcessEnvironmentBlock을 가리킨다 = PEB의 주소
이걸 어셈블리 코드로 나타내면 바로 디버거에서 PEB로 접근할 수 잇따.
다이렉트로 PEB 주소 구하기
MOV EAX, DWORD PTR FS:[30] ; FS[30] = address of PEB
TEB 구하고 옵셋 30을 더하기
MOV EAX, DWORD PTR FS:[18] ; FS[18] = address of TEB
MOV EAX, DWORD PTR DS:[EAX+30] ; DS[EAX+30] = address of PEB