2013-01-19 31 views
3

通過這個p在C#調用ReadProcessMemory時,我遇到了一些非常怪異的行爲被設置爲空/調用簽名:引起參考奇怪的行爲時調用ReadProcessMemory

[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 
    ); 

在我的應用程序」 m掃描具有讀取和寫入權限的內存區域的整個內存(以及應用了一些更多的過濾器,但這是另一部分)。

在掃描部分的代碼是這樣的:

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; 

代碼非常相似,但由於某些原因,對緩衝區的引用丟失並且緩衝區設置爲空。如果它不是一個外部電話,我會100%確定它是一個錯誤,因爲緩衝區不作爲refout參數傳遞。但是我知道.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 2012專業版(版本11.0.51106.01更新1)
  • 安裝了.NET Framework 4.5.50709
  • 運行作爲管理員
  • 在發行版和調試版本中均發生,兩者均在visual studio host ex ecutable和常規的構建可執行文件。
  • 視窗7 64位
  • 過程我讀存儲器從是32位
  • 構建配置:平臺:任何CPU

編輯:完整NativeMethods I類使用可以在這裏找到:http://paste2.org/p/2770271

編輯2:我添加了我遵循的簡單步驟編輯解決問題作爲答案,可以發現here

+0

旁註:Marshal.GetLastWin32Error()返回1008,它在啓動時返回相同的代碼。 – Aidiakapi

+0

您的MEMORY_BASIC_INFORMATION聲明是錯誤的,RegionSize是一個UIntPtr,而不是ulong。不知道如何可以破壞堆棧。 –

+0

@HansPassant謝謝,這指出我在解決問題的方向:)! – Aidiakapi

回答

2

可能因爲您是64位應用程序,您的lpNumberOfBytesRead應該是「很長」的,因此對ReadProcessMemory的調用會在返回時覆蓋(部分)緩衝區指針。

+0

只是將平臺更改爲x86,現在完全無法使用:P。 – Aidiakapi

+0

它在哪裏失敗?在64位上,你是否嘗試將你的互操作聲明改爲使用long來讀取大小? –

+0

我的完整的本地方法類可以在這裏找到(http://paste2.org/p/2770271)。 – Aidiakapi

0

byte[] lpBuffer在ReadProcessMemory進口(這是_Out_ LPVOID lpBuffer正確移植)聲明的OutAttribute指定的數據應該從被叫回主叫編組......所以你的字節數組可能正在空由被叫本身(參考KERNEL32)。

+0

爲什麼當kernel32正確填充數據時它會被kernel32清零並且它返回一個非零值(或者換句話說,沒有發生錯誤)?編輯:它編組正確,雖然因爲所有的數據在緩衝區中可用(一些代碼之後,檢查數據,它的驗證正確)。所以閱讀正確。爲什麼*它*在我提供的第一個代碼中工作,但不在第二個代碼中? – Aidiakapi

+0

您是否嘗試將該過程附加到調試器以查看發生了什麼? –

+0

對於Visual Studio的調試器是的,對於OllyDbg調試器不,你認爲這會有幫助嗎? – Aidiakapi

-2

不要將byte[] buffer標記爲[Out]參數。它更類似於ref而不是out,由於它是一個字節數組,所以它已經暗示了。由於整數(這裏的int)是一個值類型,因此它需要out參數numberOfBytesRead。這是唯一應該標記爲out的那個。

當某事被標記爲out參數時,Marshal類希望被調用者(ReadProcessMemory在這裏)提供一個值。字節數組僅僅是一個指針(地址)到內存中包含字節的位置。你不想讓這個指針被被調用者寫入。

+0

'OutAttribute'與'ref'和'out'參數調用完全無關。除此之外,你關於'int'(或者我假設其他值類型)的觀點是應該標記爲'ref'或'out'的唯一參數是完全垃圾,有許多好的設計,其中的參考類型被標記爲這樣。 'OutAttribute'涉及編組,並且是必需的。 – Aidiakapi

+0

'[OutAttribute]'不完全不相關。 AFAIK'out'與'[Out] ref'同義。你可以說你想要什麼,但是我通過P/Invoke使用'ReadProcessMemory'等編寫了大量的代碼,並且從未在緩衝區上標記爲'ref'或'[Out]'。我已經看到這些函數發生了很多奇怪的事情(通常是Win32錯誤或零讀取),但不是用'NULL'覆蓋緩衝區。我試圖通過指出與我的工作代碼不同的東西來幫助您。 – Erik

+0

這是因爲VM使用不同於默認C#行爲的互操作技術。就像我在我的問題中所說的,如果這不是一個外部函數,我會將它作爲一個錯誤進行存檔。 '[Out]'不過**不是** [Out] ref'。 Ref和Out參數是CIL的一個特性,並且與'OutAttribute'所提示的虛擬機屬性無關。無論他們的結果多麼相似,他們都有根本的不同,不應該被教導他們是相同的。 – Aidiakapi

0

由於來自Hans Passant500 - Internal Server Error(我標記爲接受答案)的提示,我設法解決了這個問題。

這是我所採取的步驟:

  1. 我選擇使用32位,而不是任何CPU的。 (主要是爲了向後兼容)
  2. 然後我使用MSDN頁面的功能和this關於Windows數據類型的頁面更新了P/Invoke簽名。並選擇簽名的32位變體。
  3. 我再次運行代碼並且緩衝區引用未被清除。

感謝您的幫助。

+0

對於1)的問題,如果我在x64環境下運行32位目標平臺的代碼,我會得到完全錯誤的信息。 –

+0

這是因爲我沒有發佈固定的P/Invoke簽名。這個主題中的問題是錯誤的,並且他們引起了這個問題(默默地)。 – Aidiakapi