-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathGOTEntry.cpp
More file actions
233 lines (218 loc) · 6.23 KB
/
GOTEntry.cpp
File metadata and controls
233 lines (218 loc) · 6.23 KB
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
#include "GOTEntry.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <link.h>
#include <elf.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <unordered_map>
#include "elfspy/ELFInfo.h"
#include "elfspy/Fail.h"
#include "elfspy/Report.h"
#ifdef __x86_64__
#define Elf_Phdr Elf64_Phdr
#define Elf_Ehdr Elf64_Ehdr
#else
#define Elf_Phdr Elf32_Phdr
#define Elf_Ehdr Elf32_Ehdr
#endif
namespace
{
int argc;
char** argv;
std::string program_name;
bool debug = getenv("ELFSPY_DEBUG") != nullptr;
// TODO: just copy the char it's in memory
std::unordered_map<std::string, unsigned char*> vtables;
std::vector<spy::ELFObject> elf_objects;
void resolve_program_name()
{
struct stat file_info;
std::string file_name = argv[0];
const char* path = getenv("PATH");
if (!path || argv[0][0] == '/') {
path = "";
}
char* path_copy = strdup(path);
char* path_token = path_copy;
char* save;
for ( ; ; ) {
int rv = stat(file_name.c_str(), &file_info);
if (rv == 0) {
program_name = file_name;
break;
}
const char* dir = strtok_r(path_token, ":", &save);
if (dir == nullptr) {
break;
}
path_token = nullptr; // for second call to strtok_r
file_name = dir;
file_name.push_back('/');
file_name.append(argv[0]);
}
free(path_copy);
}
int read_shared_object(struct dl_phdr_info* info, size_t size, void* data)
{
// the ELF header for each loaded object
unsigned char* elf_root;
const char* name = info->dlpi_name;
if (!*name) {
// this is the executable itself - sadly the name was not included in info
name = program_name.c_str();
}
elf_root = reinterpret_cast<unsigned char*>(info->dlpi_addr);
if (name && *name && strncmp(name, "linux-vdso.so", 13) != 0) {
spy::ELFInfo elf(name);
elf_objects.push_back(elf.prepare_object(elf_root));
auto entries = elf.get_vtables(elf_root);
vtables.insert(entries.begin(), entries.end());
}
return 0;
}
} // namespace
namespace spy
{
// arguments left in for future compatibility
void GOTEntry::initialise(int argc, char** argv)
{
::argc = argc;
::argv = argv;
resolve_program_name();
if (program_name.empty()) {
Fail() << "Cannot determine absolute file name of executable";
}
dl_iterate_phdr(&read_shared_object, nullptr);
}
// base + rela.plt if symbol is found and defined
// base + rela.dyn if symbol is found and undefined
void* GOTEntry::set(void* function, const char* name)
{
if (entries_.empty()) {
auto address = reinterpret_cast<unsigned char*>(function);
// first find ELF object where it is defined
ELFInfo::Symbol symbol;
std::string symbol_name;
ELFObject* defined = nullptr;
for (auto& object : elf_objects) {
// check if it is even possible for the function to exist in the object
size_t offset = address - object.base_;
if (offset < object.size_) {
// that the offset is inside the size is a tell-tale the symbol can be
// found
ELFInfo elf(object.name_);
// find name in elf file where it is defined
symbol = elf.get_symbol_rela(offset);
if (symbol.rela_offset_) {
// symbol was defined and used, offset is from the .rela.plt section
make_entry(object.base_ + symbol.rela_offset_);
}
if (symbol.name_) {
// symbol was defined
symbol_name = symbol.name_;
defined = &object;
break;
}
}
}
if (!defined) {
// rare case of looking for an STT_IFUNC when not found at all
for (auto& object : elf_objects) {
ELFInfo elf(object.name_);
symbol = elf.get_indirect_symbol_rela(object.base_, function);
if (symbol.rela_offset_) {
// symbol was defined, offset is from the .rela.plt section
make_entry(object.base_ + symbol.rela_offset_);
symbol_name = symbol.name_;
defined = &object;
break;
}
}
}
if (!defined) {
Report() << "cannot find definition of function " << name;
return nullptr;
}
// find entries where the symbol is used in other ELF functions
for (auto& object : elf_objects) {
if (&object == defined) {
continue;
}
ELFInfo elf(object.name_);
size_t rela_offset = elf.get_symbol_rela_dyn(symbol_name.c_str());
if (rela_offset) {
// symbol was undefined, offset is from the .rela.dyn section
make_entry(object.base_ + rela_offset);
}
}
}
// in file where it is defined find the .rela.plt
// if undefined find the name in the .rela.dyn using .symtab index for name
// and with strcmp
if (original_ == nullptr) {
original_ = function;
}
current_ = function;
return original_;
}
void GOTEntry::make_entry(void** address)
{
// store the previous value of the entry which points to @plt+6 as it has been
// resolved, @plt there is a indirect jump to the contents of the entry, so
// if it is set to @plt instead @plt+6 an infinite loop will occur
Entry entry;
entry.address_ = address;
entry.restore_ = *entry.address_;
entries_.push_back(entry);
}
void** GOTEntry::get_vtable_entry(const std::type_info& type,
const MethodPointer& method)
{
if (method.is_virtual()) {
// this is a virtual function
auto seek = vtables.find(type.name());
if (seek != vtables.end()) {
unsigned char* vtable = seek->second;
// there seems to be two entries that are not accounted for in the index
auto location = vtable + sizeof(void*) * 2 + (method.index_ & ~1UL);
return reinterpret_cast<void**>(location);
}
Report() << "Could not find virtual function";
return nullptr;
}
// non virtual function
return nullptr;
}
void* GOTEntry::spy_with(void* function)
{
if (!spy_count_) {
patch(function);
}
++spy_count_;
return original_;
}
void* GOTEntry::patch(void* function)
{
for (auto entry : entries_) {
*entry.address_ = function;
}
void* previous = current_;
current_ = function;
return previous;
}
void GOTEntry::restore()
{
--spy_count_;
if (!spy_count_) {
for (auto entry : entries_) {
*entry.address_ = entry.restore_;
}
}
current_ = original_;
}
} // namespace spy