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 это не используется но нужно иметь в виду :)
|