/*-----------------------------------------------------------------------*/
/* R I N G 0, I S S U E # 1 */
/*-----------------------------------------------------------------------*/
Неизвестный упаковщик: асм лоадер
by ADP/HangUP
Иногда для создания крака бывает удобно вместо длительных
извращений над exe в попытках расшифровать/распаковать или еще как то
надругаться над ним и сделать патч, проще написать небольшой загрузчик,
который пропатчит необходимый нам файл непосредственно в памяти.
Преимущества данного метода очевидны: простота, скорость и надежность.
В качестве подопытного кролика я взял FineReader4.0. Не смотрите,
что версия старенькая, это не важно, главное - показать принцип работы.
Да и не было времени искать какую-нить новую прогу. А эта уже давно мной
сломана, оставалось только упаковать ее Aspack'ом :). Перед тем, как
перейти к делу, надо показать, что мы будем патчить. Кролик не хотел
запускаться после определенного срока, это устранялась путем затирания
CALL'а:
0045E7D9: E8427E0B00 CALL 00516620
Пять байт надо заменить на 90h.
Теперь к делу. Для запуска программы используем функцию CreateProcessA:
BOOL CreateProcess(
lpApplicationName, // pointer to name of executable module
lpCommandLine, // pointer to command line string
lpProcessAttributes, // pointer to process security attributes
lpThreadAttributes, // pointer to thread security attributes
bInheritHandles, // handle inheritance flag
dwCreationFlags, // creation flags
lpEnvironment, // pointer to new environment block
lpCurrentDirectory, // pointer to current directory name
lpStartupInfo, // pointer to STARTUPINFO
lpProcessInformation // pointer to PROCESS_INFORMATION
);
Перечисляю аргументы, которые не нужны (NULL): lpCommandLine,
lpProcessAttributes, lpThreadAttributes, bInheritHandles, lpEnvironment,
lpCurrentDirectory.
Теперь поподробней об используемых аргументах: lpApplicationName:
здесь вы указываете адрес, который содержит имя файла с 0-терминатором.
dwCreationFlags: здесь указывается класс приоритета программы. Тут
необходим NORMAL_PRIORITY_CLASS. Если вы укажите IDLE_PRIORITY_CLASS, то
кролик не запустится, пока загрузчик не закончит свою работу.
lpStartupInfo: здесь необходимо указать на структуру STARTUPINFO.
Чтобы вы не грузились(в дальнейшем она не используется), я вам покажу
содержимое структуры:
STARTUPINFO:
dd 44h
dd 10 dup (0)
dd 00000401h
dd 01
dd 00,00
dd 000011A2h
dd 00,00
Она подходит к любой запускаемой проге. lpProcessInformation: здесь
указываете на структуру PROCESS_INFORMATION. После загрузки кролика в
память, в этой структуре будет информация о нем, которая нам пригодится
в дальнейшем. Вот как выглядит структура:
PROCESS_INFORMATION
hProcess dd 0 ;хендл ПРОЦЕССА
hThread dd 0 ;хендл потока
ProcessId dd 0 ;номер процесса
ThreadId dd 0 ;номер потока
Про CreateProcessA вроде все. Теперь двигаемся дальше. Чтобы
корректно пропатчить прогу надо дождаться, пока пройдет процесс
разархивации. Дожидаться будем следующим образом: читать память с адреса
0045E7D9h и проверять содержимое ее с истинным - E8,42,7E,0B,00. Если
совпадает, то патчим и выходим из загрузчика.
Тепреь разберм это поподробней. Чтобы прочитать содержимое памяти
кролика используем функцию ReadProcessMemory:
BOOL ReadProcessMemory(
hProcess, // handle of the process whose memory is read
lpBaseAddress, // address to start reading
lpBuffer, // address of buffer to place read data
nSize, // number of bytes to read
lpNumberOfBytesRead // address of number of bytes read
);
hProcess: сюда надо внести хендл ПРОЦЕССА (кролика);
lpBaseAddress: 0045E7D9h;
lpBuffer: какой-нибудь буфер, куда будут считываться байты;
nSize: 05 (считываем 5 байт);
lpNumberOfBytesRead: NULL.
После чтения памяти проверяем содержимое буфера на наличие
следующих байт: E8,42,7E,0B,00. Если не совпадает, то процесс
разархивации еще не дошел до того места, снова читаем память и тд пока
байты совпадут. При совпадении байт изменяем их на 90h, с помощью
функции WriteProcessMemory и выходим из процесса загрузчика.
BOOL WriteProcessMemory(
hProcess, // handle to process whose memory is written to
lpBaseAddress, // address to start writing to
lpBuffer, // pointer to buffer to write data to
nSize, // number of bytes to write
lpNumberOfBytesWritten // actual number of bytes written
);
Думаю теперь пояснения не нужны.
И наконец, для лучшего понимания я приведу исходник лоадера.
;PATCH FOR FINEREADER 4.0
.386p
.model flat, stdcall
extrn ExitProcess:proc
extrn MessageBoxA:proc
extrn CreateProcessA:proc
extrn WriteProcessMemory:proc
extrn ReadProcessMemory:proc
.data
skoka equ 05
addr dd 0045e7d9h
patch db skoka dup(90h)
buf db skoka dup(0)
bufcmp db 0E8h,42h,7Eh,0Bh,00
procs db "FINE32.exe",0
WTitle db "CreateProc",0
txterr1 db "Не могу запустить файл FINE32.EXE",0
procinfo:
hProcess dd 0
hThread dd 0
ProcessId dd 0
ThreadId dd 0
stupinf:
dd 44h
dd 10 dup (0)
dd 00000401h
dd 01
dd 00,00
dd 000011a2h
dd 00,00
.code
start: push offset procinfo
push offset stupinf
push 0
push 0
push 32 ;NORMAL_PRIORITY_CLASS
push 0
push 0
push 0
push 0
push offset procs
call CreateProcessA
test eax,eax
jz short err1
lop: push 0 ;не используй енто
push skoka ;число байт для считывания
push offset buf ;адрес буфера, куда записываются данные
push addr ;начальный адрес считывания
push hProcess
call ReadProcessMemory
cld
mov ecx,skoka
mov esi,offset buf
mov edi,offset bufcmp ;здесь байты которые надо изменять
repe cmpsb
jnz short lop
push 0 ;не используй енто
push skoka ;число байт для записи
push offset patch ;адрес записываемого буфера
push addr ;начальный адрес записи
push hProcess ;хендл процесса
call WriteProcessMemory
eee: push 0
call ExitProcess
err1: push 1000h
push offset WTitle
push offset txterr1
push 0
Call MessageBoxA
jmp short eee
end start
end