- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 600
- Реакции
- 16
Пытаюсь перенести логику своего трейнера из Cheat Engine в полноценный софт на VB.NET для CS2. В таблице CE все работает, адреса находит, ВХ пашет, но как только дело доходит до реализации внешнего записьщика (WPM), вылетает ошибка прав доступа.
Суть проблемы:
При попытке изменить права страницы памяти через VirtualProtectEx код выдает ошибку. Юзаю стандартный набор из kernel32.dll, но видимо где-то косячу с вычислением базового адреса или дескриптором процесса.
Сниппет кода, на котором спотыкаюсь:
Технические нюансы:
Грешу на то, что VirtualProtectEx не может прожевать нулевой размер или неверный адрес. Кто плотно сидит на дотнете и кодит под CS2 — гляньте, не слишком ли топорный метод для обхода защиты памяти в актуальном билде.
Интересно, имеет ли смысл сейчас вообще юзать VirtualProtectEx для обычного ВХ, если проще найти указатель на легитный регистр данных.
Суть проблемы:
При попытке изменить права страницы памяти через VirtualProtectEx код выдает ошибку. Юзаю стандартный набор из kernel32.dll, но видимо где-то косячу с вычислением базового адреса или дескриптором процесса.
Сниппет кода, на котором спотыкаюсь:
Код:
Imports System.Diagnostics
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Public Class Form1
<DllImport("kernel32.dll")>
Public Shared Function OpenProcess(dwDesiredAccess As Integer, bInheritHandle As Boolean, dwProcessId As Integer) As IntPtr
End Function
<DllImport("kernel32.dll")>
Public Shared Function ReadProcessMemory(hProcess As IntPtr, lpBaseAddress As IntPtr, ByVal lpBuffer As IntPtr, ByVal nSize As Integer, ByRef lpNumberOfBytesRead As Integer) As Boolean
End Function
<DllImport("kernel32.dll")>
Public Shared Function WriteProcessMemory(hProcess As IntPtr, lpBaseAddress As IntPtr, lpBuffer As IntPtr, nSize As Integer, ByRef lpNumberOfBytesWritten As Integer) As Boolean
End Function
<DllImport("kernel32.dll", SetLastError:=True)>
Public Shared Function VirtualProtectEx(ByVal hProcess As IntPtr, ByVal lpAddress As IntPtr, ByVal dwSize As UIntPtr, ByVal flNewProtect As UInt32, ByRef lpflOldProtect As UInt32) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Private Const PROCESS_ALL_ACCESS As Integer = &H1F0FFF
Private Const PAGE_EXECUTE_READWRITE As UInt32 = &H40
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim injector As New ExternalInjector()
' Инжекти ане на стойност на ад ес във фо мат "module.dll+offset"
injector.InjectMemory("cs2", "client.dll+8419A2", 2336788624)
' Инжекти ане на стойност на ад ес във фо мат "&Hoffset"
injector.InjectMemory("cs2", "&H8419A2", 2336788624)
End Sub
Private Class ExternalInjector
Public Sub InjectMemory(processName As String, addressString As String, newValue As Long)
Dim processes As Process() = Process.GetProcessesByName(processName)
If processes.Length > 0 Then
Dim process As Process = processes(0)
Dim processHandle As IntPtr = OpenProcess(PROCESS_ALL_ACCESS, False, process.Id)
If processHandle <> IntPtr.Zero Then
Dim address As IntPtr = GetModuleAddress(process, addressString)
If address <> IntPtr.Zero Then
Dim buffer As IntPtr = Marshal.AllocHGlobal(8)
Dim oldProtect As UInt32 = 0
If VirtualProtectEx(processHandle, address, UIntPtr.Zero, PAGE_EXECUTE_READWRITE, oldProtect) Then
Dim bytesWritten As Integer = 0
Marshal.WriteInt64(buffer, newValue)
If WriteProcessMemory(processHandle, address, buffer, 8, bytesWritten) Then
MessageBox.Show($"Новата стойност {newValue} е записана успешно на ад ес {address.ToString("X8")}", "Успешно инжекти ане", MessageBoxButtons.OK, MessageBoxIcon.Information)
Else
MessageBox.Show("Г ешка п и записване в паметта.", "Г ешка", MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
VirtualProtectEx(processHandle, address, UIntPtr.Zero, oldProtect, oldProtect)
Else
MessageBox.Show("Г ешка п и п омяна на п авата за достъп до паметта.", "Г ешка", MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
Marshal.FreeHGlobal(buffer)
Else
MessageBox.Show("Невалиден ад ес.", "Г ешка", MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
Else
MessageBox.Show("Не може да се отво и п оцесът.", "Г ешка", MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
Else
MessageBox.Show("П оцесът не е наме ен.", "Г ешка", MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
End Sub
Private Function GetModuleAddress(process As Process, addressString As String) As IntPtr
If Not addressString.Contains("+") Then
' Об аботка на ад еси във фо мат "&Hoffset"
Dim offsetValue As Integer = Convert.ToInt32(addressString.Substring(2), 16)
Return New IntPtr(offsetValue)
End If
Dim parts As String() = addressString.Split("+")
Dim moduleName As String = parts(0)
Dim moduleEntry As ProcessModule = process.Modules.Cast(Of ProcessModule)().FirstOrDefault(Function(m) m.ModuleName.EndsWith(moduleName, StringComparison.OrdinalIgnoreCase))
If moduleEntry Is Nothing Then
Return IntPtr.Zero
End If
Dim offsetFromModule As Integer
If parts.Length > 1 AndAlso parts(1).Contains(",") Then
Dim offsetParts As String() = parts(1).Split(",")
offsetFromModule = Convert.ToInt32(offsetParts(0), 16)
Else
offsetFromModule = Convert.ToInt32(parts(1), 16)
End If
Return moduleEntry.BaseAddress.Add(moduleEntry.BaseAddress, offsetFromModule)
End Function
End Class
End Class
Технические нюансы:
- Игра: CS2 (x64), соответственно работаем с 64-битными адресами.
- Античит: VAC Live. На внешние чекалки памяти он сейчас смотрит сквозь пальцы, но если криво дергать дескриптор, можно словить мануалбан.
- Оффсеты: Беру свежие дампы client.dll.
1. Проверь хендл процесса — OpenProcess должен открываться с PROCESS_ALL_ACCESS (&H1F0FFF), иначе VirtualProtectEx пошлет тебя лесом.
2. В CS2 многие адреса в client.dll статичны, но требуют корректного прибавления оффсета к BaseAddress модуля. В твоем исходнике был странный Add к самому себе.
3. Учти, что StringComparison.OrdinalIgnoreCase надежнее для поиска модулей в 64-битных процессах.
2. В CS2 многие адреса в client.dll статичны, но требуют корректного прибавления оффсета к BaseAddress модуля. В твоем исходнике был странный Add к самому себе.
3. Учти, что StringComparison.OrdinalIgnoreCase надежнее для поиска модулей в 64-битных процессах.
Грешу на то, что VirtualProtectEx не может прожевать нулевой размер или неверный адрес. Кто плотно сидит на дотнете и кодит под CS2 — гляньте, не слишком ли топорный метод для обхода защиты памяти в актуальном билде.
Интересно, имеет ли смысл сейчас вообще юзать VirtualProtectEx для обычного ВХ, если проще найти указатель на легитный регистр данных.