PDA

Просмотр полной версии : универсальный патчер памяти процесса для линукса



HPDX2300
08.11.2024, 02:56
создал отдельную тему, чтобы написанное в теме "как ломануть 1C 8.3 for Linux" не захламлялось пустяковыми постами. все значимые посты будут скопированы здесь, их обсуждение можно продолжать в теме "как ломануть 1C 8.3 for Linux" Тема "патчера памяти процесса" оч.перспективна для изготовления таблеток от жадности, и те, кто умеет пользоваться линуксовыми дебагерами, знают, что системные библиотеки делают такой патчинг при каждом запуске процесса, желающие узнать "о чём это я намекаю?" ищите в поисковике "Relocation Processing". Все исходники линукса есть, есть хороший дебагер IDA (не без недостатков). Хотел я проделать финт с 32-битным унипатчем (1С_UP.ехе) для линуксовой либы бэкбейс.so 32-бита разрядности, но пока ещё не нашел участок кода куда его впихнуть - там сильно мешают релоки, попадающие в тело унипатча, скрипт должен убирать несколько релоков, но его код становится от этого "чудесатее". Пока изучал в дебагере "Relocation Processing", дошел до идеи допилить либу /lib/ld-linux.so.2 (это ссылка на /lib/ld-2.17.so) до "патчера памяти процесса". Начните с экспериментов "в песочнице" - в вирт.машине установите любимый линукс, пересоберите из исходников glibc с отладочной информацией, установите дебагер IDA-free-7.6 (более новый не советую, но это на ваше усмотрение). В меню "Debugger" -> "Debugger options" включите 2-е галки "Suspend on debugging start" (или "Suspend on process entry point") и "Suspend on library load/unload" Откройте модуль толстого клиента 1cv8, начните отладку, нажав F9, и выведите окно загруженных модулей процесса - меню "Debugger" -> "Debugger windows" -> "Module list". В окне "Module list" на первой строке вверху написано имя файла запускаемого приложения (1cv8). Жмём F9 для продолжения выполнения процесса. Когда дебагер остановится при наступлении события "LIB_LOADED", то в окне "Module list" во второй строке сверху будет написано имя файла только что загруженной либы, но ещё не очончательно загруженной - смотрите в окно исполняемого кода - IDA остановился внутри функции dl_open_worker в либе ld_2.17.so, ещё не исправлены релоки (т.е. впереди будет патчинг памяти процесса в качестве примера) и не выполнен код инициализации библиотеки (секции .init и .init_array), вот удобный момент патчить память процесса - для этого вам придется дописать код glibc (а именно код загрузчика в некоторых файлах dl-*.с в папке elf). После сборки можно не выполнять make install, а проверить в дебагере работу нового загрузчика. Проверять в отладчике, отлаживая процесс загрузчика с параметром: 32-битный загрузчик ld-2.17.so /opt/1cv8/i386/8.3.25.1445/1cv8 или 64-битный загрузчик ld-2.17.so /opt/1cv8/х86_64/8.3.25.1445/1cv8 после make install создаются ссылки на файлы загрузчиков /lib/ld-linux.so.2 -> /lib/ld-2.17.so /lib64/ld-linux-x86-64.so.2 -> /lib64/ld-2.17.so либы ld-2.17.so хоть и названы одинаково, но это разной разрядности загрузчики. Чтобы не часто заниматься сборкой/отладкой надо продумать вопрос - как сообщать загрузчику инфу "в какой либе чё искать и чем заменять", полагаю это может быть текстовый файл где-нибудь в файловой системе.

HPDX2300
08.11.2024, 03:28
В линуксе есть возможность доустановить для любого пакета его отладочную информацию. Я проделал это с glibc, чтобы появились файлы с debug-инфой для загрузчика /usr/lib64/ld-2.17.so
https://www.upload.ee/image/17362025/ld-2.17.so.debug._1_.png
на снимке МС в правой панели рядом с /usr/lib64/ld-2.17.so появилась ссылка на ld-2.17.so.debug,
а левая панель МС открыта в папке реального расположения файлов .debug

документация и сам загрузчик сообщают, что его можно запускать, указав в качестве параметра имя файла приложения.
вот я запускаю загрузчик в терминале:

