14 августа 2012 г.

Пишем пакер, ну или основу криптора. (Часть 1)

Давно была у меня задумка написать пакер/криптор. Ковыряясь намедник очередной раз в формате PE я всетаки скрепился и начал писать пакер. Имея некоторый опыт в распаковке всяких  пакеров типа UPX/ASPack и немного в ASProtect. Я взял за основу болванки - UPX.

Я не смотрел исходники дабы не плагиатить подсознательно код и делал пакер разбираясь по ходу  в коде и заголовке упакованного файла и имея только бинарники.
Для изучения алгоритмов я использовал следующие программы:
IDA Pro - по большей части чисто как дизассемблер
OllyDbg - для дебага
Explorer Suite (CFF Explorer) - для изучения заголовка PE, секций и т.п.
Для начала теория.
Один из вариантов работы пакера/криптора:

  1. Маппируем исходный фаил в память.
  2. Вычисляем размер заголовка с файловым выравниванием изначального заголовка.
  3. Вычисляем реальный размер всех секций.
  4. Склеивем секции и упаковыем/криптуем их получая новый размер.
  5. Вычисляем размер кода распаковки/декриптора.
  6. Складываем все размеры применяя файловое выравнивание которое в заголовке.
  7. Создаем и маппируем фаил с получившимся размером в 6 пункте.
  8. Записываем PE заголовок украденный из упаковываемого файла.
  9. Создаем секции, учитывая размер из упаковываемого файла .
  10. Исправляем PE заголовок согласно размеру в памяти которые занимают секции.
  11. Заливаем в фаил криптованные данные.
  12. Заливаем техническую информацию, размеры, смещения етц.
  13. Заливаем код декриптора/анпакера.
  14. Вычисляем новую точку входа и записываем ее в PE заголовок.
  15. Создаем таблицу импорта с kernel32.dll и для загрузки LoadLibrary и GetProcAdrress.
  16. Пишем код распаковки/декриптора.
Ну вот в принципе и все, куча пунктов и дел нада сделать много. В этом алгоритме не учитываются секции с ресурсами и TLS, обработку добавить не сложно, да и мне уже было лень честно говоря их добавлять. Так же алгоритм не умеет работать с DLL, там несколько другие заморочки, но в принципе опять же ничего сложного.
Литература которую было бы неплохо почитать перед написанием кода:
http://wasm.ru/article.php?article=green2red02
http://www.insidepro.com/kk/019/019r.shtml

Итак, куча говнокода,  по пунктам:
1. Маппируем исходный фаил в память.
invoke    FindFirstFile, offset fileName, offset wfd ; получаем размер файла
invoke     CreateFile, offset fileName,GENERIC_READ,0, NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0 ; открываем фаил
invoke    CreateFileMapping, eax, NULL,PAGE_READONLY, 0, DWORD PTR [wfd.nFileSizeLow], NULL ; мапируем в ридонли
invoke    MapViewOfFile, eax, FILE_MAP_READ, 0, 0, 0 ; делаем проэкцию
mov        hinfile, eax ; запишем адрес, начала файла
invoke    checkPE, eax ; проверим, PE это фаил или нет

2. Вычисляем размер заголовка с файловым выравниванием изначального заголовка.
xor        ebx, ebx ; обнуляем ebx
mov        bx, WORD PTR [eax+IMAGE_DOS_HEADER.e_lfanew] ; записываем в bx размер dos заголовка
mov        dosHSize, ebx ; сохраняем размер dos заголовка в dosHSize, 
add        eax, ebx ; добавляем размер к eax, в котором лежит указатель на начало маппированного файла

; IMAGE FILE HEADER    
mov        ecx, dosHSize ; копируем размер dos заголовка в ecx
add        ecx, FULL_IMAGE_FILE_HEADER ; прибавляем к ecx размер IMAGE_FILE_HEADER и IMAGE_NT_SIGNATURE
add        cx, WORD PTR [eax+IMAGE_FILE_HEADER.SizeOfOptionalHeader+4h] ; прибавляем размер опционального заголовка
mov        pehSize, ecx ; размер PE заголовка сохраняем в pehSize

3.Вычисляем реальный размер всех секций
xor        ebx, ebx ; обнулим ebx
mov        bx, WORD PTR [eax+IMAGE_FILE_HEADER.NumberOfSections+4h] ; скопируем в bx количество секций
mov        secCount, ebx ; сохраним количество секций в secCount
imul    ebx, ebx,28h ; размер секции равено 28h умножим ее на количество секций
add        ecx, ebx ; результат прибавим к размеру заголовка полученному выше

;IMAGE OPTIONAL HEADER    
add        eax, FULL_IMAGE_FILE_HEADER ; в eax находится ссылка на начало IMAGE_OPTIONAL_HEADER прибавим к нему размер IMAGE_OPTIONAL_HEADER 
mov        ebx, DWORD PTR [eax+IMAGE_OPTIONAL_HEADER.FileAlignment] ; получим выравнивание файла
mov        fAlig, ebx  ; запишем его в fAlig
invoke    GetAlignUp, ecx, ebx ; сделаем выравнивние заголовка согласно файловому выравниванию
mov        fileSize, eax ; запишем в переменную fileSize
mov        codeStart, eax ; в codeStart записываем начало кода в файле
    
invoke    GetProcessHeap ; получим адрес кучи
mov        pHeap, eax ; сохраним адрес кучи в pHeap
mov        eax, secCount ; скопируем в eax количество секций
imul    eax, eax, SECSTRUCSIZE ; умножим количество секций на размер моей структуры SECSTRUCSIZE
invoke     HeapAlloc, pHeap,HEAP_ZERO_MEMORY, eax ; выделяем столько места в куче
mov        secMem, eax ; запишем указатель на выделеное место
    
mov        edi, hinfile ; записываем указатель на открытый файл
add        edi, pehSize ; прибавим к нему PE заголовок без секций
mov        ecx, secCount ; в счетчик запишем количество секий
xor        eax, eax ; обнуляем eax
mov        esi, secMem ; в esi указатель на выделенное место в куче
nextsec:
mov        ebx, DWORD PTR[edi+8h] ; запишем в ebx данные из Virtual Size
mov        DWORD PTR [esi], ebx ; запишем в структуру SECSTRUCSIZE их
add        eax, ebx ; прибавим к eax виртуальный размер
mov        ebx, DWORD PTR[edi+0Ch] ; запишем в ebx Virtual Address
mov        DWORD PTR [esi+4h], ebx ; запишем в структуру SECSTRUCSIZE их на вторую позицию
mov        ebx, DWORD PTR[edi+14h] ; запишем в ebx Ra wSize
mov        DWORD PTR [esi+8h], ebx ; запишеи в структуру SECSTRUCSIZE их на третью позицию
add        eax, 8h ; прибавим к eax 8байт необходимых для технической информации
add        edi, 28h ; увеличим указатель на секциях на 28h чтобы перейти ко второй секции
add        esi, SECSTRUCSIZE ; увеличим указатель в стуктуре SECSTRUCSIZE чтобы перейти ко второй секции
loop    nextsec ; повторить всю эту бесовщину с метки nextsec

Ну и для начала все, в следующих частях будет описание других пунктов. А в последней части будет ссылка на исходник.
Вторая часть.

Комментариев нет:

Отправить комментарий