Автор: joaquimandrade
Модуль Orpheu дает скриптеру возможность использовать функции, которые было бы невозможно использовать без него (включая функции от других плагинов MetaMod).Модуль работает, как на Windows, так и на Linux.
Начиная с версии 2.1 ( 29 января 2010 г.), Orpheu также поддерживает прямой доступ к памяти.
Суть работы заключается в следующем:
Модуль использует преобразования между типами С++ и Pawn, и позволяет вызывать функции, указав ее адрес в памяти, а также используемые ею аргументы. Этот метод включает в себя общие принципы работы со структурами С++.
Например, предположим, что вы хотите использовать эту функцию:
void PM_Move( struct playermove_s *ppmove, qboolean server)
Она получает один аргумент типа “playermove_s” и другой типа “qboolean”. Значения эта функция не возвращает.
Пока типы, необходимые функции, используются в модуле, вы можете использовать ее. Если один из типов не используется, то Вы можете подключить его напрямую, и все равно продолжать работать с функцией, к примеру подключать ее, так как при подключении функции Вам не надо заботиться обо всех ее аргументах.
Существует несколько способов поиска функций в библиотеках:
- путем поиска по имени: в библиотеках, компилированных в Linux, символьные имена функций закреплены за самими функциями. К несчастью, этого не происходит, если библиотека скомпилирована для Windows, что делает этот способ бесполезным, если у Вас нет исходника библиотеки и Вы не можете скомпилировать его самостоятельно так, чтобы он передавал символьные имена для функций. В этой статье описаны методы для использования функций модуля MonsterMod.
- путем нахождения набора байтов в памяти, относящихся конкретно к данной функции. Данный метод называется «сканированием сигнатур» (“signature scanning”). В общем об этом методе написано здесь, более подробно здесь и здесь. В общем, этот метод позволяет обращаться к любой функции в памяти, как только вы найдёте ее. Чтобы толком разобраться в этом, вам необходимо узнать немного больше об этом методе, что вы можете сделать по ссылкам выше. (Прим. перевод.: На нашем сайте эти статьи в скором времени будут переведены, как только мы это сделаем, заменим ссылки)
- путем получения адресов во время использования плагина. Если вы можете найти адрес функции программно, то вы можете использовать этот вариант. Это возможно, благодаря использованием модулем основных функций движка. Эти функции предусмотрены в AMXModX и без этого модуля, однако при его использовании, вы можете перехватить эти адреса в большинстве случаев (например, когда функции вызываются другими плагинами MetaMod).
Добавлено в 2.1
- путем поиска оффсетов функций в библиотеке. Этот метод используется для быстрого тестирования и на него не следует полагаться, так как найденные оффсеты могут оказаться неправильными при обновлении или изменение библиотеки.
- путем нахождения указателя функции в виртуальной таблице функций класса. Этот способ позволяет использовать множество функций, путем обнаружения всего лишь указателя. Метод был взят из Hamsandwich и переделан для использования в Orpheu. Он дает возможность захвата виртуальной функции энтити и объекта, например, CGameRules. В отличии от версии Hamsandwich добавлено следующее: теперь набор кодированных напрямую функций не ограничен, а также Вы можете захватывать некоторые зависящие от мода функции, которые не могли быть захвачены ранее.
Чтобы передать модулю информацию о функции, вы должны создать файл, отформатированный в соответствие стандарту JSON и поместить в папку “configs/orpheu/functions”.
Для функции:
CMBaseMonster* spawn_monster(int monster_type, Vector& origin, float angle, int respawn_index)
Содержимое файла будет таким:
{ "name" : "spawn_monster", "library" : "monstermod", "info" : "Spawns a monstermod monster in the world", "arguments" : [ { "type" : "int", "info" : "The type of the monster" }, { "type" : "Vector &", "info" : "The position where the monster will be placed at" }, { "type" : "float", "info" : "Angle of the monster" }, { "type" : "int", "info" : "Respawn index. Related to the order of a monster respawning if failed to spawn at the first try" } ], "return" : { "type" : "CMBaseMonster *", "info" : "The monster created" }, "identifiers": [ { "os" : "windows", "value" : "?spawn_monster@@YAPAVCMBaseMonster@@HAAVVector@@MH@Z" }, { "os" : "linux", "value" : "_Z13spawn_monsteriR6Vectorfi" } ] }
- поля “info” не обязательны;
- поле “name” должно совпадать с именем фала;
- имя библиотеки “mod” для модов, как cstrike, и “engine” для dll движка. Для библиотек MetaMod вам необходимо создать файл, как тот, что Вы можете найти в директории “configs\orpheu\libraries”, что содержит пару libraryname/libraryCvar, таким образом модуль распознается, как один из модулей Cvar.
- “identidiers” – это список групп “os”/”value” для идентификации функции. В случае, если библиотека – “mod”, необходимо добавить дополнительное поле “mod”. Пусть смысл этого поля исключительно формален, однако само поле обязательно. Оно должно выглядеть так:
"mod" : "cstrike"
В этом случае для ссылки на функцию используется метод определения имени. В случае использования сигнатур, поле “value” будет представлено последовательностью байтов, “*” или “?”, например:
"value" : [0x1,"*","?"]
“*” необходимо использовать в том случае, если значение этих байтов не важно.
То есть значение
"value" : [0x1,"*"]
равносильно
[0x1,0x0] , [0x1,0x1] , ... [0x1,0xFF]
.
“?” необходимо использовать, если не важно не только значение этих байтов, но и само их существование.
То есть
"value" : [0x1,"?"]
предполагает значения
[0x1] , [0x1,0x0] , [0x1,0x1] , ... [0x1,0xFF]
.
JSON – это широко распространенный тип файлов. Чтобы убедиться в том, что файл корректно отформатирован, вы можете использовать валидатор. В этом проекте я немного изменил библиотеку для того, чтобы JSON мог прочитать байты. Это делается для проверки файлов, не содержащих сигнатуры.
Отдельно, стоит упомянуть о функциях, принадлежащих к классу, например:
void CMController :: HandleAnimEvent( MonsterEvent_t *pEvent )
В данном случае необходимо создать папку под именем “CMController”, а в ней файл “HandleAnimEvent”. Это обязательно и необходимо для более понятной организации. Вам также необходимо добавить в файл поле “class”. В итоге файл будет выглядеть так:
{ "name" : "HandleAnimEvent", "class" : "CMController", "library" : "monstermod", "arguments" : [ { "type" : "MonsterEvent_t *" } ], "identifiers": [ { "os" : "windows", "value" : "?HandleAnimEvent@CMController@@UAEXPAUMonsterEvent_t@@@Z" }, { "os" : "linux", "value" : "_ZN12CMController15HandleAnimEventEP14MonsterEvent_t" } ] }
Другой метод использования функций имеет дело с получением адреса программно. Взгляните на http://metamod.org/sdk/dox/eiface_8h-source.html – строка 100. Здесь представлена структура “enginefuncs_s”. Эта структура содержит адреса функций движка. Модуль имеет возможность оперировать ей. Скажем, что мы хватим захватить функцию расположенную в структуре:
void (*pfnServerPrint)( const char *szMsg );
Необходимо создать такой файл:
{ "name" : "ServerPrint", "library" : "engine", "arguments" : [ { "type" : "char *" } ] }
Идентификаторы не нужны, так как адрес будет найден во время запуска, программно. В плагине появится:
native OrpheuFunction:OrpheuCreateFunction(address,libFunctionName[],classname[]="")
Затем вы можете захватить или вызвать ее. При захвате функции, Вы можете перехватить сообщение консоли такое же, что появляется при использовании серверной команды “metamod”.
Это один из вариантов (кодированный вручную) метода, использующего функцию, адрес, который есть в плагине. Другой вариант – использовать OrpheuGetDLLFunction для восстановления функций из структуры DLL_FUNCTIONS. http://metamod.org/sdk/dox/eiface_8h-source.html – строка 384. Для обычных случаев используйте native:
Модуль позволяет вам управлять структурами обычным путем. Скажем, что нам необходимо захватить функцию PM_Move, которая может быть найдена в структуре DLL_FUNCTIONS. Файл:
{ "name" : "PM_Move", "library" : "mod", "arguments" : [ { "type" : "playermove_s *" }, { "type" : "qboolean" } ] }
Захват:
public plugin_init() { new OrpheuFunction:PM_Move = OrpheuGetDLLFunction("pfnPM_Move","PM_Move") OrpheuRegisterHook(PM_Move,"OnPM_Move") } public OnPM_Move(ppmove,server) { }
Первый аргумент функции – структура. Вы можете увидеть это здесь. http://metamod.org/sdk/dox/eiface_8h-source.html – строка 92. Далее имеем дело с данными структур:
public OnPM_Move(ppmove,server) { new id = OrpheuGetParamStructMember(1,"player_index") + 1 new Float:friction = OrpheuGetParamStructMember(1,"friction") new sztexturename[20] OrpheuGetParamStructMember(1,"sztexturename",sztexturename,charsmax(sztexturename)) server_print("ID %d friction %f texturename %s",id,friction,sztexturename) }
Большую информацию по работе со структурами смотрите по ссылкам. Те же структуры содержат адреса для других функций. Ниже описан пример как захватить одну из них:
{ "name" : "PM_PlaySound", "library" : "engine", "arguments" : [ { "type" : "int" }, { "type" : "char *" }, { "type" : "float" }, { "type" : "float" }, { "type" : "int" }, { "type" : "int" } ] }
new OrpheuHook:PM_PlaySoundHook public plugin_init() { new OrpheuFunction:PM_Move = OrpheuGetDLLFunction("pfnPM_Move","PM_Move") OrpheuRegisterHook(PM_Move,"OnPM_Move") OrpheuRegisterHook(PM_Move,"OnPM_MovePost",OrpheuHookPost) } public OnPM_Move(ppmove,server) { // Retrieves the address of the function to hook new PM_PlaySoundAddress = OrpheuGetParamStructMember(1,"PM_PlaySound") // Creates the function in the module new OrpheuFunction:PM_PlaySound = OrpheuCreateFunction(PM_PlaySoundAddress,"PM_PlaySound") // Hooks it PM_PlaySoundHook = OrpheuRegisterHook(PM_PlaySound,"OnPM_PlaySoundHook") } public OnPM_PlaySoundHook(channel,sample[],Float:volume,Float:attenuation,fFlags,pitch) { server_print("Sample %s",sample) } public OnPM_MovePost(ppmove,server) { OrpheuUnregisterHook(PM_PlaySoundHook) }
Список поддерживаемых типов данных:
"bool" "byte" "long" "CBaseEntity *" "char *" "edict_s *" "float" "Vector *" "CMBaseMonster *" "char" "short" "entvars_s *"
Список поддерживаемых структур:
"movevars_s *" "usercmd_s *" "MonsterEvent_t *" "DLL_FUNCTIONS *" "playermove_s *" "enginefuncs_t *" "TraceResult *" "physent_s *" "pmplane_s *" "pmtrace_s *" "weapon_data_s *" "AmmoInfo *" "ItemInfo *" "Task_t *" "Schedule_t *"
Я добавлю несколько практических примеров позже. Если модуль не запускается на вашем устройстве Linux, то вам следует установить libstdc++.
Следующий материал действителен для Orpheu 2.1 и выше.
В версии 2.1 была добавлена поддержка виртуальных функций (основано на Hamsandwich) и поиска/исправления памяти (основано на Mem Hack).
Виртуальные функции:
Виртуальные функции – это функции, предоставляющие совместные ресурсы для нескольких различных классов (как Spawn) и, соответственно, по другому осуществляющие это предоставление. Способ, которым функции компилируются, позволяет размещать их в памяти, путем обеспечения их простыми числовыми оффсетами. Ссылки на функции хранятся в таблице так, что каждый объект класса может взаимодействовать с ними.
Эта таблица для мода Counter Strike, восстановленная из собственного Linux-файла класса CHalfLifeMultiplay для CGameRules. (Каждая строка содержит символьное имя представляемой функции):
Think__18CHalfLifeMultiplay IsAllowedToSpawn__18CHalfLifeMultiplayP11CBaseEntity FAllowFlashlight__18CHalfLifeMultiplay FShouldSwitchWeapon__18CHalfLifeMultiplayP11CBasePlayerP15CBasePlayerItem GetNextBestWeapon__18CHalfLifeMultiplayP11CBasePlayerP15CBasePlayerItem IsMultiplayer__18CHalfLifeMultiplay IsDeathmatch__18CHalfLifeMultiplay IsTeamplay__10CGameRules IsCoOp__18CHalfLifeMultiplay GetGameDescription__10CGameRules ClientConnected__18CHalfLifeMultiplayP7edict_sPCcT2Pc InitHUD__18CHalfLifeMultiplayP11CBasePlayer ClientDisconnected__18CHalfLifeMultiplayP7edict_s UpdateGameMode__18CHalfLifeMultiplayP11CBasePlayer FlPlayerFallDamage__18CHalfLifeMultiplayP11CBasePlayer FPlayerCanTakeDamage__18CHalfLifeMultiplayP11CBasePlayerP11CBaseEntity ShouldAutoAim__10CGameRulesP11CBasePlayerP7edict_s PlayerSpawn__18CHalfLifeMultiplayP11CBasePlayer PlayerThink__18CHalfLifeMultiplayP11CBasePlayer FPlayerCanRespawn__18CHalfLifeMultiplayP11CBasePlayer FlPlayerSpawnTime__18CHalfLifeMultiplayP11CBasePlayer GetPlayerSpawnSpot__18CHalfLifeMultiplayP11CBasePlayer AllowAutoTargetCrosshair__18CHalfLifeMultiplay ClientCommand_DeadOrAlive__18CHalfLifeMultiplayP11CBasePlayerPCc ClientCommand__18CHalfLifeMultiplayP11CBasePlayerPCc ClientUserInfoChanged__18CHalfLifeMultiplayP11CBasePlayerPc IPointsForKill__18CHalfLifeMultiplayP11CBasePlayerT1 PlayerKilled__18CHalfLifeMultiplayP11CBasePlayerP9entvars_sT2 DeathNotice__18CHalfLifeMultiplayP11CBasePlayerP9entvars_sT2 CanHavePlayerItem__18CHalfLifeMultiplayP11CBasePlayerP15CBasePlayerItem PlayerGotWeapon__18CHalfLifeMultiplayP11CBasePlayerP15CBasePlayerItem WeaponShouldRespawn__18CHalfLifeMultiplayP15CBasePlayerItem FlWeaponRespawnTime__18CHalfLifeMultiplayP15CBasePlayerItem FlWeaponTryRespawn__18CHalfLifeMultiplayP15CBasePlayerItem VecWeaponRespawnSpot__18CHalfLifeMultiplayP15CBasePlayerItem CanHaveItem__18CHalfLifeMultiplayP11CBasePlayerP5CItem PlayerGotItem__18CHalfLifeMultiplayP11CBasePlayerP5CItem ItemShouldRespawn__18CHalfLifeMultiplayP5CItem FlItemRespawnTime__18CHalfLifeMultiplayP5CItem VecItemRespawnSpot__18CHalfLifeMultiplayP5CItem CanHaveAmmo__10CGameRulesP11CBasePlayerPCci PlayerGotAmmo__18CHalfLifeMultiplayP11CBasePlayerPci AmmoShouldRespawn__18CHalfLifeMultiplayP15CBasePlayerAmmo FlAmmoRespawnTime__18CHalfLifeMultiplayP15CBasePlayerAmmo VecAmmoRespawnSpot__18CHalfLifeMultiplayP15CBasePlayerAmmo FlHealthChargerRechargeTime__18CHalfLifeMultiplay FlHEVChargerRechargeTime__18CHalfLifeMultiplay DeadPlayerWeapons__18CHalfLifeMultiplayP11CBasePlayer DeadPlayerAmmo__18CHalfLifeMultiplayP11CBasePlayer GetTeamID__18CHalfLifeMultiplayP11CBaseEntity PlayerRelationship__18CHalfLifeMultiplayP11CBasePlayerP11CBaseEntity GetTeamIndex__10CGameRulesPCc GetIndexedTeamName__10CGameRulesi IsValidTeam__10CGameRulesPCc ChangePlayerTeam__10CGameRulesP11CBasePlayerPCcii SetDefaultPlayerTeam__10CGameRulesP11CBasePlayer PlayTextureSounds__18CHalfLifeMultiplay FAllowMonsters__18CHalfLifeMultiplay EndMultiplayerGame__18CHalfLifeMultiplay IsFreezePeriod__10CGameRules ServerDeactivate__18CHalfLifeMultiplay CheckMapConditions__18CHalfLifeMultiplay CleanUpMap__18CHalfLifeMultiplay RestartRound__18CHalfLifeMultiplay CheckWinConditions__18CHalfLifeMultiplay RemoveGuns__18CHalfLifeMultiplay GiveC4__18CHalfLifeMultiplay ChangeLevel__18CHalfLifeMultiplay GoToIntermission__18CHalfLifeMultiplay
Чтобы использовать одну из них вам необходимо создать файл, который должен выглядеть так:
{ "name" : "GetNextBestWeapon", "class" : "CGameRules", "library" : "mod", "arguments" : [ { "type" : "CBasePlayer *" }, { "type" : "CBasePlayerItem *" } ], "return" : { "type" : "bool" }, "indexes" : [ { "os" : "windows", "mod" : "cstrike", "value" : 5 }, { "os" : "linux", "mod" : "cstrike", "value" : 6 } ] }
(В Linux виртуальная таблица включает дополнительную функцию в начале, так что оффсеты должны быть увеличены на единицу)
Это описание функции должно быть размещено в каталоге “VirtuаlFunctions” в папке “CGameRules” в файле “GetNextBestWeapon”.
Теперь вы можете использовать эти функции в плагине несколькими командами, выглядящими так:
OrpheuGetFunctionFromClass(entityClassName[],libFunctionName[],libClassName[]) OrpheuGetFunctionFromEntity(id,libFunctionName[],libClassName[]) OrpheuGetFunctionFromObject(object,libFunctionName[],libClassName[]) OrpheuGetFunctionFromMonster(id,libFunctionName[],libClassName[])
Первые две работают точно также как и в Hamsandwich:
- Первая вызывает функцию, основанную на классе энтити.
- Вторая вызывает функцию, основанную на самом энтити.
Для использования функций из CHalfLifeMultiplay, описанных выше, нам необходим нативная функция OrpheuGetFunctionFromObject. Эта функция вызывает функцию, базированную на объекте, класс которого вам необходим, таким образом нам необходим дополнительный шаг для получения объекта. Это один из путей решения для этого частного случая:
{ "name" : "InstallGameRules", "library" : "mod", "return" : { "type" : "CHalfLifeMultiplay *" }, "identifiers": [ { "os" : "windows", "mod" : "cstrike", "value" : [0x68,0x8c,0xea,"*",0xa,0xff,0x15,0xdc,0x23,"*",0xa,0x83,0xc4,0x4,0xff,0x15,0xe0,0x23,"*",0xa,0xa1,0xb8,0x25] }, { "os" : "linux", "mod" : "cstrike", "value" : "InstallGameRules__Fv" } ] }
public plugin_precache() { new OrpheuFunction:InstallGameRules = OrpheuGetFunction("InstallGameRules") OrpheuRegisterHook(InstallGameRules,"OnInstallGameRules",OrpheuHookPost) } public OnInstallGameRules() { new obj = OrpheuGetReturn() new OrpheuFunction:GetNextBestWeapon = OrpheuGetFunctionFromObject(obj,"GetNextBestWeapon","CGameRules") OrpheuRegisterHook(GetNextBestWeapon,"OnGetNextBestWeapon") } public OnGetNextBestWeapon(obj,id,weaponID) { // }
Управление памятью
Модуль теперь имеет возможность поиска и установления значения напрямую в любую позицию в памяти, где хранится библиотека. Например, таким образом можно поменять стоимость оружия, заменить строки, чтобы изменить текст появляющийся в консоли. Это может быть сделано, используя оффсеты для ссылки к памяти, которую вы хотите изменить, зная их месторасположение или сигнатуры. Как и для функций, необходимо создать файл. Этот файл содержит одно или несколько описаний ячейки памяти, к которой необходимо обратиться, или ее месторасположения.
Пример изменения стоимости AWP:
[ { "name" : "awpCost", "library" : "mod", "type" : "long", "memoryType" : "data", "identifiers" : [ { "os" : "windows", "mod" : "cstrike", "value" : [0x8E,0x12,0x00,0x00,0x7D,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x07] } ] } ]
OrpheuMemorySet(“awpCost”,1,1000)
Это всего лишь пример для “windows” и “cstrike”, однако сам модуль может быть использован и для Windows, и для Linux, а также для любого мода. Большую информацию читайте по ссылкам ниже.
Примечание: если необходимо заменить строки, напрямую размещенные в памяти (таких большинство), вам следует использовать тип “string” вместо “char *”.
Ссылки.
http://jsoncpp.sourceforge.net/
http://www.boost.org/
http://forums.alliedmods.net/showpost.php?p=994447&postcount=2
Перевод: Dani Minch
PS статья переводилась около 3-4 часов подряд и потом почти не изменялась, могут быть загоны) если что пишите в комментариях
Статьи по теме:
Orpheu: Поиск функций в библиотеках
Сигнатуры функций.
Еще у нас есть:
Тот парень. Что-то пишу, когда не лень.
Sorry, the comment form is closed at this time.