Skip to content

Commit 17ed017

Browse files
committed
Added basic debugger.
1 parent 9dd9135 commit 17ed017

16 files changed

+1029
-29
lines changed

Debugger/HardwareBreakpoint.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System;
2+
using ReClassNET.Memory;
3+
4+
namespace ReClassNET.Debugger
5+
{
6+
public enum HardwareBreakpointRegister
7+
{
8+
InvalidRegister,
9+
10+
Dr0,
11+
Dr1,
12+
Dr2,
13+
Dr3
14+
}
15+
16+
public enum HardwareBreakpointType
17+
{
18+
Access,
19+
ReadWrite,
20+
Write,
21+
}
22+
23+
public enum HardwareBreakpointSize
24+
{
25+
Size1,
26+
Size2,
27+
Size4,
28+
Size8
29+
}
30+
31+
public sealed class HardwareBreakpoint : IBreakpoint
32+
{
33+
public IntPtr Address { get; }
34+
public HardwareBreakpointRegister Register { get; }
35+
public HardwareBreakpointType Type { get; }
36+
public HardwareBreakpointSize Size { get; }
37+
38+
public HardwareBreakpoint(IntPtr address, HardwareBreakpointRegister register, HardwareBreakpointType type, HardwareBreakpointSize size)
39+
{
40+
if (register == HardwareBreakpointRegister.InvalidRegister)
41+
{
42+
throw new InvalidOperationException();
43+
}
44+
45+
Address = address;
46+
Register = register;
47+
Type = type;
48+
Size = size;
49+
}
50+
51+
public bool Set(RemoteProcess process)
52+
{
53+
return process.NativeHelper.DebuggerSetHardwareBreakpoint(process.UnderlayingProcess.Id, this, true);
54+
}
55+
56+
public void Remove(RemoteProcess process)
57+
{
58+
process.NativeHelper.DebuggerSetHardwareBreakpoint(process.UnderlayingProcess.Id, this, false);
59+
}
60+
61+
public override bool Equals(object obj)
62+
{
63+
var hwbp = obj as HardwareBreakpoint;
64+
if (hwbp == null)
65+
{
66+
return false;
67+
}
68+
69+
// Two hardware breakpoints are equal if the address and type are equal.
70+
return Address == hwbp.Address && Type == hwbp.Type;
71+
}
72+
73+
public override int GetHashCode()
74+
{
75+
return (Address.GetHashCode() * 17) ^ Type.GetHashCode();
76+
}
77+
}
78+
}

Debugger/IBreakpoint.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Diagnostics.Contracts;
3+
using ReClassNET.Memory;
4+
5+
namespace ReClassNET.Debugger
6+
{
7+
public interface IBreakpoint
8+
{
9+
IntPtr Address { get; }
10+
11+
bool Set(RemoteProcess process);
12+
void Remove(RemoteProcess process);
13+
}
14+
15+
[ContractClassFor(typeof(IBreakpoint))]
16+
internal abstract class IBreakpointContract : IBreakpoint
17+
{
18+
public IntPtr Address
19+
{
20+
get
21+
{
22+
throw new NotImplementedException();
23+
}
24+
}
25+
26+
public void Remove(RemoteProcess process)
27+
{
28+
Contract.Requires(process != null);
29+
30+
throw new NotImplementedException();
31+
}
32+
33+
public bool Set(RemoteProcess process)
34+
{
35+
Contract.Requires(process != null);
36+
37+
throw new NotImplementedException();
38+
}
39+
}
40+
}

Debugger/RemoteDebugger.cs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics.Contracts;
4+
using ReClassNET.Memory;
5+
using ReClassNET.Native;
6+
7+
namespace ReClassNET.Debugger
8+
{
9+
public class RemoteDebugger
10+
{
11+
private readonly object sync = new object();
12+
13+
private readonly RemoteProcess process;
14+
15+
private readonly HashSet<IBreakpoint> breakpoints = new HashSet<IBreakpoint>();
16+
17+
public bool IsAttached { get; private set; }
18+
19+
public RemoteDebugger(RemoteProcess process)
20+
{
21+
Contract.Requires(process != null);
22+
23+
this.process = process;
24+
}
25+
26+
public bool Attach()
27+
{
28+
if (!process.IsValid)
29+
{
30+
return false;
31+
}
32+
33+
lock (sync)
34+
{
35+
if (IsAttached)
36+
{
37+
throw new InvalidOperationException();
38+
}
39+
40+
IsAttached = process.NativeHelper.DebuggerAttachToProcess(process.UnderlayingProcess.Id);
41+
42+
return IsAttached;
43+
}
44+
}
45+
46+
public void Detach()
47+
{
48+
lock (sync)
49+
{
50+
if (!IsAttached)
51+
{
52+
return;
53+
}
54+
55+
foreach (var bp in breakpoints)
56+
{
57+
bp.Remove(process);
58+
}
59+
breakpoints.Clear();
60+
61+
process.NativeHelper.DebuggerDetachFromProcess(process.UnderlayingProcess.Id);
62+
63+
IsAttached = false;
64+
}
65+
}
66+
67+
public bool WaitForDebugEvent(ref DebugEvent e)
68+
{
69+
return process.NativeHelper.DebuggerWaitForDebugEvent(ref e);
70+
}
71+
72+
public void ContinueEvent(ref DebugEvent e)
73+
{
74+
process.NativeHelper.DebuggerContinueEvent(ref e);
75+
}
76+
77+
public bool SetBreakpoint(IBreakpoint bp)
78+
{
79+
Contract.Requires(bp != null);
80+
81+
lock (sync)
82+
{
83+
if (!breakpoints.Add(bp))
84+
{
85+
return false;
86+
}
87+
88+
bp.Set(process);
89+
}
90+
91+
return true;
92+
}
93+
94+
public void RemoveBreakpoint(IBreakpoint bp)
95+
{
96+
Contract.Requires(bp != null);
97+
98+
lock (sync)
99+
{
100+
if (breakpoints.Remove(bp))
101+
{
102+
bp.Remove(process);
103+
}
104+
}
105+
}
106+
}
107+
}

