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
|
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <glib.h>
#include "common.h"
#include "exports.h"
void *handle;
struct conf *config;
struct conf *(*configure)();
GByteArray *out;
void *cpu;
char cbuf[100];
// region GDB Imports
#pragma region GDB Imports
void cpu_single_step(void *cpu, int enabled);
int get_sstep_flags(void);
void gdb_accept_init(int fd);
int gdb_breakpoint_insert(int type, unsigned long long addr,
unsigned long long len);
int gdb_breakpoint_remove(int type, unsigned long long addr,
unsigned long long len);
void *qemu_get_cpu(int index);
int target_memory_rw_debug(void *cpu, unsigned long long addr, void *ptr,
unsigned long long len, char is_write);
int gdb_read_register(void *cs, GByteArray *mem_buf, int n);
int gdb_write_register(void *cs, char *mem_buf, int n);
void gdb_set_cpu_pc(unsigned long long pc);
void gdb_continue(void);
#pragma endregion GDB Imports
// region API
int r_mem(unsigned long long addr, unsigned long long len, void *dest) {
return target_memory_rw_debug(cpu, addr, dest, len, 0);
}
int w_mem(unsigned long long addr, unsigned long long len, void *src) {
return target_memory_rw_debug(cpu, addr, src, len, 1);
}
int r_reg(unsigned char reg, void *dest) {
g_byte_array_steal(out, NULL);
int op = gdb_read_register(cpu, out, reg);
memcpy(dest, out->data, out->len);
return op;
}
int w_reg(unsigned char reg, char *src) {
return gdb_write_register(cpu, src, reg);
}
// region Breakpoint handling
char single_stepped;
unsigned long long gen_addr;
struct ret *(*hook)();
struct ret *returned;
// Defined and imported gdbstub.c
void set_signal_callback(void (*cb)(int));
// Breakpoints are set here
void patch_block_trans_cb(struct qemu_plugin_tb *tb) {
unsigned long long addr;
addr = qemu_plugin_tb_vaddr(tb);
if (addr == config->entry_addr) {
// NOTE This means we cannot put a BP in the first basic block
gdb_accept_init(-1);
for (int i = 0; i < config->num_hooks; i++) {
gdb_breakpoint_insert(0, config->hooks[i], 1);
}
}
}
void handle_signal_callback(int sig) {
if (single_stepped) {
single_stepped = 0;
gdb_breakpoint_insert(0, gen_addr, 1);
cpu_single_step(cpu, 0);
gdb_continue();
return;
}
r_reg(config->IP_reg_num, cbuf);
gen_addr = *(unsigned long long *)cbuf;
sprintf(cbuf, "hook_%016llx", gen_addr);
// TODO maybe find a way to put the hook function pointers in the TCG data
// structure instead of this dlsym call
*(unsigned long long **)(&hook) = dlsym(handle, cbuf);
if (!hook) {
exit(-1);
}
returned = hook();
if (returned->remove_bp ||
(returned->addr ==
gen_addr)) { //* force removal of bp in returning to the same address,
//otherwise hook will be called again
gdb_breakpoint_remove(0, gen_addr, 1);
}
if (returned->addr == gen_addr) {
single_stepped = 1;
cpu_single_step(cpu, get_sstep_flags());
} else {
//* no need to rexecute the IP instruction
gdb_set_cpu_pc(returned->addr);
}
gdb_continue();
}
// region Constructor/Destructor
void patch_finish_cb(void *userdata) {
g_byte_array_free(out, 1);
dlclose(handle);
}
void patch_vpu_init_cb(unsigned int vcpu_index) {
cpu = qemu_get_cpu(vcpu_index);
}
void patch_init(char *hook_lib) {
// TODO make OS agnostic, remove dlopen
handle = dlopen(hook_lib, RTLD_NOW);
if (!handle) {
fprintf(stderr, "DLOPEN Error: %s\n", dlerror());
exit(-1);
}
single_stepped = 0;
*(void **)(&configure) = dlsym(handle, "configure");
config = configure();
set_signal_callback(handle_signal_callback);
out = g_byte_array_new();
}
|