Trapping a kernel module function call
今天測試了一下如何再kernel module中將一個function替換成另一個function, 主要參考http://www.phrack.org/issues.html?issue=68&id=11
他所提供的tool elfchger.c是給32bit ELF header, 所以要改成64bit ELF.
Step 1: orig module: evil沒有被call
#include <linux/init.h>#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
MODULE_LICENSE("GPL");
int fun(void) {
printk(KERN_ALERT "calling fun!");
return 0;
}
int evil(void) {
printk(KERN_ALERT "===== EVIL ====\n");
return 0;
}
int init(void) {
printk(KERN_ALERT "Init Original!");
fun();
return 0;
}
void clean(void) {
printk(KERN_ALERT "Exit Original!");
return;
}
module_init(init);
module_exit(clean);
Step2: 找到相對映的address
[root@new_host1 kprobe]# objdump -t orig.koorig.ko: file format elf64-x86-64
SYMBOL TABLE:
000000000000001b g F .text 000000000000001b evil
0000000000000000 g O .gnu.linkonce.this_module 0000000000000280 __this_module
0000000000000056 g F .text 0000000000000019 cleanup_module
0000000000000036 g F .text 0000000000000020 init_module
0000000000000056 g F .text 0000000000000019 clean
0000000000000000 g F .text 000000000000001b fun
0000000000000000 *UND* 0000000000000000 mcount
0000000000000000 *UND* 0000000000000000 printk
0000000000000036 g F .text 0000000000000020 init
Step 3: 利用patched過後的elfchger.c去修改fun, 將fun寫成evil的address (0x1b)
[root@new_host1 kprobe]# ./elfchger -s fun -v 1b orig.ko[+] Opening orig.ko file...
[+] Reading Elf header...
>> Done!
[+] Finding ".symtab" section...
>> Found at 0xc618
[+] Finding ".strtab" section...
>> Found at 0xc658
[+] Getting symbol' infos:
>> Symbol found at 0x159c8
>> Index in symbol table: 0x1c
[+] Replacing 0x00000000 with 0x0000001b... done!
Step 4: 再check一次symbol table, 此時fun已經被改成1b
000000000000001b g F .text 000000000000001b evil0000000000000000 g O .gnu.linkonce.this_module 0000000000000280 __this_module
0000000000000056 g F .text 0000000000000019 cleanup_module
0000000000000036 g F .text 0000000000000020 init_module
0000000000000056 g F .text 0000000000000019 clean
000000000000001b g F .text 000000000000001b fun
0000000000000000 *UND* 0000000000000000 mcount
0000000000000000 *UND* 0000000000000000 printk
0000000000000036 g F .text 0000000000000020 init
Step 5: 驗證
> insmod orig.ko> dmesg
[ 692.169913] Init Original!
[ 692.169975] ===== EVIL ====
==== patch elfchger.c for 64bit system ====
/** elfchger.c by styx^ <the.styx@gmail.com> (based on truff's code)
* June 6, 2013, Cheng-Chun Tu <u9012063@gmail.com>
* Patch for 64 bit system
* Script with two features:
*
* Usage 1: Change the symbol name value (address) in a kernel module.
* Usage 2: Change the symbol binding (from local to global) in a kernel
* module.
*
* Usage:
* 1: ./elfchger -f [symbol] -v [value] <module_name>
* 2: ./elfchger -g [symbol] <module_name>
*/
#include <stdlib.h>
#include <stdio.h>
#include <elf.h>
#include <string.h>
#include <getopt.h>
int ElfGetSectionByName (FILE *fd, Elf64_Ehdr *ehdr, char *section,
Elf64_Shdr *shdr);
int ElfGetSectionName (FILE *fd, Elf64_Word sh_name,
Elf64_Shdr *shstrtable, char *res, size_t len);
Elf64_Off ElfGetSymbolByName (FILE *fd, Elf64_Shdr *symtab,
Elf64_Shdr *strtab, char *name, Elf64_Sym *sym);
void ElfGetSymbolName (FILE *fd, Elf64_Word sym_name,
Elf64_Shdr *strtable, char *res, size_t len);
unsigned long ReorderSymbols (FILE *fd, Elf64_Shdr *symtab,
Elf64_Shdr *strtab, char *name);
int ReoderRelocation(FILE *fd, Elf64_Shdr *symtab,
Elf64_Shdr *strtab, char *name, Elf64_Sym *sym);
int ElfGetSectionByIndex (FILE *fd, Elf64_Ehdr *ehdr, Elf64_Half index,
Elf64_Shdr *shdr);
void usage(char *cmd);
int main (int argc, char **argv) {
FILE *fd;
Elf64_Ehdr hdr;
Elf64_Shdr symtab, strtab;
Elf64_Sym sym;
Elf64_Off symoffset;
Elf64_Addr value;
unsigned long new_index = 0;
int gflag = 0, vflag = 0, fflag = 0;
char *sym_name;
int sym_value = 0;
long sym_off, str_off;
int opt;
if ( argc != 4 && argc != 6 ) {
usage(argv[0]);
exit(-1);
}
while ((opt = getopt(argc, argv, "vsg")) != -1) {
switch (opt) {
case 'g':
if( argc-1 < optind) {
printf("[-] You must specify symbol name!\n");
usage(argv[0]);
exit(-1);
}
gflag = 1;
sym_name = argv[optind];
break;
case 's':
if( argc-1 < optind) {
printf("[-] You must specify symbol name!\n");
usage(argv[0]);
exit(-1);
}
fflag = 1;
sym_name = argv[optind];
break;
case 'v':
if( argc-1 < optind) {
printf("[-] You must specify new symbol address\n");
usage(argv[0]);
exit(-1);
}
vflag = 1;
sym_value = strtol(argv[optind], (char **) NULL, 16);
break;
default:
usage(argv[0]);
exit(-1);
}
}
printf("[+] Opening %s file...\n", argv[argc-1]);
fd = fopen (argv[argc-1], "r+");
if (fd == NULL) {
printf("[-] File \"%s\" not found!\n", argv[1]);
exit(-1);
}
printf("[+] Reading Elf header...\n");
if (fread (&hdr, sizeof (Elf64_Ehdr), 1, fd) < 1) {
printf("[-] Elf header corrupted!\n");
exit(-1);
}
printf("\t>> Done!\n");
printf("[+] Finding \".symtab\" section...\n");
sym_off = ElfGetSectionByName (fd, &hdr, ".symtab", &symtab);
if (sym_off == -1) {
printf("[-] Can't get .symtab section\n");
exit(-1);
}
printf("\t>> Found at 0x%x\n", (int )sym_off);
printf("[+] Finding \".strtab\" section...\n");
str_off = ElfGetSectionByName (fd, &hdr, ".strtab", &strtab);
if (str_off == -1) {
printf("[-] Can't get .strtab section!\n");
exit(-1);
}
printf("\t>> Found at 0x%x\n", (int )str_off);
printf("[+] Getting symbol' infos:\n");
symoffset = ElfGetSymbolByName (fd, &symtab, &strtab, sym_name, &sym);
if ( (int) symoffset == -1) {
printf("[-] Symbol \"%s\" not found!\n", sym_name);
exit(-1);
}
if ( gflag == 1 ) {
if ( ELF64_ST_BIND(sym.st_info) == STB_LOCAL ) {
unsigned char global;
unsigned long offset = 0;
printf("[+] Reordering symbols:\n");
new_index = ReorderSymbols(fd, &symtab, &strtab, sym_name);
printf("[+] Updating symbol' infos:\n");
symoffset = ElfGetSymbolByName(fd, &symtab, &strtab, sym_name, &sym);
if ( (int) symoffset == -1) {
printf("[-] Symbol \"%s\" not found!\n", sym_name);
exit(-1);
}
offset = symoffset+1+sizeof(Elf64_Addr)+1+sizeof(Elf64_Word)+2;
printf("\t>> Replacing flag 'LOCAL' located at 0x%x with 'GLOBAL'\
\n", (unsigned int)offset);
if (fseek (fd, offset, SEEK_SET) == -1) {
perror("[-] fseek: ");
exit(-1);
}
global = ELF64_ST_INFO(STB_GLOBAL, STT_FUNC);
if (fwrite (&global, sizeof(unsigned char), 1, fd) < 1) {
perror("[-] fwrite: ");
exit(-1);
}
printf("[+] Updating symtab infos at 0x%x\n", (int )sym_off);
if ( fseek(fd, sym_off, SEEK_SET) == -1 ) {
perror("[-] fseek: ");
exit(-1);
}
symtab.sh_info = new_index; // updating sh_info with the new index
// in symbol table.
if( fwrite(&symtab, sizeof(Elf64_Shdr), 1, fd) < 1 ) {
perror("[-] fwrite: ");
exit(-1);
}
} else {
printf("[-] Already global function!\n");
}
} else if ( fflag == 1 && vflag == 1 ) {
memset(&value, 0, sizeof(Elf64_Addr));
memcpy(&value, &sym_value, sizeof(Elf64_Addr));
printf("[+] Replacing 0x%.8x with 0x%.8x... ", sym.st_value, value);
//if (fseek (fd, symoffset+sizeof(Elf64_Word), SEEK_SET) == -1) {
//william
if (fseek (fd, symoffset + sizeof(Elf64_Word) + 2 * sizeof(unsigned char) + sizeof(Elf64_Half), SEEK_SET) == -1) {
perror("[-] fseek: ");
exit(-1);
}
if (fwrite (&value, sizeof(Elf64_Addr), 1, fd) < 1 ) {
perror("[-] fwrite: ");
exit(-1);
}
printf("done!\n");
fclose (fd);
}
return 0;
}
/* This function returns the offset relative to the symbol name "name" */
Elf64_Off ElfGetSymbolByName(FILE *fd, Elf64_Shdr *symtab,
Elf64_Shdr *strtab, char *name, Elf64_Sym *sym) {
unsigned int i;
char symname[255];
for ( i = 0; i < (symtab->sh_size/symtab->sh_entsize); i++) {
if (fseek (fd, symtab->sh_offset + (i * symtab->sh_entsize),
SEEK_SET) == -1) {
perror("\t[-] fseek: ");
exit(-1);
}
if (fread (sym, sizeof (Elf64_Sym), 1, fd) < 1) {
perror("\t[-] read: ");
exit(-1);
}
memset (symname, 0, sizeof (symname));
ElfGetSymbolName (fd, sym->st_name, strtab, symname, sizeof (symname));
if (!strcmp (symname, name)) {
printf("\t>> Symbol found at 0x%x\n",
symtab->sh_offset + (i * symtab->sh_entsize));
printf("\t>> Index in symbol table: 0x%x\n", i);
return symtab->sh_offset + (i * symtab->sh_entsize);
}
}
return -1;
}
/* This function returns the new index of symbol "name" inside the symbol
* table after re-ordering. */
unsigned long ReorderSymbols (FILE *fd, Elf64_Shdr *symtab,
Elf64_Shdr *strtab, char *name) {
unsigned int i = 0, j = 0;
char symname[255];
Elf64_Sym *all;
Elf64_Sym temp;
unsigned long new_index = 0;
unsigned long my_off = 0;
printf("\t>> Starting:\n");
all = (Elf64_Sym *) malloc(sizeof(Elf64_Sym) *
(symtab->sh_size/symtab->sh_entsize));
if ( all == NULL ) {
return -1;
}
memset(all, 0, symtab->sh_size/symtab->sh_entsize);
my_off = symtab->sh_offset;
for ( i = 0; i < (symtab->sh_size/symtab->sh_entsize); i++) {
if (fseek (fd, symtab->sh_offset + (i * symtab->sh_entsize),
SEEK_SET) == -1) {
perror("\t[-] fseek: ");
exit(-1);
}
if (fread (&all[i], sizeof (Elf64_Sym), 1, fd) < 1) {
printf("\t[-] fread: ");
exit(-1);
}
memset (symname, 0, sizeof (symname));
ElfGetSymbolName(fd, all[i].st_name, strtab, symname, sizeof(symname));
if (!strcmp (symname, name)) {
j = i;
continue;
}
}
temp = all[j];
for ( i = j; i < (symtab->sh_size/symtab->sh_entsize); i++ ) {
if ( i+1 >= symtab->sh_size/symtab->sh_entsize )
break;
if ( ELF64_ST_BIND(all[i+1].st_info) == STB_LOCAL ) {
printf("\t>> Moving symbol from %x to %x\n", i+1, i);
all[i] = all[i+1];
} else {
new_index = i;
printf("\t>> Moving our symbol from %d to %x\n", j, i);
all[i] = temp;
break;
}
}
printf("\t>> Last LOCAL symbol: 0x%x\n", (unsigned int)new_index);
if ( fseek (fd, my_off, SEEK_SET) == -1 ) {
perror("\t[-] fseek: ");
exit(-1);
}
if ( fwrite(all, sizeof( Elf64_Sym), symtab->sh_size/symtab->sh_entsize,
fd) < (symtab->sh_size/symtab->sh_entsize )) {
perror("\t[-] fwrite: ");
exit(-1);
}
printf("\t>> Done!\n");
free(all);
return new_index;
}
int ElfGetSectionByIndex (FILE *fd, Elf64_Ehdr *ehdr, Elf64_Half index,
Elf64_Shdr *shdr) {
if (fseek (fd, ehdr->e_shoff + (index * ehdr->e_shentsize),
SEEK_SET) == -1) {
perror("\t[-] fseek: ");
exit(-1);
}
if (fread (shdr, sizeof (Elf64_Shdr), 1, fd) < 1) {
printf("\t[-] Sections header corrupted");
exit(-1);
}
return 0;
}
int ElfGetSectionByName (FILE *fd, Elf64_Ehdr *ehdr, char *section,
Elf64_Shdr *shdr) {
int i;
char name[255];
Elf64_Shdr shstrtable;
ElfGetSectionByIndex (fd, ehdr, ehdr->e_shstrndx, &shstrtable);
memset (name, 0, sizeof (name));
for ( i = 0; i < ehdr->e_shnum; i++) {
if (fseek (fd, ehdr->e_shoff + (i * ehdr->e_shentsize),
SEEK_SET) == -1) {
perror("\t[-] fseek: ");
exit(-1);
}
if (fread (shdr, sizeof (Elf64_Shdr), 1, fd) < 1) {
printf("[-] Sections header corrupted");
exit(-1);
}
ElfGetSectionName (fd, shdr->sh_name, &shstrtable,
name, sizeof (name));
if (!strcmp (name, section)) {
return ehdr->e_shoff + (i * ehdr->e_shentsize);
}
}
return -1;
}
int ElfGetSectionName (FILE *fd, Elf64_Word sh_name,
Elf64_Shdr *shstrtable, char *res, size_t len) {
size_t i = 0;
if (fseek (fd, shstrtable->sh_offset + sh_name, SEEK_SET) == -1) {
perror("\t[-] fseek: ");
exit(-1);
}
while ( (i < len-1) || *res != '\0' ) {
*res = fgetc (fd);
i++;
res++;
}
return 0;
}
void ElfGetSymbolName (FILE *fd, Elf64_Word sym_name,
Elf64_Shdr *strtable, char *res, size_t len)
{
size_t i = 0;
if (fseek (fd, strtable->sh_offset + sym_name, SEEK_SET) == -1) {
perror("\t[-] fseek: ");
exit(-1);
}
while ((i < len-1) || *res != '\0') {
*res = fgetc (fd);
i++;
res++;
}
return;
}
void usage(char *cmd) {
printf("Usage: %s <option(s)> <module_name>\n", cmd);
printf("Option(s):\n");
printf(" -g [symbol]\tSymbol we want to change the binding as global\n");
printf("Or:\n");
printf(" -s [symbol]\tSymbol we want to change the value (address)\n");
printf(" -v [value] \tNew value (address) for symbol\n");
return;
}
沒有留言:
張貼留言