Итак, юные хакеры, сейчас я проведу небольшой ликбез, который
поможет вам расширить и углубить ваши познания в области крака и
вообще программирования, в данном случае речь пойдёт об Ассемблере .. 
Ну так что же такое Ассемблер? .. Этот язык появился как замена
написанию чистого двоичного кода, который понимает процессор.
В стародавние времена языков высокого уровня не было вообще и все
писали на ассемблере .. а мальчик Билли сочинял крестики-нолики..
Язык Ассемблера просто представляет инструкции процессору в виде
мнемо-команд, понятных программеру, например:

ADD eax, edx 

Эта инструкция - ADD складывает два числа .. eax и edx - это
регистры, они могут хранить значения и располагаются внутри процессора ..
После обработки этот код будет выглядеть так: 

66 03 C2 (в шестнадцатеричной системе)

Процессор считывает эти значения и выполняет соответствующую
инструкцию ..

Почему я должен использовать ассемблер?

К этому моменту у кульного хацкера типа тебя должен назреть вопрос -  

В: А нахрена это всё надо, ведь есть языки программирования высокого 
уровня где всё просто и понятно .. да и кода меньше писать надо?
О: Отвечаю: большие, комплексные программы на нём, конечно же, не
напишешь .. но кто говорит что надо? Ведь, например, можно закодить
простенький патч для клёвой игрушки или небольшой, но о-о-очень
кульный троянец, а? Ведь для написания такой фигни юзать Дельфи (40Мб
дистриб +размер кода от 100Кб) или VC++ (400Мб дистриб) взамен
Assemblera (6Мб нужные дистриб-ы +размер кода от 4Кб) - на мой
взгляд бред, а на твой? То-то же .. ну и конечно же без знания Ассемблера нельзя нормально вскрыть
ни одной программы.. 

В: Ну а если я не умею/не хочу программировать?
О: Тогда ... ничего, эта статья написана понятно (IMHO) .. и сойдёт для
общего развития .. 

Так что продолжим .. Итак новая стадия - НЕОБХОДИМОЕ ПО:

Собственно Assembler 
Что: MASM (из пакета MASM32)
Где: www.pbq.com.au/home/hutch/masm.htm
Не когда, а Зачем: собственно он и формирует объектный (понятный процу) код из того
бреда, что ввёл программер 🙂

Отладчик
Что: SoftICE
Где: www.numega.com
Зачем: хе-хе ..

