Rizin
unix-like reverse engineering framework and cli tools
io_ptrace.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2008-2017 pancake <pancake@nopcode.org>
2 // SPDX-License-Identifier: LGPL-3.0-only
3 
4 #include <rz_userconf.h>
5 #include <rz_util.h>
6 #include <rz_io.h>
7 #include <rz_lib.h>
8 #include <rz_cons.h>
9 #include <rz_debug.h>
10 
11 #if DEBUGGER && (__linux__ || __BSD__)
12 
13 #include <sys/ptrace.h>
14 #include <sys/types.h>
15 #include <sys/wait.h>
16 #include <errno.h>
17 
18 typedef struct {
19  int pid;
20  int tid;
21  int fd;
22  int opid;
23 } RzIOPtrace;
24 #define RzIOPTRACE_OPID(x) (((RzIOPtrace *)(x)->data)->opid)
25 #define RzIOPTRACE_PID(x) (((RzIOPtrace *)(x)->data)->pid)
26 #define RzIOPTRACE_FD(x) (((RzIOPtrace *)(x)->data)->fd)
27 static void open_pidmem(RzIOPtrace *iop);
28 
29 #undef RZ_IO_NFDS
30 #define RZ_IO_NFDS 2
31 #ifndef __ANDROID__
32 extern int errno;
33 #endif
34 
35 // PTRACE_GETSIGINFO is defined only since glibc 2.4 but appeared much
36 // earlier in linux kernel - since 2.3.99-pre6
37 // So we define it manually
38 #if __linux__ && defined(__GLIBC__)
39 #ifndef PTRACE_GETSIGINFO
40 #define PTRACE_GETSIGINFO 0x4202
41 #endif
42 #endif
43 
44 #if 0
45 procpidmem is buggy.. running this sometimes results in ffff
46 
47  while : ; do rizin -qc 'oo;x' -d ls ; done
48 #endif
49 #define USE_PROC_PID_MEM 0
50 
51 static int __waitpid(int pid) {
52  int st = 0;
53  return (waitpid(pid, &st, 0) != -1);
54 }
55 
56 #define debug_read_raw(io, x, y) rz_io_ptrace((io), PTRACE_PEEKTEXT, (x), (void *)(y), RZ_PTRACE_NODATA)
57 #define debug_write_raw(io, x, y, z) rz_io_ptrace((io), PTRACE_POKEDATA, (x), (void *)(y), (rz_ptrace_data_t)(z))
58 #if __OpenBSD__ || __NetBSD__ || __KFBSD__
59 typedef int ptrace_word; // int ptrace(int request, pid_t pid, caddr_t addr, int data);
60 #else
61 typedef size_t ptrace_word; // long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
62 // XXX. using int read fails on some addresses
63 // XXX. using long here breaks 'w AAAABBBBCCCCDDDD' in rizin -d
64 #endif
65 
66 static int debug_os_read_at(RzIO *io, int pid, ut32 *buf, int sz, ut64 addr) {
67  ut32 words = sz / sizeof(ut32);
68  ut32 last = sz % sizeof(ut32);
69  ut32 x, lr, *at = (ut32 *)(size_t)addr;
70  if (sz < 1 || addr == UT64_MAX) {
71  return -1;
72  }
73  for (x = 0; x < words; x++) {
74  buf[x] = (ut32)debug_read_raw(io, pid, (void *)at);
75  at = (ut32 *)((size_t)at + sizeof(ut32));
76  }
77  if (last) {
78  lr = (ut32)debug_read_raw(io, pid, at);
79  memcpy(buf + x, &lr, last);
80  }
81  return sz;
82 }
83 
84 static int __read(RzIO *io, RzIODesc *desc, ut8 *buf, int len) {
85 #if USE_PROC_PID_MEM
86  int ret, fd;
87 #endif
88  ut64 addr = io->off;
89  if (!desc || !desc->data) {
90  return -1;
91  }
92  memset(buf, '\xff', len); // TODO: only memset the non-readed bytes
93  /* reopen procpidmem if necessary */
94 #if USE_PROC_PID_MEM
95  fd = RzIOPTRACE_FD(desc);
96  if (RzIOPTRACE_PID(desc) != RzIOPTRACE_OPID(desc)) {
97  if (fd != -1) {
98  close(fd);
99  }
100  open_pidmem((RzIOPtrace *)desc->data);
101  fd = RzIOPTRACE_FD(desc);
102  RzIOPTRACE_OPID(desc) = RzIOPTRACE_PID(desc);
103  }
104  // /proc/pid/mem fails on latest linux
105  if (fd != -1) {
106  ret = lseek(fd, addr, SEEK_SET);
107  if (ret >= 0) {
108  // Workaround for the buggy Debian Wheeze's /proc/pid/mem
109  if (read(fd, buf, len) != -1) {
110  return ret;
111  }
112  }
113  }
114 #endif
115  ut32 *aligned_buf = (ut32 *)rz_malloc_aligned(len, sizeof(ut32));
116  if (aligned_buf) {
117  int res = debug_os_read_at(io, RzIOPTRACE_PID(desc), (ut32 *)aligned_buf, len, addr);
118  memcpy(buf, aligned_buf, len);
119  rz_free_aligned(aligned_buf);
120  return res;
121  }
122  return -1;
123 }
124 
125 static int ptrace_write_at(RzIO *io, int pid, const ut8 *pbuf, int sz, ut64 addr) {
126  ptrace_word *buf = (ptrace_word *)pbuf;
127  ut32 words = sz / sizeof(ptrace_word);
128  ut32 last = sz % sizeof(ptrace_word);
129  ptrace_word x, *at = (ptrace_word *)(size_t)addr;
130  ptrace_word lr;
131  if (sz < 1 || addr == UT64_MAX) {
132  return -1;
133  }
134  for (x = 0; x < words; x++) {
135  int rc = debug_write_raw(io, pid, at++, buf[x]); //((ut32*)(at)), buf[x]);
136  if (rc) {
137  return -1;
138  }
139  }
140  if (last) {
141  lr = debug_read_raw(io, pid, (void *)at);
142  memcpy(&lr, buf + x, last);
143  if (debug_write_raw(io, pid, (void *)at, lr)) {
144  return sz - last;
145  }
146  }
147  return sz;
148 }
149 
150 static int __write(RzIO *io, RzIODesc *fd, const ut8 *buf, int len) {
151  if (!fd || !fd->data) {
152  return -1;
153  }
154  return ptrace_write_at(io, RzIOPTRACE_PID(fd), buf, len, io->off);
155 }
156 
157 static void open_pidmem(RzIOPtrace *iop) {
158 #if USE_PROC_PID_MEM
159  char pidmem[32];
160  snprintf(pidmem, sizeof(pidmem), "/proc/%d/mem", iop->pid);
161  iop->fd = open(pidmem, O_RDWR);
162  if (iop->fd == -1) {
163  iop->fd = open(pidmem, O_RDONLY);
164  }
165 #if 0
166  if (iop->fd == -1)
167  eprintf ("Warning: Cannot open /proc/%d/mem. "
168  "Fallback to ptrace io.\n", iop->pid);
169 #endif
170 #else
171  iop->fd = -1;
172 #endif
173 }
174 
175 static void close_pidmem(RzIOPtrace *iop) {
176  if (iop->fd != -1) {
177  close(iop->fd);
178  iop->fd = -1;
179  }
180 }
181 
182 static bool __plugin_open(RzIO *io, const char *file, bool many) {
183  if (!strncmp(file, "ptrace://", 9)) {
184  return true;
185  }
186  if (!strncmp(file, "attach://", 9)) {
187  return true;
188  }
189  return false;
190 }
191 
192 static inline bool is_pid_already_attached(RzIO *io, int pid) {
193 #if defined(__linux__)
194  siginfo_t sig = { 0 };
195  return rz_io_ptrace(io, PTRACE_GETSIGINFO, pid, NULL, &sig) != -1;
196 #elif defined(__FreeBSD__)
197  struct ptrace_lwpinfo info = { 0 };
198  int len = (int)sizeof(info);
199  return rz_io_ptrace(io, PT_LWPINFO, pid, &info, len) != -1;
200 #elif defined(__OpenBSD__) || defined(__NetBSD__)
201  ptrace_state_t state = { 0 };
202  int len = (int)sizeof(state);
203  return rz_io_ptrace(io, PT_GET_PROCESS_STATE, pid, &state, len) != -1;
204 #else
205  return false;
206 #endif
207 }
208 
209 static RzIODesc *__open(RzIO *io, const char *file, int rw, int mode) {
210  RzIODesc *desc = NULL;
211 
212  if (!__plugin_open(io, file, 0)) {
213  return NULL;
214  }
215 
216  int pid = atoi(file + 9);
217 
218  // Safely check if the PID has already been attached to avoid printing errors
219  // and attempt attaching on failure
220  if (!is_pid_already_attached(io, pid)) {
221  int ret = rz_io_ptrace(io, PTRACE_ATTACH, pid, 0, 0);
222  if (ret == -1) {
223 #ifdef __ANDROID__
224  eprintf("ptrace_attach: Operation not permitted\n");
225 #else
226  switch (errno) {
227  case EPERM:
228  eprintf("ptrace_attach: Operation not permitted\n");
229  break;
230  case EINVAL:
231  perror("ptrace: Cannot attach");
232  eprintf("ERRNO: %d (EINVAL)\n", errno);
233  break;
234  default:
235  break;
236  }
237  return NULL;
238 #endif
239  } else if (!__waitpid(pid)) {
240  eprintf("Error in waitpid\n");
241  return NULL;
242  }
243  }
244 
245  RzIOPtrace *riop = RZ_NEW0(RzIOPtrace);
246  if (!riop) {
247  return NULL;
248  }
249 
250  riop->pid = riop->tid = pid;
251  open_pidmem(riop);
253  desc->name = rz_sys_pid_to_path(pid);
254 
255  return desc;
256 }
257 
258 static ut64 __lseek(RzIO *io, RzIODesc *fd, ut64 offset, int whence) {
259  switch (whence) {
260  case RZ_IO_SEEK_SET:
261  io->off = offset;
262  break;
263  case RZ_IO_SEEK_CUR:
264  io->off += offset;
265  break;
266  case RZ_IO_SEEK_END:
267  io->off = ST64_MAX;
268  }
269  return io->off;
270 }
271 
272 static int __close(RzIODesc *desc) {
273  int pid, fd;
274  if (!desc || !desc->data) {
275  return -1;
276  }
277  pid = RzIOPTRACE_PID(desc);
278  fd = RzIOPTRACE_FD(desc);
279  if (fd != -1) {
280  close(fd);
281  }
282  RzIOPtrace *riop = desc->data;
283  desc->data = NULL;
284  long ret = rz_io_ptrace(desc->io, PTRACE_DETACH, pid, 0, 0);
285  if (errno == ESRCH) {
286  // process does not exist, may have been killed earlier -- continue as normal
287  ret = 0;
288  }
289  free(riop);
290  return ret;
291 }
292 
293 static char *__system(RzIO *io, RzIODesc *fd, const char *cmd) {
294  RzIOPtrace *iop = (RzIOPtrace *)fd->data;
295  // printf("ptrace io command (%s)\n", cmd);
296  /* XXX ugly hack for testing purposes */
297  if (!strcmp(cmd, "")) {
298  return NULL;
299  }
300  if (!strcmp(cmd, "help")) {
301  eprintf("Usage: R!cmd args\n"
302  " R!ptrace - use ptrace io\n"
303  " R!mem - use /proc/pid/mem io if possible\n"
304  " R!pid - show targeted pid\n"
305  " R!pid <#> - select new pid\n");
306  } else if (!strcmp(cmd, "ptrace")) {
307  close_pidmem(iop);
308  } else if (!strcmp(cmd, "mem")) {
309  open_pidmem(iop);
310  } else if (!strncmp(cmd, "pid", 3)) {
311  if (iop) {
312  if (cmd[3] == ' ') {
313  int pid = atoi(cmd + 4);
314  if (pid > 0 && pid != iop->pid) {
315  (void)rz_io_ptrace(io, PTRACE_ATTACH, pid, 0, 0);
316  // TODO: do not set pid if attach fails?
317  iop->pid = iop->tid = pid;
318  }
319  } else {
320  io->cb_printf("%d\n", iop->pid);
321  }
322  return rz_str_newf("%d", iop->pid);
323  }
324  } else {
325  eprintf("Try: 'R!pid'\n");
326  }
327  return NULL;
328 }
329 
330 static int __getpid(RzIODesc *fd) {
331  RzIOPtrace *iop = (RzIOPtrace *)fd->data;
332  if (!iop) {
333  return -1;
334  }
335  return iop->pid;
336 }
337 
338 // TODO: rename ptrace to io_ptrace .. err io.ptrace ??
340  .name = "ptrace",
341  .desc = "Ptrace and /proc/pid/mem (if available) io plugin",
342  .license = "LGPL3",
343  .uris = "ptrace://,attach://",
344  .open = __open,
345  .close = __close,
346  .read = __read,
347  .check = __plugin_open,
348  .lseek = __lseek,
349  .system = __system,
350  .write = __write,
351  .getpid = __getpid,
352  .gettid = __getpid,
353  .isdbg = true
354 };
355 #else
357  .name = NULL
358 };
359 #endif
360 
361 #ifndef RZ_PLUGIN_INCORE
363  .type = RZ_LIB_TYPE_IO,
364  .data = &rz_io_plugin_ptrace,
366 };
367 #endif
size_t len
Definition: 6502dis.c:15
RzBinInfo * info(RzBinFile *bf)
Definition: bin_ne.c:86
const char * desc
Definition: bin_vsf.c:19
const lzma_allocator const uint8_t * in
Definition: block.h:527
#define RZ_API
#define NULL
Definition: cris-opc.c:27
static static fork const void static count close
Definition: sflib.h:33
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags cmd
Definition: sflib.h:79
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags static arg static fd static protocol static who struct sockaddr static addrlen static backlog struct timeval struct timezone static tz const struct iovec static count static mode const void const struct sockaddr static tolen const char static pathname void static offset struct stat static buf void long static basep lseek
Definition: sflib.h:113
uint32_t ut32
struct tab * done
Definition: enough.c:233
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
static int __read(RzIO *io, RzIODesc *fd, ut8 *buf, int count)
Definition: io_bfdbg.c:84
static bool __plugin_open(RzIO *io, const char *pathname, bool many)
Definition: io_bfdbg.c:151
static RzIODesc * __open(RzIO *io, const char *pathname, int rw, int mode)
Definition: io_bfdbg.c:159
static int __write(RzIO *io, RzIODesc *fd, const ut8 *buf, int count)
Definition: io_bfdbg.c:38
static ut64 __lseek(RzIO *io, RzIODesc *fd, ut64 offset, int whence)
Definition: io_bfdbg.c:142
static int __close(RzIODesc *fd)
Definition: io_bfdbg.c:130
static char * __system(RzIO *io, RzIODesc *fd, const char *cmd)
Definition: io_bochs.c:90
static int __getpid(RzIODesc *fd)
Definition: io_gdb.c:177
RZ_API RzLibStruct rizin_plugin
Definition: io_ptrace.c:362
struct rz_io_plugin_t rz_io_plugin_ptrace
Definition: io_ptrace.c:356
voidpf uLong offset
Definition: ioapi.h:144
const char int mode
Definition: ioapi.h:137
voidpf void * buf
Definition: ioapi.h:138
snprintf
Definition: kernel.h:364
uint8_t ut8
Definition: lh5801.h:11
return memset(p, 0, total)
memcpy(mem, inblock.get(), min(CONTAINING_RECORD(inblock.get(), MEMBLOCK, data) ->size, size))
static static fork const void static count static fd const char const char static newpath char char char static envp time_t static t const char static mode static whence const char static dir time_t static t unsigned static seconds const char struct utimbuf static buf static inc pid
Definition: sflib.h:64
while(len< limit &&buf1[len]==buf2[len])++len
int x
Definition: mipsasm.c:20
#define eprintf(x, y...)
Definition: rlcc.c:7
RZ_API void rz_free_aligned(void *p)
Definition: alloc.c:73
RZ_API void * rz_malloc_aligned(size_t size, size_t alignment)
Definition: alloc.c:62
#define RZ_IO_SEEK_CUR
Definition: rz_io.h:16
RZ_API RzIODesc * rz_io_desc_new(RzIO *io, RzIOPlugin *plugin, const char *uri, int flags, int mode, void *data)
Definition: io_desc.c:11
#define RZ_IO_SEEK_SET
Definition: rz_io.h:15
#define RZ_IO_SEEK_END
Definition: rz_io.h:17
@ RZ_LIB_TYPE_IO
Definition: rz_lib.h:69
RZ_API char * rz_str_newf(const char *fmt,...) RZ_PRINTF_CHECK(1
RZ_API char * rz_sys_pid_to_path(int pid)
Definition: sys.c:920
#define RZ_NEW0(x)
Definition: rz_types.h:284
#define RZ_PERM_X
Definition: rz_types.h:95
#define UT64_MAX
Definition: rz_types_base.h:86
#define ST64_MAX
Definition: rz_types_base.h:84
#define RZ_VERSION
Definition: rz_version.h:8
static int
Definition: sfsocketcall.h:114
#define EINVAL
Definition: sftypes.h:132
#define ESRCH
Definition: sftypes.h:113
#define O_RDONLY
Definition: sftypes.h:486
#define O_RDWR
Definition: sftypes.h:488
#define EPERM
Definition: sftypes.h:111
@ PTRACE_ATTACH
Definition: sftypes.h:617
@ PTRACE_DETACH
Definition: sftypes.h:621
#define d(i)
Definition: sha256.c:44
Definition: gzappend.c:170
const char * name
Definition: rz_io.h:115
const char * version
Definition: rz_io.h:117
Definition: rz_io.h:59
ut64 off
Definition: rz_io.h:61
PrintfCallback cb_printf
Definition: rz_io.h:91
Definition: dis.h:43
if(dbg->bits==RZ_SYS_BITS_64)
Definition: windows-arm64.h:4
ut64(WINAPI *w32_GetEnabledXStateFeatures)()
static const z80_opcode fd[]
Definition: z80_tab.h:997
static int addr
Definition: z80asm.c:58
#define SEEK_SET
Definition: zip.c:88
int read(izstream &zs, T *x, Items items)
Definition: zstream.h:115