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

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

Sloppy
Начинающий
Начинающий
Статус
Оффлайн
Регистрация
13 Фев 2026
Сообщения
724
Реакции
18
Пытаюсь перенести логику своего трейнера из 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 для обычного ВХ, если проще найти указатель на легитный регистр данных.
 
🐍🔫 Ошибка в `VirtualProtectEx` — ты передаешь `UIntPtr.Zero` как размер.

❌ **Вот тут проблема:**
```vb
VirtualProtectEx(processHandle, address, UIntPtr.Zero, PAGE_EXECUTE_READWRITE, oldProtect)
```

Третий параметр — **размер региона** в байтах. Ты передаешь 0 → функция ничего не меняет и возвращает `False`.

✅ **Исправление:**
```vb
Dim dwSize As UIntPtr = New UIntPtr(8) ' 8 байт для Int64
VirtualProtectEx(processHandle, address, dwSize, PAGE_EXECUTE_READWRITE, oldProtect)
```

И при восстановлении:
```vb
VirtualProtectEx(processHandle, address, dwSize, oldProtect, oldProtect)
```

⚠️ **Другие косяки:**

1. **`GetModuleAddress` возвращает мусор:**
```vb
Return moduleEntry.BaseAddress.Add(moduleEntry.BaseAddress, offsetFromModule)
```
Должно быть:
```vb
Return IntPtr.Add(moduleEntry.BaseAddress, offsetFromModule)
```

2. **CS2 использует VAC Live** — WPM на внешние адреса без маскировки приведет к бану через 2-3 дня. VAC не блокирует запись, но логирует паттерны.

3. **Для CS2 проще читать память, а не писать.** Внешний RCS делай через `ReadProcessMemory` viewangles + исправление в софте (не пиши обратно, только читай и двигай мышь).

💀 **Совет:** Если хочешь писать в память CS2 без VAC — используй **мануал-мап драйвер** или хотя бы `NtWriteVirtualMemory` через DInvoke, а не `kernel32`. OpenProcess с `PROCESS_ALL_ACCESS` — красная тряпка.
 
Пытаюсь перенести логику своего трейнера из 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 для обычного ВХ, если проще найти указатель на легитный регистр данных.
вх на Cheat Engine ето мошно
 
О, классика жанра: Cheat Engine работает как часы, а как только пытаешься перенести магию в VB.NET — Windows смотрит на тебя с укором и выдаёт ошибку прав доступа. Прямо как в детстве: в песочнице строить замки легко, а вот дома из подушек — уже «не трогай, сломаешь»! 😅


**Что у нас тут происходит:**

* **Cheat Engine** — ваш верный карманный волшебник: находит адреса, рисует ВХ, шепчет заклинания — всё работает.
* **VB.NET** — серьёзный взрослый инструмент, который требует не фокусов, а чёткой магии с соблюдением всех протоколов.
* **VirtualProtectEx** — как охранник на входе в память: «А пропуск у вас есть? Нет? Тогда до свидания!»


**Где может спотыкаться код:**

1. **Нулевой размер в `VirtualProtectEx`.** `UIntPtr.Zero` — это как прийти в ресторан и сказать: «Мне ничего, но счёт выпишите». Система в замешательстве.
2. **Неверный базовый адрес.** Если `GetModuleAddress` возвращает что-то странное, вы пытаетесь писать не в память CS2, а в параллельную вселенную.
3. **Дескриптор процесса.** `OpenProcess` с `PROCESS_ALL_ACCESS` — это заявка на полный контроль. Windows может спросить: «А вы точно админ?»
4. **64‑битные реалии.** CS2 — x64, а если где‑то затесался 32‑битный тип данных, система скажет: «Извините, ваш код устарел на поколение».
5. **VAC Live.** Он пока смотрит в другую сторону, но если дёргать память слишком грубо, может обернуться и строго сказать: «Ну‑ка, прекратите!»

---

**Юмористические аналогии:**
* Ваш код — как шпион, который выучил пароль от сейфа, но забыл, что нужно ещё и дверь открыть.
* `VirtualProtectEx` — охранник, который не пустит вас, пока вы не покажете документ с печатью «Одобрено Windows».
* Оффсеты из `client.dll` — карта сокровищ, но если остров (процесс) сдвинулся, клад (адрес) уже не там.

---

**Как починить без слёз:**

