unit debug;

{$MODE Delphi}

interface

{$ifdef windows}

uses windows, sysutils, dbk32functions, classes, multicpuexecution;

type TDebuggerstate=packed record
  threadid: uint64;
  causedbydbvm: uint64;
	eflags : uint64;
	eax : uint64;
	ebx : uint64;
	ecx : uint64;
	edx : uint64;
	esi : uint64;
	edi : uint64;
	ebp : uint64;
	esp : uint64;
	eip : uint64;
	r8  : uint64;
	r9  : uint64;
	r10 : uint64;
	r11 : uint64;
	r12 : uint64;
	r13 : uint64;
	r14 : uint64;
	r15 : uint64;
	cs  : uint64;
	ds  : uint64;
	es  : uint64;
	fs  : uint64;
	gs  : uint64;
	ss  : uint64;
  dr0 : uint64;
  dr1 : uint64;
  dr2 : uint64;
  dr3 : uint64;
  dr6 : uint64;
  dr7 : uint64;
  fxstate: array[0..511] of byte;
  LBR_Count: uint64;
  LBR: array [0..15] of UINT64;

end;
type PDebuggerstate=^TDebuggerstate;

type TBreakType=(bt_OnInstruction=0,bt_OnWrites=1, bt_OnIOAccess=2, bt_OnReadsAndWrites=3);
type TBreakLength=(bl_1byte=0, bl_2byte=1, bl_8byte=2{Only when in 64-bit}, bl_4byte=3);

function DBKDebug_ContinueDebugEvent(handled: BOOL): boolean; stdcall;
function DBKDebug_WaitForDebugEvent(timeout: dword): boolean; stdcall;
function DBKDebug_GetDebuggerState(state: PDebuggerstate): boolean; stdcall;
function DBKDebug_SetDebuggerState(state: PDebuggerstate): boolean; stdcall;

function DBKDebug_SetGlobalDebugState(state: BOOL): BOOL; stdcall;
function DBKDebug_StartDebugging(processid:dword):BOOL; stdcall;
function DBKDebug_StopDebugging:BOOL; stdcall;
function DBKDebug_GD_SetBreakpoint(active: BOOL; debugregspot: integer; Address: ptruint; breakType: TBreakType; breakLength: TBreakLength): BOOL; stdcall;
function DBKDebug_SetAbilityToStepKernelCode(state: boolean):BOOL; stdcall;
procedure DBKDebug_SetStoreLBR(state: BOOL);

{$endif}

implementation

{$ifdef windows}



function hookints: BOOL; stdcall;
var cc,br: dword;
begin
  if hdevice<>INVALID_HANDLE_VALUE then
  begin
    cc:=IOCTL_CE_HOOKINTS;
    result:=deviceiocontrol(hdevice,cc,nil,0,nil,0,br,nil);
  end else result:=false;
  
end;

var KernelDebugStarted: boolean;
function StartCEKernelDebug:BOOL; stdcall;
begin
  outputdebugstring('StartCEKernelDebug');
  if not KernelDebugStarted then
    KernelDebugStarted:=hookints;

  result:=KernelDebugStarted;
end;

function internal_SetGlobalDebugState(state: pointer): BOOL; stdcall;
var
  x: BOOL;
  br,cc: dword;
begin
  outputdebugstring('SetGlobalDebugState');
  x:=PBOOL(state)^;

  result:=false;
  if hdevice<>INVALID_HANDLE_VALUE then
  begin
    cc:=IOCTL_CE_SETGLOBALDEBUGSTATE;
    result:=deviceiocontrol(hdevice,cc,@x,sizeof(x),nil,0,br,nil);
  end;
end;

function DBKDebug_SetGlobalDebugState(state: BOOL): BOOL; stdcall;
begin
  result:=foreachcpu(internal_SetGlobalDebugState, @state);
end;

function internal_touchdebugregister(parameters: pointer): BOOL; stdcall;
var
  br,cc: dword;
begin
  result:=false;
  if hdevice<>INVALID_HANDLE_VALUE then
  begin
    cc:=IOCTL_CE_TOUCHDEBUGREGISTER;
    result:=deviceiocontrol(hdevice,cc,nil,0,nil,0,br,nil);
  end;
end;

procedure DBKDebug_TouchDebugRegister;
//this routine touches the debug registers on each cpu
//when global debug is enabled this facilitates in setting or unsetting changes in the breakpoint list
//this way when a breakpoint is set, it actually gets set, or unset the same
//just make sure to disable the breakpoint before removing the handler
begin
  OutputDebugString('DBKDebug_TouchDebugRegister');
  foreachcpu(internal_touchdebugregister,nil);
end;

function DBKDebug_GD_SetBreakpoint(active: BOOL; debugregspot: integer; Address: ptruint; breakType: TBreakType; breakLength: TBreakLength): BOOL; stdcall;
var
  input: record
    active: BOOL;
    debugregspot: integer;
    address: QWORD;
    breaktype: DWORD;
    breakLength: DWORD;
  end;

  br,cc: dword;
