У нас остается 2 вещи которые необходимо сделать, это сохранить данные для востановления и загрузки оригинального файла и сам алгоритм распаковки.
Данные для распаковски потребуются следующие:
DWORD - Base Import Address - адрес импорта
DWORD - Base Image - базовый образ
DWORD - Decript Key - ключ для распаковки, если он нужен
DWORD - Original Base Image - оригинальный базовый образ
DWORD - Entery Point - точка входа
DWORD - Import Table Address - собственно адрес IAT
WORD - Sections Count - количество секций
DWORD - Size Data - размер данных
DWORD - Virtual Address Data - виртуальный адрес
Эти данные будем записывать после кода распаковщика, чтобы точно знать где они лежат, так как мы точно знаем длинну распаковщика.
А для удобства создадим следующие константы:
UBIMPADDR EQU 0 ; 0h UBASEIMG EQU UBIMPADDR+4h ; 4h UDECRKEY EQU UBASEIMG+4h ; 8h UOBASEPOINT EQU UDECRKEY+4h ; 0Ch UENTRYPOINT EQU UOBASEPOINT+4h ; 10h UIMPTABLE EQU UENTRYPOINT+4h ; 14h USECCOUNT EQU UIMPTABLE+4h ; 18h USIZEDATA EQU USECCOUNT+2h ; 1Ah UVADDRDATA EQU USIZEDATA+4h ; 1Eh TABLESIZE EQU 22h
Итак, сам код:
;Write service data mov eax, DWORD PTR [ebx+IMAGE_OPTIONAL_HEADER.ImageBase] ; в eax заносим базу образа add eax, DWORD PTR [ebx+IMAGE_OPTIONAL_HEADER.BaseOfData] ; прибавляем к нему размер даты и получаем указатель на импорт mov DWORD PTR [edi], eax ; записываем в табличку Base Import Address add edi, 4h ; смещаем указатель на 4 байта mov eax, DWORD PTR [ebx+IMAGE_OPTIONAL_HEADER.BaseOfCode] ; в eax заносим откуда начинается код add eax, DWORD PTR [ebx+IMAGE_OPTIONAL_HEADER.ImageBase] ; прибавляем к базовому образу mov DWORD PTR [edi], eax ; результат записываем в Base Image add edi,4h ; смещаем указатель на 4 байта mov DWORD PTR [edi], -1h ; ключ для декриптора можно разместить например здесь, я его сделал -1 add edi, 4h ; смещаем указатель на 4 байта mov eax, hinfile ; в eax заносим адрес замапленного файла который упаковываем add eax, dosHSize ; прибавляем размер DOS заголовка add eax, FULL_IMAGE_FILE_HEADER ; прибавляем полный размер заголовка IMAGE_FILE_HEADER mov edx, DWORD PTR [eax+IMAGE_OPTIONAL_HEADER.ImageBase] ; получаем в edx ImageBase оригинального файла mov DWORD PTR [edi], edx ; заносим в Original Base Image add edi, 4h ; смещаем указатель на 4 байта mov edx, DWORD PTR [eax+IMAGE_OPTIONAL_HEADER.AddressOfEntryPoint] ; получаем точку входа оригинального файла add edx, DWORD PTR [eax+IMAGE_OPTIONAL_HEADER.ImageBase] ; прибавляем к базе mov DWORD PTR [edi], edx ; заносим в Entery Point add edi, 4h ; смещаем указатель на 4 байта mov edx, DWORD PTR [eax+IMAGE_OPTIONAL_HEADER.ImageBase] ; получаем базу add edx, DWORD PTR [eax+IMAGE_OPTIONAL_HEADER.DataDirectory+8h] ; прибавляем к адресу IAT mov DWORD PTR [edi], edx ; заносим в Import Table Address mov edx, ebx ; копируем ebx в edx add edi, 4h ; смещаем указатель на 4 байта mov ebx, hinfile ; в eax заносим адрес замапленного файла который упаковываем add ebx, pehSize ; прибавляем размер заголовка PE чтобы получить указатель на секции mov ecx, secCount ; в ecx заносим количество секий mov WORD PTR [edi], cx ; сохраняем количество секций в Sections Count add edi, 2h ; смещаем указатель на 2 байта writeservinfo: ; извлекаем необходимые данные из секций mov eax, DWORD PTR [ebx+08h] ; копируем в eax виртуальный размер mov DWORD PTR [edi], eax ; сохраняем его в Size Data add edi, 4h ; смещаем указатель в табличке на 4 байта mov eax, edx ; эта команда не нужна, видать я о чемто задумался :) mov eax, DWORD PTR [edx+IMAGE_OPTIONAL_HEADER.ImageBase] ; в eax заносим базу add eax, DWORD PTR [ebx+0Ch] ; прибавляем ее к виртуальному адресу mov DWORD PTR [edi], eax ; точный адрес секции заносим в Virtual Address Data add edi, 4h ; смещаем указатель в табличке на 4 байта add ebx, 28h ; смещаем указатель в табличке секций на 28h байта loop writeservinfo ; повторяем пока не кончились секции invoke UnmapViewOfFile, houtfile ; размапливаем invoke CloseHandle, hOFMap ; закрываем хэндл invoke CloseHandle, hOpenfile ; закрываем хэндл retВ принципе ничего сложного, просто готовим табличку для востановления оригинального файла в памяти.
А теперь сам код распаковщика, там будут великие колдунства :)
Самое главное в распаковке это держать выравнивание стека, если выравнивание стека съезжает, то последствия могут быть ужасные, например kernel32.LoadLibraryA перестает загружать все библиотеки кроме kernel32. Да и сама оригинальная программа может работать некорректно.
unpack: ; метка начала анпакера push edx ; сохраним точку входа, она нам еще понадобиться pusha ; сохраняем все регистры ибо это полезно для начала работы оригинальной программы ;move data xor ecx, ecx ; обнуляем ecx xor eax, eax ; обнуляем eax mov cx, WORD PTR [edx+UNPACKSIZE+USECCOUNT] ; в cx копируем количество секций mov esi, DWORD PTR [edx+UNPACKSIZE+UBASEIMG] ; в esi копируем базу образа movecode: ; метка на копирование push ecx ; сохраним ecx так как нам нужен счетчик секций mov edi, DWORD PTR [edx+UNPACKSIZE+UVADDRDATA+eax] ; в edi помещаем указатель на начало секций mov ecx, DWORD PTR [edx+UNPACKSIZE+USIZEDATA+eax] ; в ecx помещаем оригинальную длинну секции rep movsb ; копируем add eax, 8h ; увеличиваем eax на 8 позиции, чтобы перейти к следующей секции pop ecx ; востанавливаем счетчик секции loop movecode ; повторяем пока не кончатся секции ;load library + func mov ecx, DWORD PTR [edx+UNPACKSIZE+UIMPTABLE] ; копируем в ecx адрес таблицы импторта(IAT) nextlib: push ecx ; сохраним ecx mov ebx, DWORD PTR [ecx+0Ch] ; в ebx скопируем смещение на адрес dll add ebx, DWORD PTR [edx+UNPACKSIZE+UOBASEPOINT] ; прибавим к нему базу и получим адрес первой dll push edx ; сохраним в стеке неоригинальную точку входа которая находится в edx push ebx ; сохраним в стеке указатель на первую dll чтобы выполнить LoadLibraryA mov esi, DWORD PTR [edx+UNPACKSIZE] ; в esi поместим указатель на функцию LoadLibraryA call DWORD PTR [esi+SLOADL] ; выполняем LoadLibraryA и загружаем первую dllку pop edx ; востанавливаем неоригинальную точку входа mov ecx, DWORD PTR [esp] ; копируем в ecx указатель на оригинальный IAT mov ebx, DWORD PTR [ecx+10h] ; получаем указатель на первую функцию в IAT add ebx, DWORD PTR [edx+UNPACKSIZE+UOBASEPOINT] ; прибавляем к ней базу nextProc: ; цикл загрузки функций push eax ; сохраняем в стеке eax адрес kernel32 push edx ; сохраняем в стеке edx фейковую точку входа push ebx ; сохраняем в стеке ebx адрес 1 DLL в IAT mov ebx, DWORD PTR [ebx] ; получаем указатель на 1 функцию add ebx, DWORD PTR [edx+UNPACKSIZE+UOBASEPOINT] ; прибавляем к ней базу и получаем название функции add ebx, 2h ; прибавляем 2 байта, чтобы перепрыгнуть hint push ebx ; помещаем в стек указатель на имя функции push eax ; помещаем в в стек указатель на kernel32.LoadLibraryA mov esi, DWORD PTR [edx+UNPACKSIZE] ; копируем в esi адрес функции GetProcAddress call DWORD PTR [esi+SGPROCA] ; вызываем GetProcAddress c параметрами чтобы загрузить необходимую функцию pop ebx ; востанавливаем указатель на первую запись dll в IAT mov DWORD PTR [ebx], eax ; записываем реальный адрес функции pop edx ; востанавливаем фейковую точку входа pop eax ; востанавливаем адрес функции kernel32.LoadLibraryA add ebx, 4h ; прибавим к ebx 4 байта .if DWORD PTR [ebx] != 0 ; если там есть еще что то, то значит надо искать следующую функцию jmp nextProc ; прыгаем на следующую функцию .endif pop ecx ; востанавливаем указатель на текущую секцию IAT add ecx, 14h ; сдвигаем на 1 секцию дальше, чтобы получить указатель на следующую dllку .if DWORD PTR[ecx+0ch] != 0 ; если там есть указатель на dllку прыгаем на метку загрузки следующей jmp nextlib .endif popa ; востанавливаем все регисты pop edx ; востанавливаем фейковую точку входа push DWORD PTR [edx+UNPACKSIZE+UENTRYPOINT] ; запихиваем в стек оригинальную точку входа ret ; делаем прыжек на нее unpakend: ; конец кода распаковки UNPACKSIZE EQU unpakend-unpack ; вычислим размер кода распаковки
Ну вот и все, это простенькая основа рассказывающая как сделать упаковщик/криптор. Можно наворотить всякую работу с SEH, шифровальщиком, упаковщиком, полиморфом, украденными байтами и т.п. чтобы запутать реверсера, но в принципе идеология скелета остается таже.
Я подумал и решил всетаки не выкладывать исходники ибо много там говнокода, да и не универсальный он. Но если кому нибудь понадобятся, то можно попросить меня в коментах.
дай исходник, и ты можешь ответить на вопросы ну насчет пакера\криптора?)
ОтветитьУдалитьподелись исходником)
ОтветитьУдалитьПроект для RadASM
ОтветитьУдалитьhttp://webfile.ru/a1ddd2b1827e2dec44b59464691f7bc9
Перезалей, пожалуйста!
ОтветитьУдалитьhttp://files.webfile.ru/3bd475f145c20a84205fd9e4d5a3424f
ОтветитьУдалитьПерезалейте пожалуйста.
ОтветитьУдалить:)
Перезалейте пожалуйста.
ОтветитьУдалить:)