дизассемблерный листинг
Что за чертовщина?! Каким образом расшифровщик может существовать в extern, когда здесь прямым текстом прописаны фактические адреса динамически загружаемых функций! Но тот факт, что файл все-таки работает, убеждает нас, что да — может! Просто IDA Pro в попытке "эмуляции" загрузки elf-файла пихает в extern всякую хрень, которой там нет. ### то, чего там в действительности нет
Здесь мы подходим к одной из самых любопытных особенной строения elf-файлов. В отличии от Windows, где заполнение extern'а происходит на стадии загрузки файла в память, в UNIX это делает стартовый код, причем делает он это очень хитрым способом. Ниже показан протокол трассировки программы под отладчиком с моими комментариями, отмеченными знаком ';' и содержимым дизассемблерного листинга IDA Pro, отмеченным знаком '#'. Как говориться — сравните и почувствуйте разницу!
Для облегчения понимая возьмем незашифрованную программу, необработанную ELFCrypt'ом:
(gdb) b _start ; устанавливаем точку останова на начало стартового кода
Breakpoint 1 at 0x80482c0: file ../sysdeps/i386/elf/start.S, line 47.
(gdb) r ; запускаем программу на выполнение
Breakpoint 1, _start () at ../sysdeps/i386/elf/start.S:47
(gdb) x 0x80495DC ; ок, мы в точке входа. смотрим на extern
# extern:80495DC 7F 01 00 00 extern puts@@GLIBC_2_0:near
0x80495dc: 0x00000000
; IDA Pro нас уверяет, что extern содержит адрес 0000017Fh, но в действительности
; область extern на момент запуска файла девственно чиста и забита нулями
#.text:080482C0 _start proc near
#.text:080482C0 31 ED xor ebp, ebp
1: x/i $pc 0x80482c0 <_start>
: xor %ebp,%ebp
; // незначащие машинные инструкции пропущены
#.text:080482D7 68 90 83 04 08 push offset main
1: x/i $pc 0x80482d7 <_start+23>
: push $0x8048390
#.text:080482DC E8 CF FF FF FF call ___libc_start_main
1: x/i $pc 0x80482dc <_start+28>
: call 0x80482b0 <_init+56>
; но вот стартовый код вызывает библиотечную функцию ___libc_start_main,
; поскольку компилятор еще не знает ее фактического адреса,
; он вставляет переходник к секции .plt, содержащей переходники
; к секции .got, заполняемой динамическим загрузчиком
#.plt:080482B0 ___libc_start_main proc near
#.plt:080482B0 FF 25 D0 95 04 08 jmp ds:off_80495D0
1: x/i $pc 0x80482b0 <_init+56>
: jmp *0x80495d0
; IDA Pro
корректно отобразила plt-переходник, вызывающий функцию,
; указатель на которую расположен в двойном слове по адресу 80495D0h
#.got:080495D0 E8 95 04 08 off_80495D0 dd offset __libc_start_main
1: x/i $pc 0x80482b6 <_init+62>
: push $0x8
1: x/i $pc 0x80482bb <_init+67>
: jmp 0x8048290 <_init+24>
; а вот тут уже начались расхождения...
; IDA Pro
уверяет, что здесь расположено смещение функции __libc_start_main
; в то время как отладчик показывает, что здесь находится специальный код
; push 08h/jmp
8048290h. посмотрим, что покажет IDA Pro по адресу 8048290h
# .plt:08048290 ?? ?? ?? ?? ?? ?? dd 4 dup(?)
1: x/i $pc 0x8048290 <_init+24>
: pushl 0x80495c4
1: x/i $pc 0x8048296 <_init+30>
: jmp *0x80495c8
; парад различий продолжается!!! IDA Pro
вообще не показывает ничего!!!
; отладчик же показывает код, засылающий в стек смещение первого (считая от нуля)
; элемента таблицы .got и передающего управление по адресу, записанного во втором
; элементе таблицы .got. как следует из спецификации elf-формата, первые три элемента
; секции .got зарезервированы для служебных целей и вторая из них хранит адрес функции
; _dl_map_object_deps, которая, получив в качестве аргумента адрес начала .got'а
; читает его содержимое (а содержатся там ссылки на библиотечные функции)
; и заполняет extern фактическими адресами
0x4000bbd0 in _dl_map_object_deps () from /lib/ld-linux.so.2
1: x/i $pc 0x4000bbd0 <_dl_map_object_deps+4384>
: push %eax
; ага! вот эта функция, расположенная на моей машине по адресу 4000BBD0h,
; принадлежащему библиотеке libc.so.6 (на других машинах этот адрес может быть иным)
; она-то и выполняет всю работу по инициализации extern'а, в котором находится
; наш расшифровщик, уже расшифровавший программу, а затем вызывает __libc_start_main,
; так что загрузка динамической библиотеки происходит совершенно прозрачно