Ребят.
А вот все вокруг вроде говорят о каких-то там защитах и прочее на серверах.
Но по факту у меня получилось сделать прикольную вещь.
То что сейчас я расскажу. это лично моё ИМХО и лично мой костыль, я не разработчик настольного ПО и в дизассемблировании ничего не смыслю по большому счету.
Вариант брать в таргет какого нибудь моба сразу нужного по ID:
Суть: при нажатии TAB я отдебажил кусочек ассемблерного кода, где можно видеть как в регистр esi попадает айдишник ближайшего моба (1), а потом этот айдишник помещается в ячейку памяти которая показывает кто сейчас выбран в таргете (2) - и судя по всему потом происходят какие то внутренние процессы и моб оказывается в таргете.
Снимок экрана 2023-03-05 221245.jpg
Так вот, вся проблема заключается в том, что из за защит (наверное) мы не можем вызвать внутреннюю функцию клиента выбора моба (во всяком случае я в этом не шарю) - но мне пришла в голову мысль - а что если вызывать функцию с помощью эмуляции нажатия кнопки ТАБ - и вместо ID ближайшего моба, заинжектить ID нужного нам моба.
Так вот, для этого нам просто нужно изменить строчку 1 и вместо [ebp+08] - передать туда ID нужного нам моба.
например через CheatEngine вставим число туда 2148549012 в шестнадцатеричном представлении (0x80104194)
2.jpg
Теперь любое нажатие ТАБ или просто клик мышкой по ЛЮБОМУ мобу будет приводить к взятию в таргет именно указанного (2148549012 ) моба.
Теперь всё это можно упаковать в C# код (я делал с помощью ChatGTP, потому что я очень плохо шарю за вот такое низкоуровневое программирование)
Код:
// Код из ChatGPT
// Это объявление метода OpenProcess, который используется для получения дескриптора процесса
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
// Это объявления констант, которые используются для работы с процессами и памятью процессов
const uint PROCESS_VM_OPERATION = 0x0008;
const uint PROCESS_VM_READ = 0x0010;
const uint PROCESS_VM_WRITE = 0x0020;
const uint PAGE_EXECUTE_READWRITE = 0x40;
// Это объявление метода VirtualProtectEx, который используется для изменения прав доступа к памяти процесса:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, uint flNewProtect, out uint lpflOldProtect);
// Это объявление метода WriteProcessMemory, который используется для записи данных в память процесса
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out int lpNumberOfBytesWritten);
// Это объявление метода CloseHandle, который используется для закрытия дескриптора процесса
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);
public static void ChangeAssemblyCommand(uint address, byte[] asmBytes)
{
// Получаем дескриптор процесса
IntPtr processHandle = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, Reader.process.Id);
if (processHandle == IntPtr.Zero)
{
throw new Exception($"Failed to open process {Reader.process.Id}. Error code: {Marshal.GetLastWin32Error()}");
}
// Получаем указатель на указанный адрес памяти в процессе
IntPtr remoteAddress = new IntPtr(address);
// Изменяем права доступа к памяти на чтение и запись
if (!VirtualProtectEx(processHandle, remoteAddress, 7, PAGE_EXECUTE_READWRITE, out uint oldProtect))
{
throw new Exception($"Failed to change memory protection. Error code: {Marshal.GetLastWin32Error()}");
}
// Копируем команды ассемблера в память процесса
if (!WriteProcessMemory(processHandle, remoteAddress, asmBytes, asmBytes.Length, out _))
{
throw new Exception($"Failed to write process memory. Error code: {Marshal.GetLastWin32Error()}");
}
// Возвращаем права доступа к памяти в исходное состояние
if (!VirtualProtectEx(processHandle, remoteAddress, 7, oldProtect, out _))
{
throw new Exception($"Failed to restore memory protection. Error code: {Marshal.GetLastWin32Error()}");
}
// Закрываем дескриптор процесса
CloseHandle(processHandle);
}
Код:
public static bool ChangeGetTargetAssembly(uint value)
{
try
{
if (value == 0)
{
throw new ArgumentException();
}
byte[] bytes = BitConverter.GetBytes(value);
byte[] result = new byte[7];
result[0] = 0xBE;
if (bytes.Length < 4)
{
Array.Copy(bytes, 0, result, 1, bytes.Length);
for (int i = bytes.Length + 1; i < 7; i++)
{
result[i] = 0x90;
}
}
else
{
Array.Copy(bytes, 0, result, 1, 4);
}
ChangeAssemblyCommand(Offset.Get.SET_TARGET_FUNC_OFFSET, result);
return true;
}
catch (Exception ex)
{
ChangeAssemblyCommand(Offset.Get.SET_TARGET_FUNC_OFFSET, Offset.Get.ORIG_BYTES_FUNC_OFFSET);
return false;
}
}
Функция ChangeGetTargetAssembly это уже мой самопал, там подготавливается набор байтов, которые будут вставлены взамен старых, и при этом всего должно быть 7 байт. Если ID переданный в эту функцию будет маленький (например 123) - то его байтовое представление будет из меньшего количества байт, и оставшиеся должны заполнится пустотой 0x90 (почему именно 7 - хз, но вроде опытным путем выяснил)
В общем этой функции я подготавливаю массив байт и заменяю оригинал на новые))
И все, дальше когда моему софту требуется выделить кого-то в таргет - я тупо подставляю в память нужный мне айди - эмулирую нажатие клавиши ТАБ, берется нужны таргет, и я возвращаю оригинальные байты в код)))
Вдруг вам поможет разобраться с чем нибудь, для меня было открытием что это вот так оказывается просто инжекить свой код в чужой процесс))))