-
Notifications
You must be signed in to change notification settings - Fork 0
/
audit.c
147 lines (122 loc) · 3.83 KB
/
audit.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
#define _GNU_SOURCE
#include <link.h>
#include <stddef.h>
#include "audit.h"
unsigned
la_version(unsigned version)
{
if (version == LAV_CURRENT)
return LAV_CURRENT;
else
return 0;
}
unsigned
la_objopen(struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
{
return LA_FLG_BINDTO|LA_FLG_BINDFROM;
}
extern unsigned char trampoline_1_start, trampoline_1_end,
trampoline_1_load_target, trampoline_1_load_name,
trampoline_1_load_find_second_stage;
extern unsigned char find_second_stage;
#define TRAMPOLINE_1_OFFSET(ofs) ((unsigned long)(&trampoline_1_ ## ofs - &trampoline_1_start))
#define TRAMPOLINE_1_SIZE TRAMPOLINE_1_OFFSET(end)
/* Allocate a phase 1 trampoline */
static void *
allocate_trampoline(const char *symname, const void *addr)
{
void *buf;
const unsigned symname_size = strlen(symname) + 1;
buf = malloc(TRAMPOLINE_1_SIZE + symname_size);
memcpy(buf, &trampoline_1_start, TRAMPOLINE_1_SIZE);
memcpy(buf + TRAMPOLINE_1_SIZE, symname, symname_size);
*(unsigned long *)(buf + TRAMPOLINE_1_OFFSET(load_target) + 2) =
(unsigned long)addr;
*(unsigned long *)(buf + TRAMPOLINE_1_OFFSET(load_name) + 2) =
(unsigned long)buf + TRAMPOLINE_1_SIZE;
*(unsigned long *)(buf + TRAMPOLINE_1_OFFSET(load_find_second_stage) + 2) =
(unsigned long)&find_second_stage;
return buf;
}
uintptr_t
la_symbind64(Elf64_Sym *sym, unsigned idx, uintptr_t *refcook,
uintptr_t *defcook, unsigned *flags, const char *symname)
{
/* Don't try to interpose on non-function relocations */
if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
return sym->st_value;
if (audit_this_function(symname))
return (uintptr_t)allocate_trampoline(symname, (void *)sym->st_value);
else
return sym->st_value;
}
struct second_stage_trampoline {
struct second_stage_trampoline *next;
unsigned long target;
unsigned long retaddr;
unsigned char body[];
};
#define NR_HASH_BUCKETS 4096
static unsigned
sst_hash(unsigned long target, unsigned long ra)
{
target ^= ra;
target ^= target / NR_HASH_BUCKETS;
return target % NR_HASH_BUCKETS;
}
static struct audit_lock
sst_hash_lock;
static struct second_stage_trampoline *
sst_hash_tab[NR_HASH_BUCKETS];
extern unsigned char trampoline_2_start,
trampoline_2_loads_target_name,
trampoline_2_loads_call_post_func,
trampoline_2_loads_return_rip,
trampoline_2_end;
extern unsigned char call_post_func;
#define TRAMPOLINE_2_OFFSET(ofs) ((unsigned long)(&trampoline_2_ ## ofs - &trampoline_2_start))
#define TRAMPOLINE_2_SIZE TRAMPOLINE_2_OFFSET(end)
static struct second_stage_trampoline *
build_sst(unsigned long target_rip, unsigned long return_rip, const char *name)
{
struct second_stage_trampoline *sst;
void *buf;
unsigned name_size = strlen(name) + 1;
sst = malloc(name_size + TRAMPOLINE_2_SIZE + sizeof(*sst));
buf = sst->body;
memcpy(buf, &trampoline_2_start, TRAMPOLINE_2_SIZE);
*(unsigned long *)(buf + TRAMPOLINE_2_OFFSET(loads_target_name) + 2) =
(unsigned long)name;
*(unsigned long *)(buf + TRAMPOLINE_2_OFFSET(loads_call_post_func) + 2) =
(unsigned long)&call_post_func;
*(unsigned long *)(buf + TRAMPOLINE_2_OFFSET(loads_return_rip) + 2) =
(unsigned long)return_rip;
sst->target = target_rip;
sst->retaddr = return_rip;
return sst;
}
void *find_second_stage_trampoline(unsigned long target, unsigned long ra,
const char *name) HIDDEN;
void *
find_second_stage_trampoline(unsigned long target, unsigned long ra,
const char *name)
{
struct second_stage_trampoline **pprev, *cursor, **head;
acquire_lock(&sst_hash_lock);
head = pprev = &sst_hash_tab[sst_hash(target, ra)];
cursor = *pprev;
while (cursor) {
if (cursor->target == target &&
cursor->retaddr == ra)
break;
pprev = &cursor->next;
cursor = *pprev;
}
if (!cursor)
cursor = build_sst(target, ra, name);
*pprev = cursor->next;
cursor->next = *head;
*head = cursor;
release_lock(&sst_hash_lock);
return cursor->body;
}