Microsoft MVP성태의 닷넷 이야기
VC++: 91. 자식 스레드에 자동 상속되는 TEB의 SubProcessTag 필드 [링크 복사], [링크+제목 복사],
조회: 23466
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

(시리즈 글이 6개 있습니다.)
VC++: 64. x64 Visual C++에서 TEB 주소 구하는 방법
; https://www.sysnet.pe.kr/2/0/1387

.NET Framework: 348. .NET x64 응용 프로그램에서 Teb 주소를 구하는 방법
; https://www.sysnet.pe.kr/2/0/1388

.NET Framework: 349. .NET Thread 인스턴스로부터 COM Apartment 유형 확인하는 방법
; https://www.sysnet.pe.kr/2/0/1389

VC++: 91. 자식 스레드에 자동 상속되는 TEB의 SubProcessTag 필드
; https://www.sysnet.pe.kr/2/0/10797

디버깅 기술: 150. windbg - Wow64, x86, x64에서의 커널 구조체(예: TEB) 구조체 확인
; https://www.sysnet.pe.kr/2/0/12097

디버깅 기술: 205. Windbg - KPCR, KPRCB
; https://www.sysnet.pe.kr/2/0/13842




자식 스레드에 자동 상속되는 TEB의 SubProcessTag 필드

"Windows Internals" 책을 보다가,

Windows Internals 제6판 Vol. 1: 마이크로소프트 윈도우 커널 공식 가이드 6판 
; http://www.yes24.com/24/goods/7877539

469페이지에 실린 "서비스 태그"라는 내용에서 SubProcessTag 필드의 존재를 처음 알게 되었습니다.

내용인 즉, 마이크로소프트 측에서 Vista부터 제공하고 있던 기능인데 이 필드의 내용은 하위 스레드에 자동으로 전달된다는 것입니다. 오호~~~ 재미있는 기능이군요. ^^

일단, windbg로 x64의 windows 8.1에서 _TEB 구조체를 살펴보면 다음과 같이 0x1720 옵셋에 위치한 것을 확인할 수 있습니다.

0: kd> dt _TEB 
nt!_TEB
   +0x000 NtTib            : _NT_TIB
   +0x038 EnvironmentPointer : Ptr64 Void
   ...[생략]...
   +0x1720 SubProcessTag    : Ptr64 Void
   +0x1818 ReservedForWdf   : Ptr64 Void

실제로 이 값을 확인하려면 다음과 같은 식의 코드를 작성하면 됩니다.

// 첨부 파일에 Visual Studio 프로젝트로 포함됨

#include "stdafx.h"
#include <winternl.h>
#include <intrin.h> 

void showSbt()
{
#if defined WIN32
#define OFFSET_SUBPROCESSTAG 0x0f60
#elif defined _WIN64
#define OFFSET_SUBPROCESSTAG 0x1720
#else
#endif // _WIN32

    unsigned long subProcessTag = 0;

    HANDLE hProcess = GetCurrentProcess();

#if defined(_WIN64) // x64
    auto pTeb = reinterpret_cast<PTEB>(__readgsqword(reinterpret_cast<DWORD>(&static_cast<NT_TIB*>(nullptr)->Self)));
#elif defined(WIN32) // x86
    auto pTeb = reinterpret_cast<PTEB>(__readfsdword(reinterpret_cast<DWORD>(&static_cast<NT_TIB*>(nullptr)->Self)));
#endif
    
    BOOL result = ReadProcessMemory(hProcess, reinterpret_cast<void *>(reinterpret_cast<ULONG_PTR>(pTeb)+OFFSET_SUBPROCESSTAG)
        , reinterpret_cast<void *>(&subProcessTag)
        , sizeof(subProcessTag),
        nullptr);

    if (result == TRUE)
    {
        printf("[TID: %d] %d (0x%x)\n", GetCurrentThreadId(), subProcessTag, subProcessTag);
        // 출력결과: [TID: 10216] 0 (0x0)
    }

    CloseHandle(hProcess);
}

int _tmain(int argc, _TCHAR* argv[])
{
    showSbt();
    return 0;
}

