-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdw-disassembly.c
623 lines (526 loc) · 26.9 KB
/
dw-disassembly.c
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
#define _GNU_SOURCE
#include "dw-disassembly.h"
#include "dw-log.h"
#include "dw-protect.h"
#include "dw-registers.h"
#include <capstone/capstone.h>
#include <sys/mman.h>
#include <sys/user.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
// When an instruction accesses a protected object, we need to create an entry to
// tell us the affected registers, a buffer to emulate the instruction, and an
// epilogue to reprotect the registers if needed.
struct insn_table {
size_t size;
struct insn_entry *entries;
csh handle;
cs_insn *insn;
};
// Allocate the instruction hash table and initialize libcapstone
instruction_table*
dw_init_instruction_table(size_t size)
{
instruction_table *table = malloc(sizeof(instruction_table));
table->size = 2 * size - 1; // have a hash table about twice as large, and a power of two -1
table->entries = calloc(sizeof(struct insn_entry), table->size);
cs_err csres = cs_open(CS_ARCH_X86, CS_MODE_64, &(table->handle));
if(csres != CS_ERR_OK) dw_log(ERROR, DISASSEMBLY, "cs_open failed, returned %d\n", csres);
csres = cs_option(table->handle, CS_OPT_DETAIL, CS_OPT_ON);
table->insn = cs_malloc(table->handle);
return table;
}
// Deallocate the instruction hash table and close libcapstone
void
dw_fini_instruction_table(instruction_table *table) {
free(table->entries);
cs_free(table->insn, 1);
cs_close(&(table->handle));
free(table);
}
// Get the entry for this instruction address
struct insn_entry*
dw_get_instruction_entry(instruction_table *table, uintptr_t fault)
{
size_t hash = fault % table->size;
size_t cursor = hash;
while((void *)table->entries[cursor].insn != NULL) {
if(table->entries[cursor].insn == fault) return &(table->entries[cursor]);
cursor = (cursor + 1) % table->size;
if(cursor == hash) break;
}
return NULL;
}
// Is that register in the list or registers modified by the instruction?
// There is a lot of aliasing between registers, e.g. al, ax, eax, rax,
// all refer to the same register or portion thereof.
static bool
dw_reg_written(struct insn_entry *entry, unsigned reg)
{
if(reg == X86_REG_INVALID) return false;
for(int i = 0; i < entry->gregs_write_count; i++)
if(dw_get_reg_entry(entry->gregs_written[i])->ucontext_index == dw_get_reg_entry(reg)->ucontext_index)
return true;
return false;
}
static bool
dw_reg_si_di(struct insn_entry *entry, unsigned reg)
{
if((reg == X86_REG_RSI || reg == X86_REG_RDI) && entry->repeat) return true;
else return false;
}
// Create a new entry for this instruction address
#define UNW_LOCAL_ONLY
#include <libunwind.h>
struct insn_entry*
dw_create_instruction_entry(instruction_table *table, uintptr_t fault, uintptr_t *next, ucontext_t *uctx)
{
size_t hash = fault % table->size;
size_t cursor = hash;
while((void *)table->entries[cursor].insn != NULL) {
if(table->entries[cursor].insn == fault) dw_log(ERROR, DISASSEMBLY, "Trying to add existing instruction in hash table\n");
cursor = (cursor + 1) % table->size;
if(cursor == hash) dw_log(ERROR, DISASSEMBLY, "Instruction hash table full\n");
}
// We insert the new entry at the first empty location following the hash code index
table->entries[cursor].insn = fault;
struct insn_entry *entry = &(table->entries[cursor]);
size_t sizeds = 100;
const uint8_t *code = (uint8_t *)fault;
uint64_t instr_addr = (uint64_t) fault;
unsigned reg, base, index;
uintptr_t addr, scale, displacement, base_addr = 0, index_addr = 0;
unsigned i, j;
unsigned arg_m = 0, arg_r = 0;
bool success;
int error_code;
struct reg_entry *re;
// Disassemble the instruction with Capstone
success = cs_disasm_iter(table->handle, &code , &sizeds, &instr_addr, table->insn);
error_code = cs_errno(table->handle);
if(!success) dw_log(ERROR, DISASSEMBLY, "Capstone cannot decode instruction 0x%llx, error %d\n", fault, error_code);
// Get the symbol of the containing function, to help in debugging
unw_cursor_t cur;
unw_context_t context;
unw_getcontext(&context);
unw_init_local(&cur, &context);
unw_word_t offset;
unw_word_t pc = fault;
unw_set_reg(&cur, UNW_REG_IP, pc);
char proc_name[256];
if (unw_get_proc_name(&cur, proc_name, sizeof(proc_name), &offset) != 0) {
strcpy(proc_name, "-- no symbol --");
offset = 0;
}
entry->insn_length = table->insn->size;
entry->next_insn = *next = instr_addr;
entry->post_handler = true;
snprintf(entry->disasm_insn, sizeof(entry->disasm_insn), "%.11s %.51s", table->insn->mnemonic, table->insn->op_str);
code = (uint8_t *)fault;
char insn_code[256], *c = insn_code;
int ret, length = 256;
for(int i = 0; i < entry->insn_length; i++) {
ret = snprintf(c, length, "%02x ", *code);
c += ret; length -= ret; code++;
}
// This is info, not a warning, but we need it for debug purposes for now
dw_log(WARNING, DISASSEMBLY, "Instruction 0x%llx (%s+0x%lx), entry %lu (%llx + %lx * %lx = %llx), %d, %d, 0x%lx: %s %s, (%hu) %s\n",
fault, proc_name, offset, cursor, table->entries, cursor, sizeof(struct insn_entry), entry, success,
error_code, table->insn->address, table->insn->mnemonic, table->insn->op_str, table->insn->size,
insn_code);
cs_detail *detail = table->insn->detail;
cs_x86 *x86 = &(detail->x86);
// On control transfer instructions, the post handler hooks do not work.
// Thus, the tainted pointer will not be retainted after the execution.
// This is not a problem if the register value is not reused. Otherwise,
// a subsequent access may not be checked, or the program logic may be
// compromised if the untainted pointer is compared with a tainted pointer.
if(detail->groups_count > 0) {
for(i = 0; i < detail->groups_count; i++) {
if(detail->groups[i] < X86_GRP_VM) {
dw_log(WARNING, DISASSEMBLY, "Trap on control transfer instruction, post handler cannot be used %llx\n", fault);
entry->post_handler = false;
break;
}
}
}
cs_regs regs_read, regs_write;
uint8_t read_count = 0, write_count = 0;
error_code = cs_regs_access(table->handle, table->insn, regs_read, &read_count, regs_write, &write_count);
if(error_code != CS_ERR_OK) dw_log(ERROR, DISASSEMBLY, "Capstone cannot give register accesses\n");
if(read_count > MAX_MOD_REG) dw_log(ERROR, DISASSEMBLY, "More registers read %d than expected %d\n", read_count, MAX_MOD_REG);
if(write_count > MAX_MOD_REG) dw_log(ERROR, DISASSEMBLY, "More registers written %d than expected %d\n", write_count, MAX_MOD_REG);
entry->gregs_read_count = read_count;
entry->gregs_write_count = write_count;
if(x86->prefix[0] == X86_PREFIX_REP || x86->prefix[0] == X86_PREFIX_REPE || x86->prefix[0] == X86_PREFIX_REPNE)
entry->repeat = true;
else entry->repeat = false;
for (i = 0; i < min(read_count, MAX_MOD_REG); i++) {
reg = entry->gregs_read[i] = regs_read[i];
dw_log(INFO, DISASSEMBLY, "read: %s; (%d)\n", cs_reg_name(table->handle, reg), reg);
}
for (i = 0; i < min(write_count, MAX_MOD_REG); i++) {
reg = entry->gregs_written[i] = regs_write[i];
dw_log(INFO, DISASSEMBLY, "write: %s; (%d)\n", cs_reg_name(table->handle, reg), reg);
}
// Loop over all the instruction arguments
unsigned nb_protected = 0;
for (i = 0; i < x86->op_count; i++){
switch(x86->operands[i].type) {
// We need to know the overwritten registers to avoid retainting them
case X86_OP_REG:
reg = x86->operands[i].reg;
re = dw_get_reg_entry(reg);
if(re->ucontext_index >= 0) {
if(arg_r >= MAX_REG_ARG) dw_log(ERROR, DISASSEMBLY, "Too many destination register arguments\n");
entry->arg_r[arg_r].reg = reg;
entry->arg_r[arg_r].length = re->size;
entry->arg_r[arg_r].access = x86->operands[i].access;
arg_r++;
}
dw_log(INFO, DISASSEMBLY, "Register operand %lu, reg %s, access %hhu\n", i, cs_reg_name(table->handle, x86->operands[i].reg), x86->operands[i].access);
break;
// The memory address is given by base + (index * scale) + displacement
case X86_OP_MEM:
if(arg_m >= MAX_MEM_ARG) dw_log(ERROR, DISASSEMBLY, "Too many memory arguments\n");
// Check if we have a base register and if it is a general purpose register
entry->arg_m[arg_m].base = base = x86->operands[i].mem.base;
if(base == X86_REG_INVALID) base_addr = 0; // no base register
else {
re = dw_get_reg_entry(base);
if(re->ucontext_index < 0)
dw_log(ERROR, DISASSEMBLY, "Base register %s not general register\n", re->name);
else base_addr = dw_get_register(uctx, re->ucontext_index);
}
// Check if we have an index register and if it is a general purpose register
entry->arg_m[arg_m].index = index = x86->operands[i].mem.index;
if(index == X86_REG_INVALID) index_addr = 0;
else {
re = dw_get_reg_entry(index);
if(re->ucontext_index < 0)
dw_log(ERROR, DISASSEMBLY, "Index register %s not general register\n", re->name);
else index_addr = dw_get_register(uctx, re->ucontext_index);
}
entry->arg_m[arg_m].scale = scale = x86->operands[i].mem.scale;
entry->arg_m[arg_m].displacement = displacement = x86->operands[i].mem.disp;
entry->arg_m[arg_m].length = x86->operands[i].size;
entry->arg_m[arg_m].access = x86->operands[i].access;
addr = base_addr + (index_addr * scale) + displacement;
entry->arg_m[arg_m].base_taint = entry->arg_m[arg_m].index_taint = 0;
dw_log(INFO, DISASSEMBLY,
"Memory operand %lu, segment %d, base %s (0x%llx) + (index %s (0x%llx) x scale 0x%llx) + disp 0x%llx = 0x%llx, access %hhu\n", i,
x86->operands[i].mem.segment, cs_reg_name(table->handle, base), base_addr, cs_reg_name(table->handle, index), index_addr, scale, displacement, addr, x86->operands[i].access);
// Check that the segmentation violation is related to a tainted pointer
if(dw_is_protected((void *)base_addr)) {
nb_protected++;
entry->arg_m[arg_m].base_taint = base_addr;
if(dw_is_protected_index((void *)index_addr))
dw_log(ERROR, DISASSEMBLY,"Both base and index registers are protected\n");
}
else if(dw_is_protected_index((void *)index_addr)) {
nb_protected++;
entry->arg_m[arg_m].index_taint = index_addr;
}
arg_m++;
break;
case X86_OP_IMM:
dw_log(INFO, DISASSEMBLY, "Immediate operand %lu, value %lu\n", i, x86->operands[i].imm);
break;
default:
dw_log(INFO, DISASSEMBLY, "Invalid operand %lu\n", i);
break;
}
}
entry->nb_arg_m = arg_m;
entry->nb_arg_r = arg_r;
for(i = 0; i < arg_m; i++) {
// We need to retaint the register unless it is overwritten by the instruction
entry->arg_m[i].base_access = entry->arg_m[i].index_access = 0; // CS_AC_INVALID
// Check if the base or index register, that may become tainted, is also a register argument
for(j = 0; j < arg_r; j++) {
if(dw_get_reg_entry(entry->arg_r[j].reg)->ucontext_index ==
dw_get_reg_entry(entry->arg_m[i].base)->ucontext_index) {
entry->arg_m[i].base_access = entry->arg_r[j].access;
if(entry->arg_m[arg_m].access == (CS_AC_READ | CS_AC_WRITE))
dw_log(WARNING, DISASSEMBLY, "Memory argument is unexpectedly read and written\n");
if(arg_r != 1) dw_log(WARNING, DISASSEMBLY, "More than one register argument, may be ambiguous\n");
}
if(dw_get_reg_entry(entry->arg_r[j].reg)->ucontext_index ==
dw_get_reg_entry(entry->arg_m[i].index)->ucontext_index) {
entry->arg_m[i].index_access = entry->arg_r[j].access;
if(entry->arg_m[arg_m].access == (CS_AC_READ | CS_AC_WRITE))
dw_log(WARNING, DISASSEMBLY, "Memory argument is unexpectedly read and written\n");
if(arg_r != 1) dw_log(WARNING, DISASSEMBLY, "More than one register argument, may be ambiguous\n");
}
}
// Registers rsi and rdi are auto-incremented for some instructions with the rep prefix.
// This is accounted for by reapplying the taint, not restoring the saved register value.
// Here we check if there exists other cases apart from rsi and rdi with rep instructions.
if(dw_reg_written(entry, entry->arg_m[i].base) && (entry->arg_m[i].base_access & CS_AC_WRITE) == 0 &&
!dw_reg_si_di(entry, entry->arg_m[i].base))
dw_log(WARNING, DISASSEMBLY, "Instruction 0x%llx, base register %s implicitly modified\n",
entry->insn, dw_get_reg_entry(entry->arg_m[i].base)->name);
if(dw_reg_written(entry, entry->arg_m[i].index) && (entry->arg_m[i].index_access & CS_AC_WRITE) == 0 &&
!dw_reg_si_di(entry, entry->arg_m[i].index))
dw_log(WARNING, DISASSEMBLY, "Instruction 0x%llx, index register %s implicitly modified\n",
entry->insn, dw_get_reg_entry(entry->arg_m[i].index)->name);
}
if(nb_protected == 0) dw_log(ERROR, DISASSEMBLY,"No protected memory argument but generates a fault\n");
return entry;
}
static void
check_patch(patch_status s, char *msg)
{
if(s == PATCH_OK) return;
struct patch_error e;
patch_last_error(&e);
dw_log(WARNING, DISASSEMBLY, "Patch lib return value not OK, %d, for %s, origin %s, irritant %s, message %s\n", s, msg, e.origin, e.irritant, e.message);
}
void
dw_patch_init()
{
const struct patch_option options[] = {{.type = PATCH_OPT_ENABLE_WXE, .enable_wxe = 0}};
(void)patch_init(options, sizeof(options) / sizeof(struct patch_option));
}
// Patch the instruction accessing a protected object and attach a pre and post handler
// to unprotect and reprotect the tainted registers
bool
dw_instruction_entry_patch(struct insn_entry *entry, enum dw_strategies strategy, dw_patch_probe patch_handler)
{
struct patch_location location = {
.type = PATCH_LOCATION_ADDRESS,
.direction = PATCH_LOCATION_FORWARD,
.algorithm = PATCH_LOCATION_FIRST,
.address = entry->insn,
};
struct patch_exec_model exec_model = {
.type = PATCH_EXEC_MODEL_PROBE_AROUND_STEP,
.probe.read_registers = 0,
.probe.write_registers = 0,
.probe.clobber_registers = PATCH_REGS_ALL,
.probe.user_data = entry,
.probe.procedure = patch_handler,
};
patch_t patch;
patch_attr attr;
patch_status s;
// On control transfer instructions, post handlers are not available.
if(!(entry->post_handler)) exec_model.type = PATCH_EXEC_MODEL_PROBE;
s = patch_attr_init(&attr); check_patch(s, "attr init");
if(strategy == DW_PATCH_TRAP) {
s= patch_attr_set_trap_policy(&attr, PATCH_TRAP_POLICY_FORCE); check_patch(s, "set policy FORCE");
}
else if(strategy == DW_PATCH_JUMP) {
s= patch_attr_set_trap_policy(&attr, PATCH_TRAP_POLICY_FORBID); check_patch(s, "set policy FORBID");
}
else dw_log(ERROR, DISASSEMBLY, "Unknown patching strategy\n");
s = patch_attr_set_initial_state(&attr, PATCH_ENABLED); check_patch(s, "set enabled");
s = patch_make(&location, &exec_model, &attr, &patch, NULL); check_patch(s, "make"); if(s != PATCH_OK) return false;
s = patch_commit(); check_patch(s, "commit"); if(s != PATCH_OK) return false;
return true;
}
// With PATCH_EXEC_MODEL_AROUND_STEP_TRAP or PATCH_EXEC_MODEL_AROUND_STEP, the SIGSEGV handler will not
// get called, the patch handler (pre and post) will be called instead. It will be called either through
// the target instruction patched by a trap (int3), intercepted by libpatch with their own handler,
// or through the target instruction patched by a jump.
//
// Eventually, to avoid the cost of saving a lot of registers and making a call, we may use
// PATCH_EXEC_MODEL_DIVERT to jump to an OLX buffer that untaints, executes the relocated
// instruction, and retaints in assembly directly.
//
// We save all the relevant registers in a static structure to check if some
// registers are unexpectedly modified by the stepped instruction. This
// structure should be Thread Local Storage for multi-threaded programs.
// Once the algorithm is well tested and debugged, this saving and comparison
// step will be removed.
void
dw_print_regs(struct patch_exec_context *ctx)
{
for(int i = 0; i < dw_nb_saved_registers; i++)
dw_log(INFO, DISASSEMBLY, "%s, %llx\n", dw_get_reg_entry(dw_saved_registers[i])->name, ctx->general_purpose_registers[i]);
}
static bool dw_check_handling = false;
void dw_set_check_handling(bool f) { dw_check_handling = f; }
// A potentially tainted pointer is accessed, unprotect it before the access
// (Check use of unprotect / retaint versus flavors other than OID, single unprotect / reprotect)
void
dw_unprotect_context(struct patch_exec_context *ctx)
{
struct insn_entry *entry = ctx->user_data;
struct reg_entry *re, *reb, *rei;
struct memory_arg *arg;
unsigned i, reg, regb, regi;
uintptr_t valueb, valuei, addr;
if(dw_check_handling) {
dw_log(INFO, DISASSEMBLY, "Unprotect instruction 0x%llx: %s\n", entry->insn, entry->disasm_insn);
dw_print_regs(ctx);
}
// Untaint all possibly tainted memory arguments
for(i = 0; i < entry->nb_arg_m; i++) {
arg = &(entry->arg_m[i]);
regb = arg->base;
reb = dw_get_reg_entry(regb);
if(regb == X86_REG_INVALID) valueb = 0; // no base register
else valueb = dw_get_register(ctx, reb->libpatch_index);
regi = arg->index;
rei = dw_get_reg_entry(regi);
if(regi == X86_REG_INVALID) valuei = 0;
else valuei = dw_get_register(ctx, rei->libpatch_index);
addr = valueb + valuei * arg->scale + arg->displacement;
if(dw_is_protected((void *)valueb)) {
if(arg->base_taint == 0)
dw_log(INFO, DISASSEMBLY, "Newly tainted base for mem arg %d\n", i);
if(dw_is_protected_index((void *)valuei))
dw_log(WARNING, DISASSEMBLY, "Both index and base tainted for mem arg %d\n", i);
arg->base_taint = valueb;
valueb = (uintptr_t)dw_unprotect((void *)valueb);
dw_set_register(ctx, reb->libpatch_index, valueb);
arg->index_taint = 0;
// The base register is a pointer, if less than 8 bytes are read, this is suspicious.
if((arg->base_access & CS_AC_READ) && (arg->length < 8))
dw_log(WARNING, DISASSEMBLY,
"Instruction 0x%llx, base register %s only partially copied to/from memory\n",
entry->insn, dw_get_reg_entry(regb)->name);
}
else if(dw_is_protected_index((void *)valuei)) {
if(arg->index_taint == 0)
dw_log(INFO, DISASSEMBLY, "Newly tainted index for mem arg %d\n", i);
arg->index_taint = valuei;
valuei = (uintptr_t)dw_unprotect((void *)valuei);
dw_set_register(ctx, rei->libpatch_index, valuei);
arg->base_taint = 0;
// The index register is a pointer, if less than 8 bytes are read, this is suspicious.
if((arg->index_access & CS_AC_READ) && (arg->length < 8))
dw_log(WARNING, DISASSEMBLY,
"Instruction 0x%llx, index register %s only partially copied to/from memory\n",
entry->insn, dw_get_reg_entry(regi)->name);
}
else {
arg->base_taint = 0;
arg->index_taint = 0;
continue;
}
// With the computed tainted address, check if the access is valid.
// We take the argument length, and use the repeat count if present.
if(entry->repeat) {
size_t count = dw_get_register(ctx, dw_get_reg_entry(X86_REG_RCX)->libpatch_index);
dw_check_access((void *)addr, arg->length * count);
}
else dw_check_access((void *)addr, arg->length);
// We have a special case, the same register is used to access the memory and as argument.
// - For a memory read, register write, do not retaint the ovewritten register in post handler.
//
// - For a memory read, register read, it is presumably a comparison. We must untaint the register and
// the memory for a proper comparison, and retaint both the register and memory in the post handler.
// We save the memory address and value, and untaint the value. In the post handler
// we retaint the register, and restore the saved value at the saved address
//
// - For a memory write, register read, the untainted register is stored in memory, we should retaint
// both the register and memory in the post handler.
//
// - For a memory write, register write, not sure what to do. Not retaint the register but retaint memory?
if((arg->base_access && arg->base_taint != 0) || (arg->index_access && arg->index_taint != 0)) {
arg->saved_address = valueb + valuei * arg->scale + arg->displacement;
addr = (uintptr_t)dw_unprotect((void *)addr);
if(addr != arg->saved_address)
dw_log(WARNING, DISASSEMBLY, "Both ways to unprotect address differ 0x%llx 0x%llx\n",
addr, arg->saved_address);
if((arg->access & CS_AC_READ) &&
(((arg->base_access & CS_AC_READ) && arg->base_taint) ||
((arg->index_access & CS_AC_READ) && arg->index_taint))) {
arg->saved_value = (uintptr_t)(*((void **)arg->saved_address));
*((void **)arg->saved_address) = dw_unprotect((void *)(arg->saved_value));
}
}
}
if(dw_check_handling) {
for(i = 0; i < dw_nb_saved_registers; i++) {
reg = dw_saved_registers[i];
re = dw_get_reg_entry(reg);
dw_save_regs[i] = dw_get_register(ctx, re->libpatch_index);
dw_log(INFO, DISASSEMBLY, "%s, %llx\n", re->name, ctx->general_purpose_registers[i]);
}
}
entry->hit_count++;
}
// In the post handler, we normally retaint all registers which were untainted in the pre handler.
// There are a few special cases when the same register is used as a base or index to access the memory
// and as argument.
void
dw_reprotect_context(struct patch_exec_context *ctx)
{
struct insn_entry *entry = ctx->user_data;
struct memory_arg *arg;
struct reg_entry *re;
unsigned i, reg;
uintptr_t value;
if(dw_check_handling) {
dw_log(INFO, DISASSEMBLY, "Reprotect instruction 0x%llx: %s\n", entry->insn, entry->disasm_insn);
dw_print_regs(ctx);
for(i = 0; i < dw_nb_saved_registers; i++) {
// dw_log(INFO, MAIN, "%s = 0x%llx\n", dw_get_patch_reg_name(i), ctx->gregs[i]);
reg = dw_saved_registers[i];
re = dw_get_reg_entry(reg);
value = dw_get_register(ctx, re->libpatch_index);
if((dw_save_regs[i] != value) && dw_reg_written(entry, reg) == false)
dw_log(WARNING, MAIN, "Instruction 0x%llx, register %s modified but should not, now 0x%llx vs 0x%llx\n",
entry->insn, re->name, value, dw_save_regs[i]);
}
}
for(int i = 0; i < entry->nb_arg_m; i++) {
arg = &(entry->arg_m[i]);
// The tainted register, base or index, is retainted unless the same register
// was also an overwritten register argument.
if(arg->base_taint && ((arg->base_access & CS_AC_WRITE) == 0)) {
reg = arg->base;
re = dw_get_reg_entry(reg);
value = dw_get_register(ctx, re->libpatch_index);
dw_set_register(ctx, re->libpatch_index, (uint64_t)dw_retaint((void *)value, (void *)arg->base_taint));
}
if(arg->index_taint && ((arg->index_access & CS_AC_WRITE) == 0)) {
reg = arg->index;
re = dw_get_reg_entry(reg);
value = dw_get_register(ctx, re->libpatch_index);
dw_set_register(ctx, re->libpatch_index, (uint64_t)dw_retaint((void *)value, (void *)arg->index_taint));
}
// If the tainted register, base or index, was also a register argument,
// we have special cases to consider.
if((arg->base_access && arg->base_taint) || (arg->index_access && arg->index_taint)) {
// If the memory was read and the tainted register, base or index, was a read register argument,
// we presumably have a comparison. The memory value was untainted in the pre handler and should
// be restored here.
if(arg->access & CS_AC_READ) {
if(((arg->base_access & CS_AC_READ) && arg->base_taint) || ((arg->index_access & CS_AC_READ) && arg->index_taint))
*((void **)arg->saved_address) = (void *)(arg->saved_value);
}
// If the memory was written and the tainted register, base or index, was also a register argument
// we suppose that the untainted register was stored in memory and we need to retaint the memory
// with the saved register taint
else if(arg->access & CS_AC_WRITE) {
uintptr_t saved_taint;
if(arg->base_taint) saved_taint = arg->base_taint;
else saved_taint = arg->index_taint;
*((void **)arg->saved_address) = dw_retaint(*((void **)arg->saved_address), (void *)saved_taint);
}
}
}
}
// Dump the content of the instruction table, for knowing the
// number of instructions accessing protected objects,
// and the number of hits for each instruction.
// Print statistics about each instruction patched
void
dw_print_instruction_entries(instruction_table *table, int fd)
{
struct insn_entry *entry;
unsigned count = 0;
for(int i = 0; i < table->size; i++) {
entry = &(table->entries[i]);
if((void *)entry->insn != NULL) {
dw_fprintf(fd, "%4d 0x%lx: %9u: %2u: %1u %s;\n", count, entry->insn, entry->hit_count, entry->insn_length, entry->strategy, entry->disasm_insn);
count++;
}
}
}