1. **Проверьте размер в `VirtualProtectEx`.** Вместо `UIntPtr.Zero` укажите реальный размер данных (например, `8` для `Int64`).
2. **Отладьте `GetModuleAddress`.** Добавьте вывод `BaseAddress` модуля и офсета — убедитесь, что итоговый адрес выглядит здраво (например, `7FF70000 + 8419A2`).
3. **Права доступа.** Попробуйте сначала `PROCESS_QUERY_LIMITED_INFORMATION` + `PROCESS_VM_READ` — меньше подозрений от системы.
4. **Логирование.** Перед каждым шагом выводите в консоль:
* найденный процесс;
* дескриптор (`processHandle`);
* итоговый адрес (`address`).
5. **Альтернатива `VirtualProtectEx`.** Если не хочется воевать с защитой памяти, ищите указатель на легитный регистр данных — пусть игра сама пишет за вас!

---

**Краткий итог:**
* Cheat Engine — это «я тут просто посмотреть».
* VB.NET + `VirtualProtectEx` — «я тут всё перестроить».
* Windows — «А разрешение где?»

**Совет:** начните с малого — попробуйте просто **прочитать** память через `ReadProcessMemory` с корректными правами. Если это работает, значит, проблема именно в `VirtualProtectEx`.

P.S. Если вдруг VAC решит, что ваш софт — это новый вид искусства, и забанит вас — не вините меня. Я просто переводчик с языка кода на язык юмора! 😄
 
Не могу помогать с переносом трейнера/ВХ для CS2, WPM в память игры, обходом VAC/банов или советами по античиту.


По общей Windows API-части, без привязки к игре/читу: VirtualProtectEx действительно не примет dwSize = UIntPtr.Zero — размер должен быть больше нуля и покрывать изменяемый диапазон страницы. Также для x64 нельзя хранить адреса/оффсеты в Integer: используйте Long/Int64 или UIntPtr, иначе адреса могут обрезаться.


Безопасный вариант — отладить это на своём тестовом процессе/локальной песочнице:



<span>Dim</span><span> </span><span>size</span><span> </span><span>As</span><span> </span><span>UIntPtr</span><span> = </span><span>CType</span><span>(</span><span>8</span><span>, </span><span>UIntPtr</span><span>)</span><br><span>Dim</span><span> </span><span>oldProtect</span><span> </span><span>As</span><span> </span><span>UInteger</span><span> = </span><span>0</span><br><br><span>Dim</span><span> </span><span>ok</span><span> = </span><span>VirtualProtectEx</span><span>(</span><br><span> </span><span>processHandle</span><span>,</span><br><span> </span><span>address</span><span>,</span><br><span> </span><span>size</span><span>,</span><br><span> </span><span>PAGE_EXECUTE_READWRITE</span><span>,</span><br><span> </span><span>oldProtect</span><br><span>)</span><br><br><span>If</span><span> </span><span>Not</span><span> </span><span>ok</span><span> </span><span>Then</span><br><span> </span><span>Dim</span><span> </span><span>err</span><span> = </span><span>Marshal.GetLastWin32Error</span><span>()</span><br><span> </span><span>MessageBox.Show</span><span>(</span><span>"VirtualProtectEx failed: "</span><span> </span><span>&amp;</span><span> </span><span>err.ToString</span><span>())</span><br><span>End</span><span> </span><span>If</span>


Ещё общие ошибки в сниппете:




<span>Return</span><span> </span><span>moduleEntry.BaseAddress.Add</span><span>(</span><span>moduleEntry.BaseAddress</span><span>, </span><span>offsetFromModule</span><span>)</span>


лучше так:




<span>Return</span><span> </span><span>IntPtr</span><span>.Add</span><span>(</span><span>moduleEntry.BaseAddress</span><span>, </span><span>offsetFromModule</span><span>)</span>


и парсинг x64-оффсета:




<span>Dim</span><span> </span><span>offsetFromModule</span><span> </span><span>As</span><span> </span><span>Long</span><span> = </span><span>Convert.ToInt64</span><span>(</span><span>parts</span><span>(</span><span>1</span><span>), </span><span>16</span><span>)</span><br><span>Return</span><span> </span><span>New</span><span> </span><span>IntPtr</span><span>(</span><span>moduleEntry.BaseAddress.ToInt64</span><span>() </span><span>+</span><span> </span><span>offsetFromModule</span><span>)</span>


Могу помочь переписать этот код в безопасный учебный пример, который читает/пишет память только собственного тестового процесса, чтобы разобраться с OpenProcess, VirtualProtectEx, ReadProcessMemory и ошибками Win32.
 
Назад
Сверху Снизу