2 * peekfd.c - Intercept file descriptor read and writes
4 * Copyright (C) 2007 Trent Waddington <trent.waddington@gmail.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include <sys/ptrace.h>
27 #include <sys/types.h>
29 #include <sys/syscall.h>
40 #define REG_ORIG_ACCUM orig_eax
42 #define REG_PARAM1 ebx
43 #define REG_PARAM2 ecx
44 #define REG_PARAM3 edx
46 #define REG_ORIG_ACCUM orig_rax
48 #define REG_PARAM1 rdi
49 #define REG_PARAM2 rsi
50 #define REG_PARAM3 rdx
52 #define REG_ORIG_ACCUM gpr[0]
53 #define REG_ACCUM gpr[3]
54 #define REG_PARAM1 orig_gpr3
55 #define REG_PARAM2 gpr[4]
56 #define REG_PARAM3 gpr[5]
62 #define MAX_ATTACHED_PIDS 1024
63 int num_attached_pids = 0;
64 pid_t attached_pids[MAX_ATTACHED_PIDS];
68 for (i = 0; i < num_attached_pids; i++)
69 ptrace(PTRACE_DETACH, attached_pids[i], 0, 0);
72 void attach(pid_t pid) {
73 attached_pids[0] = pid;
74 if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
75 fprintf(stderr, _("Error attaching to pid %i\n"), pid);
83 fprintf(stderr, _("peekfd (PSmisc) %s\n"), VERSION);
85 "Copyright (C) 2007 Trent Waddington\n\n"));
87 "PSmisc comes with ABSOLUTELY NO WARRANTY.\n"
88 "This is free software, and you are welcome to redistribute it under\n"
89 "the terms of the GNU General Public License.\n"
90 "For more information about these matters, see the files named COPYING.\n"));
95 "Usage: peekfd [-8] [-n] [-c] [-d] [-V] [-h] <pid> [<fd> ..]\n"
96 " -8 output 8 bit clean streams.\n"
97 " -n don't display read/write from fd headers.\n"
98 " -c peek at any new child processes too.\n"
99 " -d remove duplicate read/writes from the output.\n"
100 " -V prints version info.\n"
101 " -h prints this help.\n"
103 " Press CTRL-C to end output.\n"));
106 int bufdiff(int pid, unsigned char *lastbuf, unsigned int addr, unsigned int len) {
108 for (i = 0; i < len; i++)
109 if (lastbuf[i] != (ptrace(PTRACE_PEEKTEXT, pid, addr + i, 0) & 0xff))
114 int main(int argc, char **argv)
116 int eight_bit_clean = 0;
118 int follow_forks = 0;
119 int remove_duplicates = 0;
126 struct option options[] = {
127 {"eight-bit-clean", 0, NULL, '8'},
128 {"no-headers", 0, NULL, 'n'},
129 {"follow", 0, NULL, 'f'},
130 {"duplicates-removed", 0, NULL, 'd'},
131 {"help", 0, NULL, 'h'},
132 {"version", 0, NULL, 'V'},
137 setlocale(LC_ALL, "");
138 bindtextdomain(PACKAGE, LOCALEDIR);
147 while ((optc = getopt_long(argc, argv, "8ncdhV",options, NULL)) != -1) {
159 remove_duplicates = 1;
170 /* First arg off the options is the PID to see */
171 if (optind >= argc) {
175 target_pid = atoi(argv[optind++]);
178 numfds = argc - optind;
179 fds = malloc(sizeof(int) * numfds);
180 for (i = 0; i < numfds; i++)
181 fds[i] = atoi(argv[optind + 1 + i]);
185 if (num_attached_pids == 0)
190 ptrace(PTRACE_SYSCALL, attached_pids[0], 0, 0);
193 int lastfd = numfds > 0 ? fds[0] : 0;
195 unsigned char *lastbuf = NULL;
196 int last_buf_size=-1;
200 int pid = wait(&status);
201 if (WIFSTOPPED(status)) {
204 regs.gpr[0] = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_R0, 0);
205 regs.gpr[3] = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_R3, 0);
206 regs.gpr[4] = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_R4, 0);
207 regs.gpr[5] = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_R5, 0);
208 regs.orig_gpr3 = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_ORIG_R3, 0);
210 struct user_regs_struct regs;
211 ptrace(PTRACE_GETREGS, pid, 0, ®s);
213 /*unsigned int b = ptrace(PTRACE_PEEKTEXT, pid, regs.eip, 0);*/
214 if (follow_forks && (regs.REG_ORIG_ACCUM == SYS_fork || regs.REG_ORIG_ACCUM == SYS_clone)) {
215 if (regs.REG_ACCUM > 0)
216 attach(regs.REG_ACCUM);
218 if ((regs.REG_ORIG_ACCUM == SYS_read || regs.REG_ORIG_ACCUM == SYS_write) && (regs.REG_PARAM3 == regs.REG_ACCUM)) {
219 for (i = 0; i < numfds; i++)
220 if (fds[i] == regs.REG_PARAM1)
222 if (i != numfds || numfds == 0) {
223 if (regs.REG_PARAM1 != lastfd || regs.REG_ORIG_ACCUM != lastdir) {
224 lastfd = regs.REG_PARAM1;
225 lastdir = regs.REG_ORIG_ACCUM;
227 printf("\n%sing fd %i:\n", regs.REG_ORIG_ACCUM == SYS_read ? "read" : "writ", lastfd);
229 if (!remove_duplicates || lastbuf == NULL
230 || last_buf_size != regs.REG_PARAM3 ||
231 bufdiff(pid, lastbuf, regs.REG_PARAM2, regs.REG_PARAM3)) {
233 if (remove_duplicates) {
236 lastbuf = malloc(regs.REG_PARAM3);
237 last_buf_size = regs.REG_PARAM3;
240 for (i = 0; i < regs.REG_PARAM3; i++) {
242 unsigned int a = bswap_32(ptrace(PTRACE_PEEKTEXT, pid, regs.REG_PARAM2 + i, 0));
244 unsigned int a = ptrace(PTRACE_PEEKTEXT, pid, regs.REG_PARAM2 + i, 0);
246 if (remove_duplicates)
247 lastbuf[i] = a & 0xff;
252 if (isprint(a & 0xff) || (a & 0xff) == '\n')
253 printf("%c", a & 0xff);
254 else if ((a & 0xff) == 0x0d)
256 else if ((a & 0xff) == 0x7f)
259 printf(" [%02x] ", a & 0xff);
267 ptrace(PTRACE_SYSCALL, pid, 0, 0);