/* * need to #define SH_USE_KERN * */ #define SH_SYSCALL_CODE #include "config.h" #ifdef HOST_IS_I86LINUX #define SH_IDT_TABLE #endif #include #include #if defined(SH_USE_KERN) #undef _ #define _(string) string #undef N_ #define N_(string) string void usage(int flag) { printf("\n"); printf("Usage: kern_head [-v | --verbose]\n"); printf(" kern_head [-h | --help]\n"); printf("\n"); /* * printf(" You need superuser privileges to use this program,\n"); * printf(" because only the superuser can read from /dev/kmem.\n"); * printf("\n"); */ exit(flag); } #if defined(HOST_IS_LINUX) #include #include #include #include #include #include #include #include #include /* number of system calls */ #define SH_MAXCALLS 512 #include "kern_head.h" static int verbose = 0; typedef struct _smap_entry { #ifdef SH_SYSCALL_CODE unsigned int code[2]; /* 8 bytes */ #endif unsigned long addr; char name[64]; } smap_entry; union { unsigned long addr_sys_call_table; unsigned char str_sys_call_table[sizeof(unsigned long)]; } sh_sys_call; #define SYS_CODE_SIZE 1024 static unsigned long addr_system_call; static unsigned char system_call_code[SYS_CODE_SIZE]; static int kmem_read (int fd, unsigned long addr, unsigned char * buf, int len) { if (lseek(fd, addr, SEEK_SET) == (off_t) (-1)) { if (verbose) perror("kmem_read: lseek"); return -1; } if (read(fd, buf, len) < 0) { if (verbose) perror("kmem_read: read"); return -1; } return 0; } static int kmem_mmap (int fd, unsigned long addr, unsigned char * buf, int len) { size_t moff, roff; size_t sz; char * kmap; /* first, try read() */ int ret_old = kmem_read (fd, addr, buf, len); if (ret_old == 0) return ret_old; if (verbose) fprintf(stderr, "kmem_mmap: read() from /dev/kmem failed, now trying mmap()\n"); sz = getpagesize(); /* unistd.h */ moff = ((size_t)(addr/sz)) * sz; /* lower page boundary */ roff = addr - moff; /* off relative to lower address of mmapped area */ kmap = mmap(0, len+sz, PROT_READ, MAP_PRIVATE, fd, moff);/* sys/mman.h */ if (kmap == MAP_FAILED) { perror("kmem_mmap: mmap"); return -1; } memcpy (buf, &kmap[roff], len); if (munmap(kmap, len+sz) != 0) { perror("kmem_mmap: munmap"); return -1; } return 0; } int read_kcode (unsigned long addr, unsigned char * buf, int len) { int fd; if (addr == 0UL) { perror("read_kcode: invalid input"); return -1; } fd = open ("/dev/kmem", O_RDONLY); if (fd < 0) { perror("read_kcode: open /dev/kmem"); return -1; } if (kmem_mmap(fd, addr, buf, len) < 0) { close (fd); return -1; } close (fd); return 0; } int get_dispatch (int * qq) { int i; if (addr_system_call == 0L || sh_sys_call.addr_sys_call_table == 0L) { fprintf(stderr, "get_dispatch: invalid data\n"); return -1; } if (0 != read_kcode (addr_system_call, system_call_code, SYS_CODE_SIZE)) { fprintf(stderr, "get_dispatch: could not read system_call code\n"); return -1; } for (i = 0; i < (SYS_CODE_SIZE - 4); ++i) { if (system_call_code[i] == sh_sys_call.str_sys_call_table[0] && system_call_code[i+1] == sh_sys_call.str_sys_call_table[1] && system_call_code[i+2] == sh_sys_call.str_sys_call_table[2] && system_call_code[i+3] == sh_sys_call.str_sys_call_table[3]) { /* fprintf(stderr, "INFO: get_dispatch: found sys_call_table in "\ "system_call code at %d\n", i); */ *qq = i; return 0; } } fprintf(stderr, "get_dispatch: did not find sys_call_table in system_call code\n"); fprintf(stderr, "** This indicates that either your System.map does not match\n"); fprintf(stderr, "** the currently running kernel, or that your System.map does\n"); fprintf(stderr, "** not provide the required information, and thus use of\n"); fprintf(stderr, "** the --with-kcheck option is not possible\n"); return -1; } unsigned long get_symbol_from_systemmap (char * systemmap, char * symbol, char flag) { FILE * fp; char buf[512], addr[16], * p; unsigned long retval = 0; fp = fopen (systemmap, "r"); if (!fp) { fprintf(stderr, "error opening <%s>\n", systemmap); perror("get_symbol_from_systemmap: fopen"); return -1; } while (fgets(buf, 512, fp) != NULL) { if (buf[9] != flag) continue; p = strchr(buf, '\n'); if (p != NULL) *p = '\0'; if (0 != strcmp(&buf[11], symbol)) continue; addr[0] = '0'; addr[1] = 'x'; addr[2] = '\0'; strncat(&addr[2], buf, 8); retval = strtoul(addr, NULL, 0); if (retval == ULONG_MAX) { perror("get_symbol_from_systemmap"); return -1; } } fclose(fp); return retval; } /* returns the number N of entries in syscall table * (0 .. N-1) with valid syscalls */ int fill_smap(smap_entry * sh_smap, int num) { FILE * fp; char buf[512], addr[16], name[128]; int i, j, count = 0, maxcall = 0; fp = fopen (SYSTEMMAP, "r"); if (!fp) { perror("fill_smap: fopen"); fprintf(stderr, "fill_smap: error opening <%s>\n", SYSTEMMAP); return -1; } /* initialize */ sh_sys_call.addr_sys_call_table = 0L; while (fgets(buf, 512, fp) != NULL) { if ( ( (buf[9] == 'D') || (buf[9] == 'd') || (buf[9] == 'R') || (buf[9] == 'r')) && 0 == strncmp("sys_call_table", &buf[11], 14)) { printf("/* found sys_call_table */\n"); /* --- copy symbol address --- */ addr[0] = '0'; addr[1] = 'x'; addr[2] = '\0'; strncat(&addr[2], buf, 8); addr[10] = '\0'; sh_sys_call.addr_sys_call_table = strtoul(addr, NULL, 0); if (sh_sys_call.addr_sys_call_table == ULONG_MAX) { perror("fill_smap"); return -1; } else { printf("#define SH_SYS_CALL_TABLE %s\n", addr); } } if (buf[9] != 'T') continue; if (0 == strncmp("system_call", &buf[11], 11)) { printf("/* found system_call */\n"); /* --- copy symbol address --- */ addr[0] = '0'; addr[1] = 'x'; addr[2] = '\0'; strncat(&addr[2], buf, 8); addr[10] = '\0'; addr_system_call = strtoul(addr, NULL, 0); if (addr_system_call == ULONG_MAX) { perror("fill_smap"); return -1; } } if ( (buf[11]!='s' || buf[12]!='y' || buf[13]!='s' || buf[14]!='_') && (buf[11]!='o' || buf[12]!='l' || buf[13]!='d' || buf[14]!='_')) continue; for (i = 0; i < num; ++i) { for (j = 0; j < 128; ++j) { if (buf[11+j] == '\n' || buf[11+j] == '\0') { name[j] = '\0'; break; } name[j] = buf[11+j]; } if (0 == strcmp(name, sh_smap[i].name)) { /* --- copy symbol address --- */ addr[0] = '0'; addr[1] = 'x'; addr[2] = '\0'; strncat(&addr[2], buf, 8); addr[10] = '\0'; sh_smap[i].addr = strtoul(addr, NULL, 0); if (sh_smap[i].addr == ULONG_MAX) { perror("fill_smap"); return -1; } ++count; if (i > maxcall) maxcall = i; /* printf("maxcall = %d\n", maxcall); */ /* break; */ } } } fclose(fp); if ((count > 0) && (maxcall > 0)) return maxcall+1; else return count; } int main(int argc, char * argv[]) { int i, count, maxcall, qq; int which = 4; int two_six_seventeen_plus = 0; smap_entry sh_smap[SH_MAXCALLS]; struct utsname utbuf; char *p = NULL; unsigned long proc_root; unsigned long proc_root_iops; unsigned long proc_root_lookup; unsigned long addr_ni_syscall = 0; if (argc > 1) { if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) usage(EXIT_SUCCESS); else if (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--verbose") == 0) verbose = 1; } if (0 != uname(&utbuf)) { perror("kern_head: uname"); exit (EXIT_FAILURE); } if (strncmp(utbuf.release, SH_KERNEL_VERSION, 3) != 0) { fprintf(stderr, "kern_head: current kernel version %s does not match\n", utbuf.release); fprintf(stderr, "kern_head: %s from config.h\n", SH_KERNEL_VERSION); fprintf(stderr, "kern_head: continuing with %s\n", SH_KERNEL_VERSION); p = SH_KERNEL_VERSION; } else { p = utbuf.release; } if (strncmp(p, "2.2", 3) == 0) which = 2; else if (strncmp(p, "2.4", 3) == 0) which = 4; else if (strncmp(p, "2.6", 3) == 0) { which = 6; if (17 >= atoi (&p[4])) { two_six_seventeen_plus = 1; } } else { fprintf(stderr, "kern_head: kernel %s not supported\n", p); exit (EXIT_FAILURE); } if (utbuf.machine[0] != 'i' || utbuf.machine[2] != '8' || utbuf.machine[3] != '6') { fprintf(stderr, "kern_head: machine %s not supported\n", utbuf.machine); exit (EXIT_FAILURE); } if (0 != getuid()) { fprintf(stderr, "\n"); fprintf(stderr, "NOTE: kern_head: must run as 'root' (need to read from /dev/kmem)\n"); fprintf(stderr, " If you get this message, then proceed as follows:\n"); fprintf(stderr, " $ su\n"); fprintf(stderr, " $ ./kern_head > sh_ks.h\n"); fprintf(stderr, " $ exit\n"); fprintf(stderr, " $ make\n\n"); exit (EXIT_FAILURE); } printf("#ifndef SH_KERN_CALLS_H\n"); printf("#define SH_KERN_CALLS_H\n\n"); printf("\n/* Kernel %s, machine %s -- use table %s */\n\n", p, utbuf.machine, (which == 2) ? "callz_2p2" : "callz_2p4"); /* initiate the system call table */ for (i = 0; i < SH_MAXCALLS; ++i) { if (which == 2) { if (callz_2p2[i] == NULL) break; strcpy(sh_smap[i].name, callz_2p2[i]); } else { if (callz_2p4[i] == NULL) break; strcpy(sh_smap[i].name, callz_2p4[i]); } sh_smap[i].addr = 0UL; } if (which == 6) /* fix syscall map for 2.6 */ { strcpy(sh_smap[0].name, "sys_restart_syscall"); strcpy(sh_smap[180].name, "sys_pread64"); strcpy(sh_smap[181].name, "sys_pwrite64"); } count = i; /* get the actual number of the highest syscalls and use no more. * get sys_call_table and system_call */ maxcall = fill_smap(sh_smap, count); if ( maxcall < 0) { printf("#endif\n"); fprintf(stderr, "kern_head: fill_smap failed\n"); exit (EXIT_FAILURE); } if (addr_system_call == 0L) { printf("#endif\n"); fprintf(stderr, "kern_head: address of system_call not found in System.map\n"); fprintf(stderr, "** This indicates that your System.map does not provide\n"); fprintf(stderr, "** the required information, and thus use of the\n"); fprintf(stderr, "** --with-kcheck option is not possible\n"); exit (EXIT_FAILURE); } for (i = 0; i < maxcall; ++i) { if (0 == strcmp(sh_smap[i].name, "sys_ni_syscall")) { addr_ni_syscall = sh_smap[i].addr; break; } } if (which < 6) { maxcall = (maxcall > 256) ? 256 : maxcall; } for (i = 0; i < maxcall; ++i) { if (sh_smap[i].addr == 0UL) { if (verbose > 0) fprintf(stderr, "** unknown syscall **: [%s]\n", sh_smap[i].name); strcpy(sh_smap[i].name, "sys_ni_syscall"); sh_smap[i].addr = addr_ni_syscall; } } /* get the location of the syscall table address within system_call */ if ( get_dispatch (&qq) < 0) { printf("#endif\n"); fprintf(stderr, "kern_head: get_dispatch failed\n"); exit (EXIT_FAILURE); } if (qq <= 252) printf("#define SYS_CALL_LOC %d\n", qq); else { printf("#endif\n"); fprintf(stderr, "kern_head: SYS_CALL_LOC (%d) too large\n", qq); exit(EXIT_FAILURE); } printf("#define SH_SYS_CALL_ADDR %#lx\n\n", addr_system_call); printf("static unsigned char system_call_code[256] = { 0 };\n"); printf("#define SH_MAXCALLS %d\n\n", maxcall); #ifdef SH_IDT_TABLE printf("static unsigned char idt_table[2048] = { 0 };\n"); #endif printf("typedef struct _sh_syscall_t {\n"); #ifdef SH_SYSCALL_CODE printf(" unsigned int code[2]; /* 8 bytes */\n"); #endif printf(" unsigned long addr;\n"); printf(" char * name;\n"); printf("} sh_syscall_t;\n\n"); printf("static sh_syscall_t sh_syscalls[] = {\n"); for (i = 0; i < maxcall; ++i) { #ifdef SH_SYSCALL_CODE printf(" /* %03d */ { { 0, 0 }, 0, N_(%c%s%c) },\n", i, '"', sh_smap[i].name, '"'); #else printf(" /* %03d */ { 0, N_(%c%s%c) },\n", i, '"', sh_smap[i].name, '"'); #endif } #ifdef SH_SYSCALL_CODE printf(" /* eof */ { { 0x00000000, 0x00000000 }, 0x00000000, NULL }\n"); #else printf(" /* eof */ { 0x00000000, NULL }\n"); #endif printf("};\n\n"); /* get proc addresses */ proc_root = get_symbol_from_systemmap (SYSTEMMAP, "proc_root", 'D'); if (proc_root == 0) { proc_root = get_symbol_from_systemmap (SYSTEMMAP, "proc_root", 'd'); } if (proc_root == 0) { proc_root = get_symbol_from_systemmap (SYSTEMMAP, "proc_root", 'R'); } if (proc_root != 0) { printf("#define PROC_ROOT_LOC %#lx\n\n", proc_root); } proc_root_lookup = get_symbol_from_systemmap (SYSTEMMAP, "proc_root_lookup", 't'); if (proc_root_lookup == 0) { proc_root_lookup = get_symbol_from_systemmap (SYSTEMMAP, "proc_root_lookup", 'T'); } if (proc_root_lookup != 0) { printf("#define PROC_ROOT_LOOKUP_LOC %#lx\n\n", proc_root_lookup); } proc_root_iops = get_symbol_from_systemmap (SYSTEMMAP, "proc_root_inode_operations", 'd'); if (proc_root_iops == 0) { proc_root_iops = get_symbol_from_systemmap (SYSTEMMAP, "proc_root_inode_operations", 'D'); } if (proc_root_iops == 0) { proc_root_iops = get_symbol_from_systemmap (SYSTEMMAP, "proc_root_inode_operations", 'R'); } if (proc_root_iops != 0) { printf("#define PROC_ROOT_IOPS_LOC %#lx\n\n", proc_root_iops); } if (two_six_seventeen_plus == 1) { printf("#define TWO_SIX_SEVENTEEN_PLUS 1\n\n"); } printf("#endif\n"); exit (EXIT_SUCCESS); } /* if defined(HOST_IS_LINUX) */ #endif /************************************************************ * * * FreeBSD Implementation * ************************************************************/ #if defined(HOST_IS_FREEBSD) || defined(__OpenBSD__) #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #endif #include #ifndef SYS_MAXSYSCALL #define SYS_MAXSYSCALL 512 #endif /* number of system calls */ #define SH_MAXCALLS 512 #include "kern_head.h" static int verbose = 0; #ifdef __OpenBSD__ struct proc; struct sysent { short sy_narg; short sy_argsize; int (*sy_call)(struct proc *, void *, register_t *); }; #endif typedef struct _smap_entry { unsigned int code[2]; /* 8 bytes */ unsigned long addr; char name[64]; } smap_entry; union { unsigned long addr_sys_call_table; unsigned char str_sys_call_table[sizeof(unsigned long)]; } sh_sys_call; struct nlist sys_list[SYS_MAXSYSCALL+1]; struct nlist list[2]; int main(int argc, char * argv[]) { int i, count, which; smap_entry sh_smap[SYS_MAXSYSCALL]; struct utsname utbuf; char errbuf[_POSIX2_LINE_MAX]; struct sysent sy; unsigned long offset = 0L; kvm_t *kd; list[0].n_name = "_sysent"; list[1].n_name = NULL; if (argc > 1) { if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) usage(EXIT_SUCCESS); else if (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--verbose") == 0) verbose = 1; } if (0 != uname(&utbuf)) { perror("kern_head: uname"); exit (EXIT_FAILURE); } #ifdef __OpenBSD__ if (utbuf.release[0] == '3') which = 38; else if (utbuf.release[0] == '4') which = 40; #else if (utbuf.release[0] == '4') which = 4; else if (utbuf.release[0] == '5') which = 5; else if (utbuf.release[0] == '6') which = 5; #endif else { fprintf(stderr, "kern_head: kernel %s not supported\n", utbuf.release); exit (EXIT_FAILURE); } if (utbuf.machine[0] != 'i' || utbuf.machine[2] != '8' || utbuf.machine[3] != '6') { fprintf(stderr, "kern_head: machine %s not supported\n", utbuf.machine); exit (EXIT_FAILURE); } if (0 != getuid()) { fprintf(stderr, "\n"); fprintf(stderr, "NOTE: kern_head: must run as 'root' "); fprintf(stderr, "(need to read from kernel)\n"); fprintf(stderr, " If you get this message, then proceed "); fprintf(stderr, "as follows:\n"); fprintf(stderr, " $ su\n"); fprintf(stderr, " $ ./kern_head > sh_ks.h\n"); fprintf(stderr, " $ exit\n"); fprintf(stderr, " $ make\n\n"); exit (EXIT_FAILURE); } kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); if (!kd) { fprintf(stderr, "check_sysent: kvm_openfiles: %s\n", errbuf); exit(EXIT_FAILURE); } i = kvm_nlist(kd, list); if (i == -1) { fprintf(stderr, "check_sysent: kvm_nlist: %s\n", kvm_geterr(kd)); exit(EXIT_FAILURE); } else if (i == 1) { fprintf(stderr, "check_sysent: kvm_nlist: _sysent not found\n"); exit(EXIT_FAILURE); } else if (list[0].n_value == 0) { fprintf(stderr, "check_sysent: kvm_nlist: zero address for _sysent\n"); exit(EXIT_FAILURE); } if (which == 4) printf("\n/* Kernel %s, machine %s -- use table %s */\n\n", utbuf.release, utbuf.machine, "callz_fbsd"); else if (which == 5 || which == 6) printf("\n/* Kernel %s, machine %s -- use table %s */\n\n", utbuf.release, utbuf.machine, "callz_fbsd5"); else if (which == 38 || which == 40) printf("\n/* Kernel %s, machine %s -- use table %s */\n\n", utbuf.release, utbuf.machine, "callz_obsd"); i = 0; if (which == 4) { while ((callz_fbsd[i] != NULL) && (i < SYS_MAXSYSCALL)) { sys_list[i].n_name = callz_fbsd[i]; /* fprintf(stderr, "sys_list[%d] = %s\n", i, sys_list[i].n_name); */ ++i; } if ((utbuf.release[1] == '.') && (utbuf.release[2] == '1') && (utbuf.release[3] == '0')) { sys_list[336].n_name = callz_fbsd[151]; /* sendfile -> nosys */ } } else if (which == 5 || which == 6) { while ((callz_fbsd5[i] != NULL) && (i < SYS_MAXSYSCALL)) { sys_list[i].n_name = callz_fbsd5[i]; /* fprintf(stderr, "sys_list[%d] = %s\n", i, sys_list[i].n_name); */ ++i; } } else if (which == 38 || which == 40) { while ((callz_obsd[i] != NULL) && (i < SYS_MAXSYSCALL)) { sys_list[i].n_name = callz_obsd[i]; /* fprintf(stderr, "sys_list[%d] = %s\n", i, sys_list[i].n_name); */ ++i; } } count = i; sys_list[i].n_name = NULL; i = kvm_nlist(kd, sys_list); if (i == -1) { fprintf(stderr, "check_sysent: kvm_nlist: %s\n", kvm_geterr(kd)); /* exit(EXIT_FAILURE); */ } else if (i != 0 && verbose != 0) { fprintf(stderr, "check_sysent: kvm_nlist: %d out of %d invalid.\n", i, count); fprintf(stderr, " Probably the table in kern_head.h\n"); fprintf(stderr, " is not for your kernel version.\n"); fprintf(stderr, " (No reason to worry, kcheck will "\ "work anyway)\n\n"); } for (i = 0; i < count /* SYS_MAXSYSCALL */; i++) { if (NULL == sys_list[i].n_name) break; if (!sys_list[i].n_value && 0 != strcmp(sys_list[i].n_name, "_nosys") && verbose != 0) { fprintf(stderr,"check_sysent: not found: slot %03d [%s]\n", i, sys_list[i].n_name); /* exit(EXIT_FAILURE); */ } offset = list[0].n_value + (i*sizeof(struct sysent)); if (kvm_read(kd, offset, &sy, sizeof(struct sysent)) < 0) { fprintf(stderr,"check_sysent: kvm_read: %s\n", kvm_geterr(kd)); exit(EXIT_FAILURE); } if (verbose > 0) fprintf(stderr, "(kvm_nlist) %#lx %#lx (sysent[%03d]) %03d [%s]\n", (unsigned long) sys_list[i].n_value, (unsigned long) sy.sy_call, i, i, sys_list[i].n_name); if((unsigned long)sy.sy_call != sys_list[i].n_value && sys_list[i].n_value != 0 && 0 != strcmp(sys_list[i].n_name, "_nosys") && (unsigned long)sy.sy_call != sys_list[151].n_value) { fprintf(stderr, "WARNING: (kvm_nlist) %#lx != %#lx (sysent[%03d]) %03d [%s]\n", (unsigned long) sys_list[i].n_value, (unsigned long) sy.sy_call, i, i, sys_list[i].n_name); } sh_smap[i].addr = (unsigned long) sy.sy_call; strncpy(sh_smap[i].name, sys_list[i].n_name, 64); if(kvm_read(kd, (unsigned int) sy.sy_call, &(sh_smap[i].code[0]), 2 * sizeof(unsigned int)) < 0) { fprintf(stderr,"check_sysent: kvm_read: %s\n", kvm_geterr(kd)); exit(EXIT_FAILURE); } } if(kvm_close(kd) < 0) { fprintf(stderr,"check_sysent: kvm_nlist: %s\n", kvm_geterr(kd)); exit(EXIT_FAILURE); } printf("#ifndef SH_KERN_CALLS_H\n"); printf("#define SH_KERN_CALLS_H\n\n"); printf("#define SH_MAXCALLS %d\n\n", count); printf("typedef struct _sh_syscall_t {\n"); printf(" unsigned int code[2]; /* 8 bytes */\n"); printf(" unsigned long addr;\n"); printf(" char * name;\n"); printf("} sh_syscall_t;\n\n"); printf("static sh_syscall_t sh_syscalls[] = {\n"); for (i = 0; i < count; ++i) { printf(" /* %03d */ {{ 0x%-8.8x, 0x%-8.8x }, 0x%-8.8lx, N_(%c%s%c) },\n", i, sh_smap[i].code[0], sh_smap[i].code[1], sh_smap[i].addr, '"', sh_smap[i].name, '"'); } printf(" /* eof */ { { 0x00000000, 0x00000000 }, 0x00000000, NULL }\n"); printf("};\n\n"); printf("#endif\n"); return 0; } /* if defined(HOST_IS_FREEBSD) */ #endif /* #if defined(SH_USE_KERN) */ #else #include #include int main() { printf("#ifndef SH_KERN_CALLS_H\n"); printf("#define SH_KERN_CALLS_H\n\n"); printf("/* Dummy header. */\n\n"); printf("typedef struct _sh_syscall_t {\n"); printf(" unsigned long addr;\n"); printf(" char * name;\n"); printf("} sh_syscall_t;\n\n"); printf("#endif\n"); return (EXIT_SUCCESS); } /* #ifdef SH_USE_KERN */ #endif