기본값은 0입니다. 그렇다면 정말 스레드 간에 자동 상속되는지 확인을 해볼까요? ^^

#include "stdafx.h"
#include <winternl.h>
#include <intrin.h> 

void showSbt(bool setSbt, DWORD newId)
{
#if defined WIN32
#define OFFSET_SUBPROCESSTAG 0x0f60
#elif defined _WIN64
#define OFFSET_SUBPROCESSTAG 0x1720
#else
#endif // _WIN32

    unsigned long subProcessTag = 0;

    HANDLE hProcess = GetCurrentProcess();

#if defined(_WIN64) // x64
    auto pTeb = reinterpret_cast<PTEB>(__readgsqword(reinterpret_cast<DWORD>(&static_cast<NT_TIB*>(nullptr)->Self)));
#elif defined(WIN32) // x86
    auto pTeb = reinterpret_cast<PTEB>(__readfsdword(reinterpret_cast<DWORD>(&static_cast<NT_TIB*>(nullptr)->Self)));
#endif
    
    DWORD written = 0;

    if (setSbt == true)
    {
        subProcessTag = newId;

        WriteProcessMemory(hProcess, reinterpret_cast<void *>(reinterpret_cast<ULONG_PTR>(pTeb)+OFFSET_SUBPROCESSTAG)
            , reinterpret_cast<void *>(&subProcessTag)
            , sizeof(subProcessTag), &written);
    }

    subProcessTag = 0;

    BOOL result = ReadProcessMemory(hProcess, reinterpret_cast<void *>(reinterpret_cast<ULONG_PTR>(pTeb)+OFFSET_SUBPROCESSTAG)
        , reinterpret_cast<void *>(&subProcessTag)
        , sizeof(subProcessTag),
        nullptr);

    if (result == TRUE)
    {
        printf("[TID: %d] %d (0x%x)\n", GetCurrentThreadId(), subProcessTag, subProcessTag);
    }

    CloseHandle(hProcess);
}

DWORD WINAPI newThreadProc2(PVOID pVoid)
{
    showSbt(false, 0);
    return 0;
}

DWORD WINAPI newThreadProc(PVOID pVoid)
{
    showSbt(false, 0);

    HANDLE newThread = ::CreateThread(nullptr, 0, newThreadProc2, nullptr, 0, nullptr);
    ::WaitForSingleObject(newThread, INFINITE);
    ::CloseHandle(newThread);

    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    showSbt(false, 0x100);

    HANDLE newThread = ::CreateThread(nullptr, 0, newThreadProc, nullptr, 0, nullptr);
    ::WaitForSingleObject(newThread, INFINITE);
    ::CloseHandle(newThread);
    
    return 0;
}

주 스레드가 newThreadProc를 가진 2차 스레드를 생성하고, 다시 newThreadProc 내부에서는 newThreadProc2를 가진 3차 스레드를 생성했는데, 결과는 예상한 대로 상속된 것으로 나옵니다.

[TID: 12536] 256 (0x100)
[TID: 13384] 256 (0x100)
[TID: 14884] 256 (0x100)

이 기능의 아쉬운 점이 있다면? 스레드 풀에는 적용되지 않는다고 합니다. 즉, 현재 스레드에서 스레드 풀의 스레드에 작업을 던진 경우, 그 동작 간에는 SubProcessTag 값의 전달이 없습니다. 아마도, 스레드 생성 시에 부모 스레드의 SubProcessTag값을 전달하는 식으로 구현된 것이 아닌가 생각됩니다. 그렇기 때문에 이미 생성된 스레드 풀의 스레드에는 영향을 미치지 못하는 것이고!




이 기능을, 마이크로소프트 측에서는 다중 서비스를 호스팅하는 프로세스 내의 스레드에 적용한다고 합니다. 이에 대해서는 다음의 글을 참고하세요. ^^

ScTagQuery: Mapping Service Hosting Threads With Their Owner Service
; http://www.alex-ionescu.com/?p=52

제가 구현한 예제 코드에서는 단순히 DWORD 값을 넣었지만, 마이크로소프트가 설정한 정보의 구조는 좀 다릅니다. 문서화되지 않은(undocumented) 기능이지만, 다음과 같이 잘 공개(?)되어 있습니다. ^^