[user@hostname lib64]$ ./ld-2.17.so
Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]
You have invoked `ld.so', the helper program for shared library executables.
This program usually lives in the file `/lib/ld.so', and special directives
in executable files using ELF shared libraries tell the system's program
loader to load the helper program from this file. This helper program loads
the shared libraries needed by the program executable, prepares the program
to run, and runs it. You may invoke this helper program directly from the
command line to load and run an ELF executable file; this is like executing
that file itself, but always uses this helper program from the file you
specified, instead of the helper program file specified in the executable
file you run. This is mostly of use for maintainers to test new versions
of this helper program; chances are you did not intend to run this program.

--list list all dependencies and how they are resolved
--verify verify that given object really is a dynamically linked
object we can handle
--inhibit-cache Do not use /etc/ld.so.cache
--library-path PATH use given PATH instead of content of the environment
variable LD_LIBRARY_PATH
--inhibit-rpath LIST ignore RUNPATH and RPATH information in object names
in LIST
--audit LIST use objects named in LIST as auditors

хочу отлаживать в дебагере процесс загрузки толстого клиента для примера:

/usr/lib64/ld-2.17.so /opt/1cv8/x86_64/8.3.25.1374/1cv8

потренируюсь на оригинальном ld-2.17.so, в дальнейшем я буду отлаживать пересобранный загрузчик.

IDA-free-8.4 не захотела начать отладку ld-2.17.so, выругалась:
Input file is a dynamic library, it cannot be run by itself.
Please specify the host application (Debugger, Process options)

в файле ida64 сделал исправление (patch) {0F 85 -> 90 E9}:
offset:19D90B: 0F 85 5D FC FF FF jnz -0x3A3 ; HACK: jnz -> jmp {0F 85 -> 90 E9} to debug ld.so

либа /usr/lib64/ld-2.17.so скопирована с именем /usr/lib64/_my_ld т.к. почти наверняка я захочу её патчить в процессе изучения, ida64 пропатчена, согласилась выполнять отладку /usr/lib64/_my_ld
в меню "Debugger" -> "Process options..." указан параметр процесса Parameters: /opt/1cv8/x86_64/8.3.25.1374/1cv8

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

https://www.upload.ee/image/17362028/ld-2.17.so.debug._2_.png
https://www.upload.ee/image/17362030/ld-2.17.so.debug._3_.png
наиболее интересные для изучения функции:
ld.so:_dl_open
libc.so:__mprotect

HPDX2300
09.11.2024, 13:57
в самом начале базы данных, созданной IDA для загрузчика ld.so, написано из каких исходных файлов он скомпилирован:

