Введение
amx и amxx файлы используются на виртуальной машине, которая содержит набор сценариев. amx файл разбит на три части: заголовок (который содержит информацию о размере), раздел кода и раздел данных. A amxx файл - это простой метод для того, чтобы сохранить два amx файла в одном. Тем самым имеем 32-разрядную и 64-разрядную совместимость в одном файле. Раздел кода содержит коды операций виртуальной машины (то есть примитивные команды виртуальной машины) для компилированного плагина. Раздел данных содержит стек, динамическую память и другие данные, такие как массивы и строки.
Виртуальная машина довольно проста. Она основана на двух регистрах (PRI и ALT), стеке и динамической памяти. Регистры - простые ячейки, используемые для хранения временной информации и их возвращения. Стек используется для локальных переменных. Стек имеет две операции: помещение элемента в стек (push) и доставание верхнего элемента из стека (pop). Динамическая память - это простой участок памяти, используемый для хранения временных переменных. Все данные в виртуальной машине вращаются вокруг интегрального типа данных, 'ячейки' (cell). Интегральный - это целочисленный тип данных, который содержит указатель на 32-bit на 32-разрядном процессоре и на 64-bit на 64-разрядном процессоре.
AMX обладает способностью вызова процедур (возможность вызывать функцию и возвращать ее значение), которые вращаются вокруг скрытого регистра под названием FRM (который располагается на стеке). При любом вызове процедуры передаются параметры стеку в обратном порядке и закончивается числом помещенных байтов. Например, чтобы вызвать функцию с параметрами A и B, сначала вы поместите параметр B, потом A, а затем число 8 в стек (или 16, если 64-bit).
Способы адресации у виртуальной машины - это прежде всего DAT и FRM. Инструкции, относящиеся к FRM, заканчиваются на '.S'. Инструкции, относящиеся к константам, заканчиваются на '.C'. Они относятся к нескольким категориям:
Переходы (JUMP)
Математические вычисления
Манипуляция со стеком (POP, PUSH)
Вызовы процедур (локальный, системный)
Память (установка и восстановление)
Инструкции, относящиеся к регистрам, заканчиваются на '.ALT' или '.PRI'.
Описание
amxxdump - это консольное приложение, предназначенное для дизассемблирования .amxx плагинов, автором которого является Steve Dudenhoeffer. Данное приложение будем использовать через командную строку Windows (cmd).
Синтаксис использования:
amxxdump [параметры] имя_файла.amxx
Список параметров
-a - не показывать адресные положения.
-c - не показывать комментарии.
-x - список всех public и stock функций плагина и их параметры.
-n - список native функций, используемых в плагине.
-D <название> - дизассемблирование указанной функции.
-d - дизассемблировать код плагина.
-s - показать все символы.
-m - показать необходимые модули.
-r <название> - поиск информации об указанной функции.
-R <название> - поиск информации об указанной native функции.
-v <значение> - показать значение адреса в разделе данных.
-A <размер> - в дополнении к параметру -v, формирует дамп в зависимости от указанного размера ячеек.
-V <значение> - показать значение адреса в разделе данных в качестве строки.
-F <значение> - показать значение адреса в разделе данных в качестве числа с плавающей точкой.
-f - показать названия всех файлов, код которых включен в плагин (stock).
-l - показать номер строки и название файла у оператора BREAK.
-j - показать метки для jump, switch и case таблиц.
-e - попытаться сформировать данные от операторов push.c/const.pri.
-E - использовать список параметров для стандартных вызовов native функций. Для работы необходимы .inc файлы.
-N - не показывать рамзерность переменных, теги и стандартные значения. Подразумевает параметр -E.
-g - список всех глобальных переменных.
-h - скрыть номера параметров и их адреса.
-! - показать лицензию программы.
-? - помощь.
Применение
Рассмотрим применение amxxdump, к примеру, на стандартном плагине antiflood.amxx из версии AMX Mod X 1.8.1.
1. Узнаем список всех public и stock функций плагина:
amxxdump -x antiflood.amxx
Получаем:
Код: Выделить всё
0x0000004C stock bool:operator!(Float:)(Float:oper)
0x00000008 stock bool:operator>(Float:,Float:)(Float:oper1,Float:oper2)
0x000001AC public chkFlood(id)
0x00000078 public plugin_init()
В данном случае функции operator! и operator> тоже самое, что операторы ! и > в условиях, поэтому их дизассемблировать не обязательно.
2. Узнаем список всех native функций, используемых в плагине:
amxxdump -n antiflood.amxx
Получаем:
Код: Выделить всё
native: floatadd
native: client_print
native: get_gametime
native: get_pcvar_float
native: register_cvar
native: register_clcmd
native: register_dictionary
native: register_plugin
native: floatcmp
3. Отобразим все символы:
amxxdump -s antiflood.amxx
Получаем:
Код: Выделить всё
codestart codeend address type name
0x00000008 0x0000004C 0x00000010 local val oper2
0x00000008 0x0000004C 0x0000000C local val oper1
0x0000004C 0x00000078 0x0000000C local val oper
0x00000210 0x0000049C 0xFFFFFFF8 local val nexTime
0x000001B8 0x000004B0 0xFFFFFFFC local val maxChat
0x000001AC 0x000004B0 0x0000000C local val id
0x0000004C 0x00000078 0x0000004C stock operator!(Float:)
0x00000008 0x0000004C 0x00000008 stock operator>(Float:,Float:)
0x00000078 0x000004B0 0x00000000 global val AMXX_VERSION_STR[11]
0x00000078 0x000004B0 0x00000134 global val amx_flood_time
0x000001AC 0x000004B0 0x000001AC public chkFlood
0x00000078 0x000004B0 0x000000B0 global val g_Flood[33]
0x00000078 0x000004B0 0x0000002C global val g_Flooding[33]
0x00000078 0x000001AC 0x00000078 public plugin_init
local - локальная переменная, то есть может быть использована только в какой-то конкретной функции.
global - глобальная переменная, то есть может быть использована во всех функциях плагина.
public - public функция.
stock - stock функция.
4. Узнаем используемые модули:
amxxdump -m antiflood.amxx
Получаем:
Код: Выделить всё
No module data detected.
Значит другие модули, кроме amxmodx, не используются.
5. Узнаем список всех глобальных переменных:
amxxdump -g antiflood.amxx
Получаем:
Код: Выделить всё
0x00000000 new AMXX_VERSION_STR[11]
0x00000134 new amx_flood_time
0x000000B0 new g_Flood[33]
0x0000002C new Float:g_Flooding[33]
AMXX_VERSION_STR - это константа, в которой содежится текущая версия AMX Mod X.
6. Получаем весь дизассемблированный код плагина:
amxxdump -d -l -j -e -E antiflood.amxx
Получаем:
Код: Выделить всё
0x8 PROC ; stock bool:operator>(Float:,Float
:)(Float:oper1,Float:oper2)
0xC BREAK ; antiflood.sma:136
0x10 BREAK ; antiflood.sma:137
0x14 PUSH.S 0x10 ; Float:oper2
0x1C PUSH.S 0xC ; Float:oper1
0x24 PUSH.C 0x8
0x2C SYSREQ.C 0x0 ; floatcmp(Float:oper1,Float:oper2)
0x34 STACK 0xC ; free 3 cells
0x3C MOVE.alt
0x40 ZERO.pri
0x44 SLESS
0x48 RETN
0x4C PROC ; stock bool:operator!(Float:)(Float:oper)
0x50 BREAK ; antiflood.sma:172
0x54 BREAK ; antiflood.sma:173
0x58 LOAD.S.pri 0xC ; Float:oper
0x60 CONST.alt 0xFFFFFFFF
0x68 AND
0x6C ZERO.alt
0x70 EQ
0x74 RETN
0x78 PROC ; public plugin_init()
0x7C BREAK ; antiflood.sma:41
0x80 BREAK ; antiflood.sma:43
0x84 PUSH.C 0x164 ; "AMXX Dev Team"
0x8C PUSH.C 0x0 ; AMXX_VERSION_STR[11] "1.8.1.3722"
0x94 PUSH.C 0x138 ; "Anti Flood"
0x9C PUSH.C 0xC
0xA4 SYSREQ.C 0x1 ; register_plugin("Anti Flood",AMXX_VERSION_STR[11]={"1.8.1.3722"},"AMXX Dev Team")
0xAC STACK 0x10 ; free 4 cells
0xB4 BREAK ; antiflood.sma:44
0xB8 PUSH.C 0x19C ; "antiflood.txt"
0xC0 PUSH.C 0x4
0xC8 SYSREQ.C 0x2 ; register_dictionary("antiflood.txt")
0xD0 STACK 0x8 ; free 2 cells
0xD8 BREAK ; antiflood.sma:45
0xDC PUSH.C 0xFFFFFFFF ; signed=-1 float=-1.#QNAN0
0xE4 PUSH.C 0x208 ; 0x0
0xEC PUSH.C 0xFFFFFFFF ; signed=-1 float=-1.#QNAN0
0xF4 PUSH.C 0x1E4 ; "chkFlood"
0xFC PUSH.C 0x1D4 ; "say"
0x104 PUSH.C 0x14
0x10C SYSREQ.C 0x3 ; register_clcmd("say","chkFlood",-1,"",-1)
0x114 STACK 0x18 ; free 6 cells
0x11C BREAK ; antiflood.sma:46
0x120 PUSH.C 0xFFFFFFFF ; signed=-1 float=-1.#QNAN0
0x128 PUSH.C 0x208 ; 0x0
0x130 PUSH.C 0xFFFFFFFF ; signed=-1 float=-1.#QNAN0
0x138 PUSH.C 0x230 ; "chkFlood"
0x140 PUSH.C 0x20C ; "say_team"
0x148 PUSH.C 0x14
0x150 SYSREQ.C 0x3 ; register_clcmd("say_team","chkFlood",-1,"",-1)
0x158 STACK 0x18 ; free 6 cells
0x160 BREAK ; antiflood.sma:47
0x164 PUSH.C 0x0 ; AMXX_VERSION_STR[11] "1.8.1.3722"
0x16C PUSH.C 0x0 ; AMXX_VERSION_STR[11] "1.8.1.3722"
0x174 PUSH.C 0x290 ; "0.75"
0x17C PUSH.C 0x254 ; "amx_flood_time"
0x184 PUSH.C 0x10
0x18C SYSREQ.C 0x4 ; register_cvar("amx_flood_time","0.75",0,0.000000)
0x194 STACK 0x14 ; free 5 cells
0x19C STOR.pri 0x134 ; amx_flood_time
0x1A4 ZERO.pri
0x1A8 RETN
0x1AC PROC ; public chkFlood(id)
0x1B0 BREAK ; antiflood.sma:50
0x1B4 BREAK ; antiflood.sma:52
; new Float:maxChat
0x1B8 STACK 0xFFFFFFFC ; allocate 1 cells
0x1C0 PUSH 0x134 ; amx_flood_time
0x1C8 PUSH.C 0x4
0x1D0 SYSREQ.C 0x5 ; Float:get_pcvar_float(amx_flood_time)
0x1D8 STACK 0x8 ; free 2 cells
0x1E0 STOR.S.pri 0xFFFFFFFC ; Float:maxChat
0x1E8 BREAK ; antiflood.sma:54
0x1EC PUSH.S 0xFFFFFFFC ; Float:maxChat
0x1F4 PUSH.C 0x4
0x1FC CALL 0x4C ; stock bool:operator!(Float:)(Float:oper)
0x204 JNZ 0x49C ; jump_0
0x20C BREAK ; antiflood.sma:56
; new Float:nexTime
0x210 STACK 0xFFFFFFFC ; allocate 1 cells
0x218 PUSH.C 0x0
0x220 SYSREQ.C 0x6 ; Float:get_gametime()
0x228 STACK 0x4 ; free 1 cells
0x230 STOR.S.pri 0xFFFFFFF8 ; Float:nexTime
0x238 BREAK ; antiflood.sma:58
0x23C CONST.alt 0x2C ; Float:g_Flooding[33]=0x0 (0.00000)
0x244 LOAD.S.pri 0xC ; id
0x24C BOUNDS 0x20
0x254 LIDX
0x258 MOVE.alt
0x25C LOAD.S.pri 0xFFFFFFF8 ; Float:nexTime
0x264 PUSH.pri
0x268 PUSH.pri
0x26C PUSH.alt
0x270 PUSH.C 0x8
0x278 CALL 0x8 ; stock bool:operator>(Float:,Float:)(Float:oper1,Float:oper2)
0x280 POP.alt
0x284 JZER 0x3DC ; jump_1
0x28C BREAK ; antiflood.sma:60
0x290 CONST.alt 0xB0 ; g_Flood[33]=0x0 (0.00000)
0x298 LOAD.S.pri 0xC ; id
0x2A0 BOUNDS 0x20
0x2A8 LIDX
0x2AC MOVE.alt
0x2B0 CONST.pri 0x3 ; 0x2E00 (11776.00000)
0x2B8 JSGRTR 0x3A0 ; jump_2
0x2C0 BREAK ; antiflood.sma:62
0x2C4 PUSH.C 0x2C8 ; "STOP_FLOOD"
0x2CC PUSH.ADR 0xC ; id
0x2D4 PUSH.C 0x2A4 ; "** %L **"
0x2DC PUSH.C 0x1 ; 0x2E000000
0x2E4 PUSH.S 0xC ; id
0x2EC PUSH.C 0x14
0x2F4 SYSREQ.C 0x7 ; client_print(id,1,"** %L **",id,"STOP_FLOOD")
0x2FC STACK 0x18 ; free 6 cells
0x304 BREAK ; antiflood.sma:63
0x308 CONST.alt 0x2C ; Float:g_Flooding[33]=0x0 (0.00000)
0x310 LOAD.S.pri 0xC ; id
0x318 BOUNDS 0x20
0x320 IDXADDR
0x324 PUSH.pri
0x328 LOAD.S.pri 0xFFFFFFFC ; Float:maxChat
0x330 LOAD.S.alt 0xFFFFFFF8 ; Float:nexTime
0x338 PUSH.pri
0x33C PUSH.alt
0x340 PUSH.C 0x8
0x348 SYSREQ.C 0x8 ; floatadd
0x350 STACK 0xC ; free 3 cells
0x358 CONST.alt 0x40400000
0x360 PUSH.pri
0x364 PUSH.alt
0x368 PUSH.C 0x8
0x370 SYSREQ.C 0x8 ; floatadd
0x378 STACK 0xC ; free 3 cells
0x380 POP.alt
0x384 STOR.I
0x388 BREAK ; antiflood.sma:64
0x38C CONST.pri 0x1 ; 0x2E000000 (771751936.00000)
0x394 STACK 0x8 ; free 2 cells
0x39C RETN
0x3A0 BREAK ; antiflood.sma:66
; target:jump_2
0x3A4 CONST.alt 0xB0 ; g_Flood[33]=0x0 (0.00000)
0x3AC LOAD.S.pri 0xC ; id
0x3B4 BOUNDS 0x20
0x3BC IDXADDR
0x3C0 PUSH.pri
0x3C4 LOAD.I
0x3C8 SWAP.pri
0x3CC INC.I
0x3D0 POP.pri
0x3D4 JUMP 0x438 ; jump_3
0x3DC BREAK ; antiflood.sma:68
; target:jump_1
0x3E0 CONST.alt 0xB0 ; g_Flood[33]=0x0 (0.00000)
0x3E8 LOAD.S.pri 0xC ; id
0x3F0 BOUNDS 0x20
0x3F8 LIDX
0x3FC JZER 0x438 ; jump_4
0x404 BREAK ; antiflood.sma:70
0x408 CONST.alt 0xB0 ; g_Flood[33]=0x0 (0.00000)
0x410 LOAD.S.pri 0xC ; id
0x418 BOUNDS 0x20
0x420 IDXADDR
0x424 PUSH.pri
0x428 LOAD.I
0x42C SWAP.pri
0x430 DEC.I
0x434 POP.pri
0x438 BREAK ; antiflood.sma:73
; target:jump_3
; target:jump_4
0x43C CONST.alt 0x2C ; Float:g_Flooding[33]=0x0 (0.00000)
0x444 LOAD.S.pri 0xC ; id
0x44C BOUNDS 0x20
0x454 IDXADDR
0x458 PUSH.pri
0x45C LOAD.S.pri 0xFFFFFFFC ; Float:maxChat
0x464 LOAD.S.alt 0xFFFFFFF8 ; Float:nexTime
0x46C PUSH.pri
0x470 PUSH.alt
0x474 PUSH.C 0x8
0x47C SYSREQ.C 0x8 ; floatadd
0x484 STACK 0xC ; free 3 cells
0x48C POP.alt
0x490 STOR.I
0x494 STACK 0x4 ; free 1 cells
0x49C BREAK ; antiflood.sma:76
; target:jump_0
0x4A0 ZERO.pri
0x4A4 STACK 0x4 ; free 1 cells
0x4AC RETN
7. Рассмотрим только функцию plugin_init.
Используем:
amxxdump -l -j -e -E -D plugin_init antiflood.amxx
Получаем:
Код: Выделить всё
0x78 PROC ; public plugin_init()
0x7C BREAK ; antiflood.sma:41
0x80 BREAK ; antiflood.sma:43
0x84 PUSH.C 0x164 ; "AMXX Dev Team"
0x8C PUSH.C 0x0 ; AMXX_VERSION_STR[11] "1.8.1.3722"
0x94 PUSH.C 0x138 ; "Anti Flood"
0x9C PUSH.C 0xC
0xA4 SYSREQ.C 0x1 ; register_plugin("Anti Flood",AMXX_VERSION_STR[11]={"1.8.1.3722"},"AMXX Dev Team")
0xAC STACK 0x10 ; free 4 cells
0xB4 BREAK ; antiflood.sma:44
0xB8 PUSH.C 0x19C ; "antiflood.txt"
0xC0 PUSH.C 0x4
0xC8 SYSREQ.C 0x2 ; register_dictionary("antiflood.txt")
0xD0 STACK 0x8 ; free 2 cells
0xD8 BREAK ; antiflood.sma:45
0xDC PUSH.C 0xFFFFFFFF ; signed=-1 float=-1.#QNAN0
0xE4 PUSH.C 0x208 ; 0x0
0xEC PUSH.C 0xFFFFFFFF ; signed=-1 float=-1.#QNAN0
0xF4 PUSH.C 0x1E4 ; "chkFlood"
0xFC PUSH.C 0x1D4 ; "say"
0x104 PUSH.C 0x14
0x10C SYSREQ.C 0x3 ; register_clcmd("say","chkFlood",-1,"",-1)
0x114 STACK 0x18 ; free 6 cells
0x11C BREAK ; antiflood.sma:46
0x120 PUSH.C 0xFFFFFFFF ; signed=-1 float=-1.#QNAN0
0x128 PUSH.C 0x208 ; 0x0
0x130 PUSH.C 0xFFFFFFFF ; signed=-1 float=-1.#QNAN0
0x138 PUSH.C 0x230 ; "chkFlood"
0x140 PUSH.C 0x20C ; "say_team"
0x148 PUSH.C 0x14
0x150 SYSREQ.C 0x3 ; register_clcmd("say_team","chkFlood",-1,"",-1)
0x158 STACK 0x18 ; free 6 cells
0x160 BREAK ; antiflood.sma:47
0x164 PUSH.C 0x0 ; AMXX_VERSION_STR[11] "1.8.1.3722"
0x16C PUSH.C 0x0 ; AMXX_VERSION_STR[11] "1.8.1.3722"
0x174 PUSH.C 0x290 ; "0.75"
0x17C PUSH.C 0x254 ; "amx_flood_time"
0x184 PUSH.C 0x10
0x18C SYSREQ.C 0x4 ; register_cvar("amx_flood_time","0.75",0,0.000000)
0x194 STACK 0x14 ; free 5 cells
0x19C STOR.pri 0x134 ; amx_flood_time
0x1A4 ZERO.pri
0x1A8 RETN
Код: Выделить всё
// PROC - говорит нам о том, что это процедура (функция), в нашем случае plugin_init
0x78 PROC ; public plugin_init()
Запишем как:
Код: Выделить всё
public plugin_init()
Код: Выделить всё
// PUSH.C - означает, что мы помещаем константу в стек
// Поэтому стек будет содержать (0x164, 0x0, 0x138, 0xC)
// Последний PUSH.C - это количество параметров в байтах. 0xC - в десятичной системе равно 12.
// Размер 32-разрядной ячейки равен 4, поэтому 12/4 = 3 параметра.
0x84 PUSH.C 0x164 ; "AMXX Dev Team" // третий параметр
0x8C PUSH.C 0x0 ; AMXX_VERSION_STR[11] "1.8.1.3722" // второй параметр
0x94 PUSH.C 0x138 ; "Anti Flood" // первый параметр
0x9C PUSH.C 0xC
// SYSREQ.C - конечная инструкция, которая работает с данными из стека
// Вызывает функцию register_plugin
0xA4 SYSREQ.C 0x1 ; register_plugin("Anti Flood",AMXX_VERSION_STR[11]={"1.8.1.3722"},"AMXX Dev Team")
// STACK - означает, что мы освобождаем из стека 4 ячейки (16 байт)
0xAC STACK 0x10 ; free 4 cells
// BREAK - означает прерывание, можно понимать это, как конец строки
0xB4 BREAK ; antiflood.sma:44
Так как используется функция register_plugin, то мы знаем ее синтаксис:
Код: Выделить всё
register_plugin(const plugin_name[], const version[], const author[])
Запишем как:
Код: Выделить всё
register_plugin("Anti Flood", AMXX_VERSION_STR, "AMXX Dev Team")
Код: Выделить всё
0xB8 PUSH.C 0x19C ; "antiflood.txt" // Параметр
0xC0 PUSH.C 0x4 // Один параметр
0xC8 SYSREQ.C 0x2 ; register_dictionary("antiflood.txt") // Вызываем функцию register_dictionary
0xD0 STACK 0x8 ; free 2 cells // Освобождаем из стека 2 ячейки
0xD8 BREAK ; antiflood.sma:45 // Конец строки
Так как используется функция register_dictionary, то мы знаем ее синтаксис:
Код: Выделить всё
register_dictionary ( const file[] )
Запишем как:
Код: Выделить всё
register_dictionary("antiflood.txt")
Код: Выделить всё
0xDC PUSH.C 0xFFFFFFFF ; signed=-1 float=-1.#QNAN0 // Пятый аргумент
0xE4 PUSH.C 0x208 ; 0x0 // Четверый аргумент
0xEC PUSH.C 0xFFFFFFFF ; signed=-1 float=-1.#QNAN0 // Третий аргумент
0xF4 PUSH.C 0x1E4 ; "chkFlood" // Второй аргумент
0xFC PUSH.C 0x1D4 ; "say" // Первый аргумент
0x104 PUSH.C 0x14 // Пять аргументов
0x10C SYSREQ.C 0x3 ; register_clcmd("say","chkFlood",-1,"",-1) // Вызываем функцию register_clcmd
0x114 STACK 0x18 ; free 6 cells // Освобождаем из стека 6 ячеек
0x11C BREAK ; antiflood.sma:46 // Конец строки
Так как используется функция register_clcmd, то мы знаем ее синтаксис:
Код: Выделить всё
register_clcmd(const client_cmd[], const function[], flags=-1, info[]="", FlagManager=-1)
Запишем как:
Код: Выделить всё
register_clcmd("say", "chkFlood", -1, "", -1)
Или:
Код: Выделить всё
register_clcmd("say", "chkFlood")
Смысл не меняется, так как -1 - означает, что мы не используем флаги, а "" что у нас нет описания команды.
Код: Выделить всё
0x120 PUSH.C 0xFFFFFFFF ; signed=-1 float=-1.#QNAN0 // Пятый аргумент
0x128 PUSH.C 0x208 ; 0x0 // Четвертый аргумент
0x130 PUSH.C 0xFFFFFFFF ; signed=-1 float=-1.#QNAN0 // Третий аргумент
0x138 PUSH.C 0x230 ; "chkFlood" // Второй аргумент
0x140 PUSH.C 0x20C ; "say_team" // Первый аргумент
0x148 PUSH.C 0x14 // Пять аргументов
0x150 SYSREQ.C 0x3 ; register_clcmd("say_team","chkFlood",-1,"",-1) // Вызываем функцию register_clcmd
0x158 STACK 0x18 ; free 6 cells // Освобождаем из стека 6 ячеек
0x160 BREAK ; antiflood.sma:47 // Конец строки
Запишем как:
Код: Выделить всё
register_clcmd("say_team", "chkFlood")
Код: Выделить всё
0x164 PUSH.C 0x0 ; AMXX_VERSION_STR[11] "1.8.1.3722" // Четвертый аргумент и комментарий AMXX_VERSION_STR[11] "1.8.1.3722" неверный, ошибка работы параметра -e
0x16C PUSH.C 0x0 ; AMXX_VERSION_STR[11] "1.8.1.3722" // Третий аргумент и комментарий AMXX_VERSION_STR[11] "1.8.1.3722" неверный, ошибка работы параметра -e
0x174 PUSH.C 0x290 ; "0.75" // Второй аргумент
0x17C PUSH.C 0x254 ; "amx_flood_time" // Первый аргумент
0x184 PUSH.C 0x10 // Четыре аргумента
0x18C SYSREQ.C 0x4 ; register_cvar("amx_flood_time","0.75",0,0.000000) // Вызываем функцию register_cvar
0x194 STACK 0x14 ; free 5 cells // Освобождаем из стека 5 ячеек
// STOR - означает копирование в указанный адрес памяти значения. Другими словами у нас идет присвоение переменной amx_flood_time определенного значения.
0x19C STOR.pri 0x134 ; amx_flood_time
Так как используется функция register_cvar, то мы знаем ее синтаксис:
Код: Выделить всё
register_cvar(const name[],const string[],flags = 0,Float:fvalue = 0.0)
Запишем как:
Код: Выделить всё
amx_flood_time = register_cvar("amx_flood_time", "0.75", 0, 0.0)
Или:
Код: Выделить всё
amx_flood_time = register_cvar("amx_flood_time", "0.75")
Смысл не изменится, так как два последних параметра имеют стандартные значения.
Код: Выделить всё
// RETN - означает return (возвращение) функции.
// ZERO.pro - говорит нам о том, что возвращается ноль (return 0). Это аналогично return PLUGIN_CONTINUE.
0x1A4 ZERO.pri
0x1A8 RETN
Запишем как:
Код: Выделить всё
return PLUGIN_CONTINUE
8. Рассмотрим только функцию chkFlood.
Используем:
amxxdump -l -j -e -E -D chkFlood antiflood.amxx
Получаем:
Код: Выделить всё
0x1AC PROC ; public chkFlood(id)
0x1B0 BREAK ; antiflood.sma:50
0x1B4 BREAK ; antiflood.sma:52
; new Float:maxChat
0x1B8 STACK 0xFFFFFFFC ; allocate 1 cells
0x1C0 PUSH 0x134 ; amx_flood_time
0x1C8 PUSH.C 0x4
0x1D0 SYSREQ.C 0x5 ; Float:get_pcvar_float(amx_flood_time)
0x1D8 STACK 0x8 ; free 2 cells
0x1E0 STOR.S.pri 0xFFFFFFFC ; Float:maxChat
0x1E8 BREAK ; antiflood.sma:54
0x1EC PUSH.S 0xFFFFFFFC ; Float:maxChat
0x1F4 PUSH.C 0x4
0x1FC CALL 0x4C ; stock bool:operator!(Float:)(Float:oper)
0x204 JNZ 0x49C ; jump_0
0x20C BREAK ; antiflood.sma:56
; new Float:nexTime
0x210 STACK 0xFFFFFFFC ; allocate 1 cells
0x218 PUSH.C 0x0
0x220 SYSREQ.C 0x6 ; Float:get_gametime()
0x228 STACK 0x4 ; free 1 cells
0x230 STOR.S.pri 0xFFFFFFF8 ; Float:nexTime
0x238 BREAK ; antiflood.sma:58
0x23C CONST.alt 0x2C ; Float:g_Flooding[33]=0x0 (0.00000)
0x244 LOAD.S.pri 0xC ; id
0x24C BOUNDS 0x20
0x254 LIDX
0x258 MOVE.alt
0x25C LOAD.S.pri 0xFFFFFFF8 ; Float:nexTime
0x264 PUSH.pri
0x268 PUSH.pri
0x26C PUSH.alt
0x270 PUSH.C 0x8
0x278 CALL 0x8 ; stock bool:operator>(Float:,Float:)(Float:oper1,Float:oper2)
0x280 POP.alt
0x284 JZER 0x3DC ; jump_1
0x28C BREAK ; antiflood.sma:60
0x290 CONST.alt 0xB0 ; g_Flood[33]=0x0 (0.00000)
0x298 LOAD.S.pri 0xC ; id
0x2A0 BOUNDS 0x20
0x2A8 LIDX
0x2AC MOVE.alt
0x2B0 CONST.pri 0x3 ; 0x2E00 (11776.00000)
0x2B8 JSGRTR 0x3A0 ; jump_2
0x2C0 BREAK ; antiflood.sma:62
0x2C4 PUSH.C 0x2C8 ; "STOP_FLOOD"
0x2CC PUSH.ADR 0xC ; id
0x2D4 PUSH.C 0x2A4 ; "** %L **"
0x2DC PUSH.C 0x1 ; 0x2E000000
0x2E4 PUSH.S 0xC ; id
0x2EC PUSH.C 0x14
0x2F4 SYSREQ.C 0x7 ; client_print(id,1,"** %L **",id,"STOP_FLOOD")
0x2FC STACK 0x18 ; free 6 cells
0x304 BREAK ; antiflood.sma:63
0x308 CONST.alt 0x2C ; Float:g_Flooding[33]=0x0 (0.00000)
0x310 LOAD.S.pri 0xC ; id
0x318 BOUNDS 0x20
0x320 IDXADDR
0x324 PUSH.pri
0x328 LOAD.S.pri 0xFFFFFFFC ; Float:maxChat
0x330 LOAD.S.alt 0xFFFFFFF8 ; Float:nexTime
0x338 PUSH.pri
0x33C PUSH.alt
0x340 PUSH.C 0x8
0x348 SYSREQ.C 0x8 ; floatadd
0x350 STACK 0xC ; free 3 cells
0x358 CONST.alt 0x40400000
0x360 PUSH.pri
0x364 PUSH.alt
0x368 PUSH.C 0x8
0x370 SYSREQ.C 0x8 ; floatadd
0x378 STACK 0xC ; free 3 cells
0x380 POP.alt
0x384 STOR.I
0x388 BREAK ; antiflood.sma:64
0x38C CONST.pri 0x1 ; 0x2E000000 (771751936.00000)
0x394 STACK 0x8 ; free 2 cells
0x39C RETN
0x3A0 BREAK ; antiflood.sma:66
; target:jump_2
0x3A4 CONST.alt 0xB0 ; g_Flood[33]=0x0 (0.00000)
0x3AC LOAD.S.pri 0xC ; id
0x3B4 BOUNDS 0x20
0x3BC IDXADDR
0x3C0 PUSH.pri
0x3C4 LOAD.I
0x3C8 SWAP.pri
0x3CC INC.I
0x3D0 POP.pri
0x3D4 JUMP 0x438 ; jump_3
0x3DC BREAK ; antiflood.sma:68
; target:jump_1
0x3E0 CONST.alt 0xB0 ; g_Flood[33]=0x0 (0.00000)
0x3E8 LOAD.S.pri 0xC ; id
0x3F0 BOUNDS 0x20
0x3F8 LIDX
0x3FC JZER 0x438 ; jump_4
0x404 BREAK ; antiflood.sma:70
0x408 CONST.alt 0xB0 ; g_Flood[33]=0x0 (0.00000)
0x410 LOAD.S.pri 0xC ; id
0x418 BOUNDS 0x20
0x420 IDXADDR
0x424 PUSH.pri
0x428 LOAD.I
0x42C SWAP.pri
0x430 DEC.I
0x434 POP.pri
0x438 BREAK ; antiflood.sma:73
; target:jump_3
; target:jump_4
0x43C CONST.alt 0x2C ; Float:g_Flooding[33]=0x0 (0.00000)
0x444 LOAD.S.pri 0xC ; id
0x44C BOUNDS 0x20
0x454 IDXADDR
0x458 PUSH.pri
0x45C LOAD.S.pri 0xFFFFFFFC ; Float:maxChat
0x464 LOAD.S.alt 0xFFFFFFF8 ; Float:nexTime
0x46C PUSH.pri
0x470 PUSH.alt
0x474 PUSH.C 0x8
0x47C SYSREQ.C 0x8 ; floatadd
0x484 STACK 0xC ; free 3 cells
0x48C POP.alt
0x490 STOR.I
0x494 STACK 0x4 ; free 1 cells
0x49C BREAK ; antiflood.sma:76
; target:jump_0
0x4A0 ZERO.pri
0x4A4 STACK 0x4 ; free 1 cells
0x4AC RETN
Код: Выделить всё
; new Float:maxChat
0x1B8 STACK 0xFFFFFFFC ; allocate 1 cells
0x1C0 PUSH 0x134 ; amx_flood_time // Аргумент
0x1C8 PUSH.C 0x4 // Один аргумент
0x1D0 SYSREQ.C 0x5 ; Float:get_pcvar_float(amx_flood_time) // Вызываем функцию get_pcvar_float
0x1D8 STACK 0x8 ; free 2 cells // Освобождаем из стека 2 ячейки
0x1E0 STOR.S.pri 0xFFFFFFFC ; Float:maxChat // Присвоение переменной maxChat
0x1E8 BREAK ; antiflood.sma:54
Так как используется функция get_pcvar_float, то мы знаем ее синтаксис:
Код: Выделить всё
get_pcvar_float(pcvar)
Запишем как:
Код: Выделить всё
new Float:maxChat = get_pcvar_float(amx_flood_time)
Код: Выделить всё
0x1EC PUSH.S 0xFFFFFFFC ; Float:maxChat // Аргумент
0x1F4 PUSH.C 0x4 // Один аргумент
// CALL - означает вызов приватной функции, которая идентифицируется, как operator!
0x1FC CALL 0x4C ; stock bool:operator!(Float:)(Float:oper)
// JNZ (Jump Not Zero) - переход на метку jump_0, если не равно нулю, то есть maxChat != 0. Но выше у нас есть вызов оператора !, который означает отрицание, то есть если !(maxChat != 0) будет true, то произойдет переход на jump_0.
0x204 JNZ 0x49C ; jump_0
0x20C BREAK ; antiflood.sma:56
Запишем как:
Код: Выделить всё
if (maxChat)
Код: Выделить всё
; new Float:nexTime
0x210 STACK 0xFFFFFFFC ; allocate 1 cells
0x218 PUSH.C 0x0 // Нет параметров
0x220 SYSREQ.C 0x6 ; Float:get_gametime() // Вызов функции get_gametime
0x228 STACK 0x4 ; free 1 cells // Освобождаем из стека одну ячейку
0x230 STOR.S.pri 0xFFFFFFF8 ; Float:nexTime // Присвоение переменной nexTime
0x238 BREAK ; antiflood.sma:58
Так как используется функция get_gametime, то мы знаем ее синтаксис:
Код: Выделить всё
get_gametime()
Запишем как:
Код: Выделить всё
new Float:nexTime = get_gametime()
Код: Выделить всё
// CONST.alt - означает, что в ALT у нас будет хранение 0x2C
0x23C CONST.alt 0x2C ; Float:g_Flooding[33]=0x0 (0.00000)
0x244 LOAD.S.pri 0xC ; id // Загружаем индекс id массива
// BOUNDS - rоличество элементов массива. В данном случае 32 (размерность 33).
0x24C BOUNDS 0x20
// LIDX - одна из инструкций, используемая для доступа к массиву.
0x254 LIDX
0x258 MOVE.alt
0x25C LOAD.S.pri 0xFFFFFFF8 ; Float:nexTime
0x264 PUSH.pri
0x268 PUSH.pri // Второй параметр
0x26C PUSH.alt // Первый параметр
0x270 PUSH.C 0x8 // Два параметра
0x278 CALL 0x8 ; stock bool:operator>(Float:,Float:)(Float:oper1,Float:oper2) // Вызываем оператор >
0x280 POP.alt
// JZER (Jump on ZERo) - так как мы имеем сверху вызов оператора >, то в данном случае это условие будет означать следующее, если g_Flooding[id] не больше nexTime, то переходим на метку jump_1.
0x284 JZER 0x3DC ; jump_1
0x28C BREAK ; antiflood.sma:60
Запишем как:
Код: Выделить всё
if (g_Flooding[id] > nexTime)
Код: Выделить всё
0x290 CONST.alt 0xB0 ; g_Flood[33]=0x0 (0.00000) // Помещаем в ALT значение g_Flood[id]
0x298 LOAD.S.pri 0xC ; id
0x2A0 BOUNDS 0x20
0x2A8 LIDX
0x2AC MOVE.alt
0x2B0 CONST.pri 0x3 ; 0x2E00 (11776.00000) // Помещаем в PRI значение 3
// JSGRTR - означает переход на метку jump_2, если PRI > ALT. То есть если g_Flood[id] меньше 3 - это true и значит переходим на метку jump_2.
0x2B8 JSGRTR 0x3A0 ; jump_2
0x2C0 BREAK ; antiflood.sma:62
Запишем как:
Код: Выделить всё
if (g_Flood[id] >= 3)
Код: Выделить всё
0x2C4 PUSH.C 0x2C8 ; "STOP_FLOOD" // Пятый аргумент
0x2CC PUSH.ADR 0xC ; id // Четвертый аргумент
0x2D4 PUSH.C 0x2A4 ; "** %L **" // Третий аргумент
0x2DC PUSH.C 0x1 ; 0x2E000000 // Второй аргумент
0x2E4 PUSH.S 0xC ; id // Первый аргумент
0x2EC PUSH.C 0x14 // Пять аргументов
0x2F4 SYSREQ.C 0x7 ; client_print(id, 1,"** %L **",id,"STOP_FLOOD") // Вызываем функцию client_print
0x2FC STACK 0x18 ; free 6 cells
0x304 BREAK ; antiflood.sma:63
Так как используется функция client_print, то мы знаем ее синтаксис:
Код: Выделить всё
client_print(index, type, const message[], ...)
Запишем как:
Код: Выделить всё
client_print(id, 1, "** %L **", id, "STOP_FLOOD")
Или:
Код: Выделить всё
client_print(id, print_notify, "** %L **", id, "STOP_FLOOD")
Потому что второй аргумент - это константа:
print_notify = 1
print_console = 2
print_chat = 3
print_center = 4
Код: Выделить всё
0x308 CONST.alt 0x2C ; Float:g_Flooding[33]=0x0 (0.00000)
0x310 LOAD.S.pri 0xC ; id
0x318 BOUNDS 0x20
0x320 IDXADDR
0x324 PUSH.pri
0x328 LOAD.S.pri 0xFFFFFFFC ; Float:maxChat // Первый параметр
0x330 LOAD.S.alt 0xFFFFFFF8 ; Float:nexTime // Второй параметр
0x338 PUSH.pri
0x33C PUSH.alt
0x340 PUSH.C 0x8 // Два параметра
// floatadd - это функция сложения двух float переменных. Аналогичное действие имеет сложение +.
0x348 SYSREQ.C 0x8 ; floatadd
0x350 STACK 0xC ; free 3 cells
0x358 CONST.alt 0x40400000 // Помещаем в ALT значение 3.0
0x360 PUSH.pri
0x364 PUSH.alt
0x368 PUSH.C 0x8 // Два параметра
0x370 SYSREQ.C 0x8 ; floatadd
0x378 STACK 0xC ; free 3 cells
0x380 POP.alt
0x384 STOR.I // Присвоение g_Flooding[id]
0x388 BREAK ; antiflood.sma:64
Так как используется функция floatadd, то мы знаем ее синтаксис:
Код: Выделить всё
floatadd(Float:oper1, Float:oper2)
Запишем как:
Код: Выделить всё
g_Flooding[id] = floatadd(floatadd(maxChat, nexTime), 3.0)
Код: Выделить всё
0x38C CONST.pri 0x1 ; 0x2E000000 (771751936.00000) // Означает return 1, что аналогично return PLUGIN_HANDLED
0x394 STACK 0x8 ; free 2 cells
0x39C RETN
0x3A0 BREAK
Запишем как:
Код: Выделить всё
return PLUGIN_HANDLED
Код: Выделить всё
// Метка jump_2
; target:jump_2
// Данный кусок кода обозначает, что это массив g_Flood размерностью 33 и индексом id
0x3A4 CONST.alt 0xB0 ; g_Flood[33]=0x0 (0.00000)
0x3AC LOAD.S.pri 0xC ; id
0x3B4 BOUNDS 0x20
0x3BC IDXADDR
0x3C0 PUSH.pri
0x3C4 LOAD.I
0x3C8 SWAP.pri
// INC - обозначает увеличение (increment), то есть операция ++
0x3CC INC.I
0x3D0 POP.pri
// JUMP - переход на метку. Но так как мы находимся уже внутри условия, поэтому это может означать, что есть еще else или else if при выполнении перехода на метку jump_1.
0x3D4 JUMP 0x438 ; jump_3
0x3DC BREAK ; antiflood.sma:68
Запишем как:
Код: Выделить всё
g_Flood[id]++
Код: Выделить всё
// Метка jump_1
; target:jump_1
// Данный кусок кода обозначает, что это массив g_Flood размерностью 33 и индексом id
0x3E0 CONST.alt 0xB0 ; g_Flood[33]=0x0 (0.00000)
0x3E8 LOAD.S.pri 0xC ; id
0x3F0 BOUNDS 0x20
0x3F8 LIDX
// Если g_Flood[id] равен 0 будет true, значит перейдем на метку jump_4
0x3FC JZER 0x438 ; jump_4
0x404 BREAK ; antiflood.sma:70
Запишем как (учитывая JUMP переход выше, следует, что у нас else if, так как в коде есть еще JZER условие):
Код: Выделить всё
else if (g_Flood[id])
Код: Выделить всё
// Данный кусок кода обозначает, что это массив g_Flood размерностью 33 и индексом id
0x408 CONST.alt 0xB0 ; g_Flood[33]=0x0 (0.00000)
0x410 LOAD.S.pri 0xC ; id
0x418 BOUNDS 0x20
0x420 IDXADDR
0x424 PUSH.pri
0x428 LOAD.I
0x42C SWAP.pri
// DEC - обозначает уменьшение (decrement), то есть операция --
0x430 DEC.I
0x434 POP.pri
0x438 BREAK ; antiflood.sma:73
Запишем как:
Код: Выделить всё
g_Flood[id]--
Код: Выделить всё
; target:jump_3
Метка 3 в нашем случае не содержит кода.
Код: Выделить всё
// Метка jump_4
; target:jump_4
// Данный кусок кода обозначает, что это массив g_Flooding размерностью 33 и индексом id
0x43C CONST.alt 0x2C ; Float:g_Flooding[33]=0x0 (0.00000)
0x444 LOAD.S.pri 0xC ; id
0x44C BOUNDS 0x20
0x454 IDXADDR
0x458 PUSH.pri
0x45C LOAD.S.pri 0xFFFFFFFC ; Float:maxChat // Помещаем maxChat в PRI
0x464 LOAD.S.alt 0xFFFFFFF8 ; Float:nexTime // Помещаем nexTime в ALT
0x46C PUSH.pri
0x470 PUSH.alt
0x474 PUSH.C 0x8 // Два параметра
0x47C SYSREQ.C 0x8 ; floatadd
0x484 STACK 0xC ; free 3 cells
0x48C POP.alt
0x490 STOR.I // Присвоение g_Floodind[id]
0x494 STACK 0x4 ; free 1 cells
0x49C BREAK ; antiflood.sma:76
Запишем как:
Код: Выделить всё
g_Flooding[id] = floatadd(maxChat, nexTime)
Код: Выделить всё
// Метка jump_0
; target:jump_0
0x4A0 ZERO.pri // Означает, что мы возвращаем 0, то есть return 0, а это аналогично return PLUGIN_CONTINUE
0x4A4 STACK 0x4 ; free 1 cells
0x4AC RETN
Запишем как:
Код: Выделить всё
return PLUGIN_CONTINUE
9. На основании полученных результатов формируем исходник (antiflood.sma).
Код: Выделить всё
#include <amxmodx>
new amx_flood_time
new g_Flood[33]
new Float:g_Flooding[33]
public plugin_init()
{
register_plugin("Anti Flood", AMXX_VERSION_STR, "AMXX Dev Team")
register_dictionary("antiflood.txt")
register_clcmd("say", "chkFlood")
register_clcmd("say_team", "chkFlood")
amx_flood_time = register_cvar("amx_flood_time", "0.75")
return PLUGIN_CONTINUE
}
public chkFlood(id)
{
new Float:maxChat = get_pcvar_float(amx_flood_time)
if (maxChat)
{
new Float:nexTime = get_gametime()
if (g_Flooding[id] > nexTime)
{
if (g_Flood[id] >= 3)
{
client_print(id, print_notify, "** %L **", id, "STOP_FLOOD")
g_Flooding[id] = floatadd(floatadd(maxChat, nexTime), 3.0)
return PLUGIN_HANDLED
}
g_Flood[id]++
}
else if (g_Flood[id])
g_Flood[id]--
g_Flooding[id] = floatadd(maxChat, nexTime)
}
return PLUGIN_CONTINUE
}
Дизассемблированный и оригинальный вариант могут отличаться по синтаксису и структуре, потому что получить точную копию оригинала очень сложно и порой невозможно, но обе версии плагина имеют одинаковый смысл и результат работы. Можно провести оптимизацию полученного исходника, например:
1. return PLUGIN_CONTINUE в plugin_init можно убрать, так как даже его не указывая будет аналогичный результат.
2. floatadd можно заменить оператором +, так как сложение float переменных нам даст точно такой же результат
Скачать Утилиту amxxdump - http://depositfiles.com/files/x3xezt8yi
Оригинальный исходник - http://depositfiles.com/files/bvu9wruia
Скомпилированный плагин, рассматриваемый в примере - http://depositfiles.com/files/c4rtqqql0
Автор: DJ_WEST