// Copyright (C) 2012 Zeex // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. #if defined AMX_HEADER_INC #endinput #endif #define AMX_HEADER_INC #tryinclude #include "amx_memory" #define AMX_HDR_BYTES (60) #define AMX_HDR_CELLS (AMX_HDR_BYTES / 4) #define AMX_TAG_MASK (0x80000000) #define AMX_TAG_STRONG (0x40000000) enum AMX_HDR { AMX_HDR_SIZE, AMX_HDR_MAGIC, AMX_HDR_FILE_VERSION, AMX_HDR_AMX_VERSION, AMX_HDR_FLAGS, AMX_HDR_DEFSIZE, AMX_HDR_COD, AMX_HDR_DAT, AMX_HDR_HEA, AMX_HDR_STP, AMX_HDR_CIP, AMX_HDR_PUBLICS, AMX_HDR_NATIVES, AMX_HDR_LIBRARIES, AMX_HDR_PUBVARS, AMX_HDR_TAGS, AMX_HDR_NAMETABLE } const AMX_HDR_OFFSET_SIZE = 0; const AMX_HDR_OFFSET_MAGIC = 4; const AMX_HDR_OFFSET_FILE_VERSION = 6; const AMX_HDR_OFFSET_AMX_VERSION = 7; const AMX_HDR_OFFSET_FLAGS = 8; const AMX_HDR_OFFSET_DEFSIZE = 10; const AMX_HDR_OFFSET_COD = 12; const AMX_HDR_OFFSET_DAT = 16; const AMX_HDR_OFFSET_HEA = 20; const AMX_HDR_OFFSET_STP = 24; const AMX_HDR_OFFSET_CIP = 28; const AMX_HDR_OFFSET_PUBLICS = 32; const AMX_HDR_OFFSET_NATIVES = 36; const AMX_HDR_OFFSET_LIBRARIES = 40; const AMX_HDR_OFFSET_PUBVARS = 44; const AMX_HDR_OFFSET_TAGS = 48; const AMX_HDR_OFFSET_NAMETABLE = 52; enum AMX_FUNCSTUBNT { AMX_FUNCSTUBNT_ADDRESS, AMX_FUNCSTUBNT_NAMEOFS } #define AMX_FUNCSTUBNT_ID AMX_FUNCSTUBNT_ADDRESS // Publics forward bool:GetPublicInfo(index, info[AMX_FUNCSTUBNT]); forward GetPublicIndexFromAddress(address); forward GetPublicIndexFromName(const name[]); forward GetPublicAddressFromName(const name[]); forward GetPublicAddressFromIndex(index); forward bool:GetPublicNameFromIndex(index, name[], size = sizeof(name)); forward bool:GetPublicNameFromAddress(address, name[], size = sizeof(name)); forward HookPublic(index, address); // Tags forward bool:GetTagInfo(index, info[AMX_FUNCSTUBNT]); forward GetTagIndexFromID(id); forward GetTagIndexFromName(const name[]); forward GetTagIDFromName(const name[]); forward GetTagIDFromIndex(index); forward bool:GetTagNameFromIndex(index, name[], size = sizeof(name)); forward bool:GetTagNameFromID(id, name[], size = sizeof(name)); // Natives forward bool:GetNativeInfo(index, info[AMX_FUNCSTUBNT]); forward GetNativeIndexFromAddress(address); forward GetNativeIndexFromName(const name[]); forward GetNativeAddressFromName(const name[]); forward GetNativeAddressFromIndex(index); forward bool:GetNativeNameFromIndex(index, name[], size = sizeof(name)); forward bool:GetNativeNameFromAddress(address, name[], size = sizeof(name)); forward HookNative(index, address); // Public Variables forward bool:GetPubVarInfo(index, info[AMX_FUNCSTUBNT]); forward GetPubVarIndexFromName(const name[]); forward GetPubVarIndexFromAddress(address); forward GetPubVarAddressFromName(const name[]); forward GetPubVarAddressFromIndex(index); forward bool:GetPubVarNameFromIndex(index, name[], size = sizeof(name)); forward bool:GetPubVarNameFromAddress(address, name[], size = sizeof(name)); // General forward GetRawAmxHeader(plain_amxhdr[AMX_HDR_CELLS]); forward GetAmxHeaderNow(amxhdr[AMX_HDR]); forward GetAmxHeader(amxhdr[AMX_HDR]); forward GetAmxHeaderComponent(AMX_HDR:comp); forward PrintAmxHeader(); stock GetRawAmxHeader(plain_amxhdr[AMX_HDR_CELLS]) { new address; #emit lctrl 1 // DAT #emit neg // -DAT #emit stor.s.pri address for (new i = 0; i < AMX_HDR_CELLS; i++) { plain_amxhdr[i] = ReadAmxMemory(address); address += 4; } } static stock copy_1(&dest, const source[], start) { #emit load.s.pri source #emit load.s.alt start #emit add #emit load.s.alt dest #emit movs 1 } static stock copy_2(&dest, const source[], start) { #emit load.s.pri source #emit load.s.alt start #emit add #emit load.s.alt dest #emit movs 2 } static stock copy_4(&dest, const source[], start) { #emit load.s.pri source #emit load.s.alt start #emit add #emit load.s.alt dest #emit movs 4 } stock GetAmxHeaderNow(amxhdr[AMX_HDR]) { new plain_amxhdr[AMX_HDR_CELLS]; GetRawAmxHeader(plain_amxhdr); copy_4(amxhdr[AMX_HDR_SIZE], plain_amxhdr, AMX_HDR_OFFSET_SIZE); copy_2(amxhdr[AMX_HDR_MAGIC], plain_amxhdr, AMX_HDR_OFFSET_MAGIC); copy_1(amxhdr[AMX_HDR_FILE_VERSION], plain_amxhdr, AMX_HDR_OFFSET_FILE_VERSION); copy_1(amxhdr[AMX_HDR_AMX_VERSION], plain_amxhdr, AMX_HDR_OFFSET_AMX_VERSION); copy_2(amxhdr[AMX_HDR_FLAGS], plain_amxhdr, AMX_HDR_OFFSET_FLAGS); copy_2(amxhdr[AMX_HDR_DEFSIZE], plain_amxhdr, AMX_HDR_OFFSET_DEFSIZE); copy_4(amxhdr[AMX_HDR_COD], plain_amxhdr, AMX_HDR_OFFSET_COD); copy_4(amxhdr[AMX_HDR_DAT], plain_amxhdr, AMX_HDR_OFFSET_DAT); copy_4(amxhdr[AMX_HDR_HEA], plain_amxhdr, AMX_HDR_OFFSET_HEA); copy_4(amxhdr[AMX_HDR_STP], plain_amxhdr, AMX_HDR_OFFSET_STP); copy_4(amxhdr[AMX_HDR_CIP], plain_amxhdr, AMX_HDR_OFFSET_CIP); copy_4(amxhdr[AMX_HDR_PUBLICS], plain_amxhdr, AMX_HDR_OFFSET_PUBLICS); copy_4(amxhdr[AMX_HDR_NATIVES], plain_amxhdr, AMX_HDR_OFFSET_NATIVES); copy_4(amxhdr[AMX_HDR_LIBRARIES], plain_amxhdr, AMX_HDR_OFFSET_LIBRARIES); copy_4(amxhdr[AMX_HDR_PUBVARS], plain_amxhdr, AMX_HDR_OFFSET_PUBVARS); copy_4(amxhdr[AMX_HDR_TAGS], plain_amxhdr, AMX_HDR_OFFSET_TAGS); copy_4(amxhdr[AMX_HDR_NAMETABLE], plain_amxhdr, AMX_HDR_OFFSET_NAMETABLE); } static gHdr[AMX_HDR]; static bool:gInitialized = false; stock ResetStaticAmxHeader() { GetAmxHeaderNow(gHdr); gInitialized = true; } #define InitStaticAmxHeader() \ if (!gInitialized) \ ResetStaticAmxHeader() stock GetAmxHeader(amxhdr[AMX_HDR]) { InitStaticAmxHeader(); amxhdr = gHdr; } stock GetAmxHeaderComponent(AMX_HDR:comp) { InitStaticAmxHeader(); return gHdr[comp]; } stock PrintAmxHeader() { InitStaticAmxHeader(); printf("------------------------"); printf("AMX Header:"); printf("------------------------"); printf("size %d", gHdr[AMX_HDR_SIZE]); printf("magic %x", gHdr[AMX_HDR_MAGIC]); printf("file_version %d", gHdr[AMX_HDR_FILE_VERSION]); printf("amx_version %d", gHdr[AMX_HDR_AMX_VERSION]); printf("flags %d", gHdr[AMX_HDR_FLAGS]); printf("defsize %d", gHdr[AMX_HDR_DEFSIZE]); printf("cod 0x%08x", gHdr[AMX_HDR_COD]); printf("dat 0x%08x", gHdr[AMX_HDR_DAT]); printf("hea 0x%08x", gHdr[AMX_HDR_HEA]); printf("stp 0x%08x", gHdr[AMX_HDR_STP]); printf("cip 0x%08x", gHdr[AMX_HDR_CIP]); printf("publics 0x%08x", gHdr[AMX_HDR_PUBLICS]); printf("natives 0x%08x", gHdr[AMX_HDR_NATIVES]); printf("libraries 0x%08x", gHdr[AMX_HDR_LIBRARIES]); printf("pubvars 0x%08x", gHdr[AMX_HDR_PUBVARS]); printf("tags 0x%08x", gHdr[AMX_HDR_TAGS]); printf("nametable 0x%08x", gHdr[AMX_HDR_NAMETABLE]); printf("------------------------"); } // Compares a string stored in the name table starting at "s1" (packed) // with another string "s2" (unpacked). static stock NtCompare(s1, const s2[]) { new index; new c1, c2; new diff; do { c1 = ReadAmxMemory(s1++) & 0xFF; c2 = s2[index++]; diff = c1 - c2; if (diff != 0) { break; } } while (!(c1 & c2 == 0)); return diff; } // Copies a name from the name table to a string. Returns the number // of characters copied. static stock NtCopy(src, dest[], size = sizeof(dest)) { new i = 0; new c; do { c = ReadAmxMemory(src++) & 0xFF; dest[i++] = c; } while (c != '\0' && i <= size); dest[i] = '\0'; // terminator return i; } stock GetNumPublics(amxhdr[AMX_HDR]) { new num_publics = (amxhdr[AMX_HDR_NATIVES] - amxhdr[AMX_HDR_PUBLICS]) / amxhdr[AMX_HDR_DEFSIZE]; return num_publics; } stock GetPublicIndexFromAddress(address) { InitStaticAmxHeader(); new num_publics = GetNumPublics(gHdr); new off = gHdr[AMX_HDR_PUBLICS] - gHdr[AMX_HDR_DAT]; // Linear search for (new i = 0; i < num_publics; i++) { if (ReadAmxMemory(off) == address) { return i; } off += gHdr[AMX_HDR_DEFSIZE]; } return -1; } stock bool:GetPublicInfo(index, info[AMX_FUNCSTUBNT]) { InitStaticAmxHeader(); new num_publics = GetNumPublics(gHdr); if (index < 0 || index >= num_publics) { return false; } new off = gHdr[AMX_HDR_PUBLICS] - gHdr[AMX_HDR_DAT] + index * gHdr[AMX_HDR_DEFSIZE]; info[AMX_FUNCSTUBNT_ADDRESS] = ReadAmxMemory(off); info[AMX_FUNCSTUBNT_NAMEOFS] = ReadAmxMemory(off + 4); return true; } stock GetPublicIndexFromName(const name[]) { InitStaticAmxHeader(); new num_publics = GetNumPublics(gHdr); // Binary search new first = 0; new last = num_publics - 1; new mid; while (first <= last) { mid = (first + last) / 2; new off = gHdr[AMX_HDR_PUBLICS] - gHdr[AMX_HDR_DAT] + mid * gHdr[AMX_HDR_DEFSIZE]; new nameofs = ReadAmxMemory(off + 4) - gHdr[AMX_HDR_DAT]; new diff = NtCompare(nameofs, name); if (diff < 0) { first = mid + 1; } else if (diff > 0) { last = mid - 1; } else { return mid; } } return -1; } stock GetPublicAddressFromIndex(index) { new info[AMX_FUNCSTUBNT]; GetPublicInfo(index, info); return info[AMX_FUNCSTUBNT_ADDRESS]; } stock GetPublicAddressFromName(const name[]) { return GetPublicAddressFromIndex(GetPublicIndexFromName(name)); } stock bool:GetPublicNameFromIndex(index, name[], size = sizeof(name)) { new info[AMX_FUNCSTUBNT]; if (!GetPublicInfo(index, info)) { return false; } return (NtCopy(info[AMX_FUNCSTUBNT_NAMEOFS] - GetAmxHeaderComponent(AMX_HDR_DAT), name, size) > 0); } stock bool:GetPublicNameFromAddress(address, name[], size = sizeof(name)) { return GetPublicNameFromIndex(GetPublicIndexFromAddress(address), name, size); } stock HookPublic(index, address) { InitStaticAmxHeader(); new num_publics = GetNumPublics(gHdr); if (index < 0 || index >= num_publics) { return 0; } new off = gHdr[AMX_HDR_PUBLICS] - gHdr[AMX_HDR_DAT] + index * gHdr[AMX_HDR_DEFSIZE]; new old_address = ReadAmxMemory(off); WriteAmxMemory(off, address); return old_address; } stock GetNumNatives(amxhdr[AMX_HDR]) { return (amxhdr[AMX_HDR_LIBRARIES] - amxhdr[AMX_HDR_NATIVES]) / amxhdr[AMX_HDR_DEFSIZE]; } stock GetNativeIndexFromAddress(address) { InitStaticAmxHeader(); new num_natives = GetNumNatives(gHdr); new off = gHdr[AMX_HDR_NATIVES] - gHdr[AMX_HDR_DAT]; // Linear search for (new i = 0; i < num_natives; i++) { if (ReadAmxMemory(off) == address) { return i; } off += gHdr[AMX_HDR_DEFSIZE]; } return -1; } stock bool:GetNativeInfo(index, info[AMX_FUNCSTUBNT]) { InitStaticAmxHeader(); // For when "index" is not valid info[AMX_FUNCSTUBNT_ADDRESS] = -1; new num_natives = GetNumNatives(gHdr); if (index < 0 || index >= num_natives) { return false; } new off = gHdr[AMX_HDR_NATIVES] - gHdr[AMX_HDR_DAT] + index * gHdr[AMX_HDR_DEFSIZE]; info[AMX_FUNCSTUBNT_ADDRESS] = ReadAmxMemory(off); info[AMX_FUNCSTUBNT_NAMEOFS] = ReadAmxMemory(off + 4); return true; } stock GetNativeIndexFromName(const name[]) { InitStaticAmxHeader(); new num_natives = GetNumNatives(gHdr); new off = gHdr[AMX_HDR_NATIVES] - gHdr[AMX_HDR_DAT]; // Linear search for (new i = 0; i < num_natives; i++) { new nameofs = ReadAmxMemory(off + 4) - gHdr[AMX_HDR_DAT]; new diff = NtCompare(nameofs, name); if (diff == 0) { return i; } off += gHdr[AMX_HDR_DEFSIZE]; } return -1; } stock GetNativeAddressFromIndex(index) { new info[AMX_FUNCSTUBNT]; GetNativeInfo(index, info); return info[AMX_FUNCSTUBNT_ADDRESS]; } stock GetNativeAddressFromName(const name[]) { return GetNativeAddressFromIndex(GetNativeIndexFromName(name)); } stock bool:GetNativeNameFromIndex(index, name[], size = sizeof(name)) { new info[AMX_FUNCSTUBNT]; if (!GetNativeInfo(index, info)) { return false; } return (NtCopy(info[AMX_FUNCSTUBNT_NAMEOFS] - GetAmxHeaderComponent(AMX_HDR_DAT), name, size) > 0); } stock bool:GetNativeNameFromAddress(address, name[], size = sizeof(name)) { return GetNativeNameFromIndex(GetNativeIndexFromAddress(address), name, size); } stock HookNative(index, address) { InitStaticAmxHeader(); new num_natives = GetNumNatives(gHdr); if (index < 0 || index >= num_natives) { return 0; } new off = gHdr[AMX_HDR_NATIVES] - gHdr[AMX_HDR_DAT] + index * gHdr[AMX_HDR_DEFSIZE]; new old_address = ReadAmxMemory(off); WriteAmxMemory(off, address); return old_address; } stock GetNumPubVars(amxhdr[AMX_HDR]) { return (amxhdr[AMX_HDR_TAGS] - amxhdr[AMX_HDR_PUBVARS]) / amxhdr[AMX_HDR_DEFSIZE]; } stock bool:GetPubVarInfo(index, info[AMX_FUNCSTUBNT]) { InitStaticAmxHeader(); new num_pubvars = GetNumPubVars(gHdr); if (index < 0 || index >= num_pubvars) { return false; } new off = gHdr[AMX_HDR_PUBVARS] - gHdr[AMX_HDR_DAT] + index * gHdr[AMX_HDR_DEFSIZE]; info[AMX_FUNCSTUBNT_ADDRESS] = ReadAmxMemory(off); info[AMX_FUNCSTUBNT_NAMEOFS] = ReadAmxMemory(off + 4); return true; } stock GetPubVarIndexFromName(const name[]) { InitStaticAmxHeader(); new num_pubvars = GetNumPubVars(gHdr); // Binary search new first = 0; new last = num_pubvars - 1; new mid; while (first <= last) { mid = (first + last) / 2; new off = gHdr[AMX_HDR_PUBVARS] - gHdr[AMX_HDR_DAT] + mid * gHdr[AMX_HDR_DEFSIZE]; new nameofs = ReadAmxMemory(off + 4) - gHdr[AMX_HDR_DAT]; new diff = NtCompare(nameofs, name); if (diff < 0) { first = mid + 1; } else if (diff > 0) { last = mid - 1; } else { return mid; } } return -1; } stock GetPubVarIndexFromAddress(address) { InitStaticAmxHeader(); new num_pubvars = GetNumPubVars(gHdr); new off = gHdr[AMX_HDR_PUBVARS] - gHdr[AMX_HDR_DAT]; // Linear search for (new i = 0; i < num_pubvars; i++) { if (ReadAmxMemory(off) == address) { return i; } off += gHdr[AMX_HDR_DEFSIZE]; } return -1; } stock GetPubVarAddressFromIndex(index) { new info[AMX_FUNCSTUBNT]; GetPubVarInfo(index, info); return info[AMX_FUNCSTUBNT_ADDRESS]; } stock GetPubVarAddressFromName(const name[]) { return GetPubVarAddressFromIndex(GetPubVarIndexFromName(name)); } stock bool:GetPubVarNameFromIndex(index, name[], size = sizeof(name)) { new info[AMX_FUNCSTUBNT]; if (!GetPubVarInfo(index, info)) { return false; } return (NtCopy(info[AMX_FUNCSTUBNT_NAMEOFS] - GetAmxHeaderComponent(AMX_HDR_DAT), name, size) > 0); } stock bool:GetPubVarNameFromAddress(address, name[], size = sizeof(name)) { return GetPubVarNameFromIndex(GetPubVarIndexFromAddress(address), name, size); } static stock GetNumTagsInternal(amxhdr[AMX_HDR]) { return (amxhdr[AMX_HDR_NAMETABLE] - amxhdr[AMX_HDR_TAGS]) / amxhdr[AMX_HDR_DEFSIZE]; } stock GetNumTags(amxhdr[AMX_HDR]) { return GetNumTagsInternal(amxhdr) + 1; } stock bool:GetTagInfo(index, info[AMX_FUNCSTUBNT]) { if (index == 0) { info[AMX_FUNCSTUBNT_ID] = AMX_TAG_MASK; info[AMX_FUNCSTUBNT_NAMEOFS] = 0; return true; } InitStaticAmxHeader(); new num_tags = GetNumTagsInternal(gHdr); if (index < 0 || index >= num_tags) { return false; } new off = gHdr[AMX_HDR_TAGS] - gHdr[AMX_HDR_DAT] + index * gHdr[AMX_HDR_DEFSIZE]; info[AMX_FUNCSTUBNT_ID] = ReadAmxMemory(off) | AMX_TAG_MASK; info[AMX_FUNCSTUBNT_NAMEOFS] = ReadAmxMemory(off + 4); return true; } stock GetTagIndexFromName(const name[]) { if (name[0] == '_' && name[1] == '\0') return 0; InitStaticAmxHeader(); new num_tags = GetNumTagsInternal(gHdr); // Binary search new first = 0; new last = num_tags - 1; new mid; while (first <= last) { mid = (first + last) / 2; new off = gHdr[AMX_HDR_TAGS] - gHdr[AMX_HDR_DAT] + mid * gHdr[AMX_HDR_DEFSIZE]; new nameofs = ReadAmxMemory(off + 4) - gHdr[AMX_HDR_DAT]; new diff = NtCompare(nameofs, name); if (diff < 0) { first = mid + 1; } else if (diff > 0) { last = mid - 1; } else { return mid; } } return -1; } stock GetTagIndexFromID(id) { id &= ~AMX_TAG_MASK; if (id == 0) return 0; InitStaticAmxHeader(); new num_tags = GetNumTagsInternal(gHdr); new off = gHdr[AMX_HDR_TAGS] - gHdr[AMX_HDR_DAT]; // Linear search for (new i = 0; i < num_tags; i++) { if (ReadAmxMemory(off) == id) { return i; } off += gHdr[AMX_HDR_DEFSIZE]; } return -1; } stock GetTagIDFromIndex(index) { if (index == 0) return AMX_TAG_MASK; new info[AMX_FUNCSTUBNT]; GetTagInfo(index, info); return info[AMX_FUNCSTUBNT_ID]; } stock GetTagIDFromName(const name[]) { if (name[0] == '_' && name[1] == '\0') return AMX_TAG_MASK; return GetTagIDFromIndex(GetTagIndexFromName(name)); } stock bool:GetTagNameFromIndex(index, name[], size = sizeof(name)) { if (index == 0) { name[0] = '_'; name[1] = '\0'; return true; } new info[AMX_FUNCSTUBNT]; if (!GetTagInfo(index, info)) { return false; } return (NtCopy(info[AMX_FUNCSTUBNT_NAMEOFS] - GetAmxHeaderComponent(AMX_HDR_DAT), name, size) > 0); } stock bool:GetTagNameFromID(id, name[], size = sizeof(name)) { if (id == 0) { name[0] = '_'; name[1] = '\0'; return true; } return GetTagNameFromIndex(GetTagIndexFromID(id), name, size); } stock bool:IsTagIDStrong(tag) { return !!(tag & AMX_TAG_STRONG); } stock bool:IsTagIDWeak(tag) { return !(tag & AMX_TAG_STRONG); } stock bool:IsTagIDEmpty(tag) { return !(tag & ~AMX_TAG_MASK); } stock bool:IsTagNameStrong(tag[]) { return ('A' <= tag[0] <= 'Z'); } stock bool:IsTagNameWeak(tag[]) { return !('A' <= tag[0] <= 'Z'); } stock bool:IsTagNameEmpty(tag[]) { return (tag[0] == '_' && tag[1] == '\0'); } stock bool:IsTagIndexStrong(tag) { new info[AMX_FUNCSTUBNT]; return (GetTagInfo(tag, info) && IsTagIDStrong(info[AMX_FUNCSTUBNT_ID])); } stock bool:IsTagIndexWeak(tag) { new info[AMX_FUNCSTUBNT]; return (GetTagInfo(tag, info) && IsTagIDWeak(info[AMX_FUNCSTUBNT_ID])); } stock bool:IsTagIndexEmpty(tag) { return (tag == 0); } #undef InitStaticAmxHeader