【问题标题】:C# - Cannot Marshal IntPtr To String CorrectlyC# - 无法将 IntPtr 编组为正确字符串
【发布时间】:2013-05-28 15:48:20
【问题描述】:

这行代码一直让我莫名其妙!

string s = Marshal.PtrToStringAnsi((IntPtr)((Int32)Buffer + 
            Marshal.SizeOf(typeof(Struct))));

这是函数的其余部分。

            Api.LvItem lvItem = new Api.LvItem();
            IntPtr lpLocalBuffer = Marshal.AllocHGlobal(1024);
            uint pid;
            uint thread = Api.GetWindowThreadProcessId(hWnd, out pid);
            IntPtr hProcess = Api.OpenProcess(0x001f0fff, false, (int)pid);
            IntPtr lpRemoteBuffer = Api.VirtualAllocEx(hProcess, IntPtr.Zero, 1024, 0x1000, 4);
            lvItem.mask = 1;
            lvItem.iItem = index;
            lvItem.iSubItem = subitem;
            lvItem.pszText = (IntPtr)((int)lpRemoteBuffer + Marshal.SizeOf(typeof(Api.LvItem)));
            lvItem.cchTextMax = 50;
            Api.WriteProcessMemory(hProcess, lpRemoteBuffer, ref lvItem, Marshal.SizeOf(typeof(Api.LvItem)), 0);
            Api.SendMessage(hWnd, 0x1005, IntPtr.Zero, lpRemoteBuffer);
            Api.ReadProcessMemory(hProcess, lpRemoteBuffer, lpLocalBuffer, 1024, 0);
            string ret = Marshal.PtrToStringAnsi((IntPtr)((int)lpLocalBuffer + Marshal.SizeOf(typeof(Api.LvItem))));
            Marshal.FreeHGlobal((IntPtr)lpLocalBuffer);
            Api.VirtualFreeEx(hProcess, lpRemoteBuffer, 0, 0x8000);
            Api.CloseHandle(hProcess);
            return ret;

此代码用于获取另一个进程的列表视图中每个项目的文本。我从这里跟踪代码(半):http://taylorza.blogspot.com/2009/08/archive-hacking-my-way-across-process.html

我很抱歉使用 0x1005。这是LV_GETITEM 消息的代码。

我已将其范围缩小到与平台相关。它适用于 x64 和 AnyCPU,但不适用于 x86。我将如何改变内存分配的大小差异等

在控制台应用程序中使用时,它会正确返回值。但是,当我在通过反射调用的 .DLL 中使用此代码(及其其余部分)时,该代码返回一个不正确的字符串。它看起来是空的,但实际上并不为空。也许只是空白......

有没有人对这种麻烦的编组有类似的经历?这让我困惑了整整三个小时......

非常感谢任何帮助或任何想法或任何东西!

【问题讨论】:

  • 可能是 64 位指针截断,但这很可能会导致访问冲突。因此,结构末尾之外的内容可能确实是空的。为什么不给我们一个说明故障的完整程序。否则我们必须猜测。你有一个完整的程序。我们不这样做是不公平的
  • 嗨,David,我已将其范围缩小到与平台相关。它适用于 x64 和 AnyCPU,但不适用于 x86。我想我现在可以从这里开始,但任何建议都是有帮助的。我正在运行 x64 位版本的 Windows。
  • 另外,我将如何改变内存分配等的大小差异。
  • 我不知道。您没有显示足够的代码。如果你坚持自己保密,我帮不了你。
  • 这里是函数,太长了,无法评论。 pastebin.com/raw.php?i=793X6PS5

标签: c# pinvoke 32bit-64bit


【解决方案1】:

您似乎正在尝试读取另一个进程的内存。您正在向列表视图控件发送LV_GETITEM 消息。这要求您在该其他进程的虚拟地址空间中分配内存。你对VirtualAllocEx的调用做了什么。

到目前为止一切都很好,但这是关键。您需要分配的结构是目标进程的正确布局。现在,让我们假设目标进程是一个 64 位进程。在这种情况下,结构包含 64 位指针。当您的进程也是 64 位时,您定义的结构将具有 64 位指针。但是当你的进程是 32 位时,结构中的任何指针都是 32 位的。

所以,现在我们遇到了一种情况,您的进程中的结构与目标进程中的结构具有不同的布局。这种不匹配足以使所有这些努力失败。为了有机会完成这项工作,您需要声明您的结构,使其布局与 64 位布局相匹配。鉴于您只访问少数字段,分配正确大小的内存块(可能是字节数组)并手动读取/写入字段可能会更简单。并避免结构编组。

即便如此,我还是怀疑 32 位进程是否能够以这种方式攻击 64 位进程。您可以从另一个方向进行,从 64 位进程到 32 位进程。但我已经尝试过,但从未在您尝试的方向上取得成功。

所有迹象都表明您应该为该任务坚持使用 64 位进程。或者找到一种官方支持的方式来做你正在做的任何事情。例如,如果您只想阅读资源管理器视图的内容,则可以使用 shell API。


顺便说一句,请停止使用魔术常量而不是消息标识符名称。没有人喜欢尝试理解0x1005 的含义。声明一个名为LV_GETITEM 的常量。您似乎根本没有执行任何错误检查。这也会让你的生活变得艰难。如果其中一个 API 调用失败,您将如何发现?

【讨论】:

  • 这对您有帮助吗,还是您在寻找更多信息?你明白我的意思吗?
  • 是的,我明白你在说什么,但我需要这个过程是 32 位的......没有例外。所以在我的情况下,您是说我需要在 64 位结构中找到 LVITEM 结构的大小,并通过该大小的 byte[] 手动分配内存?
  • 是的,不是使用结构和编组,而是使用字节数组。或者使用结构并将指针替换为long,即 64 位整数。无论如何,即使你匹配所有东西,我仍然不相信它甚至可能来自 32 位进程。您在寻求更多帮助吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-03
  • 2012-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多