This repository has been archived by the owner on Mar 28, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathcfg_valid_targets.cpp
143 lines (114 loc) · 4.95 KB
/
cfg_valid_targets.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
#define WINVER 0x0A00
#define _WIN32_WINNT 0x0A00
#include <cstdio>
#include <cstdlib>
#include <cstdint>
#include <intrin.h>
#include <windows.h>
#include <psapi.h>
// To set a target as invalid, we just need to ensure
// the CFG_CALL_TARGET_VALID bit is not defined, which this will
// do as it clears all flags bits
#define CFG_CALL_TARGET_INVALID (0x00000000)
using namespace std;
typedef int (*int_arg_fn)(int);
typedef int (*float_arg_fn)(float);
static int int_arg(int arg) {
printf("In %s: (%d)\n", __FUNCTION__, arg);
return 0;
}
static int float_arg(float arg) {
printf("CFG should protect transfer to here\n");
printf("In %s: (%f)\n", __FUNCTION__, (double)arg);
return 0;
}
static int bad_int_arg(int arg) {
printf("CFG will not protect transfer to here\n");
printf("In %s: (%d)\n", __FUNCTION__, arg);
return 0;
}
static int not_entry_point(int arg) {
// nop sled for x86 / x86-64
// these instructions act as a buffer
// for an indirect control flow transfer to skip
// a valid function entry point, but continue
// to execute normal code
__nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
__nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
__nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
__nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
__nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
__nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
__nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
__nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop();
printf("CFG ensures control flow only transfers to potentially valid destinations\n");
printf("In %s: (%d)\n", __FUNCTION__, arg);
// need to exit or the program will segfault anyway,
// since the indirect call skipped the function preamble
exit(arg);
}
struct foo {
int_arg_fn int_funcs[1];
int_arg_fn bad_int_funcs[1];
float_arg_fn float_funcs[1];
int_arg_fn not_entries[1];
};
// the struct aligns the function pointer arrays
// so indexing past the end will reliably
// call working function pointers
static struct foo f = { {int_arg}, {bad_int_arg}, {float_arg}, {(int_arg_fn)((uintptr_t)(not_entry_point)+0x20)} };
static const ULONG kCallTargets = 3;
int main(int argc, const char *argv[]) {
MODULEINFO mi;
CFG_CALL_TARGET_INFO call_dests[kCallTargets];
// Get the base address and size of the currently loaded module
// so that we can figure out where each function entry point is
// in relation to the start of the allocation of this process image
if(0 == GetModuleInformation(GetCurrentProcess(),
GetModuleHandle(nullptr),
&mi,
sizeof(mi))) {
printf("Could not get the base address for this module\n");
exit(-1);
}
printf("Base of loaded image: %p\n", mi.lpBaseOfDll);
printf("Size of loaded image: %x\n", mi.SizeOfImage);
// remove float_arg from the valid CFG mapping
call_dests[0].Flags = CFG_CALL_TARGET_INVALID;
call_dests[0].Offset = (ULONG_PTR)(float_arg) - (ULONG_PTR)(mi.lpBaseOfDll);
// remove bad_int_arg from the valid CFG mapping
call_dests[1].Flags = CFG_CALL_TARGET_INVALID;
call_dests[1].Offset = (ULONG_PTR)(bad_int_arg) - (ULONG_PTR)(mi.lpBaseOfDll);
// ensure the int_arg function is in the valid CFG mapping
call_dests[2].Flags = CFG_CALL_TARGET_VALID;
call_dests[2].Offset = (ULONG_PTR)(int_arg) - (ULONG_PTR)(mi.lpBaseOfDll);
// update valid CFG targets
if(TRUE != SetProcessValidCallTargets(
GetCurrentProcess(),
mi.lpBaseOfDll,
mi.SizeOfImage,
kCallTargets,
call_dests)) {
printf("WARNING: Failed on SetProcessValidCallTargets: %08x\n", GetLastError());
}
if(argc != 2) {
printf("Usage: %s <option>\n", argv[0]);
printf("Option values:\n");
printf("\t0\tCall correct function\n");
printf("\t1\tCall the wrong function but with the same signature\n");
printf("\t2\tCall a float function with an int function signature\n");
printf("\t3\tCall into the middle of a function\n");
printf("\n");
printf("\tAll other options are undefined, but should be caught by CFI :)\n");
printf("\n\n");
printf("Here are some pointers so members of `struct foo f` are not optimized away:\n");
printf("\tint_funcs: %p\n", (void*)f.int_funcs);
printf("\tbad_int_funcs: %p\n", (void*)f.bad_int_funcs);
printf("\tfloat_funcs: %p\n", (void*)f.float_funcs);
printf("\tnot_entries: %p\n", (void*)f.not_entries);
return 1;
}
printf("Calling a function:\n");
int idx = argv[1][0] - '0';
return f.int_funcs[idx](idx);
}