.....
LOAD:0000000000000000 ; Compiler : GNU C++
LOAD:0000000000000000 ; File Name : /usr/lib64/unipatch_ld
LOAD:0000000000000000 ; Format : ELF64 for x86-64 (Shared object)
LOAD:0000000000000000 ; Shared Name 'ld-linux-x86-64.so.2'
LOAD:0000000000000000 ;
LOAD:0000000000000000 ; Source File : 'rtld.c'
LOAD:0000000000000000 ; Source File : 'dl-load.c'
LOAD:0000000000000000 ; Source File : 'dl-lookup.c'
LOAD:0000000000000000 ; Source File : 'dl-object.c'
LOAD:0000000000000000 ; Source File : 'dl-reloc.c'
LOAD:0000000000000000 ; Source File : 'dl-deps.c'
LOAD:0000000000000000 ; Source File : 'dl-hwcaps.c'
LOAD:0000000000000000 ; Source File : 'dl-runtime.c'
LOAD:0000000000000000 ; Source File : 'dl-error.c'
LOAD:0000000000000000 ; Source File : 'dl-init.c'
LOAD:0000000000000000 ; Source File : 'dl-fini.c'
LOAD:0000000000000000 ; Source File : 'dl-debug.c'
LOAD:0000000000000000 ; Source File : 'dl-misc.c'
LOAD:0000000000000000 ; Source File : 'dl-version.c'
LOAD:0000000000000000 ; Source File : 'dl-profile.c'
LOAD:0000000000000000 ; Source File : 'dl-conflict.c'
LOAD:0000000000000000 ; Source File : 'dl-tls.c'
LOAD:0000000000000000 ; Source File : 'dl-origin.c'
LOAD:0000000000000000 ; Source File : 'dl-scope.c'
LOAD:0000000000000000 ; Source File : 'dl-execstack.c'
LOAD:0000000000000000 ; Source File : 'dl-caller.c'
LOAD:0000000000000000 ; Source File : 'dl-open.c'
LOAD:0000000000000000 ; Source File : 'dl-close.c'
LOAD:0000000000000000 ; Source File : 'dl-cache.c'
LOAD:0000000000000000 ; Source File : 'dl-tunables.c'
LOAD:0000000000000000 ; Source File : 'tlsdesc.c'
LOAD:0000000000000000 ; Source File : 'dl-get-cpu-features.c'
LOAD:0000000000000000 ; Source File : 'dl-sysdep.c'
LOAD:0000000000000000 ; Source File : 'dl-environ.c'
LOAD:0000000000000000 ; Source File : 'dl-minimal.c'
LOAD:0000000000000000 ; Source File : 'dl-brk.c'
LOAD:0000000000000000 ; Source File : 'dl-sbrk.c'
LOAD:0000000000000000 ; Source File : 'dl-getcwd.c'
LOAD:0000000000000000 ; Source File : 'dl-openat64.c'
LOAD:0000000000000000 ; Source File : 'dl-opendir.c'
LOAD:0000000000000000 ; Source File : 'dl-fxstatat64.c'
LOAD:0000000000000000 ; Source File : 'check_fds.c'
LOAD:0000000000000000 ; Source File : 'errno.c'
LOAD:0000000000000000 ; Source File : 'closedir.c'
LOAD:0000000000000000 ; Source File : 'readdir.c'
LOAD:0000000000000000 ; Source File : 'rewinddir.c'
LOAD:0000000000000000 ; Source File : 'getdents.c'
LOAD:0000000000000000 ; Source File : 'fdopendir.c'
LOAD:0000000000000000 ; Source File : 'profil.c'
LOAD:0000000000000000 ; Source File : 'prof-freq.c'
LOAD:0000000000000000 ; Source File : 'xstat.c'
LOAD:0000000000000000 ; Source File : 'fxstat.c'
LOAD:0000000000000000 ; Source File : 'lxstat.c'
LOAD:0000000000000000 ; Source File : 'fcntl.c'
LOAD:0000000000000000 ; Source File : 'mmap.c'
LOAD:0000000000000000 ; Source File : '_exit.c'
LOAD:0000000000000000 ; Source File : 'getpid.c'
LOAD:0000000000000000 ; Source File : 'environ.c'
LOAD:0000000000000000 ; Source File : 'sigaction.c'
LOAD:0000000000000000 ; Source File : 'strdup.c'
LOAD:0000000000000000 ; Source File : 'rtld-strncmp.c'
LOAD:0000000000000000 ; Source File : 'rtld-memcmp.c'
LOAD:0000000000000000 ; Source File : 'memmove.c'
LOAD:0000000000000000 ; Source File : 'wordcopy.c'
LOAD:0000000000000000 ; Source File : 'strstr-c.c'

HPDX2300
09.11.2024, 17:44
offsets in file IDA-7.6/ida64:
000E7062: 74 3C jz +3Ch ; HACK: {3C->00} F2 toggle breakpoin for .exe and .dll
001CD5D7: 0F 85 42 F5 FF FF jnz -ABEh ; HACK: {0F 85->90 E9} IDA не будет возражать отлаживать процесс ld.so
00283618: B8 01 00 00 00 mov eax,1 ; HACK: {1->0} IDA не будет возражать при изучении её модулей при отладке изделия секты надо изменить обработку exceptions: меню "Debugger" -> "Debugger options..." -> кнопка "Edit exceptions"
Code Name Suspend Passed to Report
0000000D SIGPIPE No Application Silent
00000011 SIGCHLD No Application Silent

HPDX2300
09.11.2024, 23:08
only for IDA Free 7.6.210526 Linux-64
offsets in file ida64:


# E7062: 74 3C jz +3Ch ; HACK: {3C->00} F2 будет ВКЛ/ВЫКЛ breakpoint и для windows-модулей (.exe и .dll)
printf '\x00' | dd of=ida64 bs=1 seek=$((0xE7063)) count=1 conv=notrunc

# 1CD5D7: 0F 85 42 F5 FF FF jnz -ABEh ; HACK: {0F 85->90 E9} IDA не будет возражать отлаживать процесс ld.so
printf '\x90\xE9' | dd of=ida64 bs=1 seek=$((0x1CD5D7)) count=2 conv=notrunc

