从进程内存中清除字符串

Clear strings from process memory

为了提高我的应用程序的安全性,我试图从进程内存中删除字符串数据,但由于 Internet 上关于此的信息很少,我无法编写工作代码。

谁能帮帮我?

要清除的字符串数据(来自 ProcessHacker)。

我粘贴的代码:

void MemoryStringsClear() {
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
    MEMORY_BASIC_INFORMATION mbi;
    char* addr = 0;

    while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)))
    {
        if (mbi.State != MEM_COMMIT || mbi.Protect == PAGE_NOACCESS)
        {
            //char* buffer = new char[mbi.RegionSize];

            //ReadProcessMemory(hProc, addr, buffer, mbi.RegionSize, nullptr);
            if (addr) {
                cout << "Addr: " << &addr << " is cleared!" << endl;
                memset(addr, '0', mbi.RegionSize);
            }
            
        }
        addr += mbi.RegionSize;
    }

    CloseHandle(hProc);
}

已编辑: 我选择这种解决问题的方式是因为我的应用程序由许多模块(.exe应用程序)组成,其中一些我无法更改。

您的方法存在一些问题(我的解决方案进一步下降):

列出的大部分字符串都是环境变量

您计算机上 运行 的所有程序都可以访问这些程序。它们在启动时被复制到每个程序的内存 space 中,因此每个程序都知道在哪里寻找某些文件。将它们从应用程序的内存中删除是没有意义的,因为计算机上的每个应用程序 运行ning 都已经知道它们。

您可以通过 运行ning cmd.exe、键入 set 然后按 return 来查看它们。

OpenProcessVirtualQueryEx用于访问另一个进程

您可以简单地使用 VirtualQuery,因为您只想访问您自己的进程。

我猜你是想通过这样做来访问未提交的内存页面,但是 memset 只能访问你自己程序地址 space 中已提交的可写内存页面。所以这两种方法不能混用。

不过这里还有更重要的一点:

不存在未提交的内存

如果未提交内存页,则没有实际内存分配给该地址。这意味着,您无法用零覆盖任何内容。包含您的字符串的内存可能已经分配给另一个应用程序。详细阅读一些关于虚拟内存管理的资料。

freedelete 或垃圾收集的大多数调用并不总是实际取消提交页面

出于效率原因,当您的代码分配和取消分配内存时,您的 运行time 库会将较大内存页(称为“堆”)的小碎片交给您其中已被释放。

您可以通过遍历堆条目找到已释放的内存块,但这如何工作取决于您的 C 运行time 库或其他 运行time 库。

操作系统可能会移动您的字符串

如果操作系统检测到内存不足,它可以将您的字符串保存到磁盘以释放内存供其他应用程序使用,并在您的应用程序再次激活时重新加载它们。事后通常不会费心清理磁盘。你对此没有影响(除非你格式化你的硬盘)。


我的解决方案

在您的代码中每次调用 freedelete 之前 带有敏感信息(而且只有那些)的内存,你可以调用 memset(...) 在那块内存上。在 C++ 中,您可以将其包装在一个 class 中,它会在销毁时清除其内存,正如 Alan Birtles 在他的评论中指出的那样。

我认为没有一种解决方案可以简单地弹出到现有程序中,在释放内存后清除敏感信息。

这种方法只留下最后一个问题。只有当您从不将未加密的敏感信息存储在内存中时,您才能避免这种情况。这可能是不可行的,因为这意味着您不处理它仅加密。

什么是困难的或不可能的

如果你想清除其他进程中的释放内存(你在编辑中引用的你不能更改的单独的 *.exe 文件),你必须了解它们的内部堆布局并使用 WriteProcessMemory而不是 memset.

但这并没有捕捉到其他程序实际取消提交页面的情况,因为您不知道操作系统是否已经重新分配了它。当这种情况发生时完全不在您的控制范围内。

您也可以尝试在您的 C 运行time 库中重新实现 freedelete 函数,以便它们先清除内存然后调用原始版本,但这仅如果它们实际被那些 *.exe 文件使用并且它们是动态链接的,则可以工作。如果满足这些条件,你可能仍然很难过。

定义您要防范的安全威胁

To improve the security of my application,

你到底想防范什么?您是否已验证清除进程内存确实可以抵御您要防御的安全攻击?

了解记忆的工作原理

了解您的操作系统如何分配虚拟内存和物理内存,否则对其工作方式的错误假设可能会导致您实施无效的解决方案。大多数计算机系统使用 virtual memory,这意味着您的某些内存实际上可能最终被复制到物理 RAM 或磁盘中的不同位置。另一方面,如果您的进程退出并启动新进程,大多数操作系统会在将第一个进程分配给第二个进程之前清除第一个进程使用的 RAM。

确保您可以完全控制要清除的内存

正如 Iziminza 已经提到的,您的进程具有虚拟内存,但操作系统可以选择如何使用物理内存来支持该虚拟内存。当其他进程需要 RAM 时,它可以决定将数据移动到磁盘上的交换文件,直到再次需要它为止。为了使使用 memset() 清除内存有意义,您必须确保没有副本存储在其他地方。您可以使用 VirtualLock() on Windows, or mlock() on other operating systems. Even then, if the computer is going into hibernation mode 执行此操作,即使锁定的内存也会写入磁盘。