Поиск link_map из внедренного кода?

Тема в разделе "WASM.UNIX", создана пользователем Rel, 12 сен 2011.

  1. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.242
    инжектирую код в запущенный процесс... необходимо найти glibc и разресолвить несколько функций, необходимых коду... ищу через GOT[1] по списку link_map, но для этого необходимо в коде узнать базовый адрес elf-файла процесса... во всех исходниках и статьях, что я видел используют магическую константу 0x08048000, в принципе на моих 32-битных десктопных линуксах со всеми процессами так и происходит, но мне всетки хотелось бы каким-то образом находить этот адрес в динамике... есть какие-нить предложения?)

    ЗЫ интересен метод нахождения базового адреса именно в коде, понятно, что из инфжектируемого процесса я могу распарсить /proc/<pid>/maps и тд...
    ЗЗЫ кроме того, хочу напомнить, что код не может вызывать для этого никаких библиотечных функций, я думаю, это вполне логично и понятно...
     
  2. 7mm

    7mm New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2009
    Сообщения:
    442
    Я бы не стал ориентироваться на это значение. В конечном счёте, оно задаётся в скрипте линкера, поэтому может быть кем-либо изменено. Как вариант - постраничное сканирование памяти, но это на крайний случай...
     
  3. 7mm

    7mm New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2009
    Сообщения:
    442
    Вот думаю как ограничить диапазон страница для поиска... А если через brk узнать где кончается куча и искать от этого адреса вверх, к младшим адресам -- это по крайней мере ограничит количество перебираемых страниц...
     
  4. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.242
    да, но базовый адрес является не единственной страницой с сигнатурами ELF-файла... хотя меня наверное не должно это волновать, так как от базового адреса любой .so библиотеки, загруженной стандартным загрузщиком, можно просчитать путь до link_map... но все равно хотелось бы канеш сузить диапазон поиска...

    в винде кстати с этим проще... через регистр fs -> TIB -> TEB -> PEB -> база данных загрузщика... весьма удобно на мой взгляд, а в линуксах может есть что-то подобное?)
     
  5. act

    act New Member

    Публикаций:
    0
    Регистрация:
    10 янв 2010
    Сообщения:
    8
    7mm
    А разве в gcc этот адрес также не железно вбит пока нет опции -fpie?
     
  6. 7mm

    7mm New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2009
    Сообщения:
    442
    Ну на сколько могу судить, gcc линкует с помощью ld, который в свою очередь собирает на основании скриптов. Похоже, что -fpie аналог ASLR -- в этом случае, тем более не стоит полагаться на постоянный адрес загрузки бинарника.
     
  7. herm1t

    herm1t New Member

    Публикаций:
    0
    Регистрация:
    1 янв 2004
    Сообщения:
    22
    Код (Text):
    1. void *get_base(uint32_t addr) {
    2.     addr &= ~4095;
    3.     while (*(uint32_t*)addr != 0x464c457fUL)
    4.         addr -= 4096;
    5.     return (void*)addr;
    6. }
    на вход дайте любой адрес из сегмента кода и проверьте потом найденный "заголовок" на валидность. (другие варианты - раскручивать стек до __libc_start_main, или если есть доступ к аргументам командной строки, то можно отпарсить aux-вектор, но это не так надежно и, имхо, муторно)
     
  8. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.242
    какой адрес, какой стек? это - не elf-файл, это - просто пи-код, у него нет никаких заголовков и стартует он не с __libc_start_main))) я может это не совсем понятно объяснил в первом сообщении, извините... и кстати в приведенный вами алгоритмt можно вставить __builtin_return_address(0) и с его помощью выудить адрес кода... но только этот билд-ин вроде не на всех архитектурах существует...
     
  9. reverser

    reverser New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2004
    Сообщения:
    615
    Читай /proc/<pid>/maps, ищи свой файл. Имя файла можно узнать из /proc/<pid>/exe.
    http://linux.die.net/man/5/proc
     
  10. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.242
    ну блиин, в первом же сообщении:
    то есть из кода я не могу пользовать библиотечные функции... я канеш могу зачитать файлы сисколом и руками распарсить, но мне бы хотелось сделать максимально кросс-линуксовый пи-код, по-этому я стараюсь заложиться на посиксный интерфейс (библиотеки libc.so), который есть наверное в любых линуксах...

    ЗЫ никто не видел статейку/мануал/инфу, где рассказывалось бы, как в линуксовых процессах используются сегментные регистры, мне кажется (по аналогии с виндовс) там мажет быть что-то, за что можно зацепиться)))
     
  11. herm1t

    herm1t New Member

    Публикаций:
    0
    Регистрация:
    1 янв 2004
    Сообщения:
    22
    http://www.akkadia.org/drepper/tls.pdf см. п. 3.4.2
     
  12. reverser

    reverser New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2004
    Сообщения:
    615
    Думаю на начальной стадии без сисколов не обойтись.
     
  13. slesh

    slesh New Member

    Публикаций:
    0
    Регистрация:
    6 фев 2009
    Сообщения:
    214
    Как вариант использую системные вызовы поставить свой обработчик на какой-либо используемый сигнал (тем самым получая адрес старого обработчика), затем всё вернуть на место. А от старого обработчика уже раскручивать всё в нужную сторону
     
  14. 7mm

    7mm New Member

    Публикаций:
    0
    Регистрация:
    15 дек 2009
    Сообщения:
    442
    slesh
    Годно, спасибо за идею.
     
  15. herm1t

    herm1t New Member

    Публикаций:
    0
    Регистрация:
    1 янв 2004
    Сообщения:
    22
    Я немного порылся, и получил такой вариант:
    Код (Text):
    1. #include <stdio.h>
    2. #include <stdint.h>
    3. #include <stdbool.h>
    4. #include <assert.h>
    5. #include <elf.h>
    6.  
    7. /* Type for the dtv.  */
    8. typedef union dtv
    9. {
    10.   size_t counter;
    11.   struct
    12.   {
    13.     void *val;
    14.     bool is_static;
    15.   } pointer;
    16. } dtv_t;
    17.  
    18.  
    19. typedef struct
    20. {
    21.   void *tcb;            /* Pointer to the TCB.  Not necessarily the
    22.                            thread descriptor used by libpthread.  */
    23.   dtv_t *dtv;
    24.   void *self;           /* Pointer to the thread descriptor.  */
    25.   int multiple_threads;
    26.   uintptr_t sysinfo;
    27.   uintptr_t stack_guard;
    28.   uintptr_t pointer_guard;
    29. } tcbhead_t;
    30.  
    31. int main(int argc, char **argv)
    32. {
    33.         void *get_base(uint32_t addr) {
    34.                 addr &= ~4095;
    35.                 while (*(uint32_t*)addr != 0x464c457fUL)
    36.                         addr -= 4096;
    37.                         return (void*)addr;
    38.         }
    39.         tcbhead_t *tcb;
    40.         asm ("mov %%gs:0x0,%%eax":"=a"(tcb));
    41.         dtv_t *dtv = tcb->dtv;
    42.         if (dtv->counter > 0) {
    43.                 uint32_t *tls = dtv[1].pointer.val;
    44.                 Elf32_Ehdr *ehdr = get_base(tls[0]);
    45.                 Elf32_Phdr *phdr = (Elf32_Phdr*)((char*)ehdr + ehdr->e_phoff);
    46.                 int i;
    47.                 Elf32_Dyn *dyn = NULL;
    48.                 int delta;
    49.                 uint32_t low;
    50.                 for (i = 0; i < ehdr->e_phnum; i++)
    51.                         if (phdr[i].p_type == PT_DYNAMIC)
    52.                                 dyn = (Elf32_Dyn*)phdr[i].p_vaddr;
    53.                         else
    54.                         if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == 0)
    55.                                 delta = phdr[i].p_vaddr;
    56.                 delta = (uint32_t)ehdr - delta;
    57.                 dyn = (Elf32_Dyn*)((char*)dyn + delta);
    58.                 assert(dyn != NULL);
    59.                 char *strtab = NULL;
    60.                 int soname = 0;
    61.                 while (dyn->d_tag != DT_NULL) {
    62.                         if (dyn->d_tag == DT_STRTAB)
    63.                                 strtab = (char*)dyn->d_un.d_ptr;
    64.                         else if (dyn->d_tag == DT_SONAME)
    65.                                 soname = dyn->d_un.d_val;
    66.                         dyn++;
    67.                 }
    68.                 assert(strtab != NULL && soname != 0);
    69.                 printf("%s\n", soname + strtab);
    70.         }
    71. }
    Тест:
    Код (Text):
    1. $ uname -r
    2. 2.6.18-238.19.1.el5
    3. $ cat /etc/redhat-release
    4. CentOS release 5.6 (Final)
    5. $ ./a.out
    6. libc.so.6
    7. $ uname -r
    8. 2.6.24-rc7
    9. $ cat /etc/*rel*
    10. Gentoo Base System release 1.12.11.1
    11. $ ./a.out
    12. libc.so.6
     
  16. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.242
    herm1t
    большое спасибо! похоже это как раз то, что нужно)
     
  17. herm1t

    herm1t New Member

    Публикаций:
    0
    Регистрация:
    1 янв 2004
    Сообщения:
    22
    Только вы учтите, что если приложение, в которое вы инжектитесь использует треды, то TLS libc будет не в dtv[1], а где-нибудь еще. Пока не думал, как отличить нужный адрес. И полный fail, если нет поддержки тредов, но сейчас это кажется уже редкость. Так что, я бы эту городушку еще сигналом бы защитил (а сигнал можно позвать через VDSO, адрес сискола в tcb->sysinfo).