# 283618: B8 01 00 00 00 mov eax,1 ; HACK: {1->0} IDA не будет возражать при изучении её модулей
printf '\x00' | dd of=ida64 bs=1 seek=$((0x283619)) count=1 conv=notrunc


при отладке изделия секты надо изменить обработку exceptions:
меню "Debugger" -> "Debugger options..." -> кнопка "Edit exceptions"


Name=SIGPIPE Suspend=No Passed to=Application Report=Silent
Name=SIGCHLD Suspend=No Passed to=Application Report=Silent

HPDX2300
09.11.2024, 23:09
only for IDA Free 8.4.240527 Linux-64
offsets in file ida64:


# 221AA0: B8 01 00 00 00 mov eax,1 ; HACK: {1->0} IDA не будет возражать при изучении её модулей
printf '\x00' | dd of=ida64 bs=1 seek=$((0x00221AA1)) count=1 conv=notrunc

# 19D90B: 0F 85 5D FC FF FF jnz -0x3A3 ; HACK: jnz->jmp {0F 85->90 E9} IDA не будет возражать отлаживать процесс ld.so
printf '\x90\xE9' | dd of=ida64 bs=1 seek=$((0x19D90B)) count=2 conv=notrunc

# EAD5D: 74 06: jz short loc_EAD65 ; HACK: {6->0} F2 будет ВКЛ/ВЫКЛ breakpoint и для windows-модулей (.exe,.dll, and so on)
printf '\x00' | dd of=ida64 bs=1 seek=$((0xEAD5E)) count=1 conv=notrunc


при отладке изделия секты надо изменить обработку exceptions:
меню "Debugger" -> "Debugger options..." -> кнопка "Edit exceptions"


Name=SIGPIPE Suspend=No Passed to=Application Report=Silent
Name=SIGCHLD Suspend=No Passed to=Application Report=Silent

HPDX2300
11.11.2024, 15:39
Показываю в каком виде будет доступно имя загружаемой библиотеки.
В выхлопе загрузчика увидите, в частности, строки:
relocation processing: /opt/1cv8/x86_64/8.3.18.1128/xml2.so
calling init: /opt/1cv8/x86_64/8.3.18.1128/xml2.so

буду запускать толстого клиента в окне терминала, но сперва небольшое введение:

$ export LD_DEBUG=help
$ ls
в выхлопе увидите варианты значений LD_DEBUG

посмотрим на выхлоп relocation processing

$ cd /opt/1cv8/x86_64/8.3.18.1128
$ export LD_DEBUG=reloc
$ ./1cv8
3505: relocation processing: /lib64/libc.so.6
...
3505: relocation processing: /opt/1cv8/x86_64/8.3.18.1128/libstdc++.so.6 (lazy)
...
3505: relocation processing: /opt/1cv8/x86_64/8.3.18.1128/core83.so (lazy)
3505: relocation processing: /opt/1cv8/x86_64/8.3.18.1128/coreui83.so (lazy)
3505: relocation processing: /opt/1cv8/x86_64/8.3.18.1128/wbase.so (lazy)
...
3505: relocation processing: ./1cv8 (lazy)
3505: relocation processing: /lib64/ld-linux-x86-64.so.2

