3

ReadProcessMemoryこのP/Invoke署名を介してC#で呼び出すと、非常に奇妙な動作が発生します。

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ReadProcessMemory(
    IntPtr hProcess,
    IntPtr lpBaseAddress,
    [Out] byte[] lpBuffer,
    int dwSize,
    out int lpNumberOfBytesRead
    );

私のアプリケーションでは、読み取りおよび書き込みアクセス権を持つメモリ領域のメモリ全体をスキャンしています(さらにいくつかのフィルターが適用されていますが、それは別の部分です)。

スキャン部分のコードは次のようなものです。

int numberOfBytes;
if (!NativeMethods.ReadProcessMemory(handle, region.StartAddress,
    buffer, (int)region.RegionSize, out numberOfBytes))
// The handle, region (custom struct containing some fields from the
// MEMORY_BASIC_INFORMATION struct), and buffer come from parameters.

そして、コードは完全に機能します。メモリ全体をスキャンして一連のバイトを探します。そこに問題はありません。


プログラムのフローのもう少し先に、このコードがあります。
注:前のコード(チェック済み)と同じハンドルIntPtrを使用しており、同じスレッドで実行されます。

int bytesRead;
byte[] buffer = new byte[128]; // In my real app this is some calculated value
                            // however that irrelevant. It's calculated 128.
if (!NativeMethods.ReadProcessMemory(handle, location.Location,
    buffer, buffer.Length, out bytesRead))
    continue; // Error while reading
// At this point buffer == null, so the next line causes an exception
if (bytesRead != buffer.Length) continue;

コードは非常に似ていますが、何らかの理由でバッファへの参照が失われ、バッファがnullに設定されます。外部呼び出しでない場合は、バッファがrefまたはoutパラメータとして渡されないため、バグであると100%確信しています。ただし、外部呼び出し(マーシャリングなど)に関しては、.NETがいくつかのvodoo機能を実行することを私は知っています。

状況をさらに奇妙にするのは、そのコードを次のように置き換えるとです。

int bytesRead;
byte[] buffer = new byte[128];
byte[] bufferRef = buffer;
if (!NativeMethods.ReadProcessMemory(handle, location.Location,
    buffer, buffer.Length, out bytesRead))
    continue; // Error while reading
buffer = bufferRef;
if (bytesRead != buffer.Length) continue;

コードは単純に機能します。メモリー読み取りとすべて!つまり、何らかの理由でbuffer変数が実際のバッファーへの参照を失うだけです。そして、それは私を混乱させます。


この動作は、私が間違ったこと(P / Invokeの障害など)の結果であり、危険であり(メモリリーク?)、説明可能ですか?


私の構成:

  • .NET Framework 4.0
  • Visual Studio Professional 2012(バージョン11.0.51106.01アップデート1)
  • インストールされた.NETFramework4.5.50709
  • 管理者として実行
  • Visual Studioホスト実行可能ファイルと通常のビルド実行可能ファイルの両方で、リリースビルドとデバッグビルドの両方で発生します。
  • Windows764ビット
  • 私がメモリを読み取っているプロセスは32ビットです
  • ビルド構成:プラットフォーム:任意のCPU

編集:私が使用している完全なNativeMethodsクラスはここにあります:http://paste2.org/p/2770271

Edit2:問題を修正するために実行した簡単な手順を、ここにある回答として追加しました。

4

4 に答える 4

2

おそらく、64ビットアプリであるため、lpNumberOfBytesReadは「長い」はずであり、ReadProcessMemoryの呼び出しは、戻り時にバッファーポインター(の一部)を上書きします。

于 2013-01-19T01:13:57.670 に答える
0

HansPassant500-InternalServer Error (私は承認済みの回答としてマークしました)からのヒントにより、問題を解決することができました。

これらは私が取ったステップです:

  1. AnyCPUの代わりに32ビットを使用することを選択しました。(主に下位互換性のためです。)
  2. 次に、関数のMSDNページとWindowsデータ型に関するこのページを使用してP/Invoke署名を更新しました。そして、署名の32ビットバリアントを選択しました。
  3. コードを再度実行しましたが、バッファー参照がクリアされませんでした。

助けてくれてありがとう。

于 2013-01-19T02:06:37.963 に答える
0

ReadProcessMemoryインポートOutAttributeで宣言されたof(の正しい移植)は、データを呼び出し先から呼び出し元にマーシャリングする必要があることを指定します...したがって、バイト配列はおそらく呼び出し先自体(Kernel32)によってnull参照されています。byte[] lpBuffer_Out_ LPVOID lpBuffer

于 2013-01-19T01:02:53.523 に答える
-2

byte[] bufferをパラメータとしてマークしないで[Out]ください。これは、バイト配列であるため、すでに暗示されているrefよりも似ています。out整数(intここでは)は値型であるため、のoutパラメーターが必要ですnumberOfBytesRead。でマークする必要があるのはそれだけoutです。

out何かがパラメーターとしてマークされている場合、クラスは呼び出し先(ここ)が値を返すことをMarshal期待します。ReadProcessMemoryバイト配列は、バイトが含まれているメモリ内の場所への単なるポインタ(アドレス)です。このポインタが呼び出し先によって書き込まれることは望ましくありません。

于 2013-01-19T01:45:21.623 に答える