/* 
 * ELF infection methods scanner 0.1
 * Coded by pluf  (pluf@mail.ru)
 *
 * Use:
 *
 * ./scaner -v -f file_to_check
 * 
 * file_to_check must be a ET_EXEC file
 * 
 * It runs on linux2.4.X
 * 
 * Infection methods supported:
 * + text segment infection (padding), data segment infection, or other kind of
 *   methods that change the real entry point of infected file.
 * + altplt function hijacking method
 * + dynamic section infection to allow add a new library dependance and make 
 *   easy functions hijacking
 * + plt infection and got redirection too allow funtion hijacking 
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <elf.h>

int verbose = 0;
unsigned short infected = 0x0000;

struct {
	int size;
	int vaddr;
	int offset;
} section_inf[5];

#define	A	0x0001
#define B	0x0020
#define C	0x0100
#define D	0x0200

void
show_method(type)
	int type;
{
	printf("\n  [ Results ]\n");
	if (infected & B) printf("  [+] Posible text or data segment infection: entry point dont' referer text segment\n");
	if (infected & C) printf("  [+] ALTPLT function hijacking method.\n");
	if (infected & D) printf("  [+] Dynamic section infected: posible DT_NEEDED symbol injected instead of orig DT_DEBUG\n");
	if (infected & A) printf("  [+] PLT infection and got redirection\n");
	printf("\n");
	
}

int
check_fake_entry(orig_entry)
		int orig_entry;
{
	if (orig_entry != section_inf[4].vaddr) return (1);
	return (0);
}	

int
check_function_hijacking(shnum, string, shdr)
		int shnum;
		const char *string; 
		Elf32_Shdr *shdr;
{
	Elf32_Shdr *shdrp = shdr;
	register int i;
	
	for (i = 0; i < shnum; i++) {
		if (!strcmp(&string[shdrp->sh_name], ".orig.plt")) return (1);
		++shdrp;
	}

	return (0);
}	

int
check_dynamic_infection(len, dyn)
		int len;
		Elf32_Dyn *dyn;
{
	register int i;

	for (i = 0; i < (len/sizeof(Elf32_Dyn)); i++) {
		if (dyn->d_tag == DT_DEBUG)
			return (0);
		++dyn;
	}
	
	return (1);
}

int
find_symbol(long got_entry, Elf32_Sym *dynsym, char *dynstr)
{
	register int i;
	int syndym_entries = (section_inf[1].size/sizeof(Elf32_Sym));
	int gotp;
	
	gotp = got_entry;
	
	for (i = 0; i < syndym_entries; i++) {
 		if (dynsym->st_value != 0) {
			if (dynsym->st_value == (gotp -= 0x06) ||
				dynsym->st_value == (gotp -= 16)) {
				if(verbose)
				printf("  [%3.5d] %-50.60s (plt-entry -> 0x%.7x) = [ok]\n", 
					       i, &dynstr[dynsym->st_name], dynsym->st_value);	
				return (1);
			}
		}
		gotp = got_entry;
		++dynsym;
	}

	if (verbose)
	printf("  [%c] %-50.60s (plt->entry -> 0x%.7x) = [Bad]\n", '?', &dynstr[dynsym->st_name], dynsym->st_value);
	return (0);
}	
				
int
check_plt_infection(Elf32_Sym *dynsym, char *got, char *dynstr)
{
	register int i;
	Elf32_Sym *dynsymp = dynsym;
	char *ptr = got;
	long *addr;
	int got_entries = (section_inf[0].size/4), bad_entry = 0;
	
	if(verbose)
		printf("\n  [ Checking for valid dynamic symbols ]\n\n");
	
	addr = (long*)ptr;
	for (i = 2, addr+=2; i < got_entries; i++) {
		if (*addr != 0) 
			if (!find_symbol(*addr, dynsymp, dynstr)) ++bad_entry;
		++addr;
	}

	return (bad_entry);
}

char *get_section(Elf32_Shdr *shdr,
		char *sdata,
		int shnum, 
		int fd,
		const char *string_table, 
		const char *section_name,
		unsigned char id)
{
	Elf32_Shdr *shdrp = shdr;
	register int i;
	int offset;
	int size;
	int vaddr;
	char *buf;

	shdrp = (Elf32_Shdr*)sdata;
	for (i = 0; i < shnum; i++) {
		if (!strcmp(&string_table[shdrp->sh_name], section_name)) {
			section_inf[id].offset = shdrp->sh_offset;
			section_inf[id].size = shdrp->sh_size;
			section_inf[id].vaddr = shdrp->sh_addr;
			
			offset = shdrp->sh_offset;
			size = shdrp->sh_size;
			vaddr = shdrp->sh_addr;
			if (verbose)
			printf("  [!] %-12s addr(0x%x) offset(0x%.8x) size(0x%.8x)\n", section_name, 
									vaddr,offset,size);
		}
		++shdrp;
	}	
		
	buf = (char*)malloc(size);
	if (buf == NULL) {
		perror("malloc");
		exit(-1);
	}
	if (lseek(fd, offset, SEEK_SET) < 0) {
		perror("lseek");
		exit(-1);
	}
	if (read(fd, buf, size) != size) {
		perror("read");
		exit(-1);
	}
	
	return 	(buf);
}

void
help()
{
	printf("usage: ./scanner -v -f file\n\n");
	exit(-1);
}

int
main(argc, argv)
	int argc;
	char *argv[];
{
	Elf32_Ehdr elf_header;
	Elf32_Phdr *phdr;
	Elf32_Shdr *shdr, *strtable;
	Elf32_Dyn *dynamic;
	Elf32_Sym *dynsym;
	char *pdata, *sdata, *string, *got, *dynstr, *text;
	int shlen, phlen;
	char *file;
	int fd, op;

	printf("\n\n\t--=== Elf infections scanner v0.1 by pluf ==--\n\n");
	
	while ((op=getopt(argc, argv, "vf:")) != -1) {
		switch(op) {
		case 'v':
			verbose +=1;
			break;
		case 'f':
			if (strlen(optarg) > 256)
				exit(-1);
			file = optarg;
			break;
		case '?':
		case 'h':
		default:
			help();
		}
	}

	if (argc < 3)
		help();
			
	/* open file */
	if ((fd = open(file, O_RDONLY)) == -1) { perror("open");exit(-1); }

	/*get elf */
	if (read(fd, &elf_header, sizeof(elf_header)) < 0) { perror("read");exit(-1); }

	/* get program header table */
	pdata = (char*)malloc(phlen = sizeof(*phdr)*elf_header.e_phnum);
	if (lseek(fd, elf_header.e_phoff, SEEK_SET) <0 ) { perror("lseek");exit(-1); }
	if (read(fd, pdata, phlen) < phlen) { perror("read");exit(-1); }
	
	/* get section header table */
	sdata = (char*)malloc(shlen = sizeof(*shdr)*elf_header.e_shnum);	
	if (lseek(fd, elf_header.e_shoff, SEEK_SET) < 0) { perror("lseek");exit(-1); }
	if (read(fd, sdata, shlen) < shlen) { perror("read");exit(-1); }
	shdr = (Elf32_Shdr*)sdata;
	
	/* get string table */
	strtable = &((Elf32_Shdr *)sdata)[elf_header.e_shstrndx];
	string = (char*)malloc(strtable->sh_size);
	if (string == NULL) { perror("malloc");exit(-1); }
	if (lseek(fd, strtable->sh_offset, SEEK_SET) < 0) { perror("lseek");exit(-1); }
	if (read(fd, string, strtable->sh_size) != strtable->sh_size) { perror("read");exit(-1); }
	
	if(verbose)
		printf("  [ Loading needed sections ]\n\n");
	
	/* get dynamic symbol table (.dynsym section) */
	dynsym = (Elf32_Sym*)get_section(shdr, sdata, elf_header.e_shnum, fd, string, ".dynsym",1);

	/* get dynamic symbol string table (.dynstr section) */
	dynstr = get_section(shdr, sdata, elf_header.e_shnum, fd, string, ".dynstr", 2);

	/* get .text section  */
	text = get_section(shdr, sdata, elf_header.e_shnum, fd, string, ".text",4);
	
	/* get .dynamic section */
	dynamic = (Elf32_Dyn*)get_section(shdr, sdata, elf_header.e_shnum, fd, string, ".dynamic",3);

	/* get global offset table (.got section) */
	got = get_section(shdr, sdata, elf_header.e_shnum, fd, string, ".got", 0);
		

	shdr = (Elf32_Shdr*)sdata;
	if (check_plt_infection(dynsym, got, dynstr)) infected |= A;
	if (check_fake_entry(elf_header.e_entry)) infected |= B;
	if (check_function_hijacking(elf_header.e_shnum, string, shdr)) infected |= C;
	if (check_dynamic_infection(section_inf[3].size, dynamic)) infected |= D;
	if (!infected) printf("\n  [-] This file is clean!!\n\n");
	else show_method();
	
	close(fd);

	return (0);
}
