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);
}
摘自《赛迪网》 pragmatic/THC,(版本1.0)/文