-
Notifications
You must be signed in to change notification settings - Fork 92
/
Copy pathdebugger.js
150 lines (121 loc) · 3.11 KB
/
debugger.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
const EventEmitter = require('events');
const lengths = {
byte: 1,
int: 4,
int32: 4,
uint32: 4,
int64: 8,
uint64: 8,
dword: 4,
short: 2,
long: 8,
float: 4,
double: 8,
bool: 1,
boolean: 1,
ptr: 4,
pointer: 4,
// str: 0,
// string: 0,
// vec3: 0,
// vector3: 0,
// vec4: 0,
// vector4: 0,
};
// Tracks used and unused registers
class Registers {
constructor() {
this.registers = Object.freeze({
DR0: 0x0,
DR1: 0x1,
DR2: 0x2,
DR3: 0x3,
});
this.used = [];
}
getRegister() {
const unused = Object
.values(this.registers)
.filter(r => !this.used.includes(r));
return unused[0];
}
busy(register) {
this.used.push(register);
}
unbusy(register) {
this.used.splice(this.used.indexOf(register), 1);
}
}
class Debugger extends EventEmitter {
constructor(memoryjs) {
super();
this.memoryjs = memoryjs;
this.registers = new Registers();
this.attached = false;
this.intervals = [];
}
attach(processId, killOnDetach = false) {
const success = this.memoryjs.attachDebugger(processId, killOnDetach);
if (success) {
this.attached = true;
}
return success;
}
detach(processId) {
this.intervals.map(({ id }) => clearInterval(id));
return this.memoryjs.detachDebugger(processId);
}
removeHardwareBreakpoint(processId, register) {
const success = this.memoryjs.removeHardwareBreakpoint(processId, register);
if (success) {
this.registers.unbusy(register);
}
// Find the register's corresponding interval and delete it
this.intervals.forEach(({ register: r, id }) => {
if (r === register) {
clearInterval(id);
}
});
return success;
}
setHardwareBreakpoint(processId, address, trigger, dataType) {
let size = lengths[dataType];
// If we are breakpointing a string, we need to determine the length of it
if (dataType === 'str' || dataType === 'string') {
const { handle } = this.memoryjs.openProcess(processId);
const value = this.memoryjs.readMemory(handle, address, this.memoryjs.STRING);
size = value.length;
this.memoryjs.closeProcess(handle);
}
// Obtain an available register
const register = this.registers.getRegister();
const success = this.memoryjs
.setHardwareBreakpoint(processId, address, register, trigger, size);
// If the breakpoint was set, mark this register as busy
if (success) {
this.registers.busy(register);
this.monitor(register);
}
return register;
}
monitor(register, timeout = 100) {
const id = setInterval(() => {
const debugEvent = this.memoryjs.awaitDebugEvent(register, timeout);
if (debugEvent) {
this.memoryjs.handleDebugEvent(debugEvent.processId, debugEvent.threadId);
// Global event for all registers
this.emit('debugEvent', {
register,
event: debugEvent,
});
// Event per register
this.emit(register, debugEvent);
}
}, 100);
this.intervals.push({
register,
id,
});
}
}
module.exports = Debugger;