// выполнение секций .init загруженных модулей
...
3505: calling init: /lib64/libc.so.6
...
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/libstdc++.so.6
...
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/core83.so
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/coreui83.so
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/wbase.so
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/libtcmalloc_minimal.so.4
3505: initialize program: ./1cv8
!!!=> 3505: transferring control: ./1cv8 // переход к выполнению кода приложения
...
// отсюда начинается динамическая загрузка модулей, её выполняет процесс 1cv8
3505: relocation processing: /opt/1cv8/x86_64/8.3.18.1128/xml2.so (lazy)
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/xml2.so
3505: relocation processing: /opt/1cv8/x86_64/8.3.18.1128/json.so (lazy)
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/json.so
3505: relocation processing: /opt/1cv8/x86_64/8.3.18.1128/techsys.so (lazy)
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/techsys.so
3505: relocation processing: /opt/1cv8/x86_64/8.3.18.1128/xdto.so (lazy)
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/xdto.so
3505: relocation processing: /opt/1cv8/x86_64/8.3.18.1128/pack.so (lazy)
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/pack.so
3505: relocation processing: /opt/1cv8/x86_64/8.3.18.1128/image.so (lazy)
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/image.so
3505: relocation processing: /opt/1cv8/x86_64/8.3.18.1128/libMagickCore-6.Q8.so.2 (lazy)
3505: relocation processing: /opt/1cv8/x86_64/8.3.18.1128/libMagickWand-6.Q8.so.2 (lazy)
3505: relocation processing: /opt/1cv8/x86_64/8.3.18.1128/libMagick++-6.Q8.so.6 (lazy)
3505: relocation processing: /opt/1cv8/x86_64/8.3.18.1128/grphcs.so (lazy)
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/libMagickCore-6.Q8.so.2
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/libMagickWand-6.Q8.so.2
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/libMagick++-6.Q8.so.6
3505: calling init: /opt/1cv8/x86_64/8.3.18.1128/grphcs.so
3522: relocation processing: /lib64/libc.so.6
3522: relocation processing: /lib64/libdl.so.2 (lazy)
3522: relocation processing: /lib64/libtinfo.so.5 (lazy)
3522: relocation processing: sh (lazy)
3522: relocation processing: /lib64/ld-linux-x86-64.so.2
3522: calling init: /lib64/libc.so.6
3522: calling init: /lib64/libdl.so.2
3522: calling init: /lib64/libtinfo.so.5
3522: initialize program: sh <=== это чё такое? это скрытый запуск скрипта, например получение инфы о железке
3522: transferring control: sh
...
// много строк выброшено для краткости
...
// появилось окно выбора баз, нажимаю кнопку "Выйти", начинается выполнение секций .fini загруженных модулей
...
3505: calling fini: ./1cv8 [0]
3505: calling fini: /opt/1cv8/x86_64/8.3.18.1128/xml2.so [0]
3505: calling fini: /opt/1cv8/x86_64/8.3.18.1128/json.so [0]
3505: calling fini: /opt/1cv8/x86_64/8.3.18.1128/xdto.so [0]
...
3505: calling fini: /opt/1cv8/x86_64/8.3.18.1128/core83.so [0]
3505: calling fini: /opt/1cv8/x86_64/8.3.18.1128/libicui18n.so.46 [0]
3505: calling fini: /opt/1cv8/x86_64/8.3.18.1128/libicuuc.so.46 [0]
3505: calling fini: /lib64/libdl.so.2 [0]
3505: calling fini: /opt/1cv8/x86_64/8.3.18.1128/nuke83.so [0]
3505: calling fini: /opt/1cv8/x86_64/8.3.18.1128/libstdc++.so.6 [0]

HPDX2300
11.11.2024, 17:48
в файле glibc-src/elf/dl-reloc.c находим строку "relocation processing: %s":
в процедуре
_dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], int reloc_mode, int consider_profiling)
написано:

if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_RELOC, 0))
_dl_debug_printf ("\nrelocation processing: %s%s\n",
l->l_name[0] ? l->l_name : rtld_progname,
lazy ? " (lazy)" : "");

/* DT_TEXTREL is now in level 2 and might phase out at some time.
But we rewrite the DT_FLAGS entry to a DT_TEXTREL entry to make
testing easier and therefore it will be available at all time. */
if (__builtin_expect (l->l_info[DT_TEXTREL] != NULL, 0))
{
/* Bletch. We must make read-only segments writable
long enough to relocate them. */
const ElfW(Phdr) *ph;
for (ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
if (ph->p_type == PT_LOAD && (ph->p_flags & PF_W) == 0)
{ /* read-only секция выполняемого кода (p_type==PT_LOAD) будет сделана writable перед патчингом, он же relocation; сделает это функция __mprotect */
struct textrels *newp;

newp = (struct textrels *) alloca (sizeof (*newp));
newp->len = (((ph->p_vaddr + ph->p_memsz + GLRO(dl_pagesize) - 1)
& ~(GLRO(dl_pagesize) - 1))
- (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1)));
newp->start = ((ph->p_vaddr & ~(GLRO(dl_pagesize) - 1))
+ (caddr_t) l->l_addr);

if (__mprotect (newp->start, newp->len, PROT_READ|PROT_WRITE) < 0) // секция выполняемого кода writable если функция __mprotect вернёт код возврата >=0
{
errstring = N_("cannot make segment writable for relocation");
call_error:
_dl_signal_error (errno, l->l_name, NULL, errstring);
}

#if (PF_R | PF_W | PF_X) == 7 && (PROT_READ | PROT_WRITE | PROT_EXEC) == 7
newp->prot = (PF_TO_PROT
>> ((ph->p_flags & (PF_R | PF_W | PF_X)) * 4)) & 0xf;
#else
newp->prot = 0;
if (ph->p_flags & PF_R)
newp->prot |= PROT_READ;
if (ph->p_flags & PF_W)
newp->prot |= PROT_WRITE;
if (ph->p_flags & PF_X)
newp->prot |= PROT_EXEC;
#endif
newp->next = textrels;
textrels = newp;
}
}

