|
|
kinsmod.c
Module insertion without native support
NAME : kinsmod.c
AUTHOR : Silvio Cesare
DESCRIPTION : This is a very nice program which allows us to insert LKMs on
system with no native module support.
LINK : found it by a search on http://www.antisearch.com
/**********needed include file*/
#ifndef KMEM_H
#define KMEM_H
#include <linux/module.h>
#include <unistd.h>
#include <fcntl.h>
/*
these functions are anologous to standard file routines.
*/
#define kopen(mode) open("/dev/kmem", (mode))
#define kclose(kd) close((kd))
ssize_t kread(int kd, int pos, void *buf, size_t size);
ssize_t kwrite(int kd, int pos, void *buf, size_t size);
/*
ksyms initialization and cleanup
*/
int ksyms_init(const char *map);
void ksyms_cleanup(void);
/*
print the ksym table
*/
void ksyms_print(void);
/*
return the ksym of name 'name' or NULL if no symbol exists
*/
struct kernel_sym *ksyms_find(const char *name);
#endif
/**********KMEM functions*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <linux/module.h>
#include <linux/unistd.h>
#include "kmem.h"
struct ksymlist
{
struct ksymlist* next;
struct kernel_sym ksym;
};
struct ksymlisthead
{
struct ksymlist* next;
};
/*
the hash size must be an integral power of two
*/
#define KSYM_HASH_SIZE 512
struct ksymlisthead ksymhash[KSYM_HASH_SIZE];
/*
these functions are anologous to standard file routines.
*/
ssize_t kread(int kd, int pos, void *buf, size_t size)
{
int retval;
retval = lseek(kd, pos, SEEK_SET);
if (retval != pos)
return retval;
return read(kd, buf, size);
}
ssize_t kwrite(int kd, int pos, void *buf, size_t size)
{
int retval;
retval = lseek(kd, pos, SEEK_SET);
if (retval != pos)
return retval;
return write(kd, buf, size);
}
void ksyms_print(void)
{
int i;
for (i = 0; i < KSYM_HASH_SIZE; i++)
{
struct ksymlist *head = (struct ksymlist *)&ksymhash[i];
struct ksymlist *current = ksymhash[i].next;
while (current != head)
{
printf(
"name: %s addr: %lx\n",
current->ksym.name,
current->ksym.value
);
current = current->next;
}
}
}
void ksyms_cleanup(void)
{
int i;
for (i = 0; i < KSYM_HASH_SIZE; i++)
{
struct ksymlist *head = (struct ksymlist *)&ksymhash[i];
struct ksymlist *current = head->next;
while (current != head)
{
struct ksymlist *next = current->next;
free(current);
current = next;
}
}
}
int hash(const char *name)
{
unsigned long h;
const char *p;
for (h = 0, p = name; *p; h += (unsigned char)*p, p++);
return h & (KSYM_HASH_SIZE - 1);
}
int ksyminsert(struct kernel_sym *ksym)
{
struct ksymlist *node;
struct ksymlisthead *head;
node = (struct ksymlist *)malloc(sizeof(struct ksymlist));
if (node == NULL) return -1;
head = &ksymhash[hash(ksym->name)];
memcpy(&node->ksym, ksym, sizeof(*ksym));
node->next = (struct ksymlist *)head->next;
head->next = node;
return 0;
}
int ksyms_init(const char *map)
{
char s[512];
FILE *f;
int i;
for (i = 0; i < KSYM_HASH_SIZE; i++)
ksymhash[i].next = (struct ksymlist *)&ksymhash[i];
f = fopen(map, "r");
if (f == NULL) return -1;
while (fgets(s, sizeof(s), f) != NULL)
{
struct kernel_sym ksym;
char *n, *p;
ksym.value = strtoul(s, &n, 16);
if (n == s || *n == 0) goto error;
p = n;
while (*p && isspace(*p))
++p;
if (*p == 0 || p[1] == 0 || p[2] == 0)
goto error;
p += 2;
n = p;
while (*p && !isspace(*p))
++p;
if (*p) *p = 0;
strncpy(ksym.name, n, 60);
if (ksyminsert(&ksym) < 0)
goto error;
}
fclose(f);
return 0;
error:
fclose(f);
ksyms_cleanup();
printf("--> %s\n", s);
return -1;
}
struct kernel_sym *ksyms_find(const char *name)
{
struct ksymlist *head = (struct ksymlist *)&ksymhash[hash(name)];
struct ksymlist *current = head->next;
while (current != head)
{
if (!strncmp(current->ksym.name, name, 60))
return ¤t->ksym;
current = current->next;
}
return NULL;
}
/**********MAIN PROGRAM : kinsmod.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <elf.h>
#include <getopt.h>
#include "kmem.h"
static char system_map[] = "System.kmap";
static int error = 0;
static int run = 0;
static int force = 0;
struct _module
{
Elf32_Ehdr ehdr;
Elf32_Shdr* shdr;
unsigned long maddr;
int maxlen;
int len;
int strtabidx;
char** section;
};
Elf32_Sym *local_sym_find(Elf32_Sym *symtab, int n, char *strtab, const char
*name)
{
int i;
for (i = 0; i < n; i++)
{
if (!strcmp(&strtab[symtab[i].st_name], name))
return &symtab[i];
}
return NULL;
}
Elf32_Sym *localall_sym_find(struct _module *module, const char *name)
{
char *strtab = module->section[module->strtabidx];
int i;
for (i = 0; i < module->ehdr.e_shnum; i++)
{
Elf32_Shdr *shdr = &module->shdr[i];
if (shdr->sh_type == SHT_SYMTAB)
{
Elf32_Sym *sym;
sym = local_sym_find(
(Elf32_Sym
*)module->section[i],
shdr->sh_size/sizeof(Elf32_Sym),
strtab,
name
);
if (sym != NULL) return sym;
}
}
return NULL;
}
void check_module(struct _module *module, int fd)
{
Elf32_Ehdr *ehdr = &module->ehdr;
if (read(fd, ehdr, sizeof(*ehdr)) != sizeof(*ehdr))
{
perror("read");
exit(1);
}
/* ELF checks */
if (strncmp(ehdr->e_ident, ELFMAG, SELFMAG))
{
fprintf(stderr, "File not ELF\n");
exit(1);
}
if (ehdr->e_type != ET_REL)
{
fprintf(stderr, "ELF type not ET_REL\n");
exit(1);
}
if (ehdr->e_machine != EM_386 && ehdr->e_machine != EM_486)
{
fprintf(stderr, "ELF machine type not EM_386 or
EM_486\n");
exit(1);
}
if (ehdr->e_version != EV_CURRENT)
{
fprintf(stderr, "ELF version not current\n");
exit(1);
}
}
void load_section(char **p, int fd, Elf32_Shdr *shdr)
{
if (lseek(fd, shdr->sh_offset, SEEK_SET) < 0)
{
perror("lseek");
exit(1);
}
*p = (char *)malloc(shdr->sh_size);
if (*p == NULL)
{
perror("malloc");
exit(1);
}
if (read(fd, *p, shdr->sh_size) != shdr->sh_size)
{
perror("read");
exit(1);
}
}
void load_module(struct _module *module, int fd)
{
Elf32_Ehdr *ehdr;
Elf32_Shdr *shdr;
char **sectionp;
int slen;
int i;
check_module(module, fd);
ehdr = &module->ehdr;
slen = sizeof(Elf32_Shdr)*ehdr->e_shnum;
module->shdr = (Elf32_Shdr *)malloc(slen);
if (module->shdr == NULL)
{
perror("malloc");
exit(1);
}
module->section = (char **)malloc(sizeof(char **)*ehdr->e_shnum);
if (module->section == NULL)
{
perror("malloc");
exit(1);
}
if (lseek(fd, ehdr->e_shoff, SEEK_SET) < 0)
{
perror("lseek");
exit(1);
}
¡¡¡¡¡¡¡¡if (read(fd, module->shdr, slen) != slen)
{
¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡perror("read");
¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡exit(1);
¡¡¡¡¡¡¡¡}
for (i = 0, sectionp = module->section, shdr = module->shdr;i <
ehdr->e_shnum;i++, sectionp++)
{
switch (shdr->sh_type)
{
case SHT_NULL:
case SHT_NOTE:
case SHT_NOBITS:
break;
case SHT_STRTAB:
load_section(sectionp, fd, shdr);
if (i != ehdr->e_shstrndx)
module->strtabidx = i;
break;
case SHT_SYMTAB:
case SHT_PROGBITS:
case SHT_REL:
load_section(sectionp, fd, shdr);
break;
default:
fprintf(stderr,"No handler for section
(type): %i\n",shdr->sh_type);
exit(1);
}
++shdr;
}
}
void relocate(struct _module *module, Elf32_Rel *rel, Elf32_Shdr *shdr)
{
Elf32_Sym *symtab = (Elf32_Sym *)module->section[shdr->sh_link];
Elf32_Sym *sym = &symtab[ELF32_R_SYM(rel->r_info)];
Elf32_Addr addr;
Elf32_Shdr *targshdr = &module->shdr[shdr->sh_info];
Elf32_Addr dot = targshdr->sh_addr + rel->r_offset;
Elf32_Addr *loc = (Elf32_Addr *)(
module->section[shdr->sh_info] + rel->r_offset
);
char *name = &module->section[module->strtabidx][sym->st_name];
if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL)
{
struct kernel_sym *ksym;
if (force)
{
char novname[60];
int len;
len = strlen(name);
if (len > 10 && !strncmp(name + len - 10, "_R", 2))
{
strncpy(novname, name, len - 10);
novname[len - 10] = 0;
ksym = ksyms_find(novname);
}
else
ksym = ksyms_find(name);
}
else
ksym = ksyms_find(name);
if (ksym != NULL)
{
addr = ksym->value;
#ifdef DEBUG
printf("Extern symbol is
(%s:%lx)\n",ksym->name,(unsigned long)addr);
#endif
goto next;
}
if (sym->st_shndx == 0 ||sym->st_shndx > module->ehdr.e_shnum)
{
fprintf(stderr,"ERROR: undefined symbol (%s)\n", name);
++error;
return;
}
}
addr = sym->st_value + module->shdr[sym->st_shndx].sh_addr;
#ifdef DEBUG
printf("Symbol (%s:%lx) is local\n", name, (unsigned long)addr);
#endif
next:
if (targshdr->sh_type == SHT_SYMTAB)
return;
if (targshdr->sh_type != SHT_PROGBITS)
{
fprintf(stderr,"Rel not PROGBITS or SYMTAB (type:
%i)\n",targshdr->sh_type);
exit(1);
}
switch (ELF32_R_TYPE(rel->r_info))
{
case R_386_NONE:
break;
case R_386_PLT32:
case R_386_PC32:
*loc -= dot; /* *loc += addr - dot */
case R_386_32:
*loc += addr;
break;
default:
fprintf(stderr, "No handler for Relocation (type):
%i",ELF32_R_TYPE(rel->r_info));
exit(1);
}
}
void relocate_module(struct _module *module)
{
int i;
for (i = 0; i < module->ehdr.e_shnum; i++)
{
if (module->shdr[i].sh_type == SHT_REL)
{
int j;
Elf32_Rel *relp = (Elf32_Rel
*)module->section[i];
for (j = 0;j <
module->shdr[i].sh_size/sizeof(Elf32_Rel);j++)
{
relocate(module,relp,&module->shdr[i]);
++relp;
}
}
}
}
void print_symaddr(struct _module *module, const char *symbol)
{
Elf32_Sym *sym;
sym = localall_sym_find(module, symbol);
if (sym == NULL)
{
fprintf(stderr, "No symbol (%s)\n", symbol);
++error;
return;
}
printf("%s: 0x%lx\n",symbol,(unsigned
long)module->shdr[sym->st_shndx].sh_addr+ sym->st_value);
}
void init_module(struct _module *module, unsigned long maddr)
{
int i;
unsigned long len = 0;
module->maddr = maddr;
for (i = 0; i < module->ehdr.e_shnum; i++)
{
if (module->shdr[i].sh_type != SHT_PROGBITS)
continue;
module->shdr[i].sh_addr = len + maddr;
len += module->shdr[i].sh_size;
}
module->len = len;
if (module->maxlen > 0 && module->len > module->maxlen)
{
fprintf(stderr,"Module too large: (modsz:
%i)\n",module->len);
exit(1);
}
printf("Module length: %i\n", module->len);
relocate_module(module);
print_symaddr(module, "init_module");
print_symaddr(module, "cleanup_module");
}
void do_module(struct _module *module, int fd)
{
int kd;
int i;
#ifdef DEBUG
for (i = 0; i < module->ehdr.e_shnum; i++)
{
if (module->shdr[i].sh_type != SHT_PROGBITS) continue;
if (lseek(fd, module->shdr[i].sh_offset,
SEEK_SET) < 0)
{
perror("lseek");
exit(1);
}
if (
write(fd, module->section[i], module->shdr[i].sh_size)
!= module->shdr[i].sh_size)
{
perror("write");
exit(1);
}
}
#else
kd = open("/dev/kmem", O_RDWR);
if (kd < 0)
{
perror("open");
exit(1);
}
if (lseek(kd, module->maddr, SEEK_SET) < 0)
{
perror("lseek");
exit(1);
}
for (i = 0; i < module->ehdr.e_shnum; i++)
{
if (module->shdr[i].sh_type != SHT_PROGBITS)
continue;
if (write(kd, module->section[i],
module->shdr[i].sh_size) != module->shdr[i].sh_size)
{
perror("write");
exit(1);
}
}
close(kd);
#endif
}
int main(int argc, char *argv[])
{
char *map = system_map;
struct _module module;
int fd;
int ch;
int retval = 0;
while ((ch = getopt(argc, argv, "m:tf")) != EOF)
{
switch (ch)
{
case 'm':
map = optarg;
break;
case 't':
++run;
break;
case 'f':
++force;
break;
}
}
/*
so we can move options in and out without changing the codes idea
of what argv and argc look like.
*/
--optind;
argv += optind;
argc -= optind;
if (argc != 3 && argc != 4)
{
fprintf(stderr,"usage: k module [-t] [-m map] maddr(hex)
[maxlen]\n");
exit(1);
}
#ifdef DEBUG
fd = open(argv[1], O_RDWR);
#else
fd = open(argv[1], O_RDONLY);
#endif
if (fd < 0)
{
perror("open");
exit(1);
}
if (ksyms_init(map) < 0)
{
perror("ksyms_init");
exit(1);
}
module.maxlen = (argc == 4 ? atoi(argv[3]) : -1);
load_module(&module, fd);
init_module(&module, strtoul(argv[2], NULL, 16));
if (run == 0)
{
if (error == 0)
{
do_module(&module, fd);
}
else
{
fprintf(stderr,"FAILED: (%i) errors.
Exiting...\n", error);
++retval;
}
}
ksyms_cleanup();
exit(retval);
}
|
|