vb 2008 WriteProcessMemory() возвращает 0

#.net #windows #vb.net #winapi #process

#.net #Windows #vb.net #winapi #процесс

Вопрос:

здравствуйте, я использую vb 2008 вот часть моего кода:

 Private Declare Function WriteProcessMemory Lib "kernel32" Alias "WriteProcessMemory" ( _
      ByVal hProcess As Integer, _
      ByVal lpBaseAddress As Integer, _
      ByVal lpBuffer As Integer, _
      ByVal nSize As Integer, _
      ByVal lpNumberOfBytesWritten As Integer _
   ) As Integer

    Dim proc() As Process = Process.GetProcessesByName("process")
    If proc.Length = 0 Then Console.WriteLine("Process Not Found") : Exit Sub
    Dim p = proc(0)
    Dim pH As Integer = OpenProcess(PROCESS_ALL_ACCESS, 0, p.Id)
    Dim address As Integer
    address = amp;H98544
    Dim memory As IntPtr
    Call ReadProcessMemory(pH, address, memory, 4, 0)
    Console.WriteLine(memory.ToString)
    Dim data As Integer = 2000
    Call WriteProcessMemory(pH, address, data, Len(data), 0amp;)
  

Но WriteProcessMemory() возвращает 0, и значение не меняется

