Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Armadillo и CopyMem II

Автор: Hex

Я не люблю всякие анпакеры, потому что они приучают лениться. А потом автор анпакера пропадет куда-нить и все - фиг чего-то сделаешь. Т.к. не знаешь как оно работает. Вот и к анпакеру Armkiller у меня такое же отношение. Поэтому опишу как ловить армадилину раскриптовку при CopyMem II.

Для тех кто в танке:

Armadillo работает так: запускает себя в двух экземплярах.

1) Этот тот что вызвали мы. Это типа сервер
2) Созданный серевером. (клиент)

Так вот сервер запускает клиента в режиме отладки и помере происходящих DebugEvents он его разжимает, делает импорт и т.д. и потом запускает. CopyMem II: это такая фича у сервера. Когда она включена сервер динамически раскриптовывает секцию кода клиента блоками по 1000h байт. После выполненния кода этой страницы - закриптовывает назад. Т.е. сервер следит за Page Fault. И когда оно происходит - раскриптовывает недостающий блок и ставит все на место. Так вот, наша цель: заставить сервер распаковать прогу полностью и больше не закриптовывать.

Инструменты: Softice, IDA.

Для примера беру сам Armadillo 2.61 Public build 2000. Он является последним к моменту написания статьи и в нем используется CopyMem II.

Загружаем Armadillo 2.61 в IDA пусть дизасмится. Запускаем армадилу жмем Basic и больше никуда не лезем. Теперь ставим bpx Writeprocessmemory и жмем "Protection" - "Edit Project". Вылезет айс. Делаем bd * и жмем 3 раза F12. Видим вот это (я тут уже поподписывал):


_text1:0044D6BD push 0 ; EnCryptBit - если 1 - закриптовать, 0 - раскриптовать
_text1:0044D6BF mov esi, [ebp+PageNumber]
_text1:0044D6C5 shl esi, 4
_text1:0044D6C8 mov eax, [ebp+PageNumber]
_text1:0044D6CE and eax, 80000007h
_text1:0044D6D3 jns short loc_0_44D6DA
_text1:0044D6D5 dec eax
_text1:0044D6D6 or eax, 0FFFFFFF8h
_text1:0044D6D9 inc eax
_text1:0044D6DA 
_text1:0044D6DA loc_0_44D6DA: ; CODE XREF: sub_0_44C5FD+10D6j
_text1:0044D6DA xor ecx, ecx
_text1:0044D6DC mov cl, byte ptr ds:unk_0_4591E0[eax]
_text1:0044D6E2 mov edx, [ebp+PageNumber]
_text1:0044D6E8 and edx, 80000007h
_text1:0044D6EE jns short loc_0_44D6F5
_text1:0044D6F0 dec edx
_text1:0044D6F1 or edx, 0FFFFFFF8h
_text1:0044D6F4 inc edx
_text1:0044D6F5 
_text1:0044D6F5 loc_0_44D6F5: ; CODE XREF: sub_0_44C5FD+10F1j
_text1:0044D6F5 xor eax, eax
_text1:0044D6F7 mov al, byte ptr ds:unk_0_4591E1[edx]
_text1:0044D6FD mov edi, ds:dword_0_456238[ecx*4]
_text1:0044D704 xor edi, ds:dword_0_456238[eax*4]
_text1:0044D70B mov ecx, [ebp+PageNumber]
_text1:0044D711 and ecx, 80000007h
_text1:0044D717 jns short loc_0_44D71E
_text1:0044D719 dec ecx
_text1:0044D71A or ecx, 0FFFFFFF8h
_text1:0044D71D inc ecx
_text1:0044D71E 
_text1:0044D71E loc_0_44D71E: ; CODE XREF: sub_0_44C5FD+111Aj
_text1:0044D71E xor edx, edx
_text1:0044D720 mov dl, byte ptr ds:unk_0_4591E2[ecx]
_text1:0044D726 xor edi, ds:dword_0_456238[edx*4]
_text1:0044D72D mov eax, [ebp+PageNumber]
_text1:0044D733 cdq
_text1:0044D734 mov ecx, 1Ch
_text1:0044D739 idiv ecx
_text1:0044D73B mov ecx, edx
_text1:0044D73D shr edi, cl
_text1:0044D73F and edi, 0Fh
_text1:0044D742 add esi, edi
_text1:0044D744 
_text1:0044D744 DeCrypting:
_text1:0044D744 mov edx, ds:dword_0_459A74
_text1:0044D74A lea eax, [edx+esi*4]
_text1:0044D74D push eax ; hHash
_text1:0044D74E mov ecx, [ebp+PageNumber]
_text1:0044D754 push ecx ; PageNumber - номер страницы для раскриптовки.
_text1:0044D755 call ArmaCryptDecrypt
_text1:0044D75A add esp, 0Ch
_text1:0044D75D and eax, 0FFh
_text1:0044D762 test eax, eax
_text1:0044D764 jz short loc_0_44D770