Редактор ресурсов 
Что: Borland Resource Workshop
Где: www.crackstore.com
Зачем: Редактор ресурсов используется для создания ресурсов
(изображения, диалоги, bimap'ы, менюшки).

Текстовый редактор
Что: Notepad 🙂 можно и Ultraedit
Где: www.ultraedit.com 
Зачем: 😉

Полезное и нужное
Что: Microsoft Win32 Programmer's Reference (.hlp файл ок. 24Мб)
Где: ищите в Рунете (online - http://msdn.microsoft.com/library/default.asp)
Зачем: Для работы с Виндозой требуется знать
- какие функции мы можем
использовать, каких и сколько у них параметров (входных данных), и
называется эта радость API Function Reference (файл вроде win32.hlp,
возможно также одноимённый или bc52hlp ZIP размером около 6Мб )

Установка утиля 
Без комментариев 😉

ОПКОДЫ 

Проги на ассемблере пишутся в основном опкодами (кодами операций)
вроде следующего:
ADD 

Этой инструкцией мы складываем два числа из регистров:
ADD eax, edx 

Операция ADD имеет два параметра: источник (src,source) и получатель 
(dst,destination) которые складываются между собой после чего результат
записываются в получатель. Параметры могут быть разного типа:
регистры, адреса памяти и, собственно, сами числа
(ещё есть макросы и др., но это - не в этой статье 🙁

РЕГИСТРЫ

Имеющиеся размеры регистров: 8 бит, 16 бит, 32 бит (и более на
современных процах).
В 16-битной программе, можно юзать только 16-бит и 8-бит регистры.
В 32-битной проге все остальные.
Некоторые регистры являются частью других регистров; например, если EAX содержит EA7823BBh, то можно сказать что:

EAX = EA 78 23 BB (32 бит)
AX = 23 BB (младшие 16 бит)
AH = 23 (старшие 8 бит из АХ)
AL = BB (младшие 8 бит из AХ) 

те AX, AH, AL - части EAX .

Пример использования регистров (пока не обращай внимание на опкоды):

mov eax, 01234567h
;загружаем число в регистр 
;1234567h - шестнадцатеричное число, тк. есть приставка - 'h'

mov cl, ah ;перемещает ah в cl
sub cl, 10 ;вычитает 10 (дес.) из cl и результат заносит в cl 
mov al, cl ;перемещает cl обратно в al 

Типы регистров:

Общего назначения:
32-битные(16/8 для их частей):
eax (ax/ah/al) Аккумулятор 
ebx (bx/bh/bl) Базовый 
ecx (cx/ch/cl) Счётчик 
edx (dx/dh/dl) Данные

Несмотря на то, что эти регистры имеют имена (пошло
с очень
старых  процов) их можно использовать для любых целей.

Сегментные регистры:
В программировании под Виндозой малополезны, но всё равно, вот они:
CS сегмент кода
DS сегмент 
SS сегмент стека 
ES дополнительный сегмент
FS (тока 286+) общего назначения 
GS (тока 386+) общего назначения 

Регистры указатели:
о это кул регистры: содержат всё, что угодно, как и общего назначения,
а ещё используются некоторыми командами при работе с памятью
(scasb, stosb и др.):

esi (si) Индекс источника
edi (di) Индекс получателя 
eip (ip) указатель инструкции 

EIP (IP в старых процах) указывает на текущую исполняемую инструкцию

Стековые регистры:
esp (sp) Указатель стека (указывает на вершину стека)
ebp (bp) Базовый указатель

Регистр флагов :
Содержит в каждом бите признаки выполнения каждой операции 
(переполнение, знак результата, признак переноса и др.)

БАЗОВЫЕ ОПКОДЫ

MOV 

Эта инструкция используется, чтобы переместить (на самом деле -
скопировать) величину из одного места в другое.

mov куда, откуда

заметьте:
правильно - mov edx, ecx ;переместит ecx -> edx
неправилно - mov al, ecx ;несоразмерны параметры, Error!

Можно также считать параметр из памяти:
как может выглядеть (побайтно) часть памяти программы : 

смещение        
34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 41 42 
данные            
0D 0A 50 32 44 57 25 7A 5E 72 EF 7D FF AD C7 

итак нам надо считать данные (размером 4 байта) из 3A-ой ячейки:

mov eax, dword (размер того, что надо
переслать) ptr [0000003Ah] (адрес памяти (смещение))

после чего в EAX будем иметь 725E7A25h, это происходит
т.к. в памяти  данные хранятся в ОБРАТНОМ порядке - 25 7A 5E
72 (гаду придумавшему  такое .. надо 🙂

размеры данных в битах: dword - 32; word - 16; byte -8;
если же размер не указать, то считаются данные размером в юзаемый
регистр (в данном случае - 32 бит)

Можно писать и сами числа (не забывая о размере регистров):

mov edx, 5006 ;запишет число 5006 (дес.) в edx

маленькая фишка - если писать число (или регистр) в квадратных
скобках [], то само число(содержимое регистра) не переместится,
это число (содержимое регистра) будет воспринято как АДРЕС памяти,
по которому следует считать/записать число. 

Как делать простые вычисления или опкоды ADD, SUB, MUL, DIV.

формат: опкод получатель, источник

add получатель, источник (получатель = получатель + источник)
sub получатель, источник (получатель = получатель - источник)
mul получатель, источник (получатель = получатель * источник)
div источник (eax = eax / источник, edx = остаток)

Пример: 
sub ecx,4h ;ecx=ecx-4h

пр.: источник команды div нельзя задавать непосредственно числом

Битовые операции (выполняются для каждого бита числа отдельно)

Пример:

mov ax, 3406
mov dx, 13EAh
xor ax, dx
not ax

Инкремент/Декремент:

inc reg -> reg = reg + 1
dec reg -> reg = reg - 1
inc dword ptr [103405] -> число по [103405] увеличится на единицу.
dec dword ptr [103405] -> число по [103405] уменьшится на единицу.

NOP - ничегонеделание 1 - штук .. если надо потом в это место кода
вставить что-то полезное, HexEdit'ом скажем ..

Сдвиги

SHL что_сдвигаем, на_сколько_бит_сдвигаем ;побитно сдвиг влево
(младшие->старшие)
SHR что_сдвигаем, на_сколько_бит_сдвигаем ;побитно сдвиг вправо
(старшие->младшие)

Пример:

mov al, 01011011b ; (двоичное)
shr al, 3

Арифметические сдвиги:

SAL что_сдвигаем, на_сколько_бит_сдвигаем ;сдвиг влево
SAR что_сдвигаем, на_сколько_бит_сдвигаем ;сдвиг вправо

записывают самый старший (знаковый) бит на место в(ы)двигаемого бита.
Пример:

;al = 10100110
sar al, 3
;al = 11110100
sar al, 2
;al = 11111101

;bl = 00100110
sar bl, 3
;bl = 00000010

Вращение (циклический сдвиг)
rol что_вращаем, на_сколько_бит; вращаем влево
ror что_вращаем, на_сколько_бит; вращаем влево
rcl что_вращаем, на_сколько_бит; вращаем через carry влево
rcr что_вращаем, на_сколько_бит; вращаем через carry вправо

Принцип: ror (вращение вправо)

Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 
  1    0     0*  1    
1    0     1    1 
  1    0     0    1    
1    0     1    1

Делаем ror на 3

  1    1    
0    1     0    0*  
1    1 

RCL и ROR работают аналогично, но последний в(ы)двигаемый бит у RCL 
будет в флаге carry.

Обмен 

Просто пример:

;eax = 237h
;ecx = 978h
xchg eax, ecx
;eax = 978h 
;ecx = 237h 

Структура файлов

Исходные файлы Ассемблера делятся на разделы. Разделы включают - код, 
данные, неинициализированные данные, константы, ресурсы и relocations. 

Описания разделов в программе:

.code ; начало сегмента, содержащего код
.data ; начало сегмента, содержащего данные
.data? ; начало сегмента, содержащего неинициализированные данные
.const ; начало сегмента, содержащего константы

Выполняемые файлы (*.exe, *.dll и иные) - (в win32) находятся в
формате portable executable-format (PE). Этот формат довольно сложен,
так что упомяну только самое важное. Разделы определяются в PE-заголовке несколькими характеристиками:
имя раздела, RVA, смещение, raw(линейный) размер,
виртуальный размер и флаги. RVA (Относительный Виртуальный Адрес) является относительным,
от той позиции куда будет загружен этот раздел, адресом памяти. Этот
адрес - тоже указан в PE-заголовке, но может изменяться PE-загрузчиком (используя раздел relocation). Смещение является исходным
линейным смещением в ехе-файле. Виртуальный размер является размером,
который он займёт в памяти. Флаги используются для управления доступом к разделу:
чтение/запись/исполнение и т.п..

Простая прога для демонстрации написания разделов:

Поехали:
.data ;секция данных
Number1 dd 12033h ;1 число по 32-бит
Number2 dw 100h,200h,300h,400h ;4 числа по 16 бит подряд
Number3 db "f@#k it",0 ;байтовая строка с 00 в конце
;(нуль -терминируемая то бишь)

.data? ;секция неинициализированных данных
Value dd ? ;чёрт знает что (неопредёленно)

.code ;секция кода
mov eax, Number1 ;простые операции 😉
mov ecx, offset Number2
add ax, word ptr [ecx+4]
mov Value, eax

Ассемблироваться, кстати, не будет, но как пример пойдёт..
РАЗбирать этот код, как предлагает автор исходной статьи, не буду да
простят меня люди, т.к. ну о-о-очень здесь всё просто ..

дополню:
.data?
ManyBytes1 db 5000 dup (?)

.data
ManyBytes2 db 5000 dup (0)

(5000 dup значит: 5000 повторов. Те. Val1 db 4,4,4,4,4,4,4 аналогично 
Val1 db 7 dup (4); знак '?' значит, что число не определено)

ПЕРЕХОДЫ

Позволяют программе перейти в заданный участок кода.

Безусловный:

JMP Куда
где Куда - метка в теле программы в формате "Метка:"

Пример:

loc1:
mov eax,ebx
jmp loc1 ;зацикливаем участок

Условные:

Просто приведу пример кода:

.code
mov eax, edx
sub eax, ecx
cmp eax, 2 
; CMP - CoMPare - сравнение 1-го числа со вторым и установка флага
; ZF=1 (ZeroFlag, флаг признака 0) если числа одинаковы ..
jz loc1 ;условный переход на loc1 если ZF=1
xor eax, eax ;еах=0
jmp loc2
loc1:
xor eax, eax
inc eax
loc2:

аналог на C++:
if ((edx-ecx)==2){
eax = 1;
}
else {
eax = 0;
}

аналог на BASIC:
IF (edx-ecx)=2 THEN
EAX = 1
ELSE
EAX = 0
END IF

Регистр флагов

Содержит признаки выполненной операции

ZF (Флаг нуля) Этот флаг устанавливается, когда результат вычисления является нулем (кстати сравнение - то же вычитание, но без сохранения результата).

SF (Знак) Ставится если результат вычисления отрицателен.

CF (Признак переноса) Флаг переноса содержит старший бит результата если был перенос из старшего разряда.

OF (Переполнение) Указывает на переполнение при вычислениях, что
происходит когда результат "не влезает" в получатель.

Есть и другие флаги (Parity, Auxiliary, Trap, Interrupt, Direction,
IOPL, Nested Task, Resume, & Virtual Mode), но эта статья их не
затрагивает ..

Список условных переходов.

"Jump if above" значит:
cmp x, y
JMP если x больше(above) y 

Опкод Расшифровка Условия 

JA Jump if above CF=0 и ZF=0 
JAE Jump if above or equal CF=0 
JB Jump if below CF=1 
JBE Jump if below or equal CF=1 или ZF=1 
JC Jump if carry CF=1 
JCXZ Jump if CX=0 регистр CX=0 
JE (аналог JZ) Jump if equal ZF=1 
JG Jump if greater (знаковое) ZF=0 и SF=OF
JGE Jump if greater or equal (знаковое) SF=OF 
JL Jump if less (знаковое) SF не= OF 
JLE Jump if less or equal (знаковое) ZF=1 или SF не= OF
JMP Безусловный переход - 
JNA J0ump if not above CF=1 или ZF=1
JNAE Jump if not above or equal CF=1 
JNB Jump if not below CF=0 
JNBE Jump if not below or equal CF=1 и ZF=0 
JNC Jump if not carry CF=0 
JNE (аналог JNZ) Jump if not equal ZF=0 
JNG Jump if not greater (знаковое) ZF=1 или SF не= OF
JNGE Jump if not greater or equal (знаковое) S F не= OF 
JNL Jump if not less (знаковое) SF=OF 
JNLE Jump if not less or equal (знаковое) ZF=0 и SF=OF 
JNO Jump if not overflow (знаковое) OF=0 
JNP Jump if no parity PF=0 
JNS Jump if not знаковое (знаковое) SF=0 
JNZ Jump if not zero ZF=0 
JO Jump if overflow (знаковое) OF=1 
JP Jump if parity PF=1 
JPE Jump if parity even PF=1 
JPO Jump if paity odd PF=0 
JS Jump if знаковое (знаковое) SF=1 
JZ Jump if zero ZF=1 

Операции переходов имеют только 1 параметр - адрес(метку) перехода.
Знаковое число - значит что в этом числе старший (самый левый) разряд 
отведён под знак, т. е. чисел в данный регистр можно запихнуть в 2 раза
меньше (со знаком).

Ещё опкоды:

TEST 

Выполняет логическую И над числами и по результату (не помещая его в
получатель) ставит флаг ZF:

test eax, 100b ; 'b' - означает двоичное число
jnz bitset ; переход если 3й справа бит равен 1

test ecx, ecx ; проверка ecx на 0
jz somewhere ; если ecx=0 - переход

Работа со стеком

СТЕК - некоторая область памяти, адресуемая регистром ESP,
чтение/запись в которую происходит так: число записывается по адресу
из ESP затем значение ESP уменьшается/увеличивается. Те. последнее
записанное число извлечётся первым. Зачем? Это должно ускорить обращение к памяти ..

PUSH Число(Регистр) - добавление в стек
POP Число(Регистр) - извлечение из стека

Пример:

mov ecx, 100
mov eax, 200
push ecx ;сохраним ecx и eax
push eax
xor ecx, eax ;что-то сделаем
add ecx, 400
mov edx, ecx
pop ebx ;восстановим старые значения eax -> ebx
pop ecx ; ecx -> ecx

CALL & RET 

формат: CALL Метка(или адрес)
RET

CALL - Переход на подпрограмму - переход с сохранением в стеке адреса,
следующей после перехода инструкции:

..код..
call 0455659 ;переход на адрес 0455659 подпрограммы
add esp,8 ;сюда перейдём после выхода из подпрограммы 
..другой код..

код по адресу 455659:
add eax, 500
mul eax, edx
ret ;возврат из подпрограммы

Используя стек, в подпрограмму можно также передавать параметры :

push 0 ;запишем параметры в стек
push eax
call FunctionName
add esp,8 ;удалим параметры из стека (аналогично 2м POP'ам)

внутри подпрограммы в этом случае их можно считать так:

mov eax,[esp+4] - первый параметр
mov eax,[esp+8] - второй параметр

где 4 - размер в байтах у данных в стеке;

ВЫНДОЗА

Зная всё предыдущее сделаем, свою первую прогу на Ассемблере
сразу под Винду: 

Вот она эта прога:

;first.asm
;возможный вариант заголовка в 32-битной программе
.486 
.model flat, stdcall 
option casemap:none

;библиотеки импорта
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

;файлы с описаниями внешних макросов и функций
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\windows.inc

;наши данные
.data
MsgText db "Hello & BYE !",0
MsgTitle db "Eto Okno Soobcshenija",0

.code

;поехали
start:

;вызываем окно сообщения с помощью функции MessageBox 
;(см. Windows API Reference)
;invoke - макрос пакета MacroASM для передачи параметров через стек ..
;аналог записи invoke Function, Par1, Par2 без макросов:
;push Par2
;push Par1
;call Function
;MB_OK и MB_ICONINFORMATION - стили окна (см. API Ref)
;вместо ADDR можно также использовать offset

invoke MessageBox, NULL, ADDR MsgText, ADDR MsgTitle, MB_OK or 
MB_ICONINFORMATION
invoke ExitProcess, NULL ;выходим из проги

end start

Далее - создание ехе-шника проги:
создаём и запускаем make.bat в директории наших исходников:
@echo off
REM Путь к компилятору должен указываться в системной переменной PATH
REM Запускаем компилятор для получения объектного кода
ml /c /coff first.asm
REM Линкуем этот код в ехе-шник
link /subsystem:windows first.obj
pause>nul

Итак с примером разобрались, запустили и порадовались, теперь перейдём к созданию окна. Это требует дополнительно создания класса этого окна, введения цикла обработки сообщений Винды (так она информирует окно о каком-либо событии типа клика мышью, вызова меню, нажатия батона на клаве), а так же оконной процедуры.

Here we go:
;second.asm 
.486
.model flat, stdcall
option casemap:none

;библиотек и импортов теперь больше
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\gdi32.lib

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\gdi32.inc

;объявим новые процедуры
WinMain PROTO STDCALL :DWORD, :DWORD, :DWORD, :DWORD
WndProc PROTO STDCALL :DWORD, :DWORD, :DWORD, :DWORD
.data?
hInstance dd ?

.data
ClassName db "FirstWindowClass",0
AppName db "NewF@#inWindow",0

.code
start:
;берём идентификатор текущего модуля
invoke GetModuleHandle, NULL
mov hInstance, eax
;запусаем приложение 
invoke WinMain, hInstance, NULL, NULL, SW_SHOWNORMAL
;выходим
invoke ExitProcess, NULL

;главная процедура (!) в проге, её и вызываем при запуске
WinMain proc hInst:HINSTANCE,hPrevInst: HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL hwnd:DWORD
LOCAL msg:MSG

;заполним свойства класса нашего будущего окна (см. API Reference)
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
;иконка должна быть в файле rsrc.rc и иметь идентификатор IDI_APPLICATION
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon, eax
mov wc.hIconSm, eax
;курсор тоже должен быть в файле rsrc.rc, его идентификатор - IDC_ARROW
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
;создадим такой класс
invoke RegisterClassEx, addr wc

;создадим основное окно: WS_ .. - определяют то, как оно будет выглядеть
INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW-WS_SIZEBOX-WS_MAXIMIZEBOX,CW_USEDEFAULT,\
CW_USEDEFAULT,400,300,NULL,NULL,\
hInst,NULL
;запишем его идентификатор в hwnd
mov hwnd,eax 

;выведем его на экран и перерисуем
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd

;это стандартный цикл обработки оконных сообщений
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
;выходим из проги по завершению цикла (т.е. по закрытию окна)
mov eax,msg.wParam
ret
WinMain endp

;наша оконная процедура
WndProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
mov eax, uMsg
push esi ;сохраним используемые Виндой регистры!
push edi
push ebx
;что за сообщение передала Винда?
;а) WM_CREATE - при создании окна ..
.IF eax==WM_CREATE 
;просто создадим, маленькое такое, окошко типа MessageBox
invoke MessageBox, NULL, ADDR AppName, ADDR AppName, NULL
;б) WM_DESTROY - при удалении окна ..
.ELSEIF eax==WM_DESTROY
;пошлём сообщение на завершение работы нашего окна
invoke PostQuitMessage, NULL
.ELSE
;все остальные сообщения не обрабатываем ..
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
.ENDIF
pop ebx ;восстановим сохранённые в начале регистры
pop edi
pop esi
ret
WndProc endp
end start

...

примеры файлов, полный набор .asm команд и заготовку для крака 
на Ассемблере ищите тут:
http://home.od.ua/~blackw
ещё файлы и инфа по программированию под Win32 -
http://win32asm.chat.ru
а приколоться и отдохнуть от этой статьи 😉 не забыв про asm - 
http://www.assembler.ru
и главное - статья написана на основе Win32Asm Tutorial by Exagone
(Eng) спасибы ему за это, найти её и ещё инфу можно на сайте - 
http://exagone.cjb.net 

ЗЫ: Почему написана эта статья? Ну просто достало, когда народ спрашивает: как что-то взломать, и каждому надо объяснять всё сначала ..

BRg, LTrapper

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии