@Rostu, я отправил пул запрос на GitHub с протестированным кодом, а здесь оставлю подробное описание.
Что добавлено:
1) GameDataEx.inc - методмап для работы с GameData конфигами.
(финальная версия с исправлением всех упомянутых багов, работает через SMC Parser, базовый тип - всё так же KeyValues).
- умеет подгружать GameData конфиги с кастомными названиями модулей.
- допускается формат Hex для смещений (оффсетов).
Pointer GetAddress(char[] name) // возвращает адрес функции (при этом, секция "Address" в GameData не требуется).
int GetOffset(char[] name) // возвращает оффсет из секции "Offsets" по его имени.
2) MemSearcher.inc
bool IsValidAddress(Address addr, int &numbytes) // отвечает, доступен ли адрес для чтения (определяет это через перечисление всех регионов памяти и проверки прав доступа)
// как вариант, можно было бы переименовать в ! IsBadReadPtr
Потом сможете воспользоваться этими же функциями для реализации самого Mem Searcher, или что там задумывалось.
3) BinaryFile.inc - методмап для работы с бинарными файлами (используется далее в LinuxFunction)
Предоставляет методы с похожими названиями как и при работе с памятью (в идеале, для LinuxFunction.inc переделать под BinaryStream, чтобы не читать постоянно с диска, а замапить сразу весь файл на память).
enum FILE_ACCESS
{
FILE_READ = 1,
FILE_WRITE = 2,
FILE_APPEND = 4,
FILE_OVERWRITE = 8
}
new BinaryFile(char[] path, FILE_ACCESS access) // открытие файла в бинарном режиме с указанным флагом (-ами) доступа
int LoadFromAddress(Address base, NumberType size) // прочитать число (1, 2 или 4 байта) по указаному адресу (из файла)
Address LoadFromAddressEx(Address base, NumberType size) // тоже самое, результат возвращается с типом Address
int StoreToAddress(Address base, int data, NumberType size) // записать число (1, 2 или 4 байта) по указаному адресу (в файл). (не тестировалось)
int ReadString(Address base, char[] sResult, int iMaxLength) // прочитать ANSI строку по адресу (функция - копия вашего BaseMemory.inc)
int ReadUnicodeString(Address base, char[] sResult, int iMaxLength) // прочитать Unicode строку по адресу (функция - копия вашего BaseMemory.inc)
int WriteString(Address base, const char[] sString, bool bNull = true) // записать строку по адресу (не тестировалось)
4) Новые методы для Stocks.inc
int LoadFromAddressInt24(Address base) // прочитать 3 байта из памяти по адресу
void StoreToAddressArray(Address base, int[] data, int iSize) // записать в память по адресу содержимое массива
int GetByte(int iNum32, int iByteIndex) // извлечь байт из Dword (int) по номеру байта (справа налево). Счёт от единицы.
void ArrayPushDword(int[] array, int &index, int data) // расщепить dword на байты и записать в байтовый массив, начиная с указанного индекса; index возвращается инкрементированным на 4 (удобно для подготовки пэйлоада с динамическими адресами (для джампов и т.п.)
Изменения:
1) Повтыкал блокировки повторных инклудов
2) Модифицированы LinuxParseMapsFile / WindowsGetDllList. Новый опциональный аргумент bAddressAsKey.
/*
bAddressAsKey -> true
StringMap
key == базовый адрес модуля
value == полный путь к модулю
bAddressAsKey -> false - старое поведение
StringMap
key == имя модуля
value == структура LibraryInfo { base; size }
*/
Получить новую форму StringMap можно через:
stock StringMap GetListModulePath()
3) Дописан GetProcAddressLinux.
Сразу говорю, что разбором ELF занимаюсь впервые, так что могут быть ошибки/неточности. Распишу то, как я понял.
Дело в том, что в формате эльфа есть 2 строковые таблицы с символами для экспортируемых функций:
- .dynsym
- .symtab
При этом в режиме исполнения в памяти хранится только первая (.dynsym).
Несмотря на то, что вы извлекаете из динамического сегмента указатель на динамическую секцию, имеющую тип DT_SYMTAB - пусть вас это название не заводит в заблуждение, указатель ведёт именно на .dynsym, которая содержит самый минимальный набор функций. В то же время, .symtab в памяти нет вообще и его можно прочитать только из файла нужного модуля.
У меня в коде из файла читается таблица секций, выбираются типы SHT_STRTAB и SHT_SYMTAB, который собственно и указывает на .symtab.
Дальше таблица читается аналогично, как у вас, и в итоге к целевому адресу функции в файле добавляется адрес базового модуля в памяти, чтобы получить адрес функции в памяти.
Поиск по имени функции не ведётся сразу. Вместо этого читается вся таблица целиком в StringMap, и затем 2 и сл. разы адрес забирается уже из кеша. И да, в угоду простоты, кеш получился смешанным, т.е. сюда сбрасываются имена функций от скана любых других имён модулей, если таковые буду запрошены.
Из минусов, работа с файлом ведётся напрямую (без маппинга). Чтения всей .symtab таблицы (а например, на L4D2 это 56000 символов) у меня занимает почти секунду (на SSD). В перспективе лучше переделать, прочитав файл в память целиком.
4) MemoryAlloc.inc - VirtualAlloc/FreeMemory под Win временно переделал под получение адреса из таблицы импорта srcds.exe, чтобы пока хотя бы так работало.