РЕШЕНИЕ:

 <DllImport("kernel32", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function WriteProcessMemory( _
                                      ByVal hProcess As IntPtr, _
                                      ByVal lpBaseAddress As IntPtr, _
                                      ByVal lpBuffer As Byte(), _
                                      ByVal nSize As UInt32, _
                                      ByRef lpNumberOfBytesWritten As UInt32 _
                                      ) As Boolean
End Function

        Dim proc() As Process = Process.GetProcessesByName("process")
        If proc.Length = 0 Then Console.WriteLine("Process Not Found") : Exit Sub
        Dim p = proc(0)
        Dim pH As Integer = OpenProcess(PROCESS_ALL_ACCESS, 0, p.Id)
        Dim address As Integer
        address = amp;H98544
        Dim memory As IntPtr
        Call ReadProcessMemory(pH, address, memory, 4, 0)
        Console.WriteLine(memory.ToString)
        Dim data() As Byte = BitConverter.GetBytes(2000)
        Call WriteProcessMemory(pH, address, data, 4, 0amp;)
  

Комментарии:

1. Заранее извините — по сути, это не ответ, а, возможно, скорее подсказка. Я сам начинаю работать с этой функцией и подозреваю, что проблема, с которой мы сталкиваемся, связана с привилегиями токена.

Ответ №1:

Когда вы вызываете функции Windows API, вы должны самостоятельно реализовать код проверки ошибок. Они не будут генерировать исключения, когда что-то пойдет не так, как стандартные функции .NET Framework.

Конкретные требуемые проверки на ошибки варьируются в зависимости от функции. (Подобная несогласованность именно поэтому.Вместо этого NET использует исключения.) Проверка документации необходима.

WriteProcessMemory работает довольно распространенным способом. Возвращаемым значением является BOOL (целое число, которое библиотеки Win32 обрабатывают как логическое значение, где 0 = FALSE и 1 = TRUE). Если функция завершается с ошибкой, возвращаемое значение равно 0 (FALSE). В случае успеха возвращаемое значение равно 1 (TRUE):

Если функция выполнена успешно, возвращаемое значение ненулевое.

Если функция завершается с ошибкой, возвращаемое значение равно 0 (нулю). Чтобы получить расширенную информацию об ошибке, вызовите GetLastError . Функция завершается с ошибкой, если запрошенная операция записи переходит в область процесса, которая недоступна.

Интересная часть там, конечно, заключается в том, что нам говорят, что мы должны вызвать GetLastError функцию, чтобы определить, почему произошел сбой функции. Отчасти это правда. Вы вызываете GetLastError при написании неуправляемого кода на C . В .NET вам нужно сделать что-то еще. Есть два шага:

  1. Пометьте подпись P / Invoke с помощью SetLastError = True , которая заставляет среду CLR автоматически вызывать GetLastError и сохранять ее значение.
  2. Вызовите Marshal.GetLastWin32Error , чтобы получить это значение.

Итак, для его отладки вам нужно будет переписать свой код следующим образом (используя стандартный синтаксис P / Invoke, а не более ограниченное Declare ключевое слово, уникальное для VB.NET ):

 <DllImport("kernel32", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Shared Function WriteProcessMemory(
                                           ByVal hProcess As Integer, _
                                           ByVal lpBaseAddress As Integer, _
                                           ByVal lpBuffer As Integer, _
                                           ByVal nSize As Integer, _
                                           ByVal lpNumberOfBytesWritten As Integer _
                                          ) As Integer
End Function

Dim proc() As Process = Process.GetProcessesByName("process")
If proc.Length = 0 Then Console.WriteLine("Process Not Found") : Exit Sub
Dim p = proc(0)
Dim pH As Integer = OpenProcess(PROCESS_ALL_ACCESS, 0, p.Id)
Dim address As Integer
address = amp;H98544
Dim memory As IntPtr
Call ReadProcessMemory(pH, address, memory, 4, 0)
Console.WriteLine(memory.ToString)
Dim data As Integer = 2000
If WriteProcessMemory(pH, address, data, Len(data), 0amp;) = 0 Then
   ' The function failed, so find out what the error was
   MessageBox.Show("WriteProcessMemory failed with error " amp; Marshal.GetLastWin32Error)
End If
  

Как только вы узнаете код ошибки, вы можете посмотреть его в списке системных кодов ошибок, чтобы увидеть, что это значит.


Оказывается, ошибка имеет номер 299, ERROR_PARTIAL_COPY , который, как говорится в документации, просто означает:

Была выполнена только часть запроса ReadProcessMemory или WriteProcessMemory.

Хм, не очень полезно… Я надеялся, что вы получите что-нибудь получше. C’est la vie.

Для решения этой проблемы вам потребуется немного дополнительных знаний. В документации, опять же, дается подсказка. Каждый раз, когда он говорит, что параметр является «указателем» (обозначается в синтаксисе C звездочкой * ), это означает, что в VB.NET оно должно быть передано как ссылка, а не как значение. Чтобы указать, что что-то передается в качестве ссылки, используйте ByRef ключевое слово. Для передачи по значению, конечно, вы указываете ключевое слово по умолчанию ByVal .

Кроме того, обратите внимание, что когда в документации говорится, что что-то является дескриптором или адресом, вы должны объявить это в VB.NET как IntPtr тип, а не как целое число. IntPtr является особенным, поскольку его размер изменяется в зависимости от того, является ли базовая операционная система 32-разрядной или 64-разрядной (в отличие от целого числа, которое имеет фиксированный размер, независимо от того, на какой платформе вы работаете). Даже если это не вызывает вашей непосредственной проблемы, это вызовет проблемы с совместимостью при компиляции кода для 64-разрядной версии.

Таким образом, оказывается, что ваше объявление на самом деле неверно. Повторно объявите это следующим образом:

 <DllImport("kernel32", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Shared Function WriteProcessMemory(
                                           ByVal hProcess As IntPtr, _
                                           ByVal lpBaseAddress As IntPtr, _
                                           ByVal lpBuffer As Integer, _
                                           ByVal nSize As Integer, _
                                           ByRef lpNumberOfBytesWritten As Integer _
                                          ) As Integer
End Function
  

Вы могли бы также учесть, что третьим параметром ( lpBuffer ) должен быть массив байтов ( Byte() ), поскольку он определяет данные, подлежащие записи в адресное пространство.

Комментарии:

1. он показывает ошибку «299» (системные коды ошибок (0-499)), но все еще не может понять, в чем проблема.

2. @John: 299 является ERROR_PARTIAL_COPY : Была выполнена только часть запроса ReadProcessMemory или WriteProcessMemory. Оказывается, это не очень полезно. Я думал, это подскажет вам кое-что получше. Позвольте мне отредактировать мой ответ с исправлением.

3. я обновил свой вопрос решением, спасибо за вашу помощь

Ответ №2:

В документации говорится:

Если функция завершается с ошибкой, возвращаемое значение равно 0 (нулю). Чтобы получить расширенную информацию об ошибке, вызовите GetLastError. Функция завершается с ошибкой, если запрошенная операция записи переходит в область процесса, которая недоступна.

Я рекомендую вам сделать то, что он говорит, чтобы выяснить, почему это не удалось.

Учитывая это, представляется вероятным, что причиной сбоя является то, что адрес 2000, на который вы пытаетесь выполнить запись, не является допустимым адресом в этом процессе.

lpBuffer Параметр является указателем, и в качестве этого параметра передается целочисленное значение 2000. Следовательно, это интерпретируется и является адресом памяти. Я подозреваю, что вы на самом деле намеревались передать адрес data .