-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinject.c
146 lines (127 loc) · 3.56 KB
/
inject.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
/*
* Inject a syscall into a child process
* Language: C11
*/
#include "inject.h"
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <sys/types.h>
#include <linux/ptrace.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
// Return number of arguments expected of a particular syscall
static int getNumberOfArguments(int syscall)
{
#define SYSCALL_ARG_INFO(n) {.number_of_arguments=n, .valid=true}
struct SyscallArgInfo{
int number_of_arguments;
bool valid;
};
static const struct SyscallArgInfo syscall_arg_map[] =
{
[SYS_exit] = SYSCALL_ARG_INFO(1),
[SYS_fork] = SYSCALL_ARG_INFO(0),
[SYS_mprotect] = SYSCALL_ARG_INFO(3),
[SYS_write] = SYSCALL_ARG_INFO(3),
};
if(syscall < 0)
return -1;
if((size_t)syscall >= sizeof(syscall_arg_map)/sizeof(syscall_arg_map[0]))
return -1;
if(!(syscall_arg_map[syscall].valid))
return -1;
return syscall_arg_map[syscall].number_of_arguments;
#undef SYSCALL_ARG_INFO
}
static int inject_syscall(pid_t child, const void* site, int syscall, ...)
{
va_list args;
struct user_regs_struct regs;
struct user_regs_struct orig_regs;
if(child <= 0 || syscall < 0 || site == NULL)
return -1;
// Copy current state
if(0>ptrace(PTRACE_GETREGS, child, NULL, &orig_regs))
return -1;
long original_code = ptrace(PTRACE_PEEKTEXT, child, site, NULL);
if(NULL == memcpy(®s, &orig_regs, sizeof(orig_regs)))
return -1;
// Inject "syscall" instruction
regs.rip = (long)site;
if(ptrace(PTRACE_POKETEXT, child, (void*)regs.rip, (void*)0x050f))
{
perror("Poking of text failed");
fprintf(stderr, "\tInjection site: %p\n", (void*)regs.rip);
return -1;
}
// Set syscall arguments
va_start(args, syscall);
regs.rax = syscall;
int num_args = getNumberOfArguments(syscall);
if(num_args < 0)
return -1;
if(num_args >= 1)
regs.rdi = va_arg(args, long);
if(num_args >= 2)
regs.rsi = va_arg(args, long);
if(num_args >= 3)
regs.rdx = va_arg(args, long);
if(num_args >= 4)
return -1;
va_end(args);
// Replace registers
if(0>ptrace(PTRACE_SETREGS, child, NULL, ®s))
return -1;
// Resume
// Stop at entry
if(0>ptrace(PTRACE_SYSCALL, child, NULL, NULL))
return -1;
if(0>waitpid(child, NULL, 0))
return -1;
// Stop at exit
if(0>ptrace(PTRACE_SYSCALL, child, NULL, NULL))
return -1;
if(0>waitpid(child, NULL, 0))
return -1;
/* Get return value */
if(0>ptrace(PTRACE_GETREGS, child, NULL, ®s))
return -1;
const long return_value = regs.rax;
if(return_value)
{
printf("!!!Return value:%ld\n", return_value);
if(return_value == syscall)
{
printf("!!!Syscall was apparently not executed @%p\n",
(void*)regs.rip);
}
}
// Restore code
if(ptrace(PTRACE_POKETEXT, child, site, (void*)original_code))
return -1;
if(original_code != ptrace(PTRACE_PEEKTEXT, child, site, NULL))
{
fprintf(stderr, "Failed to write back");
return -1;
}
// Restore regs
if(0>ptrace(PTRACE_SETREGS, child, NULL, &orig_regs))
return -1;
return return_value;
}
int inject_syscall_mprotect(pid_t child, const void* site, void* addr, size_t len, int prot)
{
return inject_syscall(child, site, SYS_mprotect, (long) addr, (long) len, (long) prot);
}
/*
* References used:
* https://stackoverflow.com/questions/22444526/x86-64-linux-syscall-arguments
* https://stackoverflow.com/questions/53024196/can-ptrace-cause-the-traced-process-to-perform-a-syscall-without-access-to-an-ex
* https://stackoverflow.com/questions/23969524/injected-mprotect-system-call-into-traced-process-fails-with-efault
*/