Физические адреса соответствуют номерам ячеек оперативной памяти, где в действительности расположены или будут расположены переменные и команды. Переход от виртуальных адресов к физическим может осуществляться двумя способами. Перемещающий загрузчик на основании имеющихся у него исходных данных о начальном адресе физической памяти, в которую предстоит загружать программу, и информации, предоставленной транслятором об адресно-зависимых константах программы, выполняет загрузку программы, совмещая ее с заменой виртуальных адресов физическими[4].
Преобразование адресов. Обычно, программа делится на сегменты – сегмент кода, сегмент данных и дополнительные сегменты, для каждого сегмента существует:
RVA – относительный виртуальный адрес, который используется для корректного обращения к данным в памяти,
PhysOffs физический адрес – смещение сегмента относительно начала файла.
То есть, при загрузке программы весь файл загружается, начиная с адреса VA (virtual address – текущий свободный виртуальный адрес), а каждый сегмент, начинающийся в файле с адреса PhysOffs, помещается с адреса VA+RVA. Поэтому для преобразования физического адреса в виртуальный нам необходимо сделать следующую операцию: VirtualAddress=VA+PhysicalAddress+RVA-PhyssOffs.
Чтобы в QView узнать адрес Image Base VA смотрите в PE-заголовке - при нажатии клавиши F8 (это возможно в режимах «HEX VIEW» и «ASM VIEW», если вы находитесь в режиме «TEXT VIEW» то для переключения в другие режимы нажмите F4).
Перед нами таблица сегментов (см. рисунок 2) :
Сначала нужно определить к какому сегменту относится найденный нами адрес. PhysOffs сегмента «.data» лежит в диапазоне 1B200-1F200, что удовлетворяет нашему адресу 1B368, то есть, строка будет находится именно в этом сегменте. Вычислим ее виртуальный адрес по вышеописанной формуле, он равен 1001D168.
Рис. 2. Таблица сегментов
Теперь попытаемся найти строку содержания «PUSH 1001D168». Для этого перейдем в режим «ASM VIEW», нажимая F4 до тех пор, пока не увидим на экране команды ассемблера. Еще необходимо перевести редактор в 32-битный режим – для этого нажмем F2, чтобы внизу окна рядом с надписью «F2 16/32» - «/32» выделялось черным цветом. Нажав F6, мы вызовем режим ассемблерного поиска, куда введем push 1001D168.
Е
сли
все сделать правильно, то поиск укажет
на строку с адресом 00000B08.
Чтобы сделать удобной навигацию по
файлу, находясь на этой строке нажмем
ALT
и «плюс» (но не на цифровой клавиатуре)
– это создаст метку указывающую на
строку нашу строку (см. рис. 3).
Рис.3. Ассемблерный режим
Функция, которая принимает адрес строки как параметр – не является MessageBox. Скорее всего, это «оберточная функция» языка программирования, на котором написана программа (а написана она на Visual C++). Эта функция в свою очередь, проведя некоторые, не интересующие нас операции над входными данными, в конечном счете вызовет MessageBox… В этом можно убедиться, если нажать комбинацию SHIFT+1 – то есть переход по адресу указанному в CALL.
Так как мы находимся «в хвосте» защитного механизма (точнее в ветке, которая исполняется при неправильно введенном S/N), то теперь нужно найти код, который на эту ветку переключает. То есть, если в выражении
if (S_N==VALID_KEY) goto GOOD;
else goto BAD;
изменить «равно» (==) на «не равно» (!=), то программа будет считать себя зарегистрированной при любых введенных данных, кроме случая, когда вводится правильный S/N.
Итак, продолжаем исследование. Команды выполняются последовательно друг за другом за исключением случаев, когда ход выполнения меняется командами переходов (jmp, ja,jae,jb,jbe,jc,je,jz,jnz,jg и т.д.) или командами call и ret. Поэтому начинаем просматривать программу «вверх».
00000AFF: 5E pop esi
00000B00: C3 ret
00000B01: 6A30 push 00000030
00000B03: 6880D10110 push 1001D180
00000B08: 6868D10110 push 1001D168
00000B0D: 8BCE mov ecx,esi
00000B0F: E8D41C0100 (1) call 000127E8
По адресу 00000B00 находится команда ret. Этой командой обычно заканчиваются функции – и ее назначение вернуть управление в место, откуда эта функция была вызвана. Это означает, что сама команда, и все что лежит выше – относится к другой подпрограмме, либо это второй «хвост». Следовательно, что наша функция начинается по адресу 00000B01.
Теперь остается найти код, который ее вызвал. Если код вызывается при помощи функции call, то нам придется преобразовывать физический адрес в виртуальный… Если же на данный участок кода происходит «прыжок» через команду семейства jump, то этого делать нет смысла, потому что при близких переходах jmp XX переходит на XX байт вперед относительно себя. То есть, какую бы адресацию мы не задали, количество байт между адресами в одном сегменте всегда будет одинаковым. Так что можно пробовать искать адрес «B01» напрямую.
В режиме «ASM VIEW», (см. рис. 4) нажмем F6 и введем «@B01» (символ «@» означает, что это подстрока).
Давайте проанализируем увиденное:
Рис. 4. Режим ASM VIEW
00000AEA: 51 push ecx // передать параметры
00000AEB: 52 push edx // передать параметры
00000AEC: E8AF000000 (1) call 00000BA0 // что-то вызвать
00000AF1: 83C40C add esp,0000000C //баланс стека
00000AF4: 84C0 test al,al // проверить al
00000AF6: 7409 (2) jz 00000B01 // если al=0, то jmp
00000AF8: 8BCE mov ecx,esi
00000AFA: E827C80000 (3) call 0000D326
00000AFF: 5E pop esi
00000B00: C3 ret
00000B01: 6A30 push 00000030
00000B03: 6880D10110 push 1001D180
00000B08: 6868D10110 push 1001D168 // “Wrong reg”
00000B0D: 8BCE mov ecx,esi
00000B0F: E8D41C0100 (4) call 000127E8 //Сообщить «BAD»
Возможно, мы находимся в том самом месте, где нужно изменить «равно» на «не равно». Тогда проведем эксперимент – изменим jz на jnz. Для этого нужно включить режим редактирования, нажав ALT+F3 (переключиться на столбик команд можно клавишей <Tab>).
Взлом-то, конечно, осуществлен, но при каждом запуске нажимать Enter – непорядок. Нужно бы изменить программу так, чтобы она всегда считала себя зарегистрированной. Как?
Ну к примеру, взять и удалить весть процесс регистрации. Мы знаем, что весь код регистрации находится в DLL, то есть программе в любом случае необходимо ее загрузить. Давайте попытаемся найти в программе ссылку на DLL_REG.DLL[5].
Для этого (см. рис. 5) загрузим в Qview файл Denoiser.exe и в 32-битном режиме «ASM VIEW» в «ассемблерном поиске» (по F6), введем «@DLL_REG».
Рис. 5.Загрузка в Qview файл Denoiser.exe и в 32-битном режиме «ASM VIEW» в «ассемблерном поиске»
Ловко и быстро мы находим следующий код:
00004CA7: E8F6B00200 call 0002FDA2
00004CAC: 8B4008 mov eax,dword ptr [eax+08]
00004CAF: 6818D44300 push 0043D418
00004CB4: 6AFF push FFFFFFFF
00004CB6: 50 push eax
00004CB7: FF154C664400 call DLL_reg.dll,?Registration…
00004CBD: 85C0 test eax,eax
00004CBF: 7409 jz 00004CCA
00004CC1: 66C705B81E44000100 mov word ptr [00441EB8],0001
00004CCA: 6A00 push 00000000
00004CCC: 8D4C2408 lea ecx,dword ptr [esp+08]
00004CD0: E800000000 call 00004CD5
1. Перечислите категории взлома программного обеспечения.
2. Опишите алгоритм регистрации программы Denoiser.exe.
3. Перечислите и охарактеризуйте основные функции редактора QVIEW.
4. Назовите типы адресных пространств.
5. Опишите процедуру преобразования адресов.
6. Опишите процесс удаления процедуры регистрации команды.
7. Каково назначение команды ret?
2.2 Практическая работа №2: «Восстановление данных с файловой системы ext3»
Third Extended File System (третья версия расширенной файловой системы), сокращённо ext3 или ext3fs — журналируемая файловая система, используемая в операционных системах на ядре Linux, является файловой системой по умолчанию во многих дистрибутивах. Основана на ФС ext2. Основное отличие от ext2 состоит в том, что ext3 журналируема, то есть в ней предусмотрена запись некоторых данных, позволяющих восстановить файловую систему при сбоях в работе компьютера.
Из этого можно сделать вывод, что самый быстрый вариант восстановления удаленного файла — аварийное завершение работы ОС.[9].
2.2.2 Восстановление данных с помощью утилиты ext3grep
Для начала необходимо установить ext3grep. В debian-like системах это можно сделать следующим образом:
# apt-get install ext3grep
или
# aptitude install ext3grep
Утилита присутсвует во всех базовых репозиториях.
В rpm дистрибутивах, установка производится следующим образом:
для fedora, CentOS и тп:
# yum install ext3grep
для SUSE:
# yast --install ext3grep
При использовании ext3grep не рекомендуется проводить восстановление данных на смонтированных разделах. Если работать придется с корневым разделом, то лучше использовать live-cd или сделать дамп диска и работать с ним:
# dd if=/dev/sda1 of=/root/sda1_dump.dd
Следующий шаг — анализ журнала файловой системы, с целью выявления удаленных файлов (см. рисунок 6). У ext3grep много параметров, которые все подробно описаны в документации (man ext3grep). Нам понадобятся лишь некоторые из них:
--dump-names — Пути всех файлов из журнала будут записаны в файл.
--restore-file путь_к_файлу — восстановление указанного файла
--restore-all — восстановление всех файлов.
$
ext3grep /root/sda1_dump.dd —dump-names
Рис. 6. Окно анализа журнала файловой системы, с целью выявления удаленных файлов
Ext3grep на первом этапе проверяет все группы файлов, на наличие в них удаленных inode.
Рис. 7. Окно поиска пути к файлу и его имени
На втором этапе производится поиск пути к файлу и его имени (см. рисунок 7).
В процессе выполнения в текущем каталоге будет создано два файла с названиями sda1_dump.ext3grep.stage1 и sda1_dump.ext3grep.stage2.
В
1ом содержаться номера inode в которых
хранились файлы, а во втором — сопоставления
номеров inode и пути к файлу.
Рис. 8. Окно поиска по файлу
Среди большого списка файлов нужно найти test.txt, а потом выполнить команду восстановления(см. рисунок 9):
$ ext3grep /root/sda1_dump.dd --restore-file home/user/test.txt
Или другой вариант, который восстановит все файлы и папки из sda1_dump.ext3grep.stage2:
$ ext3grep /root/sda1_dump.dd --restore-all
Рис. 9. Результат применения команды восстановления
После выполнения любой из этих команд, в текущем каталоге будет создана папка RESTORED_FILES, в которой будут восстановленные файлы.
Этот метод не гарантирует 100% восстановления данных, как и любой другой во всех ОС, но он гораздо безопаснее первого метода.
При работе напрямую с жестким диском есть риск окончательно потерять удаленные данные и повредить неудаленные. Так же возможен крах системы, при повреждении важного системного файла. Поэтому лучше работать с дампом диска.
При работе с дампом можно обезопасится от новых потерь, т. к. работа производится с файлом и не вмешивается в работу файловой системы[8].
Существует еще масса методов для восстановления данных с файловых систем ext2/3/4, но в основном все они или узкоспециализированные или малоэффективные. Наиболее надежным и безопасным методом восстановления является использование ext3grep.