#c #assembly #memory #binary #elf
Вопрос:
Я работаю над проектом бинарного анализа. У меня есть файл elf в качестве входных данных, который я разбираю через objdump. Моя цель состоит в том, чтобы выполнить динамическое символьное выполнение с использованием z3 каждой инструкции в ассемблерном коде, следуя заданному графику потока управления. Для имитации эффектов каждой инструкции. Я реализовал свою собственную модель памяти и регистров в виде объектов карты C (MEM и REG соответственно). Для простоты давайте предположим, что данные внутри регистров и памяти хранятся в десятичном формате(не в реальном случае), и давайте пока рассмотрим операции z3 как обычные математические операции.
Объявляю свою память и регистрирую модели:
map<string,int> MEM; //string address as key, int data as value
map<string,int> REG; //string register name as key, int data as value
например, инструкция по хранению str r3, [fp, #-8]
в коде будет выглядеть следующим образом MEM[REG[fp]-8]=REG[r3]
и инструкция по добавлению add fp, sp, #4
в коде будет выглядеть так REG[fp]=REG[sp] 4
Я моделирую влияние каждой инструкции на свою модель памяти и регистрирую модель таким же образом для всех инструкций в ассемблерном коде.
когда я запускаю свой инструмент. Я начинаю с инициализации модели памяти с использованием данных в файле сборки. Ниже приведен ассемблерный код функции _ZSt9terminatev программы, которую я анализирую:
0000bf6c <_ZSt9terminatev>:
bf6c: b508 push {r3, lr}
bf6e: 4b03 ldr r3, [pc, #12] ; (bf7c <_ZSt9terminatev 0x10>)
bf70: 6818 ldr r0, [r3, #0]
bf72: f3bf 8f5b dmb ish
bf76: f7ff ffe5 bl bf44 <_ZN10__cxxabiv111__terminateEPFvvE>
bf7a: bf00 nop
bf7c: 00021bc0 .word 0x00021bc0
Например, последняя строка функции bf7c: 00021bc0 .word 0x00021bc0
_ZSt9terminatev реализована в моем коде как MEM["0000bf7c"]= 138176 \138176 is decimal conversion of 0x21bc0 just for simplicity
. Я делаю то же самое для всех данных в файле сборки, чтобы заполнить свою модель памяти перед запуском (должен ли я делать то же самое со всеми другими строками, такими как даже инструкции кода, такие как инструкция nop по адресу 0000bf7a(т. Е. MEM[«0000bf7a»]=десятичное число(«0000bf00»))? .
Я думал, что этого достаточно, чтобы инициализировать мою модель памяти, однако, когда я начинаю выполнять первую инструкцию в точке входа моей программы, я столкнулся с проблемой «то, что я называю ключевой ошибкой» (ниже приведен фрагмент первых 3 строк функции точки входа моей программы).:
000080ec <_mainCRTStartup>:
80ec: 4b13 ldr r3, [pc, #76] ; (813c <_mainCRTStartup 0x50>)
80ee: 2b00 cmp r3, #0
80f0: bf08 it eq
похоже, что первая инструкция ldr r3, [pc, #76]
(т. е. REG[r3]=MEM[REG[pc] 76]) загружается из неинициализированной области памяти.
итак, наконец, к моему вопросу, как я могу правильно инициализировать свою модель памяти, прежде чем перейти к точке входа в мою программу?. Я понимаю роль загрузчика компоновщика(например, /lib/ld-linux.so), который загружает библиотеки, от которых зависит мой elf, но как я могу реализовать это в коде, учитывая, что у меня есть только файл elf программы для начала. Возможно, в моем файле elf есть раздел, показывающий, какие данные поступают на какие адреса памяти, прежде чем перейти к точке входа программы?
Комментарии:
1. Заголовок программы ELF сообщает ядру, какие диапазоны виртуальной памяти сопоставляются с какими частями файла, или заполняется нулями, если файл не поддерживается. (Или, если memsiz > filesiz для сопоставления, более поздняя часть обнуляется, например
.bss
, раздел>, смежный с.data
одним сегментом компоновщика.) Используйтеreadelf -a
для просмотра сегментов (и сопоставления разделов->сегментов, если вам интересно, но обратите внимание, что загрузчик программ ELF ядра >заботится только о сегментах программы в заголовке программы.) Вы, вероятно, захотите использовать libbfd или какую-либо другую библиотеку для анализа эльфов.