/** * @file thread.cpp * @author Sina Karvandi (sina@hyperdbg.org) * @brief show and change threads * @details * @version 0.1 * @date 2021-11-23 * * @copyright This project is released under the GNU Public License v3. * */ #include "pch.h" // // Global Variables // extern BOOLEAN g_IsSerialConnectedToRemoteDebuggee; /** * @brief help of the .thread command * * @return VOID */ VOID CommandThreadHelp() { ShowMessages(".thread, .thread2 : shows and changes the threads. " "This command needs public symbols for 'ntoskrnl.exe' if " "you want to see the threads list. Please visit the " "documentation to know about the difference between '.thread' " "and '.thread2'.\n\n"); ShowMessages("syntax : \t.thread\n"); ShowMessages("syntax : \t.thread [list] [process Eprocess (hex)]\n"); ShowMessages("syntax : \t.thread [tid ThreadId (hex)]\n"); ShowMessages("syntax : \t.thread [thread Ethread (hex)]\n"); ShowMessages("syntax : \t.thread2 [tid ThreadId (hex)]\n"); ShowMessages("syntax : \t.thread2 [thread Ethread (hex)]\n"); ShowMessages("\n"); ShowMessages("\t\te.g : .thread\n"); ShowMessages("\t\te.g : .thread tid 48a4\n"); ShowMessages("\t\te.g : .thread2 tid 48a4\n"); ShowMessages("\t\te.g : .thread thread ffff948c`c8970200\n"); ShowMessages("\t\te.g : .thread list\n"); ShowMessages("\t\te.g : .thread list process ffff948c`a1279880\n"); } BOOLEAN CommandThreadListThreads(UINT64 Eprocess) { UINT32 ThreadListHeadOffset = 0; // nt!_EPROCESS.ThreadListHead UINT32 ThreadListEntryOffset = 0; // nt!_ETHREAD.ThreadListEntry UINT32 CidOffset = 0; // nt!_ETHREAD.Cid UINT32 OffsetOfActiveProcessLinks = 0; // nt!_EPROCESS.ActiveProcessLinks UINT64 AddressOfActiveProcessHead = 0; // nt!PsActiveProcessHead DEBUGGEE_THREAD_LIST_NEEDED_DETAILS ThreadListNeededItems = {0}; // // Query for nt!_EPROCESS.ThreadListHead, nt!_ETHREAD.ThreadListEntry, // from the top of nt!_EPROCESS, and nt!_ETHREAD address and check if // we find them or not, otherwise, it means that the PDB for ntoskrnl.exe // is not available // if (ScriptEngineGetFieldOffsetWrapper((CHAR *)"nt!_EPROCESS", (CHAR *)"ThreadListHead", &ThreadListHeadOffset) && ScriptEngineGetFieldOffsetWrapper((CHAR *)"nt!_ETHREAD", (CHAR *)"ThreadListEntry", &ThreadListEntryOffset) && ScriptEngineGetFieldOffsetWrapper((CHAR *)"nt!_ETHREAD", (CHAR *)"Cid", &CidOffset) && ScriptEngineGetFieldOffsetWrapper((CHAR *)"nt!_EPROCESS", (CHAR *)"ActiveProcessLinks", &OffsetOfActiveProcessLinks) && SymbolConvertNameOrExprToAddress("nt!PsActiveProcessHead", &AddressOfActiveProcessHead)) { ThreadListNeededItems.ThreadListHeadOffset = ThreadListHeadOffset; ThreadListNeededItems.ThreadListEntryOffset = ThreadListEntryOffset; ThreadListNeededItems.CidOffset = CidOffset; ThreadListNeededItems.ActiveProcessLinksOffset = OffsetOfActiveProcessLinks; ThreadListNeededItems.PsActiveProcessHead = AddressOfActiveProcessHead; ThreadListNeededItems.Process = Eprocess; // // Check if it's connected to a remote debuggee or not // if (!g_IsSerialConnectedToRemoteDebuggee) { // // Get the thread details in VMI mode // ObjectShowProcessesOrThreadList(FALSE, NULL, NULL, &ThreadListNeededItems); } else { // // Send the packet to list threads // KdSendSwitchThreadPacketToDebuggee(DEBUGGEE_DETAILS_AND_SWITCH_THREAD_GET_THREAD_LIST, NULL, NULL, FALSE, &ThreadListNeededItems); } return TRUE; } else { return FALSE; } } /** * @brief .thread command handler * * @param CommandTokens * @param Command * * @return VOID */ VOID CommandThread(vector CommandTokens, string Command) { UINT32 TargetThreadId = 0; UINT64 TargetThread = 0; UINT64 TargetProcess = 0; BOOLEAN CheckByClkIntr = FALSE; if (CommandTokens.size() >= 5) { ShowMessages("incorrect use of the '%s'\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(0)).c_str()); CommandThreadHelp(); return; } if (CommandTokens.size() == 1) { // // Check if it's connected to a remote debuggee or not // if (!g_IsSerialConnectedToRemoteDebuggee) { ObjectShowProcessesOrThreadDetails(FALSE); } else { // // Send the packet to get current thread // KdSendSwitchThreadPacketToDebuggee(DEBUGGEE_DETAILS_AND_SWITCH_THREAD_GET_THREAD_DETAILS, NULL, NULL, FALSE, NULL); } } else if (CommandTokens.size() == 2) { if (CompareLowerCaseStrings(CommandTokens.at(1), "list")) { // // Sending null as the nt!_EPROCESS indicates that the target process is // the current process (Current nt!_EPROCESS) // if (!CommandThreadListThreads(NULL)) { ShowMessages("err, the need offset to iterate over threads not found, " "make sure to load ntoskrnl.exe's PDB file. use '.help .sym' for " "more information\n"); return; } } else { ShowMessages( "err, unknown parameter at '%s'\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(1)).c_str()); CommandThreadHelp(); return; } } else if (CommandTokens.size() == 3) { // // Check if it's connected to a remote debuggee or not // if (!g_IsSerialConnectedToRemoteDebuggee) { ShowMessages("err, you're not connected to any debuggee in Debugger Mode, " "you can use the '.attach', or the '.detach' commands if you're " "operating in VMI Mode\n"); return; } if (CompareLowerCaseStrings(CommandTokens.at(1), "tid")) { if (!ConvertTokenToUInt32(CommandTokens.at(2), &TargetThreadId)) { ShowMessages( "please specify a correct hex value for the thread id that you " "want to operate on it\n\n"); CommandThreadHelp(); return; } } else if (CompareLowerCaseStrings(CommandTokens.at(1), "thread")) { if (!SymbolConvertNameOrExprToAddress(GetCaseSensitiveStringFromCommandToken(CommandTokens.at(2)), &TargetThread)) { ShowMessages( "please specify a correct hex value for the thread (nt!_ETHREAD) that you " "want to operate on it\n\n"); CommandThreadHelp(); return; } } else if (CompareLowerCaseStrings(CommandTokens.at(1), "list") && CompareLowerCaseStrings(CommandTokens.at(2), "process")) { ShowMessages( "please specify a hex value for the process\n\n"); CommandThreadHelp(); return; } else { ShowMessages( "err, unknown parameter at '%s'\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(2)).c_str()); CommandThreadHelp(); return; } if (CompareLowerCaseStrings(CommandTokens.at(0), ".thread2")) { // // Check by changes to gs:[188] // CheckByClkIntr = FALSE; } else { // // Check on clock interrupt changes // CheckByClkIntr = TRUE; } // // Send the packet to change the thread // KdSendSwitchThreadPacketToDebuggee(DEBUGGEE_DETAILS_AND_SWITCH_THREAD_PERFORM_SWITCH, TargetThreadId, TargetThread, CheckByClkIntr, NULL); } else if (CommandTokens.size() == 4) { if (CompareLowerCaseStrings(CommandTokens.at(1), "list")) { if (CompareLowerCaseStrings(CommandTokens.at(2), "process")) { if (!SymbolConvertNameOrExprToAddress(GetCaseSensitiveStringFromCommandToken(CommandTokens.at(3)), &TargetProcess)) { ShowMessages( "please specify a correct hex value for the process (nt!_EPROCESS) that you " "want to see its threads\n\n"); CommandThreadHelp(); return; } // // List the target process's threads // if (!CommandThreadListThreads(TargetProcess)) { ShowMessages("err, the need offset to iterate over threads not found, " "make sure to load ntoskrnl.exe's PDB file. use '.help .sym' for " "more information\n"); return; } } else { ShowMessages( "err, unknown parameter at '%s'\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(2)).c_str()); CommandThreadHelp(); return; } } else { ShowMessages( "err, unknown parameter at '%s'\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(1)).c_str()); CommandThreadHelp(); return; } } else { ShowMessages("invalid parameter\n\n"); CommandThreadHelp(); return; } }