forked from adsr/phpspy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
addr_objdump.c
113 lines (107 loc) · 4.18 KB
/
addr_objdump.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
#include "phpspy.h"
static int get_php_bin_path(pid_t pid, char *path_root, char *path);
static int get_php_base_addr(pid_t pid, char *path_root, char *path, uint64_t *raddr);
static int get_symbol_offset(char *path_root, const char *symbol, uint64_t *raddr);
static int popen_read_line(char *buf, size_t buf_size, char *cmd_fmt, ...);
int get_symbol_addr(addr_memo_t *memo, pid_t pid, const char *symbol, uint64_t *raddr) {
char *php_bin_path, *php_bin_path_root;
uint64_t *php_base_addr;
uint64_t addr_offset;
php_bin_path = memo->php_bin_path;
php_bin_path_root = memo->php_bin_path_root;
php_base_addr = &memo->php_base_addr;
if (*php_bin_path == '\0' && get_php_bin_path(pid, php_bin_path_root, php_bin_path) != 0) {
return 1;
} else if (*php_base_addr == 0 && get_php_base_addr(pid, php_bin_path_root, php_bin_path, php_base_addr) != 0) {
return 1;
} else if (get_symbol_offset(php_bin_path_root, symbol, &addr_offset) != 0) {
return 1;
}
*raddr = *php_base_addr + addr_offset;
return 0;
}
static int get_php_bin_path(pid_t pid, char *path_root, char *path) {
char buf[PHPSPY_STR_SIZE];
char *cmd_fmt = "awk '/libphp[78]/{print $NF; exit 0} END{exit 1}' /proc/%d/maps"
" || readlink /proc/%d/exe";
if (popen_read_line(buf, sizeof(buf), cmd_fmt, (int)pid, (int)pid) != 0) {
log_error("get_php_bin_path: Failed\n");
return 1;
}
if (snprintf(path_root, PHPSPY_STR_SIZE, "/proc/%d/root/%s", (int)pid, buf) > PHPSPY_STR_SIZE - 1) {
log_error("get_php_bin_path: snprintf overflow\n");
return 1;
}
if (access(path_root, F_OK) != 0) {
snprintf(path_root, PHPSPY_STR_SIZE, "/proc/%d/exe", (int)pid);
}
strcpy(path, buf);
return 0;
}
static int get_php_base_addr(pid_t pid, char *path_root, char *path, uint64_t *raddr) {
/**
* This is very likely to be incorrect/incomplete. I thought the base
* address from `/proc/<pid>/maps` + the symbol address from `readelf` would
* lead to the actual memory address, but on at least one system I tested on
* this is not the case. On that system, working backwards from the address
* printed in `gdb`, it seems the missing piece was the 'virtual address' of
* the LOAD section in ELF headers. I suspect this may have to do with
* address relocation and/or a feature called 'prelinking', but not sure.
*/
char buf[PHPSPY_STR_SIZE];
uint64_t start_addr;
uint64_t virt_addr;
char *cmd_fmt = "grep -m1 ' %s$' /proc/%d/maps";
if (popen_read_line(buf, sizeof(buf), cmd_fmt, path, (int)pid) != 0) {
log_error("get_php_base_addr: Failed to get start_addr\n");
return 1;
}
start_addr = strtoull(buf, NULL, 16);
cmd_fmt = "objdump -p %s | awk '/LOAD/{print $5; exit}'";
if (popen_read_line(buf, sizeof(buf), cmd_fmt, path_root) != 0) {
log_error("get_php_base_addr: Failed to get virt_addr\n");
return 1;
}
virt_addr = strtoull(buf, NULL, 16);
*raddr = start_addr - virt_addr;
return 0;
}
static int get_symbol_offset(char *path_root, const char *symbol, uint64_t *raddr) {
char buf[PHPSPY_STR_SIZE];
char *cmd_fmt = "objdump -Tt %s | awk '/ %s$/{print $1; exit}'";
if (popen_read_line(buf, sizeof(buf), cmd_fmt, path_root, symbol) != 0) {
log_error("get_symbol_offset: Failed\n");
return 1;
}
*raddr = strtoull(buf, NULL, 16);
return 0;
}
static int popen_read_line(char *buf, size_t buf_size, char *cmd_fmt, ...) {
FILE *fp;
char cmd[PHPSPY_STR_SIZE];
int buf_len;
va_list cmd_args;
va_start(cmd_args, cmd_fmt);
vsprintf(cmd, cmd_fmt, cmd_args);
va_end(cmd_args);
if (!(fp = popen(cmd, "r"))) {
perror("popen");
return 1;
}
if (fgets(buf, buf_size-1, fp) == NULL) {
log_error("popen_read_line: No stdout; cmd=%s\n", cmd);
pclose(fp);
return 1;
}
pclose(fp);
buf_len = strlen(buf);
while (buf_len > 0 && buf[buf_len-1] == '\n') {
--buf_len;
}
if (buf_len < 1) {
log_error("popen_read_line: Expected strlen(buf)>0; cmd=%s\n", cmd);
return 1;
}
buf[buf_len] = '\0';
return 0;
}