-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhollow.c
197 lines (166 loc) · 5.9 KB
/
hollow.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
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
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/wait.h>
#include "elf.h"
#include "ptrace.h"
#include "util.h"
#define R_SYSCALL rax
#define R_IP rip
#define R_ARG0 rdi
#define R_ARG1 rsi
#define R_ARG2 rdx
#define R_ARG3 r10
#define R_ARG4 r8
#define R_ARG5 r9
#define SYSCALL_STUB_ADDR 0x100000
#define X64_SYSCALL_BRK 0x9090909090cc050f // syscall; int 3
#define SYS_kill 62
#define SYS_mmap 9
#define SYS_mprotect 10
#define SYS_munmap 11
unsigned long remote_syscall_addr = 0;
int remote_syscall(pid_t pid, int syscall_id, unsigned long arg0, unsigned long arg1, unsigned long arg2,
unsigned long arg3, unsigned long arg4, unsigned long arg5) {
struct user_regs_struct saved_regs, work_regs;
int status;
// save current register state
printf("Saving current register state for pid %d\n", pid);
ptrace_peekregs(pid, &saved_regs);
// setup arguments for the syscall
work_regs = saved_regs;
work_regs.R_IP = remote_syscall_addr;
work_regs.R_SYSCALL = syscall_id;
work_regs.R_ARG0 = arg0;
work_regs.R_ARG1 = arg1;
work_regs.R_ARG2 = arg2;
work_regs.R_ARG3 = arg3;
work_regs.R_ARG4 = arg4;
work_regs.R_ARG5 = arg5;
// set the regs
printf("Setting registers for syscall, RIP will be %llx\n", work_regs.R_IP);
ptrace_pokeregs(pid, &work_regs);
// run the program until breakpoint
printf("Running syscall\n");
if (ptrace(PTRACE_CONT, pid, 0, SIGCONT) == -1) {
perror("Continuing program failed: ptrace");
exit(-1);
}
waitpid(pid, &status, 0);
while (WSTOPSIG(status) == SIGSTOP && ptrace(PTRACE_CONT, pid, 0, SIGCONT) != -1) {
waitpid(pid, &status, 0);
}
if (WSTOPSIG(status) == SIGTRAP) {
printf("Hit breakpoint in syscall stub!\n");
}
else {
printf("Program terminated by signal %s\n", strsignal(WTERMSIG(status)));
}
// gets the regs for the return value
ptrace_peekregs(pid, &work_regs);
printf("RIP after running syscall: %llx\n", work_regs.R_IP);
// restore original regs
ptrace_pokeregs(pid, &saved_regs);
// send SIGSTOP to the process and resume it to deliver the signal?
syscall(SYS_kill, pid, SIGSTOP);
if ((ptrace(PTRACE_CONT, pid, 0, 0) == -1) || (waitpid(pid, &status, 0), WSTOPSIG(status) != SIGSTOP)) {
printf("Failed to SIGSTOP and restart child\n");
exit(-1);
}
// syscall number register holds the return value
return work_regs.R_SYSCALL;
}
void setup_remote_syscall(pid_t pid, unsigned long start_addr) {
int retval;
unsigned long data;
// write syscall stub to start_addr
printf("Writing syscall stub to executable section...\n");
ptrace_peek(pid, start_addr, &data);
ptrace_poke(pid, start_addr, (unsigned long)X64_SYSCALL_BRK);
remote_syscall_addr = start_addr;
// make syscall to map page at SYSCALL_STUB_ADDR
printf("Making syscall to map page at %lx\n", (unsigned long)SYSCALL_STUB_ADDR);
retval = remote_syscall(pid, SYS_mmap, SYSCALL_STUB_ADDR, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
printf("SYS_mmap returned %d\n", retval);
if (retval == SYSCALL_STUB_ADDR) {
// our mapping worked, put our syscall stub at SYSCALL_STUB_ADDR and
// change to that addr for future syscalls
ptrace_poke(pid, SYSCALL_STUB_ADDR, (unsigned long)X64_SYSCALL_BRK);
remote_syscall_addr = SYSCALL_STUB_ADDR;
// restore original data
ptrace_poke(pid, start_addr, data);
}
}
void child(char *procname, char *argv[]) {
printf("In child, executing %s\n", procname);
ptrace(PTRACE_TRACEME, 0, 0, 0);
execv(procname, argv);
}
void parent(pid_t pid, char *progname) {
int status;
list_node *maps, *maps_ptr;
int retval;
waitpid(pid, &status, 0);
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
// modify process
printf("cat /proc/%d/maps\n", pid);
printf("Enter to continue");
getchar();
// find an executable mapping and setup our remote syscall stub
maps = parse_maps(pid);
maps_ptr = maps;
for (maps_ptr; maps_ptr != NULL; maps_ptr = maps_ptr->next) {
struct map *map = maps_ptr->data;
if (map->is_x) {
printf("Found executable section, doing remote syscall setup...\n");
setup_remote_syscall(pid, map->start);
break;
}
}
if (remote_syscall_addr == 0) {
printf("Did not find executable section to setup syscall? Impossible\n");
exit(-1);
}
printf("Removing all mappings of original program...");
maps_ptr = maps;
for (maps_ptr; maps_ptr != NULL; maps_ptr = maps_ptr->next) {
struct map *map = maps_ptr->data;
if (strstr(map->text, progname) != NULL) {
printf(" * Removing mapping at %lx... ", map->start);
retval = remote_syscall(pid, SYS_munmap, map->start, (size_t)(map->end-map->start), 0, 0, 0, 0);
if (retval == 0) {
printf("success\n");
}
else {
printf("failed!\n");
}
}
}
printf("About to restart process, Enter to continue....\n");
getchar();
ptrace(PTRACE_CONT, pid, 1, 0);
}
}
int main(int argc, char *argv[]) {
pid_t pid;
pid = fork();
switch (pid) {
case 0:
child(argv[1], &argv[1]);
break;
case -1:
// error
break;
default:
parent(pid, argv[1]);
break;
}
return 0;
}