Debugger/SoftwareBreakpoint.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using ReClassNET.Memory;
3+
4+
namespace ReClassNET.Debugger
5+
{
6+
public sealed class SoftwareBreakpoint : IBreakpoint
7+
{
8+
public IntPtr Address { get; }
9+
10+
private byte orig;
11+
12+
public SoftwareBreakpoint(IntPtr address)
13+
{
14+
Address = address;
15+
}
16+
17+
public bool Set(RemoteProcess process)
18+
{
19+
var temp = new byte[1];
20+
if (!process.ReadRemoteMemoryIntoBuffer(Address, ref temp))
21+
{
22+
return false;
23+
}
24+
orig = temp[0];
25+
26+
return process.WriteRemoteMemory(Address, new byte[] { 0xCC });
27+
}
28+
29+
public void Remove(RemoteProcess process)
30+
{
31+
process.WriteRemoteMemory(Address, new byte[] { orig });
32+
}
33+
34+
public override bool Equals(object obj)
35+
{
36+
var bp = obj as SoftwareBreakpoint;
37+
if (bp == null)
38+
{
39+
return false;
40+
}
41+
42+
return Address == bp.Address;
43+
}
44+
45+
public override int GetHashCode()
46+
{
47+
return Address.GetHashCode();
48+
}
49+
}
50+
}

Memory/RemoteProcess.cs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,22 @@
88
using System.Threading;
99
using System.Threading.Tasks;
1010
using ReClassNET.AddressParser;
11+
using ReClassNET.Debugger;
1112
using ReClassNET.Native;
1213
using ReClassNET.Symbols;
1314
using ReClassNET.Util;
1415

1516
namespace ReClassNET.Memory
1617
{
18+
public delegate void UnderlayingProcessChangedEvent(RemoteProcess sender);
19+
1720
public class RemoteProcess : IDisposable
1821
{
1922
private readonly object processSync = new object();
2023

2124
private readonly NativeHelper nativeHelper;
22-
public NativeHelper NativeHelper => nativeHelper;
2325

24-
private ProcessInfo process;
25-
private IntPtr handle;
26-
public ProcessInfo UnderlayingProcess => process;
27-
28-
public delegate void RemoteProcessChangedEvent(RemoteProcess sender);
29-
public event RemoteProcessChangedEvent ProcessChanged;
26+
private readonly RemoteDebugger debugger;
3027

3128
private readonly Dictionary<IntPtr, string> rttiCache = new Dictionary<IntPtr, string>();
3229

@@ -35,6 +32,18 @@ public class RemoteProcess : IDisposable
3532
private readonly List<Section> sections = new List<Section>();
3633

3734
private readonly SymbolStore symbols = new SymbolStore();
35+
36+
private ProcessInfo process;
37+
private IntPtr handle;
38+
39+
public event UnderlayingProcessChangedEvent ProcessChanged;
40+
41+
public NativeHelper NativeHelper => nativeHelper;
42+
43+
public RemoteDebugger Debugger => debugger;
44+
45+
public ProcessInfo UnderlayingProcess => process;
46+
3847
public SymbolStore Symbols => symbols;
3948

4049
public bool IsValid => process != null && nativeHelper.IsProcessValid(handle);
@@ -44,13 +53,17 @@ public RemoteProcess(NativeHelper nativeHelper)
4453
Contract.Requires(nativeHelper != null);
4554

4655
this.nativeHelper = nativeHelper;
56+
57+
debugger = new RemoteDebugger(this);
4758
}
4859

4960
public void Dispose()
5061
{
5162
Close();
5263
}
5364

65+
/// <summary>Opens the given process to gather informations from.</summary>
66+
/// <param name="info">The process information.</param>
5467
public void Open(ProcessInfo info)
5568
{
5669
Contract.Requires(info != null);
@@ -72,13 +85,14 @@ public void Open(ProcessInfo info)
7285
}
7386
}
7487

88+
/// <summary>Closes the underlaying process. If the debugger is attached, it will automaticly detached.</summary>
7589
public void Close()
7690
{
7791
if (process != null)
7892
{
7993
lock (processSync)
8094
{
81-
//detach debugger, remove breakpoints
95+
debugger.Detach();
8296

8397
nativeHelper.CloseRemoteProcess(handle);
8498

0 commit comments

Comments
 (0)