Microsoft MVP성태의 닷넷 이야기
VC++: 91. 자식 스레드에 자동 상속되는 TEB의 SubProcessTag 필드 [링크 복사], [링크+제목 복사],
조회: 21721
글쓴 사람
정성태 (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

비밀번호

댓글 작성자
 




... 91  92  93  94  95  96  97  98  99  100  101  102  103  104  [105]  ...
NoWriterDateCnt.TitleFile(s)
11300정성태9/10/201721157.NET Framework: 681. dotnet.exe - run, exec, build, restore, publish 차이점 [3]
11299정성태9/9/201719823개발 환경 구성: 330. Hyper-V VM의 Internal Network를 Private 유형으로 만드는 방법
11298정성태9/8/201723149VC++: 119. EnumProcesses / EnumProcessModules API 사용 시 주의점 [1]
11297정성태9/8/201719810디버깅 기술: 96. windbg - 풀 덤프에 포함된 모든 닷넷 모듈을 파일로 저장하는 방법
11296정성태9/8/201722932웹: 36. Edge - "이 웹 사이트는 이전 기술에서 실행되며 Internet Explorer에서만 작동합니다." 끄는 방법
11295정성태9/7/201720421디버깅 기술: 95. Windbg - .foreach 사용법
11294정성태9/4/201720090개발 환경 구성: 329. 마이크로소프트의 CoreCLR 프로파일러 예제 빌드 방법 [1]
11293정성태9/4/201720686개발 환경 구성: 328. Visual Studio(devenv.exe)를 배치 파일(.bat)을 통해 실행하는 방법
11292정성태9/4/201718884오류 유형: 419. Cannot connect to WMI provider - Invalid class [0x80041010]
11291정성태9/3/201720710개발 환경 구성: 327. 아파치 서버 2.4를 위한 mod_aspdotnet 마이그레이션
11290정성태9/3/201723970개발 환경 구성: 326. 아파치 서버에서 ASP.NET을 실행하는 mod_aspdotnet 모듈 [2]
11289정성태9/3/201721622개발 환경 구성: 325. GAC에 어셈블리 등록을 위해 gacutil.exe을 사용하는 경우 주의 사항
11288정성태9/3/201718425개발 환경 구성: 324. 윈도우용 XAMPP의 아파치 서버 구성 방법
11287정성태9/1/201727593.NET Framework: 680. C# - 작업자(Worker) 스레드와 UI 스레드 [11]
11286정성태8/28/201714990기타: 67. App Privacy Policy
11285정성태8/28/201723512.NET Framework: 679. C# - 개인 키 보안의 SFTP를 이용한 파일 업로드파일 다운로드1
11284정성태8/27/201721535.NET Framework: 678. 데스크톱 윈도우 응용 프로그램에서 UWP 라이브러리를 이용한 비디오 장치 열람하는 방법 [1]파일 다운로드1
11283정성태8/27/201717313오류 유형: 418. CSS3117: @font-face failed cross-origin request. Resource access is restricted.
11282정성태8/26/201719754Math: 22. 행렬로 바라보는 피보나치 수열
11281정성태8/26/201721585.NET Framework: 677. Visual Studio 2017 - NuGet 패키지를 직접 참조하는 PackageReference 지원 [2]
11280정성태8/24/201718586디버깅 기술: 94. windbg - 풀 덤프에 포함된 모든 모듈을 파일로 저장하는 방법
11279정성태8/23/201730183.NET Framework: 676. C# Thread가 Running 상태인지 아는 방법
11278정성태8/23/201718413오류 유형: 417. TFS - Warning - Unable to refresh ... because you have a pending edit. [1]
11277정성태8/23/201719659오류 유형: 416. msbuild - error MSB4062: The "TransformXml" task could not be loaded from the assembly
11276정성태8/23/201723921.NET Framework: 675. C# - (파일) 확장자와 연결된 실행 파일 경로 찾기 [2]파일 다운로드1
11275정성태8/23/201732942개발 환경 구성: 323. Visual Studio 설치 없이 빌드 환경 구성 - Visual Studio 2017용 Build Tools [1]
... 91  92  93  94  95  96  97  98  99  100  101  102  103  104  [105]  ...