HOWTO: Use I_QueryTagInformation
; http://wj32.org/wp/2010/03/30/howto-use-i_querytaginformation/




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 5/23/2015]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 166  167  [168]  169  170  171  172  173  174  175  176  177  178  179  180  ...
NoWriterDateCnt.TitleFile(s)
876정성태5/31/201021062제니퍼 .NET: 8. 성능 관리 퀴즈 두 번째 문제 (닷넷 개발자 컨퍼런스) [2]
875정성태5/30/201022859제니퍼 .NET: 7. 성능 관리 퀴즈 첫 번째 문제 (닷넷 개발자 컨퍼런스)
873정성태5/19/201029727제니퍼 .NET: 6. 제니퍼를 위한 방화벽 설정
872정성태5/15/201029081제니퍼 .NET: 5. 제니퍼 서버 - NT 서비스로 구동시키는 방법
871정성태5/13/201035664VC++: 40. MSBuild를 이용한 VC++ 프로젝트 빌드파일 다운로드1
870정성태5/12/201026934제니퍼 .NET: 4. 닷넷 APM 솔루션 - 제니퍼 닷넷의 기능 요약 [2]
869정성태11/8/201928191오류 유형 : 95. WCF 인증서 설정 관련 오류 정리 [4]
865정성태5/5/201030599개발 환경 구성: 75. 인증서의 개인키를 담은 물리 파일 위치 알아내는 방법파일 다운로드1
864정성태5/4/201034360.NET Framework: 178. WCF - 사용자 정의 인증 구현 예제 [4]파일 다운로드1
863정성태5/4/201060316개발 환경 구성: 74. 인증서 관련(CER, PVK, SPC, PFX) 파일 만드는 방법 [1]파일 다운로드1
862정성태5/3/201022097제니퍼 .NET: 3. 제2회 닷넷 개발자 컨퍼런스에서 뵙겠습니다. ^^
861정성태5/1/201022580.NET Framework: 177. .NET 2.0 Profiler에 .NET 4.0 지원 추가
860정성태4/30/201027824오류 유형: 95. .NET 4.0 설치 오류 - 0x800c0005 [1]
858정성태4/29/201031640제니퍼 .NET: 2. JENNIFER .NET을 이용한 .NET 웹 사이트 모니터링 (2) - 설치 [3]
857정성태4/27/201030048제니퍼 .NET: 1. JENNIFER .NET을 이용한 .NET 웹 사이트 모니터링 (1) - 임시 라이선스 요청 및 다운로드
855정성태4/24/201026950.NET Framework: 176. DataContext가 thread-safe한 것인가?
854정성태4/22/201023534오류 유형: 94. 팀 프로젝트 생성 오류 - TF218017, TF250044
853정성태4/22/201024222오류 유형: 93. TFS 2010 오류: rsProcessingAborted, rsErrorExecutingCommand
852정성태4/21/201025533Team Foundation Server: 33. 단일 서버에 Team Foundation Server 2010 설치하는 방법
851정성태4/14/201036364오류 유형: 92. Task Scheduler 오류: 2147942667 (0x8007010B)
850정성태4/7/201024570.NET Framework: 175. WCF - webHttpBinding + PUT 메서드 구현파일 다운로드1
848정성태3/30/201038268개발 환경 구성: 73. 한 대의 PC에서 여러 개의 키입력 매크로 프로그램이 가능할까? [5]파일 다운로드5
846정성태3/29/201036177VC++: 39. C++에서 싱글톤 구현하기 [8]파일 다운로드1
844정성태3/19/201023580개발 환경 구성: 72. Adobe Creative Suite 3 Master Collection - 하드 디스크로부터 설치하는 방법
843정성태3/19/201032627Windows: 50. Windows Server 2008 R2 시스템에 Windows 7 멀티 부팅하는 방법
842정성태3/17/201028136Windows: 49. VHD 파일 지원
... 166  167  [168]  169  170  171  172  173  174  175  176  177  178  179  180  ...