Skip to content

Instantly share code, notes, and snippets.

@kohnakagawa
Created November 14, 2019 06:24
Show Gist options
  • Save kohnakagawa/9f6b791ed86ea616213b9bb130a13502 to your computer and use it in GitHub Desktop.
Save kohnakagawa/9f6b791ed86ea616213b9bb130a13502 to your computer and use it in GitHub Desktop.
TEBに含まれるTLS Slotsに含まれる内容を確認するために使うコード
#include <windows.h>
#include <winnt.h>
#include <stdio.h>
#define THREADCOUNT 4
DWORD dwTlsIndex;
VOID ErrorExit(LPSTR);
VOID CommonFunc(VOID)
{
LPVOID lpvData;
// Retrieve a data pointer for the current thread.
lpvData = TlsGetValue(dwTlsIndex);
if ((lpvData == 0) && (GetLastError() != ERROR_SUCCESS))
ErrorExit((LPSTR)"TlsGetValue error");
// Use the data stored for the current thread.
printf("common: thread %d: lpvData=%lx\n",
GetCurrentThreadId(), lpvData);
Sleep(5000);
}
DWORD WINAPI ThreadFunc(VOID)
{
LPVOID lpvData;
// Initialize the TLS index for this thread.
lpvData = (LPVOID) LocalAlloc(LPTR, 256);
if (! TlsSetValue(dwTlsIndex, lpvData))
ErrorExit((LPSTR)"TlsSetValue error");
_TEB* teb = NtCurrentTeb();
DWORD* pteb = (DWORD*)((CHAR*)teb + 0x1488);
printf("tlsindex %d: thread %d: lpvData=%lx tlsslot=%x\n",
dwTlsIndex,
GetCurrentThreadId(),
lpvData,
*pteb
);
CommonFunc();
// Release the dynamic memory before the thread returns.
lpvData = TlsGetValue(dwTlsIndex);
if (lpvData != 0)
LocalFree((HLOCAL) lpvData);
return 0;
}
int main(VOID)
{
DWORD IDThread;
HANDLE hThread[THREADCOUNT];
int i;
// Allocate a TLS index.
if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)
ErrorExit((LPSTR)"TlsAlloc failed");
// Create multiple threads.
for (i = 0; i < THREADCOUNT; i++)
{
hThread[i] = CreateThread(NULL, // default security attributes
0, // use default stack size
(LPTHREAD_START_ROUTINE) ThreadFunc, // thread function
NULL, // no thread function argument
0, // use default creation flags
&IDThread); // returns thread identifier
// Check the return value for success.
if (hThread[i] == NULL)
ErrorExit((LPSTR)"CreateThread error\n");
}
for (i = 0; i < THREADCOUNT; i++)
WaitForSingleObject(hThread[i], INFINITE);
TlsFree(dwTlsIndex);
return 0;
}
VOID ErrorExit (LPSTR lpszMessage)
{
fprintf(stderr, "%s\n", lpszMessage);
ExitProcess(0);
}
@kohnakagawa
Copy link
Author

kohnakagawa commented Nov 14, 2019

簡単な説明

コードはMSのサンプルコードを改良したものを使っている。

サンプルコードから追加した部分は以下の部分

   _TEB* teb = NtCurrentTeb();
   DWORD* pteb = (DWORD*)((CHAR*)teb + 0x1488);

TEBの先頭から0x1488バイト (TEB内部で定義された TLS Slots の1番目のエントリーの内容に等しい) のDWORDを参照している。この値は

   if (! TlsSetValue(dwTlsIndex, lpvData)) // dwTlsIndex は 1 に等しい

で設定された lpvData の値と等しくなることがわかる。以下は実際にプログラム実行した時の出力。

// lpvData の値と TEB から直接取得した tlsslot の値が一致している
tlsindex 1: thread 19940: lpvData=ca1d5340 tlsslot=ca1d5340
common: thread 19940: lpvData=ca1d5340
tlsindex 1: thread 9604: lpvData=ca1d8400 tlsslot=ca1d8400
common: thread 9604: lpvData=ca1d8400
tlsindex 1: thread 15720: lpvData=ca1d6d20 tlsslot=ca1d6d20
common: thread 15720: lpvData=ca1d6d20
tlsindex 1: thread 23664: lpvData=ca1d81e0 tlsslot=ca1d81e0
common: thread 23664: lpvData=ca1d81e0

つまり

各スレッドはTLS Slots を持っており、スレッド固有の記憶領域を持っている。これはポインターの配列となっており、各スレッドで LocalAlloc して確保した領域の先頭アドレスを登録することができる。どの領域にアクセスするのかは TLS Slots のインデックス (上記の例でいうと、dwTlsIndex) で指定すれば良い。

@kohnakagawa
Copy link
Author

これ x64 でないと動かんようになっている

@blackwingcat
Copy link

blackwingcat commented Jun 5, 2023

なんか色々間違ってるので、フォークして32bitも動くようにした ・ω・

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment