mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-15 03:59:39 +00:00
574 lines
15 KiB
C++
574 lines
15 KiB
C++
/**
|
|
* @file r.cpp
|
|
* @author Alee Amini (alee@hyperdbg.org)
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @brief r command
|
|
* @details
|
|
* @version 0.1
|
|
* @date 2021-02-27
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
// using namespace std;
|
|
|
|
//
|
|
// Global Variables
|
|
//
|
|
extern BOOLEAN g_IsSerialConnectedToRemoteDebuggee;
|
|
extern ACTIVE_DEBUGGING_PROCESS g_ActiveProcessDebuggingState;
|
|
|
|
std::map<std::string, REGS_ENUM> RegistersMap = {
|
|
{"rax", REGISTER_RAX},
|
|
{"eax", REGISTER_EAX},
|
|
{"ax", REGISTER_AX},
|
|
{"ah", REGISTER_AH},
|
|
{"al", REGISTER_AL},
|
|
{"rbx", REGISTER_RBX},
|
|
{"ebx", REGISTER_EBX},
|
|
{"bx", REGISTER_BX},
|
|
{"bh", REGISTER_BH},
|
|
{"bl", REGISTER_BL},
|
|
{"rcx", REGISTER_RCX},
|
|
{"ecx", REGISTER_ECX},
|
|
{"cx", REGISTER_CX},
|
|
{"ch", REGISTER_CH},
|
|
{"cl", REGISTER_CL},
|
|
{"rdx", REGISTER_RDX},
|
|
{"edx", REGISTER_EDX},
|
|
{"dx", REGISTER_DX},
|
|
{"dh", REGISTER_DH},
|
|
{"dl", REGISTER_DL},
|
|
{"rsi", REGISTER_RSI},
|
|
{"esi", REGISTER_ESI},
|
|
{"si", REGISTER_SI},
|
|
{"sil", REGISTER_SIL},
|
|
{"rdi", REGISTER_RDI},
|
|
{"edi", REGISTER_EDI},
|
|
{"di", REGISTER_DI},
|
|
{"dil", REGISTER_DIL},
|
|
{"rbp", REGISTER_RBP},
|
|
{"ebp", REGISTER_EBP},
|
|
{"bp", REGISTER_BP},
|
|
{"bpl", REGISTER_BPL},
|
|
{"rsp", REGISTER_RSP},
|
|
{"esp", REGISTER_ESP},
|
|
{"sp", REGISTER_SP},
|
|
{"spl", REGISTER_SPL},
|
|
{"r8", REGISTER_R8},
|
|
{"r8d", REGISTER_R8D},
|
|
{"r8w", REGISTER_R8W},
|
|
{"r8h", REGISTER_R8H},
|
|
{"r8l", REGISTER_R8L},
|
|
{"r9", REGISTER_R9},
|
|
{"r9d", REGISTER_R9D},
|
|
{"r9w", REGISTER_R9W},
|
|
{"r9h", REGISTER_R9H},
|
|
{"r9l", REGISTER_R9L},
|
|
{"r10", REGISTER_R10},
|
|
{"r10d", REGISTER_R10D},
|
|
{"r10w", REGISTER_R10W},
|
|
{"r10h", REGISTER_R10H},
|
|
{"r10l", REGISTER_R10L},
|
|
{"r11", REGISTER_R11},
|
|
{"r11d", REGISTER_R11D},
|
|
{"r11w", REGISTER_R11W},
|
|
{"r11h", REGISTER_R11H},
|
|
{"r11l", REGISTER_R11L},
|
|
{"r12", REGISTER_R12},
|
|
{"r12d", REGISTER_R12D},
|
|
{"r12w", REGISTER_R12W},
|
|
{"r12h", REGISTER_R12H},
|
|
{"r12l", REGISTER_R12L},
|
|
{"r13", REGISTER_R13},
|
|
{"r13d", REGISTER_R13D},
|
|
{"r13w", REGISTER_R13W},
|
|
{"r13h", REGISTER_R13H},
|
|
{"r13l", REGISTER_R13L},
|
|
{"r14", REGISTER_R14},
|
|
{"r14d", REGISTER_R14D},
|
|
{"r14w", REGISTER_R14W},
|
|
{"r14h", REGISTER_R14H},
|
|
{"r14l", REGISTER_R14L},
|
|
{"r15", REGISTER_R15},
|
|
{"r15d", REGISTER_R15D},
|
|
{"r15w", REGISTER_R15W},
|
|
{"r15h", REGISTER_R15H},
|
|
{"r15l", REGISTER_R15L},
|
|
{"ds", REGISTER_DS},
|
|
{"es", REGISTER_ES},
|
|
{"fs", REGISTER_FS},
|
|
{"gs", REGISTER_GS},
|
|
{"cs", REGISTER_CS},
|
|
{"ss", REGISTER_SS},
|
|
{"rflags", REGISTER_RFLAGS},
|
|
{"eflags", REGISTER_EFLAGS},
|
|
{"flags", REGISTER_FLAGS},
|
|
{"cf", REGISTER_CF},
|
|
{"pf", REGISTER_PF},
|
|
{"af", REGISTER_AF},
|
|
{"zf", REGISTER_ZF},
|
|
{"sf", REGISTER_SF},
|
|
{"tf", REGISTER_TF},
|
|
{"if", REGISTER_IF},
|
|
{"df", REGISTER_DF},
|
|
{"of", REGISTER_OF},
|
|
{"iopl", REGISTER_IOPL},
|
|
{"nt", REGISTER_NT},
|
|
{"rf", REGISTER_RF},
|
|
{"vm", REGISTER_VM},
|
|
{"ac", REGISTER_AC},
|
|
{"vif", REGISTER_VIF},
|
|
{"vip", REGISTER_VIP},
|
|
{"id", REGISTER_ID},
|
|
{"idtr", REGISTER_IDTR},
|
|
{"gdtr", REGISTER_GDTR},
|
|
{"ldtr", REGISTER_LDTR},
|
|
{"tr", REGISTER_TR},
|
|
{"cr0", REGISTER_CR0},
|
|
{"cr2", REGISTER_CR2},
|
|
{"cr3", REGISTER_CR3},
|
|
{"cr4", REGISTER_CR4},
|
|
{"cr8", REGISTER_CR8},
|
|
{"dr0", REGISTER_DR0},
|
|
{"dr1", REGISTER_DR1},
|
|
{"dr2", REGISTER_DR2},
|
|
{"dr3", REGISTER_DR3},
|
|
{"dr6", REGISTER_DR6},
|
|
{"dr7", REGISTER_DR7},
|
|
{"rip", REGISTER_RIP},
|
|
{"eip", REGISTER_EIP},
|
|
{"ip", REGISTER_IP},
|
|
};
|
|
|
|
/**
|
|
* @brief help of the r command
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
CommandRHelp()
|
|
{
|
|
ShowMessages("r : reads or modifies registers.\n\n");
|
|
|
|
ShowMessages("syntax : \tr\n");
|
|
ShowMessages("syntax : \tr [Register (string)] [= Expr (string)]\n");
|
|
|
|
ShowMessages("\n");
|
|
ShowMessages("\t\te.g : r\n");
|
|
ShowMessages("\t\te.g : r @rax\n");
|
|
ShowMessages("\t\te.g : r rax\n");
|
|
ShowMessages("\t\te.g : r rax = 0x55\n");
|
|
ShowMessages("\t\te.g : r rax = @rbx + @rcx + 0n10\n");
|
|
}
|
|
|
|
/**
|
|
* @brief Read all registers
|
|
* @param GuestRegisters The guest registers
|
|
* @param ExtraRegisters The extra registers
|
|
*
|
|
* @return BOOLEAN Returns true if it was successful
|
|
*/
|
|
BOOLEAN
|
|
HyperDbgReadAllRegisters(GUEST_REGS * GuestRegisters, GUEST_EXTRA_REGISTERS * ExtraRegisters)
|
|
{
|
|
PGUEST_REGS Regs;
|
|
PGUEST_EXTRA_REGISTERS ExtraRegs;
|
|
DEBUGGEE_REGISTER_READ_DESCRIPTION * RegState = NULL;
|
|
UINT32 SizeOfRegState = 0;
|
|
|
|
//
|
|
// Calculate the size of the register state
|
|
//
|
|
SizeOfRegState = sizeof(DEBUGGEE_REGISTER_READ_DESCRIPTION) + sizeof(GUEST_REGS) + sizeof(GUEST_EXTRA_REGISTERS);
|
|
|
|
//
|
|
// Allocate memory for the register state
|
|
//
|
|
RegState = (DEBUGGEE_REGISTER_READ_DESCRIPTION *)malloc(SizeOfRegState);
|
|
|
|
//
|
|
// Check if the memory allocation was successful
|
|
//
|
|
if (RegState == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Set the register ID to show all registers
|
|
//
|
|
RegState->RegisterId = DEBUGGEE_SHOW_ALL_REGISTERS;
|
|
|
|
//
|
|
// Check whether a kernel debugger is connected or a user-mode debugger is active
|
|
//
|
|
if (g_IsSerialConnectedToRemoteDebuggee)
|
|
{
|
|
if (!KdSendReadRegisterPacketToDebuggee(RegState, SizeOfRegState))
|
|
{
|
|
free(RegState);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (g_ActiveProcessDebuggingState.IsActive && g_ActiveProcessDebuggingState.IsPaused)
|
|
{
|
|
//
|
|
// It's stepping over user debugger
|
|
//
|
|
if (!UdSendReadRegisterToUserDebugger(g_ActiveProcessDebuggingState.ProcessDebuggingToken,
|
|
g_ActiveProcessDebuggingState.ThreadId,
|
|
RegState,
|
|
SizeOfRegState))
|
|
{
|
|
free(RegState);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (RegState->KernelStatus == DEBUGGER_OPERATION_WAS_SUCCESSFUL)
|
|
{
|
|
//
|
|
// Copy the registers and extra registers to the output
|
|
//
|
|
Regs = (GUEST_REGS *)(((CHAR *)RegState) + sizeof(DEBUGGEE_REGISTER_READ_DESCRIPTION));
|
|
ExtraRegs = (GUEST_EXTRA_REGISTERS *)(((CHAR *)RegState) + sizeof(DEBUGGEE_REGISTER_READ_DESCRIPTION) + sizeof(GUEST_REGS));
|
|
|
|
if (GuestRegisters != NULL)
|
|
{
|
|
memcpy(GuestRegisters, Regs, sizeof(GUEST_REGS));
|
|
}
|
|
|
|
if (ExtraRegisters != NULL)
|
|
{
|
|
memcpy(ExtraRegisters, ExtraRegs, sizeof(GUEST_EXTRA_REGISTERS));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ShowErrorMessage(RegState->KernelStatus);
|
|
free(RegState);
|
|
return FALSE;
|
|
}
|
|
|
|
free(RegState);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Read target register
|
|
* @param RegisterId The register ID
|
|
* @param TargetRegister The value of the target register
|
|
*
|
|
* @return BOOLEAN Returns true if it was successful
|
|
*/
|
|
BOOLEAN
|
|
HyperDbgReadTargetRegister(REGS_ENUM RegisterId, UINT64 * TargetRegister)
|
|
{
|
|
DEBUGGEE_REGISTER_READ_DESCRIPTION RegState = {0};
|
|
|
|
//
|
|
// Set the register ID
|
|
//
|
|
RegState.RegisterId = (UINT32)RegisterId;
|
|
|
|
if (g_IsSerialConnectedToRemoteDebuggee)
|
|
{
|
|
if (!KdSendReadRegisterPacketToDebuggee(&RegState, sizeof(DEBUGGEE_REGISTER_READ_DESCRIPTION)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (g_ActiveProcessDebuggingState.IsActive && g_ActiveProcessDebuggingState.IsPaused)
|
|
{
|
|
//
|
|
// It's stepping over user debugger
|
|
//
|
|
if (!UdSendReadRegisterToUserDebugger(g_ActiveProcessDebuggingState.ProcessDebuggingToken,
|
|
g_ActiveProcessDebuggingState.ThreadId,
|
|
&RegState,
|
|
sizeof(DEBUGGEE_REGISTER_READ_DESCRIPTION)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (RegState.KernelStatus == DEBUGGER_OPERATION_WAS_SUCCESSFUL)
|
|
{
|
|
if (TargetRegister != NULL)
|
|
{
|
|
*TargetRegister = RegState.Value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ShowErrorMessage(RegState.KernelStatus);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Write target register
|
|
* @param RegisterId The register ID
|
|
* @param Value The value of the target register
|
|
*
|
|
* @return BOOLEAN Returns true if it was successful
|
|
*/
|
|
BOOLEAN
|
|
HyperDbgWriteTargetRegister(REGS_ENUM RegisterId, UINT64 Value)
|
|
{
|
|
DEBUGGEE_REGISTER_WRITE_DESCRIPTION RegState = {0};
|
|
|
|
//
|
|
// Set the register ID
|
|
//
|
|
RegState.RegisterId = (UINT32)RegisterId;
|
|
RegState.Value = Value;
|
|
|
|
if (!KdSendWriteRegisterPacketToDebuggee(&RegState))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (RegState.KernelStatus == DEBUGGER_OPERATION_WAS_SUCCESSFUL)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
ShowErrorMessage(RegState.KernelStatus);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief handler of r show all registers
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
HyperDbgRegisterShowAll()
|
|
{
|
|
GUEST_REGS Regs = {0};
|
|
GUEST_EXTRA_REGISTERS ExtraRegs = {0};
|
|
RFLAGS Rflags = {0};
|
|
|
|
if (!HyperDbgReadAllRegisters(&Regs, &ExtraRegs))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Show the result of reading registers like rax=0000000000018b01
|
|
//
|
|
Rflags.AsUInt = ExtraRegs.RFLAGS;
|
|
|
|
ShowMessages(
|
|
"RAX=%016llx RBX=%016llx RCX=%016llx\n"
|
|
"RDX=%016llx RSI=% 016llx RDI=%016llx\n"
|
|
"RIP=%016llx RSP=%016llx RBP=%016llx\n"
|
|
"R8 =%016llx R9 =%016llx R10=%016llx\n"
|
|
"R11=%016llx R12=%016llx R13=%016llx\n"
|
|
"R14=%016llx R15=%016llx IOPL=%02x\n"
|
|
"%s %s %s %s\n%s %s %s %s\n"
|
|
"CS %04x SS %04x DS %04x ES %04x FS %04x GS %04x\n"
|
|
"RFLAGS=%016llx\n",
|
|
Regs.rax,
|
|
Regs.rbx,
|
|
Regs.rcx,
|
|
Regs.rdx,
|
|
Regs.rsi,
|
|
Regs.rdi,
|
|
ExtraRegs.RIP,
|
|
Regs.rsp,
|
|
Regs.rbp,
|
|
Regs.r8,
|
|
Regs.r9,
|
|
Regs.r10,
|
|
Regs.r11,
|
|
Regs.r12,
|
|
Regs.r13,
|
|
Regs.r14,
|
|
Regs.r15,
|
|
Rflags.IoPrivilegeLevel,
|
|
Rflags.OverflowFlag ? "OF 1" : "OF 0",
|
|
Rflags.DirectionFlag ? "DF 1" : "DF 0",
|
|
Rflags.InterruptEnableFlag ? "IF 1" : "IF 0",
|
|
Rflags.SignFlag ? "SF 1" : "SF 0",
|
|
Rflags.ZeroFlag ? "ZF 1" : "ZF 0",
|
|
Rflags.ParityFlag ? "PF 1" : "PF 0",
|
|
Rflags.CarryFlag ? "CF 1" : "CF 0",
|
|
Rflags.AuxiliaryCarryFlag ? "AXF 1" : "AXF 0",
|
|
ExtraRegs.CS,
|
|
ExtraRegs.SS,
|
|
ExtraRegs.DS,
|
|
ExtraRegs.ES,
|
|
ExtraRegs.FS,
|
|
ExtraRegs.GS,
|
|
ExtraRegs.RFLAGS);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief handler of r show the target register
|
|
* @param RegisterId The register ID
|
|
*
|
|
* @return BOOLEAN Returns true if it was successful
|
|
*/
|
|
BOOLEAN
|
|
HyperDbgRegisterShowTargetRegister(REGS_ENUM RegisterId)
|
|
{
|
|
UINT64 TargetRegister = 0;
|
|
|
|
//
|
|
// Read target register
|
|
//
|
|
if (!HyperDbgReadTargetRegister(RegisterId, &TargetRegister))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
ShowMessages("%s=%016llx\n",
|
|
RegistersNames[RegisterId],
|
|
TargetRegister);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief handler of r command
|
|
*
|
|
* @param CommandTokens
|
|
* @param Command
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
CommandR(vector<CommandToken> CommandTokens, string Command)
|
|
{
|
|
REGS_ENUM RegKind;
|
|
std::vector<std::string> Tmp;
|
|
std::string SetRegisterValue;
|
|
|
|
if (CommandTokens.size() == 1)
|
|
{
|
|
//
|
|
// show all registers
|
|
//
|
|
if (g_IsSerialConnectedToRemoteDebuggee ||
|
|
(g_ActiveProcessDebuggingState.IsActive && g_ActiveProcessDebuggingState.IsPaused))
|
|
{
|
|
HyperDbgRegisterShowAll();
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("err, reading registers (r) is not valid in the current "
|
|
"context, you should connect to a debuggee or attach to a process\n");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// clear additional space of the command string
|
|
//
|
|
|
|
//
|
|
// if command does not contain a '=' means user wants to read it
|
|
//
|
|
if (Command.find('=', 0) == string::npos)
|
|
{
|
|
//
|
|
// erase '=' from the string now we have just the name of register
|
|
//
|
|
Command.erase(0, 1);
|
|
ReplaceAll(Command, "@", "");
|
|
ReplaceAll(Command, " ", "");
|
|
if (RegistersMap.find(Command) != RegistersMap.end())
|
|
{
|
|
RegKind = RegistersMap[Command];
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// set the Reg to -1(invalid register)
|
|
//
|
|
RegKind = (REGS_ENUM)-1;
|
|
}
|
|
|
|
if (RegKind != -1)
|
|
{
|
|
//
|
|
// send the request
|
|
//
|
|
if (g_IsSerialConnectedToRemoteDebuggee ||
|
|
(g_ActiveProcessDebuggingState.IsActive && g_ActiveProcessDebuggingState.IsPaused))
|
|
{
|
|
HyperDbgRegisterShowTargetRegister(RegKind);
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("err, reading registers (r) is not valid in the current "
|
|
"context, you should connect to a debuggee or attach to a process\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("err, invalid register\n");
|
|
}
|
|
}
|
|
|
|
//
|
|
// if command contains a '=' means user wants modify the register
|
|
//
|
|
else if (Command.find('=', 0) != string::npos)
|
|
{
|
|
Command.erase(0, 1);
|
|
Tmp = Split(Command, '=');
|
|
if (Tmp.size() == 2)
|
|
{
|
|
ReplaceAll(Tmp[0], " ", "");
|
|
string tmp = Tmp[0];
|
|
if (RegistersMap.find(Tmp[0]) != RegistersMap.end())
|
|
{
|
|
RegKind = RegistersMap[Tmp[0]];
|
|
}
|
|
else
|
|
{
|
|
ReplaceAll(tmp, "@", "");
|
|
if (RegistersMap.find(tmp) != RegistersMap.end())
|
|
{
|
|
RegKind = RegistersMap[tmp];
|
|
}
|
|
else
|
|
{
|
|
RegKind = (REGS_ENUM)-1;
|
|
}
|
|
}
|
|
if (RegKind != -1)
|
|
{
|
|
//
|
|
// send the request
|
|
//
|
|
SetRegisterValue = "@" + tmp + '=' + Tmp[1] + "; ";
|
|
|
|
//
|
|
// Send data to the target user debugger or kernel debugger
|
|
//
|
|
ScriptEngineExecuteSingleExpression((CHAR *)SetRegisterValue.c_str(), TRUE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// error
|
|
//
|
|
ShowMessages("err, invalid register\n");
|
|
}
|
|
}
|
|
}
|
|
}
|