Системный шпионаж в *nix часть 2 Крис Касперски ака мыщъх Хакер, номер #088, стр. 088-098-2 unsigned char buf_code[]={0xE8, 0x0, 0x0, 0x0,0x0}; // call 00000h call_r(char *lib_name, char *from, char *to, int delta) { unsigned char *base, *from, *to; base = dlopen(lib_name,RTLD_NOW); if (!base) return -1; from = dlsym(base,from); if (!from) return -1; to = dlsym(base,to); if (!to) return -1; *((unsigned int*)&buf_code[1]) = to - from - sizeof(buf_code) - delta; return 666; } Функция call_r вызывается из программы-инсталлятора, например нашей mem.c, и генерирует относительный вызов call по адресу from на адрес to. Она может использоваться для вызова любых функций, а не только _dl_addr. Модернизируем программу mem.c и отпатчим функцию gets так, чтобы она выводила символ «*» на экран. Мы будем вызывать функцию write из библиотеки libc со следующими параметрами: write(1,&"*",1). Обрати внимание на конструкцию &«*». Мы заталкиваем в стек символ "*" и передаем функции его указатель. А что еще остается делать? Сегмент данных ведь недоступен! Приходится использовать стек! При желании туда можно затолкать не только один символ, но и ASCIIZ-строку (только не забудь потом вытолкнуть обратно — некоторые забывают, в результате чего имеют несбалансированный стек и получают segmentation fault). Модернизированный вариант программы mem.c, внедряющий в начало gets вызов write(1,&"*",1); // начало thunk-кода. Заталкиваем в стек аргументы функции write, но саму функцию еще не вызываем, так как не знаем ее адреса unsigned char buf_pre[]={ 0x6A,0x2A, /* push 2Ah */ 0x8B,0xDC, /* mov ebx,esp */ 0x33,0xC0, /* xor eax,eax */ 0x40, /* inc eax */ 0x50, /* push eax */ 0x53, /* push ebx */ 0x50 /* push eax */ }; // сюда записывается сгенерированный относительный вызов функции write unsigned char buf_code[]={0xE8,0x0,0x0,0x0,0x0}; // конец thunk-кода. Выталкиваем аргументы из стека вместе с символом "*" и возвращаемся по ret unsigned char buf_post[]={ 0x83,0xC4,0x10, /* add esp,10 */ 0xC3 /* ret */ }; // буфер, в который будет записан собранный thunk-код в следующей последовательности: buf_pre + buf_code + buf_post unsigned char buf_dst[sizeof(buf_pre) + sizeof(buf_code) + sizeof(buf_post)]; // генерируем относительный вызов write call_r("libc.so.6", "gets", "write", sizeof(buf_pre)); // собираем thunk-код memcpy(buf_dst, buf_pre, sizeof(buf_pre)); memcpy(buf_dst + sizeof(buf_pre), buf_code, sizeof(buf_code)); memcpy(buf_dst + sizeof(buf_pre) + sizeof(buf_code), buf_post, sizeof(buf_post)); // ставим C3h (ret) или восстанавливаем стандартный пролог обратно if (page_buf[((unsigned int)p) % PAGE_SIZE] == 0xC3) page_buf[((unsigned int)p) % PAGE_SIZE] = 0x55; else page_buf[((unsigned int)p) % PAGE_SIZE] = 0xC3; // копируем thunk-код поверх функции gets memcpy(&page_buf[((unsigned int)p) % PAGE_SIZE], buf_dst,sizeof(buf_dst)); Компилируем программу и убеждаемся, что она работает, вплотную приближая нас к созданию полноценного перехватчика. Чуть-чуть усложнив thunk-код, мы сможем не только выводить свои данные на экран, но и сохранять log в файл! |