HPDX2300
11.11.2024, 18:15
в файле glibc-src/elf/dl-init.c находим строку "calling init: %s":
в процедуре
call_init (struct link_map *l, int argc, char **argv, char **env)
написано:


/* Print a debug message if wanted. */
if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
_dl_debug_printf ("\ncalling init: %s\n\n",
l->l_name[0] ? l->l_name : rtld_progname);

/*
Now run the local constructors. There are two forms of them:
- the one named by DT_INIT
- the others in the DT_INIT_ARRAY.
*/
if (l->l_info[DT_INIT] != NULL)
{
init_t init = (init_t) DL_DT_INIT_ADDRESS
(l, l->l_addr + l->l_info[DT_INIT]->d_un.d_ptr);

/* Call the function. */
init (argc, argv, env);
}

/*
Next see whether there is an array with initialization functions.
*/
ElfW(Dyn) *init_array = l->l_info[DT_INIT_ARRAY];
if (init_array != NULL)
{
unsigned int j;
unsigned int jm;
ElfW(Addr) *addrs;

jm = l->l_info[DT_INIT_ARRAYSZ]->d_un.d_val / sizeof (ElfW(Addr));

addrs = (ElfW(Addr) *) (init_array->d_un.d_ptr + l->l_addr);
for (j = 0; j < jm; ++j)
((init_t) addrs[j]) (argc, argv, env);
}

HPDX2300
11.11.2024, 18:59
находим вызовы call_init - они все в файле glibc-src/elf/dl-init.c в процедуре _dl_init


void
internal_function
_dl_init (struct link_map *main_map, int argc, char **argv, char **env)
{
ElfW(Dyn) *preinit_array = main_map->l_info[DT_PREINIT_ARRAY];
ElfW(Dyn) *preinit_array_size = main_map->l_info[DT_PREINIT_ARRAYSZ];
unsigned int i;

if (__builtin_expect (GL(dl_initfirst) != NULL, 0))
{
call_init (GL(dl_initfirst), argc, argv, env); <====== здесь вызов call_init
GL(dl_initfirst) = NULL;
}

/* Don't do anything if there is no preinit array. */
if (__builtin_expect (preinit_array != NULL, 0)
&& preinit_array_size != NULL
&& (i = preinit_array_size->d_un.d_val / sizeof (ElfW(Addr))) > 0)
{
ElfW(Addr) *addrs;
unsigned int cnt;

if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
_dl_debug_printf ("\ncalling preinit: %s\n\n",
main_map->l_name[0]
? main_map->l_name : rtld_progname);

addrs = (ElfW(Addr) *) (preinit_array->d_un.d_ptr + main_map->l_addr);
for (cnt = 0; cnt < i; ++cnt)
((init_t) addrs[cnt]) (argc, argv, env);
}

/* Stupid users forced the ELF specification to be changed. It now
says that the dynamic loader is responsible for determining the
order in which the constructors have to run. The constructors
for all dependencies of an object must run before the constructor
for the object itself. Circular dependencies are left unspecified.

This is highly questionable since it puts the burden on the dynamic
loader which has to find the dependencies at runtime instead of
letting the user do it right. Stupidity rules! */ <======== "Шобла тупых побеждает упрямством"

i = main_map->l_searchlist.r_nlist;
while (i-- > 0)
call_init (main_map->l_initfini[i], argc, argv, env); <====== здесь вызов call_init

#ifndef HAVE_INLINED_SYSCALLS
/* Finished starting up. */
INTUSE(_dl_starting_up) = 0;
#endif
}

HPDX2300
12.11.2024, 03:31
после редактирования первого поста ему пришел пипец - всё в одну строку.
повторяю его с дополнениями:

Пока изучал в дебагере "Relocation Processing", дошел до идеи допилить либу /lib/ld-linux.so.2 (это ссылка на /lib/ld-2.17.so) до "патчера памяти процесса".

