mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-15 03:59:39 +00:00
693 lines
23 KiB
C++
693 lines
23 KiB
C++
/**
|
|
* @file dt-struct.cpp
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @brief dt and struct command
|
|
* @details
|
|
* @version 0.1
|
|
* @date 2021-12-13
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
//
|
|
// Global Variables
|
|
//
|
|
extern BOOLEAN g_IsSerialConnectedToRemoteDebuggee;
|
|
extern ACTIVE_DEBUGGING_PROCESS g_ActiveProcessDebuggingState;
|
|
|
|
/**
|
|
* @brief help of the dt command
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
CommandDtHelp()
|
|
{
|
|
ShowMessages("dt !dt : displays information about a local variable, global "
|
|
"variable or data type.\n\n");
|
|
|
|
ShowMessages("If you want to read physical memory then add '!' at the "
|
|
"start of the command\n\n");
|
|
|
|
ShowMessages("syntax : \tdt [Module!SymbolName (string)] [AddressExpression (string)] "
|
|
"[pid ProcessId (hex)] [padding Padding (yesno)] [offset Offset (yesno)] "
|
|
"[bitfield Bitfield (yesno)] [native Native (yesno)] [decl Declaration (yesno)] "
|
|
"[def Definitions (yesno)] [func Functions (yesno)] [pragma Pragma (yesno)] "
|
|
"[prefix Prefix (string)] [suffix Suffix (string)] [inline Expansion (string)] "
|
|
"[output FileName (string)]\n\n");
|
|
ShowMessages("syntax : \t!dt [Module!SymbolName (string)] [AddressExpression (string)] "
|
|
"[padding Padding (yesno)] [offset Offset (yesno)] [bitfield Bitfield (yesno)] "
|
|
"[native Native (yesno)] [decl Declaration (yesno)] [def Definitions (yesno)] "
|
|
"[func Functions (yesno)] [pragma Pragma (yesno)] [prefix Prefix (string)] "
|
|
"[suffix Suffix (string)] [inline Expansion (string)] [output FileName (string)]\n");
|
|
|
|
ShowMessages("\n");
|
|
ShowMessages("\t\te.g : dt nt!_EPROCESS\n");
|
|
ShowMessages("\t\te.g : dt nt!_EPROCESS fffff8077356f010\n");
|
|
ShowMessages("\t\te.g : dt nt!_EPROCESS $proc\n");
|
|
ShowMessages("\t\te.g : dt nt!_KPROCESS @rax+@rbx+c0\n");
|
|
ShowMessages("\t\te.g : !dt nt!_EPROCESS 1f0300\n");
|
|
ShowMessages("\t\te.g : dt MyModule!_MY_STRUCT 7ff00040 pid 1420\n");
|
|
ShowMessages("\t\te.g : dt nt!_EPROCESS $proc inline all\n");
|
|
ShowMessages("\t\te.g : dt nt!_EPROCESS fffff8077356f010 inline no\n");
|
|
}
|
|
|
|
/**
|
|
* @brief help of the struct command
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
CommandStructHelp()
|
|
{
|
|
ShowMessages("struct : displays a data type, enum, or structure derived from PDB symbols.\n\n");
|
|
|
|
ShowMessages("syntax : struct [Module!SymbolName (string)] [offset Offset (yesno)] [bitfield Bitfield (yesno)] "
|
|
"[native Native (yesno)] [decl Declaration (yesno)] [def Definitions (yesno)] "
|
|
"[func Functions (yesno)] [pragma Pragma (yesno)] [prefix Prefix (string)] "
|
|
"[suffix Suffix (string)] [inline Expantion (string)] [output FileName (string)]\n");
|
|
|
|
ShowMessages("\n");
|
|
ShowMessages("\t\te.g : struct nt!_EPROCESS\n");
|
|
ShowMessages("\t\te.g : struct nt!*\n");
|
|
ShowMessages("\t\te.g : struct nt!* output ntheader.h\n");
|
|
ShowMessages("\t\te.g : struct nt!* func yes output ntheader.h\n");
|
|
ShowMessages("\t\te.g : struct nt!* func yes output ntheader.h\n");
|
|
ShowMessages("\t\te.g : struct nt!* func yes output \"c:\\users\\sina\\desktop\\nt header.h\"\n");
|
|
}
|
|
|
|
/**
|
|
* @brief Convert HyperDbg arguments for dt and struct commands to pdbex arguments
|
|
* @details This is because we don't wanna modify the internal structure of pdbex
|
|
* so let's pdbex parse its standard commands
|
|
*
|
|
* @param ExtraArgs
|
|
* @param PdbexArgs
|
|
* @param ProcessId
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
CommandDtAndStructConvertHyperDbgArgsToPdbex(vector<CommandToken> ExtraArgs,
|
|
std::string & PdbexArgs,
|
|
UINT32 * ProcessId)
|
|
{
|
|
UINT32 TargetProcessId = NULL;
|
|
BOOLEAN NextItemIsYesNo = FALSE;
|
|
BOOLEAN NextItemIsString = FALSE;
|
|
BOOLEAN NextItemIsInline = FALSE;
|
|
BOOLEAN NextItemIsFileName = FALSE;
|
|
BOOLEAN NextItemIsProcessId = FALSE;
|
|
|
|
//
|
|
// Clear the args
|
|
//
|
|
PdbexArgs = "";
|
|
|
|
//
|
|
// Traverse through the extra arguments
|
|
//
|
|
for (auto Item : ExtraArgs)
|
|
{
|
|
//
|
|
// Check for output file name
|
|
//
|
|
if (NextItemIsFileName)
|
|
{
|
|
PdbexArgs += "\"" + GetCaseSensitiveStringFromCommandToken(Item) + "\" ";
|
|
|
|
NextItemIsFileName = FALSE;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check for process id
|
|
//
|
|
if (NextItemIsProcessId)
|
|
{
|
|
if (!ConvertTokenToUInt32(Item, &TargetProcessId))
|
|
{
|
|
ShowMessages("err, you should enter a valid process id\n\n");
|
|
return FALSE;
|
|
}
|
|
|
|
NextItemIsProcessId = FALSE;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check if we expect yes/no answers
|
|
//
|
|
if (NextItemIsYesNo)
|
|
{
|
|
if (CompareLowerCaseStrings(Item, "yes"))
|
|
{
|
|
PdbexArgs += " ";
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "no"))
|
|
{
|
|
PdbexArgs += "- ";
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Yes/no expected but didn't see it
|
|
//
|
|
ShowMessages("err, please insert 'yes' or 'no' as the argument\n\n");
|
|
return FALSE;
|
|
}
|
|
|
|
NextItemIsYesNo = FALSE;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check if we expect inline param answers
|
|
//
|
|
if (NextItemIsInline)
|
|
{
|
|
if (CompareLowerCaseStrings(Item, "none"))
|
|
{
|
|
PdbexArgs += "n ";
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "all"))
|
|
{
|
|
PdbexArgs += "a ";
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "unnamed") || CompareLowerCaseStrings(Item, "unamed"))
|
|
{
|
|
PdbexArgs += "i ";
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// none/unnamed/all expected but didn't see it
|
|
//
|
|
ShowMessages("err, please insert 'none', 'unnamed', or 'all' as the argument\n\n");
|
|
return FALSE;
|
|
}
|
|
|
|
NextItemIsInline = FALSE;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check if we expect string answers
|
|
//
|
|
if (NextItemIsString)
|
|
{
|
|
PdbexArgs += GetCaseSensitiveStringFromCommandToken(Item) + " ";
|
|
|
|
NextItemIsString = FALSE;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check for args
|
|
//
|
|
if (CompareLowerCaseStrings(Item, "pid"))
|
|
{
|
|
NextItemIsProcessId = TRUE;
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "output"))
|
|
{
|
|
NextItemIsFileName = TRUE;
|
|
PdbexArgs += "-o ";
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "inline"))
|
|
{
|
|
NextItemIsInline = TRUE;
|
|
PdbexArgs += "-e ";
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "prefix"))
|
|
{
|
|
NextItemIsString = TRUE;
|
|
PdbexArgs += "-r ";
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "suffix"))
|
|
{
|
|
NextItemIsString = TRUE;
|
|
PdbexArgs += "-g ";
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "padding"))
|
|
{
|
|
NextItemIsYesNo = TRUE;
|
|
PdbexArgs += "-p";
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "offset") || CompareLowerCaseStrings(Item, "offsets"))
|
|
{
|
|
NextItemIsYesNo = TRUE;
|
|
PdbexArgs += "-x";
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "bitfield") || CompareLowerCaseStrings(Item, "bitfields"))
|
|
{
|
|
NextItemIsYesNo = TRUE;
|
|
PdbexArgs += "-b";
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "native"))
|
|
{
|
|
NextItemIsYesNo = TRUE;
|
|
PdbexArgs += "-i";
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "decl"))
|
|
{
|
|
NextItemIsYesNo = TRUE;
|
|
PdbexArgs += "-n";
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "def"))
|
|
{
|
|
NextItemIsYesNo = TRUE;
|
|
PdbexArgs += "-l";
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "func"))
|
|
{
|
|
NextItemIsYesNo = TRUE;
|
|
PdbexArgs += "-f";
|
|
}
|
|
else if (CompareLowerCaseStrings(Item, "pragma"))
|
|
{
|
|
NextItemIsYesNo = TRUE;
|
|
PdbexArgs += "-z";
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Unknown args
|
|
//
|
|
ShowMessages("err, unknown argument at '%s'\n\n",
|
|
GetCaseSensitiveStringFromCommandToken(Item).c_str());
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if user entered yes/no or string when expected or not
|
|
//
|
|
if (NextItemIsYesNo || NextItemIsString || NextItemIsInline || NextItemIsFileName || NextItemIsProcessId)
|
|
{
|
|
ShowMessages("err, incomplete argument\n\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Set the process id
|
|
//
|
|
*ProcessId = TargetProcessId;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Show data based on the symbol structure and data types
|
|
*
|
|
* @param TypeName
|
|
* @param Address
|
|
* @param IsStruct
|
|
* @param BufferAddress
|
|
* @param TargetPid
|
|
* @param IsPhysicalAddress
|
|
* @param AdditionalParameters
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
CommandDtShowDataBasedOnSymbolTypes(
|
|
const CHAR * TypeName,
|
|
UINT64 Address,
|
|
BOOLEAN IsStruct,
|
|
PVOID BufferAddress,
|
|
UINT32 TargetPid,
|
|
BOOLEAN IsPhysicalAddress,
|
|
const CHAR * AdditionalParameters)
|
|
{
|
|
UINT64 StructureSize = 0;
|
|
BOOLEAN ResultOfFindingSize = FALSE;
|
|
DEBUGGER_DT_COMMAND_OPTIONS DtOptions = {0};
|
|
|
|
//
|
|
// Check for pid
|
|
//
|
|
|
|
if (g_IsSerialConnectedToRemoteDebuggee && TargetPid != 0)
|
|
{
|
|
//
|
|
// Check to prevent using process id in dt command
|
|
//
|
|
ShowMessages(ASSERT_MESSAGE_CANNOT_SPECIFY_PID);
|
|
return FALSE;
|
|
}
|
|
else if (TargetPid == NULL)
|
|
{
|
|
//
|
|
// By default if the user-debugger is active, we use these commands
|
|
// on the memory layout of the debuggee process
|
|
//
|
|
if (g_ActiveProcessDebuggingState.IsActive)
|
|
{
|
|
TargetPid = g_ActiveProcessDebuggingState.ProcessId;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Use the current process for the pid
|
|
//
|
|
TargetPid = PlatformGetCurrentProcessId();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the options
|
|
//
|
|
DtOptions.TypeName = TypeName;
|
|
DtOptions.Address = Address;
|
|
DtOptions.IsStruct = IsStruct;
|
|
DtOptions.BufferAddress = NULL; // we didn't read it yet
|
|
DtOptions.TargetPid = TargetPid;
|
|
DtOptions.AdditionalParameters = AdditionalParameters;
|
|
|
|
if (Address != NULL)
|
|
{
|
|
//
|
|
// *** We need to read the memory here ***
|
|
//
|
|
|
|
//
|
|
// Get the field size
|
|
//
|
|
ResultOfFindingSize = ScriptEngineGetDataTypeSizeWrapper((char *)TypeName, &StructureSize);
|
|
|
|
//
|
|
// Check if size is found
|
|
//
|
|
if (!ResultOfFindingSize || StructureSize == 0)
|
|
{
|
|
//
|
|
// Field not found or size is invalid
|
|
//
|
|
ShowMessages("err, couldn't resolve error at '%s'\n", TypeName);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Set the type (structure) size
|
|
//
|
|
DtOptions.SizeOfTypeName = StructureSize;
|
|
|
|
//
|
|
// Read the memory
|
|
//
|
|
HyperDbgShowMemoryOrDisassemble(DEBUGGER_SHOW_COMMAND_DT,
|
|
Address,
|
|
IsPhysicalAddress ? DEBUGGER_READ_PHYSICAL_ADDRESS : DEBUGGER_READ_VIRTUAL_ADDRESS,
|
|
READ_FROM_KERNEL,
|
|
TargetPid,
|
|
(UINT32)StructureSize,
|
|
&DtOptions);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's a simple structure without an address
|
|
// Call the pdbex wrapper
|
|
//
|
|
return ScriptEngineShowDataBasedOnSymbolTypesWrapper(TypeName, Address, IsStruct, BufferAddress, AdditionalParameters);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief dt and struct command handler
|
|
*
|
|
* @param CommandTokens
|
|
* @param Command
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
CommandDtAndStruct(vector<CommandToken> CommandTokens, string Command)
|
|
{
|
|
CommandToken TempTypeNameHolder;
|
|
std::string PdbexArgs = "";
|
|
BOOLEAN IsStruct = FALSE;
|
|
UINT64 TargetAddress = NULL;
|
|
PVOID BufferAddressRetrievedFromDebuggee = NULL;
|
|
UINT32 TargetPid = NULL;
|
|
BOOLEAN IsPhysicalAddress = FALSE;
|
|
|
|
//
|
|
// Check if command is 'struct' or not
|
|
//
|
|
if (CompareLowerCaseStrings(CommandTokens.at(0), "struct") ||
|
|
CompareLowerCaseStrings(CommandTokens.at(0), "structure"))
|
|
{
|
|
IsStruct = TRUE;
|
|
}
|
|
else
|
|
{
|
|
IsStruct = FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if command is '!dt' for physical address or not
|
|
//
|
|
if (!IsStruct && CompareLowerCaseStrings(CommandTokens.at(0), "!dt"))
|
|
{
|
|
IsPhysicalAddress = TRUE;
|
|
}
|
|
else
|
|
{
|
|
IsPhysicalAddress = FALSE;
|
|
}
|
|
|
|
if (CommandTokens.size() == 1)
|
|
{
|
|
ShowMessages("incorrect use of the '%s'\n\n",
|
|
GetCaseSensitiveStringFromCommandToken(CommandTokens.at(0)).c_str());
|
|
|
|
if (IsStruct)
|
|
{
|
|
CommandStructHelp();
|
|
}
|
|
else
|
|
{
|
|
CommandDtHelp();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Create TempSplitTokens by copying elements from CommandTokens, excluding the first element
|
|
//
|
|
std::vector<CommandToken> TempSplitTokens(CommandTokens.begin() + 1, CommandTokens.end());
|
|
|
|
//
|
|
// If the size is zero, then it's only a type name
|
|
//
|
|
if (TempSplitTokens.size() == 1)
|
|
{
|
|
//
|
|
// Call the dt parser wrapper, it's only a structure (type) name
|
|
// Call it with default configuration
|
|
//
|
|
CommandDtShowDataBasedOnSymbolTypes(GetCaseSensitiveStringFromCommandToken(TempSplitTokens.at(0)).c_str(),
|
|
NULL,
|
|
IsStruct,
|
|
NULL,
|
|
TargetPid,
|
|
IsPhysicalAddress,
|
|
PDBEX_DEFAULT_CONFIGURATION);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// When we're here, it means we have size() >= 2, so we have to check
|
|
// the first and the second method to see which one is the type and
|
|
// which one is the symbol
|
|
//
|
|
|
|
//
|
|
// Check if the first parameter is an address or valid expression
|
|
//
|
|
if (IsStruct || !SymbolConvertNameOrExprToAddress(GetCaseSensitiveStringFromCommandToken(TempSplitTokens.at(0)).c_str(),
|
|
&TargetAddress))
|
|
{
|
|
//
|
|
// No it's not, we'll get the first argument as the structure (type) name
|
|
// And we have to check whether the second argument is a buffer address or not
|
|
//
|
|
if (IsStruct || !SymbolConvertNameOrExprToAddress(GetCaseSensitiveStringFromCommandToken(TempSplitTokens.at(1)).c_str(),
|
|
&TargetAddress))
|
|
{
|
|
//
|
|
// The second argument is also not buffer address
|
|
// probably the user entered a structure (type) name along with some params
|
|
//
|
|
TempTypeNameHolder = TempSplitTokens.at(0);
|
|
|
|
//
|
|
// Remove the first argument
|
|
//
|
|
TempSplitTokens.erase(TempSplitTokens.begin());
|
|
|
|
//
|
|
// Convert to pdbex args
|
|
//
|
|
if (!CommandDtAndStructConvertHyperDbgArgsToPdbex(TempSplitTokens, PdbexArgs, &TargetPid))
|
|
{
|
|
if (IsStruct)
|
|
{
|
|
CommandStructHelp();
|
|
}
|
|
else
|
|
{
|
|
CommandDtHelp();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Call the wrapper of pdbex
|
|
//
|
|
CommandDtShowDataBasedOnSymbolTypes(GetCaseSensitiveStringFromCommandToken(TempTypeNameHolder).c_str(),
|
|
TargetAddress,
|
|
IsStruct,
|
|
BufferAddressRetrievedFromDebuggee,
|
|
TargetPid,
|
|
IsPhysicalAddress,
|
|
PdbexArgs.c_str());
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The second argument is a buffer address
|
|
// The user entered a structure (type) name along with buffer address
|
|
//
|
|
if (TempSplitTokens.size() == 2)
|
|
{
|
|
//
|
|
// There is not parameters, only a symbol name and then a buffer address
|
|
// Call it with default configuration
|
|
//
|
|
CommandDtShowDataBasedOnSymbolTypes(GetCaseSensitiveStringFromCommandToken(TempSplitTokens.at(0)).c_str(),
|
|
TargetAddress,
|
|
IsStruct,
|
|
BufferAddressRetrievedFromDebuggee,
|
|
TargetPid,
|
|
IsPhysicalAddress,
|
|
PDBEX_DEFAULT_CONFIGURATION);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Other than the first argument which is a structure (type) name, and
|
|
// the second argument which is buffer address, there are other parameters, so
|
|
// we WON'T call it with default parameters
|
|
//
|
|
TempTypeNameHolder = TempSplitTokens.at(0);
|
|
|
|
//
|
|
// Remove the first, and the second arguments
|
|
//
|
|
TempSplitTokens.erase(TempSplitTokens.begin());
|
|
TempSplitTokens.erase(TempSplitTokens.begin());
|
|
|
|
//
|
|
// Convert to pdbex args
|
|
//
|
|
if (!CommandDtAndStructConvertHyperDbgArgsToPdbex(TempSplitTokens, PdbexArgs, &TargetPid))
|
|
{
|
|
if (IsStruct)
|
|
{
|
|
CommandStructHelp();
|
|
}
|
|
else
|
|
{
|
|
CommandDtHelp();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Call the wrapper of pdbex
|
|
//
|
|
CommandDtShowDataBasedOnSymbolTypes(GetCaseSensitiveStringFromCommandToken(TempTypeNameHolder).c_str(),
|
|
TargetAddress,
|
|
IsStruct,
|
|
BufferAddressRetrievedFromDebuggee,
|
|
TargetPid,
|
|
IsPhysicalAddress,
|
|
PdbexArgs.c_str());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The first argument is a buffer address, so we get the first argument as
|
|
// a buffer address and the second argument as the structure (type) name
|
|
//
|
|
if (TempSplitTokens.size() == 2)
|
|
{
|
|
//
|
|
// There is not parameters, only a buffer address and then a symbol name
|
|
// Call it with default configuration
|
|
//
|
|
CommandDtShowDataBasedOnSymbolTypes(GetCaseSensitiveStringFromCommandToken(TempSplitTokens.at(1)).c_str(),
|
|
TargetAddress,
|
|
IsStruct,
|
|
BufferAddressRetrievedFromDebuggee,
|
|
TargetPid,
|
|
IsPhysicalAddress,
|
|
PDBEX_DEFAULT_CONFIGURATION);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Other than the first argument which is a buffer address, and the second
|
|
// argument which is structure (type) name, there are other parameters, so
|
|
// we WON'T call it with default parameters
|
|
//
|
|
TempTypeNameHolder = TempSplitTokens.at(1);
|
|
|
|
//
|
|
// Remove the first, and the second arguments
|
|
//
|
|
TempSplitTokens.erase(TempSplitTokens.begin());
|
|
TempSplitTokens.erase(TempSplitTokens.begin());
|
|
|
|
//
|
|
// Convert to pdbex args
|
|
//
|
|
if (!CommandDtAndStructConvertHyperDbgArgsToPdbex(TempSplitTokens, PdbexArgs, &TargetPid))
|
|
{
|
|
if (IsStruct)
|
|
{
|
|
CommandStructHelp();
|
|
}
|
|
else
|
|
{
|
|
CommandDtHelp();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Call the wrapper of pdbex
|
|
//
|
|
CommandDtShowDataBasedOnSymbolTypes(GetCaseSensitiveStringFromCommandToken(TempTypeNameHolder).c_str(),
|
|
TargetAddress,
|
|
IsStruct,
|
|
BufferAddressRetrievedFromDebuggee,
|
|
TargetPid,
|
|
IsPhysicalAddress,
|
|
PdbexArgs.c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|