Last active
July 5, 2024 23:40
-
-
Save olliencc/df200e0049fa17036d9f867e024f57ad to your computer and use it in GitHub Desktop.
Detect Windows threads which are impersonating
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
TEB Detect Impersonating Threads for Microsoft Windows | |
Released as open source by NCC Group Plc - http://www.nccgroup.com/ | |
Developed by Ollie Whitehouse, ollie dot whitehouse at nccgroup dot com | |
Released under AGPL see LICENSE for more information | |
*/ | |
#pragma once | |
#include "stdafx.h" | |
// Globals | |
HANDLE hProcess; | |
TCHAR strErrMsg[1024]; | |
DWORD dwModuleRelocs = 0; | |
DWORD dwCountError = 0; | |
DWORD dwCountOK = 0; | |
DWORD dwOpen = 0; | |
// Manual import | |
typedef NTSTATUS(WINAPI* NTQUERYINFOMATIONTHREAD)(HANDLE, LONG, PVOID, ULONG, PULONG); | |
NTQUERYINFOMATIONTHREAD myNtQueryInformationThread = (NTQUERYINFOMATIONTHREAD)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "NtQueryInformationThread"); | |
// Structures to hold process information | |
#pragma pack(push, 1) | |
struct procNfoStuct { | |
DWORD PID; | |
TCHAR Name[MAX_PATH]; | |
unsigned long long TotalExecMem = 0; | |
}; | |
#pragma pack(pop) | |
procNfoStuct Procs[4098]; | |
DWORD NumOfProcs = 0; | |
// | |
// Function : SetDebugPrivilege | |
// Role : Gets privs for our process | |
// Notes : | |
// | |
BOOL SetPrivilege(HANDLE hProcess, LPCTSTR lPriv) | |
{ | |
LUID luid; | |
TOKEN_PRIVILEGES privs; | |
HANDLE hToken = NULL; | |
DWORD dwBufLen = 0; | |
char buf[1024]; | |
ZeroMemory(&luid, sizeof(luid)); | |
if (!LookupPrivilegeValue(NULL, lPriv, &luid)) return false; | |
privs.PrivilegeCount = 1; | |
privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | |
memcpy(&privs.Privileges[0].Luid, &luid, sizeof(privs.Privileges[0].Luid)); | |
if (!OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken)) | |
return false; | |
if (!AdjustTokenPrivileges(hToken, FALSE, &privs, | |
sizeof(buf), (PTOKEN_PRIVILEGES)buf, &dwBufLen)) | |
return false; | |
CloseHandle(hProcess); | |
CloseHandle(hToken); | |
return true; | |
} | |
void AnalyzeTEB(HANDLE hProcess, HANDLE hThread, DWORD dwPID, TCHAR *cProcess, LPCVOID ptrTEB) { | |
MYTEB myTEB; | |
SIZE_T szRead=0; | |
memset(&myTEB, 0x00, sizeof(myTEB)); | |
if (ReadProcessMemory(hProcess, ptrTEB, &myTEB, sizeof(myTEB), &szRead) == TRUE) { | |
if(szRead != sizeof(myTEB)) fwprintf(stdout, _TEXT("[i] [%d][%s] Size Delta\n"), dwPID, cProcess); | |
// fwprintf(stdout, _TEXT("[i] [%d][%s] Got TEB %llx\n"), dwPID, cProcess, ptrTEB); | |
//if(myTEB.InitialThread>0)fwprintf(stdout, _TEXT("[i] [%d][%s] Initial thread: %d\n"), dwPID, cProcess, myTEB.InitialThread); | |
if (myTEB.CountOfOwnedCriticalSections >0) fwprintf(stdout, _TEXT("[i] [%d][%s] Count of Owned Critical Sections: %d\n"), dwPID, cProcess, myTEB.CountOfOwnedCriticalSections); | |
if(myTEB.IsImpersonating > 0 ) fwprintf(stdout, _TEXT("[i] [%d][%s] is impersonating\n"), dwPID, cProcess); | |
} | |
} | |
/// <summary> | |
/// Analyze the process and its memory regions | |
/// </summary> | |
/// <param name="dwPID">Process ID</param> | |
void AnalyzeProc(DWORD dwPID) | |
{ | |
DWORD dwRet, dwMods; | |
HANDLE hProcess; | |
HMODULE hModule[4096]; | |
TCHAR cProcess[MAX_PATH]; // Process name | |
BOOL bIsWow64 = FALSE; | |
BOOL bIsWow64Other = FALSE; | |
DWORD dwRES = 0; | |
// Get process handle by hook or by crook | |
hProcess = OpenProcess(PROCESS_ALL_ACCESS | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID); | |
if (hProcess == NULL) | |
{ | |
if (GetLastError() == 5) { | |
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID); | |
if (hProcess == NULL) { | |
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwPID); | |
if (hProcess == NULL) { | |
fwprintf(stderr, _TEXT("[!] [%d][UNKNOWN] Failed to OpenProcess - %d\n"), dwPID, GetLastError()); | |
dwCountError++; | |
return; | |
} | |
} | |
} | |
else { | |
fwprintf(stderr, _TEXT("[!] [%d][UNKNOWN] Failed to OpenProcess - %d\n"), dwPID, GetLastError()); | |
dwCountError++; | |
return; | |
} | |
} | |
// Enumerate the process modules | |
if (EnumProcessModules(hProcess, hModule, 4096 * sizeof(HMODULE), &dwRet) == FALSE) | |
{ | |
DWORD dwSz = MAX_PATH; | |
if (QueryFullProcessImageName(hProcess, 0, cProcess, &dwSz) == TRUE) { | |
fwprintf(stdout, _TEXT("[i] [%d][%s] not analysed %d\n"), dwPID, cProcess, GetLastError()); | |
dwOpen++; | |
} | |
else { | |
fwprintf(stdout, _TEXT("[i] [%d][%s] not analysed %d\n"), dwPID, _TEXT("UNKNOWN"), GetLastError()); | |
dwOpen++; | |
} | |
if (GetLastError() == 299) { | |
//fprintf(stderr, "64bit process and we're 32bit - sad panda! skipping PID %d\n", dwPID); | |
} | |
else { | |
//fprintf(stderr, "Error in EnumProcessModules(%d),%d\n", dwPID, GetLastError()); | |
} | |
dwCountError++; | |
return; | |
} | |
dwMods = dwRet / sizeof(HMODULE); | |
// Get the processes name from the first module returned by the above | |
GetModuleBaseName(hProcess, hModule[0], cProcess, MAX_PATH); | |
Procs[NumOfProcs].PID = dwPID; | |
_tcscpy_s(Procs[NumOfProcs].Name, MAX_PATH, cProcess); | |
//fwprintf(stdout, _TEXT("[i] [%d][%s] analyzing\n"), dwPID, cProcess); | |
NumOfProcs++; | |
// | |
// Get the | |
// | |
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); | |
if (h != INVALID_HANDLE_VALUE) { | |
THREADENTRY32 te; | |
te.dwSize = sizeof(te); | |
if (Thread32First(h, &te)) { | |
do { | |
if (te.th32OwnerProcessID == dwPID && GetProcessId(NULL) != dwPID) { | |
HANDLE hThread = INVALID_HANDLE_VALUE; | |
hThread = OpenThread(THREAD_QUERY_INFORMATION, false, te.th32ThreadID); | |
if (hThread != INVALID_HANDLE_VALUE) { | |
THREAD_BASIC_INFORMATION threadTBI; | |
NTSTATUS statRes = myNtQueryInformationThread(hThread, (THREADINFOCLASS)(0), &threadTBI, sizeof(threadTBI), NULL); | |
if (statRes == 0) { | |
//fwprintf(stdout, _TEXT("[i] [%d][%s] Address of TEB %llx\n"), dwPID, cProcess, threadTBI.TebBaseAddress); | |
AnalyzeTEB(hProcess, hThread, dwPID,cProcess,threadTBI.TebBaseAddress); | |
} | |
else | |
{ | |
fwprintf(stdout, _TEXT("[!] [%d][%s] Failed to get TBI %d\n"), dwPID, cProcess,(DWORD)statRes); | |
} | |
CloseHandle(hThread); | |
} | |
} | |
} while (Thread32Next(h, &te)); | |
} | |
CloseHandle(h); | |
} | |
dwCountOK++; | |
CloseHandle(hProcess); | |
} | |
/// <summary> | |
/// Enumerate all the processes on the system and | |
/// pass off to the analysis function | |
/// </summary> | |
void EnumerateProcesses() | |
{ | |
DWORD dwPIDArray[4096], dwRet, dwPIDS, intCount; | |
NumOfProcs = 0; | |
// Privs | |
SetPrivilege(GetCurrentProcess(), SE_DEBUG_NAME); | |
// Be clean | |
memset(Procs, 0x00, sizeof(Procs)); | |
// | |
// Enumerate | |
// | |
if (EnumProcesses(dwPIDArray, 4096 * sizeof(DWORD), &dwRet) == 0) | |
{ | |
DWORD dwRet = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, strErrMsg, 1023, NULL); | |
if (dwRet != 0) { | |
_ftprintf(stderr, TEXT("[!] EnumProcesses() failed - %s"), strErrMsg); | |
} | |
else | |
{ | |
_ftprintf(stderr, TEXT("[!] EnumProcesses() - Error: %d\n"), GetLastError()); | |
} | |
return; | |
} | |
// Total nuber of process IDs | |
dwPIDS = dwRet / sizeof(DWORD); | |
// | |
// Analyze | |
// | |
for (intCount = 0; intCount < dwPIDS; intCount++) | |
{ | |
//fwprintf(stdout, _TEXT("[i] Analyzing PID %d\n"), dwPIDArray[intCount]); | |
AnalyzeProc(dwPIDArray[intCount]); | |
} | |
fwprintf(stdout, _TEXT("[i] Total of %d processes - didn't open %d \n"), dwPIDS, dwOpen); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
TEB Detect Impersonating Threads for Microsoft Windows | |
Released as open source by NCC Group Plc - http://www.nccgroup.com/ | |
Developed by Ollie Whitehouse, ollie dot whitehouse at nccgroup dot com | |
Released under AGPL see LICENSE for more information | |
*/ | |
#pragma once | |
#include "stdafx.h" | |
#include <stdio.h> | |
#include <tchar.h> | |
#include <windows.h> | |
#include <Winternl.h> | |
#include <Psapi.h> | |
#include <Aclapi.h> | |
#include <tlhelp32.h> | |
#include <wtsapi32.h> | |
#include <strsafe.h> | |
#include <winevt.h> | |
#include <evntprov.h> | |
// | |
extern bool bFirstRun; | |
extern bool bConsole; | |
extern bool bService; | |
// https://github.com/edouarda/thread_explorer/blob/master/thread_explorer/thread_explorer.cpp | |
typedef struct _THREAD_BASIC_INFORMATION | |
{ | |
NTSTATUS ExitStatus; | |
PTEB TebBaseAddress; | |
CLIENT_ID ClientId; | |
ULONG_PTR AffinityMask; | |
KPRIORITY Priority; | |
LONG BasePriority; | |
} THREAD_BASIC_INFORMATION, * PTHREAD_BASIC_INFORMATION; | |
// http://daaxr.blogspot.com/2016/07/teb-structure-for-windows-10-pro-x64.html | |
typedef struct _MYNT_TIB | |
{ | |
EXCEPTION_REGISTRATION_RECORD* ExceptionList; | |
void* StackBase; | |
void* StackLimit; | |
void* SubSystemTib; | |
union | |
{ | |
void* FiberData; | |
unsigned int Version; | |
}; | |
void* ArbitraryUserPointer; | |
_MYNT_TIB* Self; | |
} MYNT_TIB, * PMYNT_TIB; | |
typedef struct _MYCLIENT_ID | |
{ | |
void* UniqueProcess; | |
void* UniqueThread; | |
} MYCLIENT_ID, * PMYCLIENT_ID; | |
typedef struct _GDI_TEB_BATCH | |
{ | |
unsigned __int32 Offset : 31; | |
unsigned __int32 HasRenderingCommand : 1; | |
unsigned __int64 HDC; | |
unsigned int Buffer[310]; | |
} GDI_TEB_BATCH, * PGDI_TEB_BATCH; | |
typedef const struct _TEB_ACTIVE_FRAME_CONTEXT | |
{ | |
unsigned int Flags; | |
const char* FrameName; | |
} TEB_ACTIVE_FRAME_CONTEXT, * PTEB_ACTIVE_FRAME_CONTEXT; | |
typedef struct _TEB_ACTIVE_FRAME | |
{ | |
unsigned int Flags; | |
_TEB_ACTIVE_FRAME* Previous; | |
_TEB_ACTIVE_FRAME_CONTEXT* Context; | |
} TEB_ACTIVE_FRAME, * PTEB_ACTIVE_FRAME; | |
typedef struct _myTEB | |
{ | |
MYNT_TIB NtTib; | |
void* EnvironmentPointer; | |
CLIENT_ID ClientId; | |
void* ActiveRpcHandle; | |
void* ThreadLocalStoragePointer; | |
PEB* ProcessEnvironmentBlock; | |
unsigned int LastErrorValue; | |
unsigned int CountOfOwnedCriticalSections; | |
void* CsrClientThread; | |
void* Win32ThreadInfo; | |
unsigned int User32Reserved[26]; | |
unsigned int UserReserved[5]; | |
void* WOW32Reserved; | |
unsigned int CurrentLocale; | |
unsigned int FpSoftwareStatusRegister; | |
void* ReservedForDebuggerInstrumentation[16]; | |
void* SystemReserved1[38]; | |
int ExceptionCode; | |
char Padding0[4]; | |
__int64* ActivationContextStackPointer; | |
unsigned __int64 InstrumentationCallbackSp; | |
unsigned __int64 InstrumentationCallbackPreviousPc; | |
unsigned __int64 InstrumentationCallbackPreviousSp; | |
unsigned int TxFsContext; | |
char InstrumentationCallbackDisabled; | |
char Padding1[3]; | |
GDI_TEB_BATCH GdiTebBatch; | |
CLIENT_ID RealClientId; | |
void* GdiCachedProcessHandle; | |
unsigned int GdiClientPID; | |
unsigned int GdiClientTID; | |
void* GdiThreadLocalInfo; | |
unsigned __int64 Win32ClientInfo[62]; | |
void* glDispatchTable[233]; | |
unsigned __int64 glReserved1[29]; | |
void* glReserved2; | |
void* glSectionInfo; | |
void* glSection; | |
void* glTable; | |
void* glCurrentRC; | |
void* glContext; | |
unsigned int LastStatusValue; | |
char Padding2[4]; | |
UNICODE_STRING StaticUnicodeString; | |
wchar_t StaticUnicodeBuffer[261]; | |
char Padding3[6]; | |
void* DeallocationStack; | |
void* TlsSlots[64]; | |
LIST_ENTRY TlsLinks; | |
void* Vdm; | |
void* ReservedForNtRpc; | |
void* DbgSsReserved[2]; | |
unsigned int HardErrorMode; | |
char Padding4[4]; | |
void* Instrumentation[11]; | |
GUID ActivityId; | |
void* SubProcessTag; | |
void* PerflibData; | |
void* EtwTraceData; | |
void* WinSockData; | |
unsigned int GdiBatchCount; | |
union | |
{ | |
_PROCESSOR_NUMBER CurrentIdealProcessor; | |
unsigned int IdealProcessorValue; | |
struct DUMMYSTRUCTNAME | |
{ | |
char ReservedPad0; | |
char ReservedPad1; | |
char ReservedPad2; | |
char IdealProcessor; | |
}; | |
}; | |
unsigned int GuaranteedStackBytes; | |
char Padding5[4]; | |
void* ReservedForPerf; | |
void* ReservedForOle; | |
unsigned int WaitingOnLoaderLock; | |
char Padding6[4]; | |
void* SavedPriorityState; | |
unsigned __int64 ReservedForCodeCoverage; | |
void* ThreadPoolData; | |
void** TlsExpansionSlots; | |
void* DeallocationBStore; | |
void* BStoreLimit; | |
unsigned int MuiGeneration; | |
unsigned int IsImpersonating; | |
void* NlsCache; | |
void* pShimData; | |
unsigned __int16 HeapVirtualAffinity; | |
unsigned __int16 LowFragHeapDataSlot; | |
char Padding7[4]; | |
void* CurrentTransactionHandle; | |
TEB_ACTIVE_FRAME* ActiveFrame; | |
void* FlsData; | |
void* PreferredLanguages; | |
void* UserPrefLanguages; | |
void* MergedPrefLanguages; | |
unsigned int MuiImpersonation; | |
union | |
{ | |
volatile unsigned __int16 CrossTebFlags; | |
struct DUMMYSTRUCTNAME | |
{ | |
unsigned __int16 SpareCrossTebBits : 16; | |
}; | |
}; | |
union | |
{ | |
unsigned __int16 SameTebFlags; | |
struct DUMMYSTRUCTNAME | |
{ | |
unsigned __int16 SafeThunkCall : 1; | |
unsigned __int16 InDebugPrint : 1; | |
unsigned __int16 HasFiberData : 1; | |
unsigned __int16 SkipThreadAttach : 1; | |
unsigned __int16 WerInShipAssertCode : 1; | |
unsigned __int16 RanProcessInit : 1; | |
unsigned __int16 ClonedThread : 1; | |
unsigned __int16 SuppressDebugMsg : 1; | |
unsigned __int16 DisableUserStackWalk : 1; | |
unsigned __int16 RtlExceptionAttached : 1; | |
unsigned __int16 InitialThread : 1; | |
unsigned __int16 SessionAware : 1; | |
unsigned __int16 LoadOwner : 1; | |
unsigned __int16 LoaderWorker : 1; | |
unsigned __int16 SpareSameTebBits : 2; | |
}; | |
}; | |
void* TxnScopeEnterCallback; | |
void* TxnScopeExitCallback; | |
void* TxnScopeContext; | |
unsigned int LockCount; | |
int WowTebOffset; | |
void* ResourceRetValue; | |
void* ReservedForWdf; | |
unsigned __int64 ReservedForCrt; | |
GUID EffectiveContainerId; | |
} MYTEB, * PMYTEB; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment