-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathslower.cpp
293 lines (242 loc) · 7.24 KB
/
slower.cpp
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
// NOTE: A lot of the KVM setup code is copied directly from
// https://github.com/dpw/kvm-hello-world
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <string.h>
#include <stdint.h>
#include <linux/kvm.h>
#include <string>
#include "primes.h"
/* CR0 bits */
#define CR0_PE 1u
#define CR0_MP (1U << 1)
#define CR0_EM (1U << 2)
#define CR0_TS (1U << 3)
#define CR0_ET (1U << 4)
#define CR0_NE (1U << 5)
#define CR0_WP (1U << 16)
#define CR0_AM (1U << 18)
#define CR0_NW (1U << 29)
#define CR0_CD (1U << 30)
#define CR0_PG (1U << 31)
/* CR4 bits */
#define CR4_VME 1
#define CR4_PVI (1U << 1)
#define CR4_TSD (1U << 2)
#define CR4_DE (1U << 3)
#define CR4_PSE (1U << 4)
#define CR4_PAE (1U << 5)
#define CR4_MCE (1U << 6)
#define CR4_PGE (1U << 7)
#define CR4_PCE (1U << 8)
#define CR4_OSFXSR (1U << 8)
#define CR4_OSXMMEXCPT (1U << 10)
#define CR4_UMIP (1U << 11)
#define CR4_VMXE (1U << 13)
#define CR4_SMXE (1U << 14)
#define CR4_FSGSBASE (1U << 16)
#define CR4_PCIDE (1U << 17)
#define CR4_OSXSAVE (1U << 18)
#define CR4_SMEP (1U << 20)
#define CR4_SMAP (1U << 21)
#define EFER_SCE 1
#define EFER_LME (1U << 8)
#define EFER_LMA (1U << 10)
#define EFER_NXE (1U << 11)
/* 32-bit page directory entry bits */
#define PDE32_PRESENT 1
#define PDE32_RW (1U << 1)
#define PDE32_USER (1U << 2)
#define PDE32_PS (1U << 7)
/* 64-bit page * entry bits */
#define PDE64_PRESENT 1
#define PDE64_RW (1U << 1)
#define PDE64_USER (1U << 2)
#define PDE64_ACCESSED (1U << 5)
#define PDE64_DIRTY (1U << 6)
#define PDE64_PS (1U << 7)
#define PDE64_G (1U << 8)
struct vm {
int sys_fd;
int fd;
char *mem;
};
void vm_init(struct vm *vm, size_t mem_size) {
int api_ver;
struct kvm_userspace_memory_region memreg;
vm->sys_fd = open("/dev/kvm", O_RDWR);
if (vm->sys_fd < 0) {
perror("open /dev/kvm");
exit(1);
}
api_ver = ioctl(vm->sys_fd, KVM_GET_API_VERSION, 0);
if (api_ver < 0) {
perror("KVM_GET_API_VERSION");
exit(1);
}
if (api_ver != KVM_API_VERSION) {
fprintf(stderr, "Got KVM api version %d, expected %d\n",
api_ver, KVM_API_VERSION);
exit(1);
}
vm->fd = ioctl(vm->sys_fd, KVM_CREATE_VM, 0);
if (vm->fd < 0) {
perror("KVM_CREATE_VM");
exit(1);
}
if (ioctl(vm->fd, KVM_SET_TSS_ADDR, 0xfffbd000) < 0) {
perror("KVM_SET_TSS_ADDR");
exit(1);
}
vm->mem = (char*)mmap(NULL, mem_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
if (vm->mem == MAP_FAILED) {
perror("mmap mem");
exit(1);
}
madvise(vm->mem, mem_size, MADV_MERGEABLE | MADV_NOHUGEPAGE);
memreg.slot = 0;
memreg.flags = 0;
memreg.guest_phys_addr = 0;
memreg.memory_size = mem_size;
memreg.userspace_addr = (unsigned long)vm->mem;
if (ioctl(vm->fd, KVM_SET_USER_MEMORY_REGION, &memreg) < 0) {
perror("KVM_SET_USER_MEMORY_REGION");
exit(1);
}
}
struct vcpu {
int fd;
struct kvm_run *kvm_run;
};
void vcpu_init(struct vm *vm, struct vcpu *vcpu) {
int vcpu_mmap_size;
vcpu->fd = ioctl(vm->fd, KVM_CREATE_VCPU, 0);
if (vcpu->fd < 0) {
perror("KVM_CREATE_VCPU");
exit(1);
}
vcpu_mmap_size = ioctl(vm->sys_fd, KVM_GET_VCPU_MMAP_SIZE, 0);
if (vcpu_mmap_size <= 0) {
perror("KVM_GET_VCPU_MMAP_SIZE");
exit(1);
}
vcpu->kvm_run = (struct kvm_run*)mmap(NULL, vcpu_mmap_size, PROT_READ | PROT_WRITE,
MAP_SHARED, vcpu->fd, 0);
if (vcpu->kvm_run == MAP_FAILED) {
perror("mmap kvm_run");
exit(1);
}
}
void run_vm(struct vcpu *vcpu) {
printf("Starting VM. PID = %d\n", (int)getpid());
if (ioctl(vcpu->fd, KVM_RUN, 0) < 0) {
perror("KVM_RUN");
exit(1);
}
fprintf(stderr, "exit_reason = %d\n", vcpu->kvm_run->exit_reason);
exit(1);
}
static void setup_protected_mode(struct kvm_sregs *sregs) {
struct kvm_segment seg = {
.base = 0,
.limit = 0xffffffff,
.selector = 1 << 3,
.type = 11, /* Code: execute, read, accessed */
.present = 1,
.dpl = 0,
.db = 1,
.s = 1, /* Code/data */
.l = 0,
.g = 1, /* 4KB granularity */
};
sregs->cr0 |= CR0_PE; /* enter protected mode */
sregs->cs = seg;
seg.type = 3; /* Data: read/write, accessed */
seg.selector = 2 << 3;
sregs->ds = sregs->es = sregs->fs = sregs->gs = sregs->ss = seg;
}
static void setup_paged_32bit_mode(struct vm *vm, struct kvm_sregs *sregs) {
uint32_t next_frame = 0xFFFFF000;
uint32_t pd_addr = next_frame;
uint32_t* pd = (uint32_t*)(vm->mem + pd_addr);
for (uint32_t i = 0; i < 1022; i++) {
next_frame -= 0x1000;
uint32_t pt_addr = next_frame;
pd[i] = PDE32_PRESENT | PDE32_RW | PDE32_USER | pt_addr;
uint32_t* pt = (uint32_t*)(vm->mem + pt_addr);
for (uint32_t j = 0; j < 1024; j++) {
uint32_t frame = (i * 1024u + j) * 0x1000u;
pt[j] = PDE32_PRESENT | PDE32_RW | PDE32_USER | frame;
}
}
sregs->cr3 = pd_addr;
sregs->cr4 = 0;
sregs->cr0
= CR0_PE | CR0_MP | CR0_ET | CR0_NE | CR0_WP | CR0_AM | CR0_PG;
sregs->cr0 = CR0_PE | CR0_MP | CR0_ET | CR0_NE | CR0_AM | CR0_PG;
sregs->efer = 0;
}
void run_paged_32bit_mode(struct vm *vm, struct vcpu *vcpu, uint32_t approx_shift)
{
struct kvm_sregs sregs;
struct kvm_regs regs;
if (ioctl(vcpu->fd, KVM_GET_SREGS, &sregs) < 0) {
perror("KVM_GET_SREGS");
exit(1);
}
setup_protected_mode(&sregs);
setup_paged_32bit_mode(vm, &sregs);
if (ioctl(vcpu->fd, KVM_SET_SREGS, &sregs) < 0) {
perror("KVM_SET_SREGS");
exit(1);
}
memset(®s, 0, sizeof(regs));
/* Clear all FLAGS bits, except bit 1 which is always set. */
regs.rflags = 2;
regs.rip = 0x1000;
if (ioctl(vcpu->fd, KVM_SET_REGS, ®s) < 0) {
perror("KVM_SET_REGS");
exit(1);
}
auto perm = generate_cyclic_permutation(1 << 26, approx_shift);
constexpr size_t size = 1 << 30;
uint32_t jump_base = 0x1000;
uint32_t target_base = jump_base + (size / 2);
auto get_jmp_addr = [=](uint32_t i) -> uint32_t {
return (jump_base + (i * 7));
};
auto get_target_addr = [=](uint32_t i) -> uint32_t {
return (target_base + (i * 8));
};
for (uint32_t i = 0; i < perm.size(); i++) {
uint32_t J = get_jmp_addr(i);
uint32_t T = get_target_addr(i);
uint32_t Ti = (uint32_t)(uintptr_t)T;
uint32_t nextJ = get_jmp_addr(perm.at(i));
*(uint32_t*)(vm->mem + T) = (uint32_t)(uintptr_t)nextJ;
vm->mem[J+0] = 0xff;
vm->mem[J+1] = 0x24;
vm->mem[J+2] = 0x25;
*(uint32_t*)(vm->mem + J + 3) = Ti;
}
run_vm(vcpu); // never returns
}
int main(int argc, char** argv) {
if (argc != 2) {
printf("Usage: %s <approx_shift>\n", argv[0]);
return 1;
}
uint32_t approx_shift = std::stoi(argv[1]);
struct vm vm;
struct vcpu vcpu;
vm_init(&vm, 1l << 32);
vcpu_init(&vm, &vcpu);
run_paged_32bit_mode(&vm, &vcpu, approx_shift);
return 0;
}