int is_in_syscall(pid_t pid, struct user *user) { long inst; /* If it's in the vsyscall DSO, we've most likely interrupted a syscall. */ if (user->regs.eip >= vdso_start && user->regs.eip < vdso_end) return 1; inst = ptrace(PTRACE_PEEKDATA, pid, user->regs.eip-2, 0); if (errno) { perror("ptrace(PEEKDATA)"); return 0; } return is_a_syscall(inst, 0); }
static int get_one_vma(pid_t pid, char* line, struct cp_vma *vma, int get_library_data, int vma_no, long *bin_offset) { char *ptr1, *ptr2; int dminor, dmajor; int old_vma_prot = -1; int keep_vma_data; static long last_vma_end; memset(vma, 0, sizeof(struct cp_vma)); /* Parse a line that looks like one of the following: 08048000-080ab000 r-xp 00000000 03:03 1309106 /home/b/dev/sp/test 080ab000-080ae000 rw-p 00062000 03:03 1309106 /home/b/dev/sp/test 080ae000-080db000 rwxp 00000000 00:00 0 40000000-40203000 rw-p 00000000 00:00 0 bfffe000-c0000000 rwxp 00000000 00:00 0 */ ptr1 = line; if ((ptr2 = strchr(ptr1, '-')) == NULL) { fprintf(stderr, "No - in map line!\n"); return 0; } *ptr2 = '\0'; vma->start = strtoul(ptr1, NULL, 16); if (vma->start >= TRAMPOLINE_ADDR && vma->start <= TRAMPOLINE_ADDR+PAGE_SIZE) { fprintf(stderr, " Ignoring map - looks like resumer trampoline.\n"); return 0; } if (vma->start >= RESUMER_START && vma->start <= RESUMER_END) { fprintf(stderr, " Ignoring map - looks like resumer.\n"); return 0; } ptr1 = ptr2+1; if ((ptr2 = strchr(ptr1, ' ')) == NULL) { fprintf(stderr, "No end of length in map line!\n"); return 0; } *ptr2 = '\0'; vma->length = strtoul(ptr1, NULL, 16) - vma->start; if (vma->start >= get_task_size()) { if (strstr(ptr2+1, "[vdso]")) { vdso_start = vma->start; vdso_end = vma->start + vma->length; fprintf(stderr, " Ignoring map - vsyscall page.\n"); } else fprintf(stderr, " Ignoring map - in kernel space.\n"); return 0; } vma->prot = 0; ptr1 = ptr2+1; if (ptr1[0] == 'r') vma->prot |= PROT_READ; else if (ptr1[0] != '-') fprintf(stderr, "Bad read flag: %c\n", ptr1[0]); if (ptr1[1] == 'w') vma->prot |= PROT_WRITE; else if (ptr1[1] != '-') fprintf(stderr, "Bad write flag: %c\n", ptr1[1]); if (ptr1[2] == 'x') vma->prot |= PROT_EXEC; else if (ptr1[2] != '-') fprintf(stderr, "Bad exec flag: %c\n", ptr1[2]); vma->flags = MAP_FIXED; if (ptr1[3] == 's') vma->flags |= MAP_SHARED; else if (ptr1[3] != 'p') fprintf(stderr, "Bad shared flag: %c\n", ptr1[3]); else vma->flags |= MAP_PRIVATE; ptr1 = ptr1+5; /* to pgoff */ if ((ptr2 = strchr(ptr1, ' ')) == NULL) { fprintf(stderr, "No end of pgoff in map line!\n"); return 0; } *ptr2 = '\0'; vma->pg_off = strtoul(ptr1, NULL, 16); if ((signed long)vma->pg_off < 0) { vma->flags |= MAP_GROWSDOWN; } ptr1 = ptr2+1; if ((ptr2 = strchr(ptr1, ':')) == NULL) { fprintf(stderr, "No end of major dev in map line!\n"); return 0; } *ptr2 = '\0'; dmajor = strtoul(ptr1, NULL, 16); ptr1 = ptr2+1; if ((ptr2 = strchr(ptr1, ' ')) == NULL) { fprintf(stderr, "No end of minor dev in map line!\n"); return 0; } *ptr2 = '\0'; dminor = strtoul(ptr1, NULL, 16); vma->dev = MKDEV(dmajor, dminor); /* Decide if we just missed the heap entirely */ if (vma_no == 2 && vma->prot != (PROT_READ | PROT_WRITE)) { if (bin_offset && !*bin_offset) *bin_offset = last_vma_end; } ptr1 = ptr2+1; if ((ptr2 = strchr(ptr1, ' ')) != NULL) { *ptr2 = '\0'; vma->inode = strtoul(ptr1, NULL, 10); ptr1 = ptr2+1; while (*ptr1 == ' ') ptr1++; if (*ptr1 != '\n') { /* we have a filename too to grab */ ptr2 = strchr(ptr1, '\n'); if (ptr2) *ptr2 = '\0'; vma->filename = strdup(ptr1); if (bin_offset && !*bin_offset && !strcmp(vma->filename, "[heap]")) { *bin_offset = vma->start; vma->flags |= MAP_ANONYMOUS; vma->is_heap = 1; } } else { if (bin_offset && !*bin_offset && ((vma->prot & (PROT_READ|PROT_WRITE)) == (PROT_READ|PROT_WRITE))) { /* First rw* anonymous segment off the rank - well it looks like * a heap :) */ *bin_offset = vma->start; vma->is_heap = 1; } vma->flags |= MAP_ANONYMOUS; } } else { vma->inode = strtoul(ptr1, NULL, 10); } /* we have all the info we need, regurgitate it for confirmation */ fprintf(stderr, "Map: %08lx-%08lx %c%c%c%c %08lx %02x:%02x %-10d %s\n", vma->start, vma->start + vma->length, (vma->prot & PROT_READ)?'r':'-', (vma->prot & PROT_WRITE)?'w':'-', (vma->prot & PROT_EXEC)?'x':'-', (vma->flags & MAP_SHARED)?'s':'p', vma->pg_off, MAJOR(vma->dev), MINOR(vma->dev), vma->inode, vma->filename); if (!(vma->prot & PROT_READ)) { /* we need to modify it to be readable */ old_vma_prot = vma->prot; if (syscall_loc) { r_mprotect(pid, (void*)vma->start, vma->length, PROT_READ); } else { /* We need to come back to this later. */ return -1; } } /* Decide if it's scribble worthy - find a nice anonymous mapping */ if (scribble_zone == 0 && !vma->filename && (vma->flags & MAP_PRIVATE) && !(vma->flags & MAP_SHARED) && ((vma->prot & (PROT_READ|PROT_WRITE)) == (PROT_READ|PROT_WRITE))) { scribble_zone = vma->start; debug("[+] Found scribble zone: 0x%lx", scribble_zone); } /* Fetch the data, at least for checksumming purposes. */ vma->data = xmalloc(vma->length); memcpy_from_target(pid, vma->data, (void*)vma->start, vma->length); vma->checksum = checksum(vma->data, vma->length, 0); /* Decide if it contains a syscall function that's of use to us */ if (syscall_loc == 0 && (vma->flags & MAP_PRIVATE) && !(vma->flags & MAP_SHARED) && (vma->prot & (PROT_READ|PROT_EXEC))) { char *p, *end; p = (char*)vma->data; end = p + vma->length - sizeof(long) + 1; while (p < end) { if (is_a_syscall(*(long*)p, 1)) { syscall_loc = vma->start + (p - (char*)vma->data); debug("[+] Found a syscall location at 0x%lx", syscall_loc); break; } #ifdef ARCH_HAS_ALIGNED_INSTRUCTIONS p += sizeof(long); #else p++; #endif } } /* Cases where we want to keep the VMA in the image */ keep_vma_data = ( get_library_data || ((vma->prot & PROT_WRITE) && (vma->flags & MAP_PRIVATE)) || (vma->flags & MAP_ANONYMOUS) ); /* If it's on disk and we're not saving libraries, checksum the source to * verify it really is the same. */ if (!keep_vma_data && vma->filename) { int lfd; int remaining; unsigned int c; static char buf[4096]; keep_vma_data = 1; /* Assume guiltly until proven innocent */ if ((lfd = open(vma->filename, O_RDONLY)) == -1) goto out; if (lseek(lfd, vma->pg_off, SEEK_SET) != vma->pg_off) goto out_close; remaining = vma->length; c = 0; while (remaining > 0) { int len = sizeof(buf), rlen; if (len > remaining) len = remaining; rlen = read(lfd, buf, len); if (rlen == -1) goto out_close; if (rlen == 0) break; c = checksum(buf, rlen, c); remaining -= rlen; } /* Pad out the rest with NULLs */ memset(buf, 0, sizeof(buf)); while (remaining > 0) { int remsz = sizeof(buf); if (remsz > remaining) remsz = remaining; c = checksum(buf, remsz, c); remaining -= remsz; } /* So did we have a good checksum after all that? */ if (c == vma->checksum) keep_vma_data = 0; out_close: close(lfd); } out: /* Figure out if we need to keep it */ if (vma->data && keep_vma_data) { vma->have_data = 1; } else { free(vma->data); vma->data = NULL; } if (old_vma_prot != -1) r_mprotect(pid, (void*)vma->start, vma->length, old_vma_prot); last_vma_end = vma->start + vma->length; return 1; }