Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебник 400.docx
Скачиваний:
25
Добавлен:
30.04.2022
Размер:
3.61 Mб
Скачать

2.1.3 Физические и виртуальные адреса

Физические адреса соответствуют номерам ячеек оперативной памяти, где в действительности расположены или будут расположены переменные и команды. Переход от виртуальных адресов к физическим может осуществляться двумя способами. Перемещающий загрузчик на основании имеющихся у него исходных данных о начальном адресе физической памяти, в которую предстоит загружать программу, и информации, предоставленной транслятором об адресно-зависимых константах программы, выполняет загрузку программы, совмещая ее с заменой виртуальных адресов физическими[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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]