инжектирую код в запущенный процесс... необходимо найти glibc и разресолвить несколько функций, необходимых коду... ищу через GOT[1] по списку link_map, но для этого необходимо в коде узнать базовый адрес elf-файла процесса... во всех исходниках и статьях, что я видел используют магическую константу 0x08048000, в принципе на моих 32-битных десктопных линуксах со всеми процессами так и происходит, но мне всетки хотелось бы каким-то образом находить этот адрес в динамике... есть какие-нить предложения?) ЗЫ интересен метод нахождения базового адреса именно в коде, понятно, что из инфжектируемого процесса я могу распарсить /proc/<pid>/maps и тд... ЗЗЫ кроме того, хочу напомнить, что код не может вызывать для этого никаких библиотечных функций, я думаю, это вполне логично и понятно...
Я бы не стал ориентироваться на это значение. В конечном счёте, оно задаётся в скрипте линкера, поэтому может быть кем-либо изменено. Как вариант - постраничное сканирование памяти, но это на крайний случай...
Вот думаю как ограничить диапазон страница для поиска... А если через brk узнать где кончается куча и искать от этого адреса вверх, к младшим адресам -- это по крайней мере ограничит количество перебираемых страниц...
да, но базовый адрес является не единственной страницой с сигнатурами ELF-файла... хотя меня наверное не должно это волновать, так как от базового адреса любой .so библиотеки, загруженной стандартным загрузщиком, можно просчитать путь до link_map... но все равно хотелось бы канеш сузить диапазон поиска... в винде кстати с этим проще... через регистр fs -> TIB -> TEB -> PEB -> база данных загрузщика... весьма удобно на мой взгляд, а в линуксах может есть что-то подобное?)
Ну на сколько могу судить, gcc линкует с помощью ld, который в свою очередь собирает на основании скриптов. Похоже, что -fpie аналог ASLR -- в этом случае, тем более не стоит полагаться на постоянный адрес загрузки бинарника.
Код (Text): void *get_base(uint32_t addr) { addr &= ~4095; while (*(uint32_t*)addr != 0x464c457fUL) addr -= 4096; return (void*)addr; } на вход дайте любой адрес из сегмента кода и проверьте потом найденный "заголовок" на валидность. (другие варианты - раскручивать стек до __libc_start_main, или если есть доступ к аргументам командной строки, то можно отпарсить aux-вектор, но это не так надежно и, имхо, муторно)
какой адрес, какой стек? это - не elf-файл, это - просто пи-код, у него нет никаких заголовков и стартует он не с __libc_start_main))) я может это не совсем понятно объяснил в первом сообщении, извините... и кстати в приведенный вами алгоритмt можно вставить __builtin_return_address(0) и с его помощью выудить адрес кода... но только этот билд-ин вроде не на всех архитектурах существует...
Читай /proc/<pid>/maps, ищи свой файл. Имя файла можно узнать из /proc/<pid>/exe. http://linux.die.net/man/5/proc
ну блиин, в первом же сообщении: то есть из кода я не могу пользовать библиотечные функции... я канеш могу зачитать файлы сисколом и руками распарсить, но мне бы хотелось сделать максимально кросс-линуксовый пи-код, по-этому я стараюсь заложиться на посиксный интерфейс (библиотеки libc.so), который есть наверное в любых линуксах... ЗЫ никто не видел статейку/мануал/инфу, где рассказывалось бы, как в линуксовых процессах используются сегментные регистры, мне кажется (по аналогии с виндовс) там мажет быть что-то, за что можно зацепиться)))
Как вариант использую системные вызовы поставить свой обработчик на какой-либо используемый сигнал (тем самым получая адрес старого обработчика), затем всё вернуть на место. А от старого обработчика уже раскручивать всё в нужную сторону
Я немного порылся, и получил такой вариант: Код (Text): #include <stdio.h> #include <stdint.h> #include <stdbool.h> #include <assert.h> #include <elf.h> /* Type for the dtv. */ typedef union dtv { size_t counter; struct { void *val; bool is_static; } pointer; } dtv_t; typedef struct { void *tcb; /* Pointer to the TCB. Not necessarily the thread descriptor used by libpthread. */ dtv_t *dtv; void *self; /* Pointer to the thread descriptor. */ int multiple_threads; uintptr_t sysinfo; uintptr_t stack_guard; uintptr_t pointer_guard; } tcbhead_t; int main(int argc, char **argv) { void *get_base(uint32_t addr) { addr &= ~4095; while (*(uint32_t*)addr != 0x464c457fUL) addr -= 4096; return (void*)addr; } tcbhead_t *tcb; asm ("mov %%gs:0x0,%%eax":"=a"(tcb)); dtv_t *dtv = tcb->dtv; if (dtv->counter > 0) { uint32_t *tls = dtv[1].pointer.val; Elf32_Ehdr *ehdr = get_base(tls[0]); Elf32_Phdr *phdr = (Elf32_Phdr*)((char*)ehdr + ehdr->e_phoff); int i; Elf32_Dyn *dyn = NULL; int delta; uint32_t low; for (i = 0; i < ehdr->e_phnum; i++) if (phdr[i].p_type == PT_DYNAMIC) dyn = (Elf32_Dyn*)phdr[i].p_vaddr; else if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == 0) delta = phdr[i].p_vaddr; delta = (uint32_t)ehdr - delta; dyn = (Elf32_Dyn*)((char*)dyn + delta); assert(dyn != NULL); char *strtab = NULL; int soname = 0; while (dyn->d_tag != DT_NULL) { if (dyn->d_tag == DT_STRTAB) strtab = (char*)dyn->d_un.d_ptr; else if (dyn->d_tag == DT_SONAME) soname = dyn->d_un.d_val; dyn++; } assert(strtab != NULL && soname != 0); printf("%s\n", soname + strtab); } } Тест: Код (Text): $ uname -r 2.6.18-238.19.1.el5 $ cat /etc/redhat-release CentOS release 5.6 (Final) $ ./a.out libc.so.6 $ uname -r 2.6.24-rc7 $ cat /etc/*rel* Gentoo Base System release 1.12.11.1 $ ./a.out libc.so.6
Только вы учтите, что если приложение, в которое вы инжектитесь использует треды, то TLS libc будет не в dtv[1], а где-нибудь еще. Пока не думал, как отличить нужный адрес. И полный fail, если нет поддержки тредов, но сейчас это кажется уже редкость. Так что, я бы эту городушку еще сигналом бы защитил (а сигнал можно позвать через VDSO, адрес сискола в tcb->sysinfo).