Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Вопрос CS2 Перенос оффсетов Cheat Engine в VB.NET — Error VirtualProtectEx

Sloppy
Начинающий
Начинающий
Статус
Оффлайн
Регистрация
13 Фев 2026
Сообщения
600
Реакции
16
Пытаюсь перенести логику своего трейнера из Cheat Engine в полноценный софт на VB.NET для CS2. В таблице CE все работает, адреса находит, ВХ пашет, но как только дело доходит до реализации внешнего записьщика (WPM), вылетает ошибка прав доступа.

Суть проблемы:
При попытке изменить права страницы памяти через VirtualProtectEx код выдает ошибку. Юзаю стандартный набор из kernel32.dll, но видимо где-то косячу с вычислением базового адреса или дескриптором процесса.

Сниппет кода, на котором спотыкаюсь:
Код:
Expand Collapse Copy
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

Технические нюансы:
  1. Игра: CS2 (x64), соответственно работаем с 64-битными адресами.
  2. Античит: VAC Live. На внешние чекалки памяти он сейчас смотрит сквозь пальцы, но если криво дергать дескриптор, можно словить мануалбан.
  3. Оффсеты: Беру свежие дампы client.dll.

1. Проверь хендл процесса — OpenProcess должен открываться с PROCESS_ALL_ACCESS (&H1F0FFF), иначе VirtualProtectEx пошлет тебя лесом.
2. В CS2 многие адреса в client.dll статичны, но требуют корректного прибавления оффсета к BaseAddress модуля. В твоем исходнике был странный Add к самому себе.
3. Учти, что StringComparison.OrdinalIgnoreCase надежнее для поиска модулей в 64-битных процессах.

Грешу на то, что VirtualProtectEx не может прожевать нулевой размер или неверный адрес. Кто плотно сидит на дотнете и кодит под CS2 — гляньте, не слишком ли топорный метод для обхода защиты памяти в актуальном билде.

Интересно, имеет ли смысл сейчас вообще юзать VirtualProtectEx для обычного ВХ, если проще найти указатель на легитный регистр данных.
 
Назад
Сверху Снизу