├── Makefile ├── README.md ├── pstrings.1 └── pstrings.c /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX := /usr/local 2 | CFLAGS=-g -Wall -Os 3 | 4 | pstrings: pstrings.o 5 | 6 | clean: 7 | rm -f pstrings.o pstrings 8 | 9 | install: pstrings pstrings.1 10 | cp pstrings ${PREFIX}/bin 11 | cp pstrings.1 ${PREFIX}/share/man/man1 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | = pstrings = 3 | 4 | pstrings is a strings program for Linux processes. You specify the pid, and it dumps all the printable strings for the processes' address space. This can be useful to get data out of uncooperative programs. 5 | 6 | To build: 7 | 8 | make 9 | sudo make install (to /usr/local/bin, overwrite with PREFIX=....) 10 | 11 | To use: 12 | 13 | pstrings $(pidof process-name) 14 | Options: 15 | -nMINLENGTH only display strings of MINLENGTH and longer (default 4) 16 | -r include read-only mappings (conflicts with -a) 17 | -x include executable mappings (conflicts with -a) 18 | -a include all mappings (conflicts with -r/-x) 19 | -o prefix each string with address in program 20 | -p prefix each string with pid 21 | -m prefix each string with mapping name 22 | -fPERCENT only output strings with at least PERCENT alpha-numeric characters 23 | -lLOCALE use LOCALE to decide for printable strings (only 8bit)); 24 | 25 | 26 | Andi Kleen 27 | -------------------------------------------------------------------------------- /pstrings.1: -------------------------------------------------------------------------------- 1 | .TH PSTRINGS "1" "April 2010" "Firstfloor Electronix" "Hacks" 2 | .SH NAME 3 | pstrings \- dump strings from running processes 4 | .SH SYNOPSIS 5 | pstrings options... pid ...\n"); 6 | .SH DESCRIPTION 7 | pstrings displays printable strings from the memory of running processes specified by pid(s). 8 | By default only writable and non executable areas are dumped. 9 | 10 | .SH OPTIONS 11 | .BR -nMINLENGTH 12 | .br 13 | Only display strings of MINLENGTH and longer (default 4) 14 | .PP 15 | 16 | .BR -r 17 | .br 18 | Include read-only mappings (conflicts with -a) 19 | .PP 20 | 21 | .BR -x 22 | .br 23 | Include executable mappings (conflicts with -a) 24 | .PP 25 | .BR -a 26 | .br 27 | Include all mappings (conflicts with -r/-x). 28 | .PP 29 | .BR -o 30 | .br 31 | Prefix each string with address in program. 32 | .PP 33 | .BR -p 34 | .br 35 | Prefix each string with pid. 36 | .PP 37 | .BR -m 38 | .br 39 | Prefix each string with mapping name. 40 | .PP 41 | .BR -fPERCENT 42 | .br 43 | Only output strings with at least 44 | .I PERCENT 45 | alpha-numeric characters. 46 | .PP 47 | .BR -lLOCALE 48 | .br 49 | Use 50 | .I LOCALE 51 | to determine what a printable string is. 52 | 53 | .SH NOTES 54 | When the process is already debugged it cannot be dumped. 55 | 56 | The process is stopped during the whole dump process. This can be arbitarily 57 | long depending on the speed of the pstrings output device and the memory size of the process. 58 | 59 | What a printable string is, is decided by using isprint in the POSIX C locale by default, 60 | unless you use the -l option. The environment locale is ignored. 61 | 62 | There is no support for wide character strings. 63 | .SH SEE ALSO 64 | .BR strings(1), 65 | .BR gdb(1) 66 | -------------------------------------------------------------------------------- /pstrings.c: -------------------------------------------------------------------------------- 1 | /* strings on a running process 2 | * 3 | * Copyright (c) 2013 Intel Corporation 4 | * Author: Andi Kleen 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that: (1) source code distributions 8 | * retain the above copyright notice and this paragraph in its entirety, (2) 9 | * distributions including binary code include the above copyright notice and 10 | * this paragraph in its entirety in the documentation or other materials 11 | * provided with the distribution 12 | * 13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 14 | * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 | * 17 | * Compilation: make pstrings 18 | * Run without arguments for usage. 19 | * 20 | * Notebook: 21 | * Process is stopped for full dump. Yield regularly? 22 | * Support for wide character sets. 23 | */ 24 | 25 | #define _GNU_SOURCE 1 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #define DEFAULT_PAT "rw[^x]?" 41 | 42 | int minlength = 4; 43 | char mappat[10] = DEFAULT_PAT; 44 | float filterp; 45 | 46 | enum { 47 | PID = (1 << 0), 48 | ADDRESS = (1 << 1), 49 | MAPPING = (1 << 2), 50 | ALNUM_P = (1 << 3), 51 | } printflags; 52 | 53 | /* Return nth field in a string. */ 54 | /* not generic. does not terminate field. no error check. */ 55 | char *nth_field(char *s, int n) 56 | { 57 | int i; 58 | for (i = 0; i < n; i++) { 59 | s += strcspn(s, " \t"); 60 | s += strspn(s, " \t"); 61 | } 62 | return s; 63 | } 64 | 65 | /* Check percentage of alpha-numeric characters in a string */ 66 | int alnum_filter(char *s, int len) 67 | { 68 | int i; 69 | int n = 0; 70 | float p; 71 | 72 | for (i = 0; i < len; i++) 73 | if (isalnum(s[i])) 74 | n++; 75 | p = ((float)n / (float)len) * 100; 76 | return p >= filterp; 77 | } 78 | 79 | struct context { 80 | pid_t pid; 81 | char *mapping; 82 | char *buf; 83 | }; 84 | 85 | /* Print a string with optional annotations. */ 86 | void dump(char *s, int len, unsigned long long addr, struct context *ctx) 87 | { 88 | if (printflags) { 89 | if ((printflags & ALNUM_P) && !alnum_filter(s, len)) 90 | return; 91 | if (printflags & PID) 92 | printf("%u:", ctx->pid); 93 | if (printflags & MAPPING) 94 | printf("%.*s:", (int)strcspn(ctx->mapping, "\n"), 95 | ctx->mapping); 96 | if (printflags & ADDRESS) 97 | printf("%llx:", addr); 98 | } 99 | fwrite(s, 1, len, stdout); 100 | putchar('\n'); 101 | } 102 | 103 | /* Find printable strings in a memory area. */ 104 | void strings(char *buf, int buflen, unsigned long long addr, struct context *ctx) 105 | { 106 | int i; 107 | int len = 0; 108 | 109 | for (i = 0; i < buflen; i++) { 110 | if (isprint(buf[i])) { 111 | len++; 112 | continue; 113 | } 114 | if (len >= minlength) 115 | dump(buf + i - len, len, addr + i - len, ctx); 116 | len = 0; 117 | } 118 | if (len >= minlength) 119 | dump(buf + i - len, len, addr + i - len, ctx); 120 | } 121 | 122 | #define TRANSFER (1 << 20) 123 | 124 | /* Copy and process a single mapping. */ 125 | int mapping_strings(int fd, unsigned long long start, 126 | unsigned long long end, struct context *ctx) 127 | { 128 | while (start < end) { 129 | uint64_t len = end - start; 130 | if (len > TRANSFER) 131 | len = TRANSFER; 132 | 133 | int n = pread(fd, ctx->buf, len, start); 134 | if (n > 0) 135 | strings(ctx->buf, n, start, ctx); 136 | else if (n < 0) 137 | fprintf(stderr, "Cannot read %llx-%llx: %s\n", 138 | start, start + len, 139 | strerror(errno)); 140 | 141 | start += len; 142 | } 143 | return 0; 144 | } 145 | 146 | /* Find all mappings in a process */ 147 | int process_mappings(struct context *ctx, int memfd) 148 | { 149 | char fn[64]; 150 | 151 | snprintf(fn, sizeof fn, "/proc/%u/maps", ctx->pid); 152 | FILE *mf = fopen(fn, "r"); 153 | if (!mf) { 154 | perror(fn); 155 | return -1; 156 | } 157 | 158 | char *line = NULL; 159 | size_t linelen = 0; 160 | int n; 161 | while ((n = getline(&line, &linelen, mf)) > 0) { 162 | unsigned long long start, end; 163 | char acc[10]; 164 | 165 | if (sscanf(line, "%llx-%llx %9s", &start, &end, acc) != 3) 166 | continue; 167 | 168 | if (fnmatch(mappat, acc, 0) == 0) { 169 | ctx->mapping = nth_field(line, 5); 170 | if (ctx->mapping[0] == '\n') 171 | ctx->mapping = "anon"; 172 | mapping_strings(memfd, start, end, ctx); 173 | } 174 | } 175 | free(line); 176 | fclose(mf); 177 | return 0; 178 | } 179 | 180 | /* Attach to a pid and call workers */ 181 | int attach(pid_t pid) 182 | { 183 | int ret = -1; 184 | int memfd = -1; 185 | struct context ctx; 186 | 187 | if (ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) { 188 | perror("PTRACE_ATTACH"); 189 | return -1; 190 | } 191 | 192 | if (waitpid(pid, NULL, 0) < 0) 193 | perror("waitpid"); 194 | 195 | char fn[60]; 196 | snprintf(fn, sizeof fn, "/proc/%u/mem", pid); 197 | memfd = open(fn, O_RDONLY); 198 | if (memfd < 0) { 199 | perror(fn); 200 | goto error; 201 | } 202 | 203 | ctx.buf = malloc(TRANSFER); 204 | if (!ctx.buf) 205 | exit(ENOMEM); 206 | ctx.pid = pid; 207 | 208 | ret = process_mappings(&ctx, memfd); 209 | free(ctx.buf); 210 | error: 211 | close(memfd); 212 | if (ptrace(PTRACE_DETACH, pid, 0, 0) < 0) 213 | perror("PTRACE_DETACH"); 214 | return ret; 215 | } 216 | 217 | void usage(void) 218 | { 219 | fprintf(stderr, "Usage: pstrings options... pid ...\n"); 220 | fprintf(stderr, 221 | "Display strings in the memory of a running process.\n" 222 | "Options:\n" 223 | "-nMINLENGTH only display strings of MINLENGTH and longer (default 4)\n" 224 | "-r include read-only mappings (conflicts with -a)\n" 225 | "-x include executable mappings (conflicts with -a)\n" 226 | "-a include all mappings (conflicts with -r/-x)\n" 227 | "-o prefix each string with address in program\n" 228 | "-p prefix each string with pid\n" 229 | "-m prefix each string with mapping name\n" 230 | "-fPERCENT only output strings with at least PERCENT alpha-numeric characters\n" 231 | "-lLOCALE use LOCALE to decide for printable strings (only 8bit)\n"); 232 | exit(1); 233 | } 234 | 235 | int main(int ac, char **av) 236 | { 237 | int err = 0; 238 | int c; 239 | 240 | while ((c = getopt(ac, av, "n:rxf:mpoal:")) != -1) { 241 | switch (c) { 242 | case 'n': 243 | minlength = atoi(optarg); 244 | if (minlength < 1 || minlength > TRANSFER) 245 | usage(); 246 | break; 247 | case 'r': 248 | if (mappat[0] == '*') 249 | usage(); 250 | mappat[1] = '?'; 251 | break; 252 | case 'x': 253 | if (mappat[0] == '*') 254 | usage(); 255 | mappat[2] = '*'; 256 | mappat[3] = 0; 257 | break; 258 | case 'a': 259 | if (strcmp(mappat, DEFAULT_PAT)) 260 | usage(); 261 | strcpy(mappat, "*"); 262 | break; 263 | case 'm': 264 | printflags |= MAPPING; 265 | break; 266 | case 'p': 267 | printflags |= PID; 268 | break; 269 | case 'o': 270 | printflags |= ADDRESS; 271 | break; 272 | case 'f': 273 | printflags |= ALNUM_P; 274 | if (sscanf(optarg, "%f", &filterp) != 1) 275 | usage(); 276 | break; 277 | case 'l': 278 | if (setlocale(LC_CTYPE, optarg) == NULL) { 279 | fprintf(stderr, "Cannot set locale `%s'\n", optarg); 280 | exit(1); 281 | } 282 | break; 283 | default: 284 | usage(); 285 | } 286 | 287 | } 288 | if (optind == ac) 289 | usage(); 290 | av += optind - 1; 291 | 292 | while (*++av) { 293 | char *end; 294 | long pid = strtoul(*av, &end, 0); 295 | if (end == *av) 296 | usage(); 297 | err |= attach(pid); 298 | } 299 | return err; 300 | } 301 | --------------------------------------------------------------------------------