Начните с экспериментов "в песочнице" - в вирт.машине установите любимый линукс, доустановите debug-символы и исходники для пакета glibc (репозитории Debuginfo, Sources), установите дебагер IDA-free-7.6 (более новый не советую, но это на ваше усмотрение).

Запустив дебагер IDA, в меню "Debugger" -> "Debugger options" включите 2-е галки "Suspend on debugging start" (или "Suspend on process entry point") и "Suspend on library load/unload"

Откройте модуль толстого клиента 1cv8, начните отладку, нажав F9, и выведите окно загруженных модулей процесса - меню "Debugger" -> "Debugger windows" -> "Module list".

В окне "Module list" на первой строке вверху написано имя файла запускаемого приложения (1cv8).

Включаем в меню "Debugger" -> "Use source-level debugging", если вы доустановили исходники glibc.

Жмём F9 для продолжения выполнения процесса. Когда дебагер остановится при наступлении события "LIB_LOADED", то в окне "Module list" во второй строке сверху будет написано имя файла либы, с которой сейчас работает загрузчик ld-linux-x86-64.so.2

https://www.upload.ee/image/17378277/ld-2.17.so.debug_with_src._1_.png

снимок сделан в момент динамической загрузки первого модуля - xml2.so, либа ещё не очончательно загружена - смотрите в окно исходного кода - IDA остановился внутри функции dl_open_worker в файле исходника dl-open.c на 301 строке.

фрагмент исходника dl-open.c с номерами строк:


227: _dl_debug_initialize (0, args->nsid);
228:
229: /* Load the named object. */
230: struct link_map *new;
231: args->map = new = _dl_map_object (call_map, file, lt_loaded, 0,
232: mode | __RTLD_CALLMAP, args->nsid);
полагаю _dl_map_object делает отображение (mapping) файла в виртуальную память процесса

267: /* Load that object's dependencies. */
268: _dl_map_object_deps (new, NULL, 0, 0,
269: mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT));
полагаю _dl_map_object_deps читая заголовок ELF-файла, замаппит в память процесса статически слинкованные либы

297: /* Notify the debugger all new objects are now ready to go. */
298: struct r_debug *r = _dl_debug_initialize (0, args->nsid);
299: r->r_state = RT_CONSISTENT;
300: _dl_debug_state ();
301: LIBC_PROBE (map_complete, 3, args->nsid, r, new);

суть 4-х строк 298-301 - уведомить дебагер о том, что "all new objects are now ready to go",
в строке 301 "map_complete" подсказывает, что дебагер уведомляется о событии "mapping выполнен"

В либе xml2.so ещё не исправлены релоки (т.е. впереди будет патчинг памяти процесса в качестве примера) и не выполнен код инициализации библиотеки (секции .init и .init_array)

Полагаю, что удобный момент патчить память процесса - после "relocation processing", но до выполнения инициализации библиотеки (секции .init и .init_array). Релоки будут уже исправлены и мне не придется с ними бороться - мой код запишет новое поверх старого и оно уже не будет "исправляться".

после "mapping processing" начинается выполнение "relocation processing"


307: /* Only do lazy relocation if `LD_BIND_NOW' is not set. */
308: int reloc_mode = mode & __RTLD_AUDIT;
309: if (GLRO(dl_lazy))
310: reloc_mode |= mode & RTLD_LAZY;

// выполнен "mapping processing"

554: /* Notify the debugger all new objects have been relocated. */
555: if (relocation_in_progress)
556: LIBC_PROBE (reloc_complete, 3, args->nsid, r, new);

// +++ вот тут я внедрю код исправления орфографических ошибок в приложении - набрали, блядь, неграмотных по объявлению

// начинается выполнение "initializer functions"
558: /* Run the initializer functions of new objects. */
559: _dl_init (new, args->argc, args->argv, args->env);

HPDX2300
12.11.2024, 03:50
заметил опечатку (давно пора спать) исправляю:

после "mapping processing" начинается выполнение "relocation processing"


307: /* Only do lazy relocation if `LD_BIND_NOW' is not set. */
308: int reloc_mode = mode & __RTLD_AUDIT;
309: if (GLRO(dl_lazy))
310: reloc_mode |= mode & RTLD_LAZY;


выполнен "relocation processing"