begin
  OutputDebugString('DBKDebug_GD_SetBreakpoint');
  if not active then OutputDebugString('Deactivating breakpoint');

  if hdevice<>INVALID_HANDLE_VALUE then
  begin
    result:=StartCEKernelDebug;
    input.active:=active;
    input.debugregspot:=debugregspot;
    input.address:=address;
    input.breaktype:=dword(breaktype);
    input.breakLength:=dword(breaklength);

    outputdebugstring(pchar(format('sizeof(input)=%d, breaktype=%d breaklength=%d',[sizeof(input), integer(input.breaktype), integer(input.breakLength)])));

    cc:=IOCTL_CE_GD_SETBREAKPOINT;
    result:=result and deviceiocontrol(hdevice,cc,@input,sizeof(input),@input,0,br,nil);
    DBKDebug_TouchDebugRegister; //update the system state
  end else result:=false;
end;


function DBKDebug_StartDebuggingInternal(processid: pointer):BOOL; stdcall;
type Tinput=record
  ProcessID:DWORD;
end;
var input:TInput;
    br,cc: dword;
begin
  if hdevice<>INVALID_HANDLE_VALUE then
  begin
    result:=StartCEKernelDebug;
    input.Processid:=PDWORD(processid)^;
    cc:=IOCTL_CE_DEBUGPROCESS;
    result:=result and deviceiocontrol(hdevice,cc,@input,sizeof(input),@input,0,br,nil);
  end else result:=false;
end;





function DBKDebug_StartDebugging(processid:dword):BOOL; stdcall;
begin
  result:=foreachcpu(DBKDebug_StartDebuggingInternal, @processid);
end;

function internal_StopDebugging(parameters: pointer):BOOL; stdcall;
var x,cc: dword;
begin
  outputdebugstring('DBK32: StopDebugging called');
  result:=false;
  if hdevice<>INVALID_HANDLE_VALUE then
  begin
    cc:=IOCTL_CE_STOPDEBUGGING;
    result:=deviceiocontrol(hdevice,cc,nil,0,nil,0,x,nil);
  end;
end;

function DBKDebug_StopDebugging:BOOL; stdcall;
begin
  result:=foreachcpu(internal_StopDebugging,nil);
end;

function DBKDebug_GetDebuggerState(state: PDebuggerstate): boolean; stdcall;
var
  Output: TDebuggerstate;
  cc: dword;
begin
  FillMemory(state,sizeof(TDebuggerState),1);
  
  OutputDebugString('DBKDebug_GetDebuggerState');
  result:=false;
  if (hdevice<>INVALID_HANDLE_VALUE) then
  begin
    cc:=IOCTL_CE_GETDEBUGGERSTATE;
    result:=deviceiocontrol(hdevice,cc,nil,0,@output,sizeof(output),cc,nil);
    if result then
    begin
      OutputDebugString('result = true');
      state^:=output;
    end;

//    state.fxstate
  end;
end;

function DBKDebug_SetDebuggerState(state: PDebuggerstate): boolean; stdcall;
var
  input: TDebuggerstate;
  cc: dword;
begin
  OutputDebugString('DBKDebug_SetDebuggerState');
  result:=false;
  if (hdevice<>INVALID_HANDLE_VALUE) then
  begin
    cc:=IOCTL_CE_SETDEBUGGERSTATE;
    input:=state^;
    result:=deviceiocontrol(hdevice,cc,@input,sizeof(Input),nil,0,cc,nil);
  end;
end;


function DBKDebug_ContinueDebugEvent(handled: BOOL): boolean; stdcall;
var
  cc: dword;
  Input: record
    handled: BOOL;
  end;
begin
  result:=false;
  input.handled:=handled;
  if (hdevice<>INVALID_HANDLE_VALUE) then
  begin
    cc:=IOCTL_CE_CONTINUEDEBUGEVENT;
    result:=deviceiocontrol(hdevice,cc,@Input,sizeof(input),nil,0,cc,nil);
  end;
end;

function DBKDebug_WaitForDebugEvent(timeout: dword): boolean; stdcall;
type TInput=record
  Timeout: DWORD;
end;
var cc: dword;
    x: TInput;
begin
  result:=false;
  x.timeout:=timeout;

  if (hdevice<>INVALID_HANDLE_VALUE) then
  begin
    cc:=IOCTL_CE_WAITFORDEBUGEVENT;
    result:=deviceiocontrol(hdevice,cc,@x,sizeof(x),nil,0,cc,nil);
  end;
end;

function DBKDebug_SetAbilityToStepKernelCode(state: boolean):BOOL; stdcall;
type Tinput=record
  state: integer;
end;
var input:TInput;
    br,cc: dword;
begin

  if hdevice<>INVALID_HANDLE_VALUE then
  begin
    result:=false;
    if state then input.state:=1 else input.state:=0;


    cc:=IOCTL_CE_SETKERNELSTEPABILITY;
    result:=deviceiocontrol(hdevice,cc,@input,sizeof(input),nil,0,br,nil);
  end else result:=false;
end;

procedure DBKDebug_SetStoreLBR(state: BOOL);
var br,cc: dword;
begin
  outputdebugstring(pchar('DBKDebug_SetStoreLBR('+BoolToStr(state,'true','false')+')'));
  if hdevice<>INVALID_HANDLE_VALUE then
  begin
    cc:=IOCTL_CE_SETSTORELBR;
    deviceiocontrol(hdevice,cc,@state,sizeof(state),nil,0,br,nil);
  end;
end;

{$endif}

end.