Все что идет от _text1:0044D6BF до _text1:0044D74D - это просто вычисление кодов для раскриптовки указаной страницы. Чтобы раскриптовать нужную страницу нужно после выполнения _text1:0044D75A add esp, 0Ch загнать в [ebp+PageNumber] номер нужной страницы и прыгнуть к _text1:0044D6BD push 0 Таким образом можно прямо в айсе написать небольшой цикл который раскриптует все страницы. Как используются номера страниц? Номер страницы*1000+401000 = адрес, куда будет записан раскриптованый кусок. Таким образом чтобы узнать сколько у нас страниц смотрим в PE: Rva секции .text = 1000, Rva следующей секции(.rdata) = 2A000 т.е. число страниц = ((2A000 - 1000 ) / 1000) - 1 = 28h штук (-1 это я опытным путем выявил :) Но перед тем как писать цикл такой нужно обратить внимание еще на 1 особенность. Внутри ArmaCryptDecrypt есть еще 2 разных вызова одной и той же процедуры:

Раз:


_text1:0044E3FF push 0 ; Decrypt...
_text1:0044E401 mov ecx, [ebp+hHash]
_text1:0044E404 push ecx
_text1:0044E405 mov edx, [ebp+PageNumber]
_text1:0044E408 push edx
_text1:0044E409 call Crypting
_text1:0044E40E add esp, 0Ch

Два:


_text1:0044E48F push 1 ; Crypt...
...............
_text1:0044E542 mov edx, ds:dword_0_459A74
_text1:0044E548 lea eax, [edx+esi*4]
_text1:0044E54B push eax ; Crypting...
_text1:0044E54C mov ecx, ds:dword_0_459A88
_text1:0044E552 mov edx, ds:dword_0_459A8C
_text1:0044E558 mov eax, [edx+ecx*4]
_text1:0044E55B push eax
_text1:0044E55C call Crypting
_text1:0044E561 add esp, 0Ch

Первая всегда раскриптовывает, а вторая закриптовывает и удаляет страницу из памяти (т.е. ставит No_access) Нам на удаление страниц совсем не нужно, поэтому оставим только первый вызов, а второй CALL происходит если _text1:0044E47C jle loc_0_44E57C не прыгает.

Таким образом чтоб раскриптовать секцию кода нужно пропатчить _text1:0044E47C jle loc_0_44E57C на _text1:0044E47C jmp loc_0_44E57C И сделать цикл про который я говорил ранее. Потом просто делаем дамп раскриптованной секции кода. Найти OEP проще всего если поставить BPX SetProcessWorkingsetSize там буквально через 20 строчек OEP. Дальше делаем дамп на OEP, вставляем в него полученную секцию кода. Восстанавливаем импорт(лучше под NT/2k, но и в 98 его тоже не проблема восстановить). И долбаемся с остатками лицензирования...

Что еще можно сказать... Если глянуть по адресам 44D60B и 44D57B то увидим такие же куски кода с двумя отличиями:


_text1:0044D57B push 1 - Криптовать!
...............
_text1:0044D5E8 mov eax, [ebp+PageNumber]
_text1:0044D5EE sub eax, 1 - Предидущая страница
_text1:0044D5F1 push eax ; PageNumber
_text1:0044D5F2 call ArmaCryptDecrypt

и


_text1:0044D60B push 1 - Криптовать!
.........................
_text1:0044D678 mov eax, [ebp+PageNumber]
_text1:0044D67E add eax, 1 - Следующая страница
_text1:0044D681 push eax ; PageNumber
_text1:0044D682 call ArmaCryptDecrypt

Т.е. перед раскриптовкой новой станицы он криптует предидущую и следующую. В Armadillo 2.61 Public build 2000 это не используется но нужно иметь в виду :)

Проект Delphi World © Выпуск 2002 - 2004
Автор проекта: ___Nikolay