554: /* Notify the debugger all new objects have been relocated. */
555: if (relocation_in_progress) ^^^^^^^^^^^^^^^^^^^
556: LIBC_PROBE (reloc_complete, 3, args->nsid, r, new);
^^^^^^^^^^^^^^
вот тут я внедрю код исправления орфографических ошибок в приложении - набрали, блядь, неграмотных по объявлению

// начинается выполнение "initializer functions"
558: /* Run the initializer functions of new objects. */
559: _dl_init (new, args->argc, args->argv, args->env);

HPDX2300
15.11.2024, 16:57
>вот тут я внедрю код исправления орфографических ошибок в приложении
неудачный выбор, надо было пораньше лечь спать.
правильное место для добавления своего кода - конец процедуры _dl_relocate_object, подробности будут позже.

А сейчас покажу на примере backbas.so x86_64 ver.25-1374 покажу "почему в случае unpatch-x64 не пришлось бороться с релоками?"

после применения unpatch.py к backbas.so:
выхлоп:

patching 25-1374/backbas.so at offset=0xc80370
зная размер блока unpatch-x64 получаем смещения в файле начала и конца унипатча:
0000000000C80370: begin of unipatch-x64
0000000000C82D77: end of unipatch-x64

посмотрим "Section Headers" насчет таблиц релоков


$ readelf -W -e backbas.so
выхлоп:
....
Section Headers: ES=Entry Size
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .note.gnu.build-id NOTE 0000000000000270 000270 000018 00 A 0 0 4
[ 2] .dynsym DYNSYM 0000000000000288 000288 007878 18 A 6 1 8
[ 3] .gnu.version VERSYM 0000000000007b00 007b00 000a0a 02 A 2 0 2
[ 4] .gnu.version_r VERNEED 000000000000850c 00850c 000220 00 A 6 7 4
[ 5] .gnu.hash GNU_HASH 0000000000008730 008730 000714 00 A 2 0 8
[ 6] .dynstr STRTAB 0000000000008e44 008e44 00fd3f 00 A 0 0 1
[ 7] .rela.dyn RELA 0000000000018b88 018b88 117a80 18 A 2 0 8 <== таблица релоков (Size/ES=117a80h/18h=BA70h=47728 entries)
[ 8] .rela.plt RELA 0000000000130608 130608 005aa8 18 AI 2 25 8 <== таблица релоков (Size/ES=5aa8h/18h=3C7h=967 entries)
[ 9] .gcc_except_table PROGBITS 00000000001360b0 1360b0 050690 00 A 0 0 4
[10] .rodata PROGBITS 0000000000186740 186740 46aa14 00 AMS 0 0 64
[11] .eh_frame_hdr PROGBITS 00000000005f1154 5f1154 031c84 00 A 0 0 4
[12] .eh_frame PROGBITS 0000000000622dd8 622dd8 1223b4 00 A 0 0 8
[13] .text PROGBITS 0000000000747000 746000 6F0A8A 00 AX 0 0 4096 <== unipatch-x64 там
[14] .init PROGBITS 0000000000E37A8C E36A8C 00001F 00 AX 0 0 4
[15] .fini PROGBITS 0000000000E37AAC E36AAC 000009 00 AX 0 0 4
[16] .plt PROGBITS 0000000000E37AC0 E36AC0 003C80 00 AX 0 0 16
[17] .fini_array FINI_ARRAY 0000000000E3C740 E3A740 000008 08 WA 0 0 8 <== первый релок там
[18] .init_array INIT_ARRAY 0000000000E3C748 E3A748 000650 00 WA 0 0 8 <== там основная масса релоков


посмотрим таблицу релоков:


$ readelf -W -r backbas.so
выхлоп:
Relocation section '.rela.dyn' at offset 0x18b88 contains 47728 entries:

Offset Info Type
0000000000E3C740 0000000000000008 R_X86_64_RELATIVE
0000000000E3C748 0000000000000008 R_X86_64_RELATIVE
0000000000E3C750 0000000000000008 R_X86_64_RELATIVE
0000000000E3C758 0000000000000008 R_X86_64_RELATIVE
более 47000 записей пропущено для краткости

релоки применяются по смещению 0xE3C740 и далее, а код унипатча находится в файле с 0xC80370 по 0xC82D77
т.е. никакой релок не попадает в код HASP_API64 - удачное стечение обстоятельств.