ошибка доступа к памяти при чтении записей PT_DYNAMIC Elf32 на Android 6.0

Чтобы перехватить функцию libc приложения Android (то есть app_process32), я сначала читаю все адресное пространство, сохраненное в /proc/self/maps, и для каждого загруженного ELF я перезаписываю соответствующую запись о перемещении.

Чтение строки /proc/self/maps:

Elf32_Addr start, end;
sscanf(line, "%8x-%8x", &start, &end);

Затем я проверяю, эльф ли это, проверяя магию. Если это ELF, я читаю его сегмент PT_DYNAMIC и просматриваю его записи:

Elf32_Ehdr *ehdr = (Elf32_Ehdr *) start;
Elf32_Phdr *phdr = (Elf32_Phdr *) ((unsigned char *) ehdr + ehdr->e_phoff);
Elf32_Half phnum = ehdr->e_phnum;
Elf32_Addr dynamic = 0;    

for (; phnum > 0; --phnum, ++phdr) {
    if (phdr->p_type == PT_DYNAMIC) {
        dynamic = start + phdr->p_vaddr;
        break;
    }
}

И вот как я перебираю динамические записи:

Elf32_Dyn *dyn;
for (dyn = (Elf32_Dyn *) dynamic; dyn->d_tag; dyn++) {
    Elf32_Addr addr = dyn->d_un.d_ptr;
    Elf32_Sword val = dyn->d_un.d_val;

    switch (dyn->d_tag) {
        // rest of the code.
    }
}

Для некоторых общих объектов это нормально, однако для некоторых я получаю SIGSEGV при проверке условия цикла for dyn->d_tag. Почему PT_DYNAMIC указывает на место, которое я не могу прочитать? Также я заметил, что dynamic обычно > end, это нормально?

Я использую устройство под управлением 32-разрядной версии Android 6.0.


person frogatto    schedule 15.03.2019    source источник


Ответы (1)


Этот расчет:

dynamic = start + phdr->p_vaddr;

корректно только для образов ELF, связанных по адресу 0, что типично для разделяемых библиотек и независимых от позиции исполняемых файлов, но не является обязательным.

Я предполагаю, что у вас есть некоторые общие библиотеки, которые были предварительно связаны с адресом, отличным от 0.

Эта библиотека не была предварительно связана:

readelf -Wl foo.so | egrep 'LOAD|DYNAMIC'
  LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x000684 0x000684 R E 0x200000
  LOAD           0x000e40 0x0000000000200e40 0x0000000000200e40 0x0001e0 0x0001e8 RW  0x200000
  DYNAMIC        0x000e50 0x0000000000200e50 0x0000000000200e50 0x000190 0x000190 RW  0x8

Та же библиотека после prelink -r 0x120000 foo.so:

readelf -Wl foo.so | egrep 'LOAD|DYNAMIC'
  LOAD           0x000000 0x0000000012000000 0x0000000012000000 0x000684 0x000684 R E 0x200000
  LOAD           0x000e40 0x0000000012200e40 0x0000000012200e40 0x0001e0 0x0001e8 RW  0x200000
  DYNAMIC        0x000e50 0x0000000012200e50 0x0000000012200e50 0x000190 0x000190 RW  0x8

Если предварительно связанная библиотека загружена в ваше приложение по предварительно связанному адресу (start==0x12200000), вы получите dynamic == 0x12200e50+0x12200000, что явно является подделкой.

Для учета необходимо:

dynamic = start + phdr->p_vaddr - first_pt_load->p_vaddr;

где first_pt_load — самый низкий сегмент PT_LOAD (который будет иметь .p_vaddr == 0 для библиотеки без предварительной привязки и 0x12000000 для предварительно связанной).

person Employed Russian    schedule 16.03.2019