【问题标题】:Performance issue while searching string in memory在内存中搜索字符串时出现性能问题
【发布时间】:2015-09-15 22:32:22
【问题描述】:

我正在 Win32 下开发 DLL,它做一个简单的工作:它扫描主机的虚拟内存中的子字符串。但由于某种原因,与使用单线程扫描的 Cheat Engine、ArtMoney 甚至 OllyDbg 相比,它的运行速度非常慢。这是我使用 VirtualQuery() 获得的扫描单个内存部分的函数的代码。主机(.exe 应用程序)提交大约 300-400 MiB 的内存,我必须扫描大约 170 个不同大小的内存部分,从 4KiB 到 32MiB。我只扫描 MEM_PRIVATE、MEM_COMMIT 区域,不扫描 PAGE_GUARD、PAGE_NOACCESS、PAGE_READONLY,跳过 DLL 自己的内存。

由于某种原因,性能很糟糕 - 查找单个字符串需要 10-12 秒。例如 OllyDbg 在 ~2-3 秒内找到字符串。

UINT __stdcall ScanAndReplace(UCHAR* pStartAddress, UCHAR* pEndAddress, const char* csSearchFor, const char* csReplaceTo, UINT iLength)
{
    // This function runs inside the single memory section and looks for a specific substring

    // pStartAddress: UCHAR* - The begining of the memory section
    // pEndAddress: UCHAR* - The ending of the memory section
    // csSearchFor: const char* - The pointer to the substring to search for
    // csReplaceTo: const char* - The pointer to the substring to replace with
    // iLength: UINT - max length of csSearchFor substring

    // Total iterations
    UINT iHits = 0;

    // Scan from pStartAddress to (pEndAddress - iLength) and don't overrun memory section
    for (pStartAddress; pStartAddress < (pEndAddress - iLength); ++pStartAddress)
    {
        UINT iIterator = 0;

        // Scan for specific string that begins at current address (pStartAddress) until condition breaks
        for (iIterator; (iIterator < iLength) && (pStartAddress[iIterator] == csSearchFor[iIterator]); ++iIterator);

        // String matches if iIterator == iLength
        if (iIterator == iLength)
        {
            // Found, do something (edit/replace, etc), increment counter...
            ++iHits;
        }

        /*
        // Even if you search for single byte it's very slow
        if (*pStartAddress == 'A')
            ++iHits;
        */
    }

    return iHits;
}

我正在使用 MSVS 2010。

编译器命令行:

/nologo /W3 /WX- /O2 /Os /Oy- /GL /D "WIN32" /D "NDEBUG" /D "_WINDOWS"
  /D "_USRDLL" /D "MYDLL_EXPORTS" /D "_WINDLL" /GF /Gm- /MD /GS- /Gy
  /fp:precise /Zc:wchar_t /Zc:forScope /Fp"Release\MyDll.pch" /FAcs
  /Fa"Release\" /Fo"Release\" /Fd"Release\vc100.pdb" /Gd /TC /analyze-
  /errorReport:queue

链接器命令行:

/OUT:"D:\MyDll\Release\MyDll.dll" /INCREMENTAL:NO /NOLOGO /DLL "Dbghelp.lib"
  "msvcrt.lib" "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib"
  "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib"
  "uuid.lib" "odbc32.lib" "odbccp32.lib" /NODEFAULTLIB /MANIFEST:NO
  /ManifestFile:"Release\MyDll.dll.intermediate.manifest" /ALLOWISOLATION
  /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG
  /PDB:"D:\MyDll\Release\MyDll.pdb" /SUBSYSTEM:WINDOWS /OPT:REF /OPT:ICF
  /PGD:"D:\MyDll\Release\MyDll.pgd" /LTCG /TLBID:1 /ENTRY:"DllMain"
  /DYNAMICBASE /NXCOMPAT /MACHINE:X86 /ERRORREPORT:QUEUE

我做错了什么?是我的算法不好还是其他内存扫描仪使用了某种“魔法”?

【问题讨论】:

  • 枚举所有部分需要多长时间?我的意思是 - 如果你将 ScanAndReplace() 函数减少到零,它仍然需要相当长的时间吗?也许您在错误的地方寻找问题?
  • 如果我从这个函数中删除所有代码(或只是发生实际读取的部分 - 内部 FOR 循环),它几乎会立即传递整个虚拟内存。我想我已经找到了问题的根源,它与算法无关,但主机应用程序以某种方式影响了内存读取速度。我需要更多的时间来弄清楚会发生什么,以及为什么这个 DLL 在“测试”exe 应用程序中加载并具有相同数量的提交内存按预期工作得很快,但在“真实”应用程序中却很慢。可能是 try/catch {}。

标签: performance memory dll memcmp virtualquery


【解决方案1】:

其他内存扫描器可能会以更好的搜索算法的形式使用“魔法”,例如Boyer-Moore。他们可能还对他们的搜索算法进行了额外的微优化,但我猜算法的选择将解释你所看到的大部分差异。

【讨论】:

  • 感谢有用的链接。但即使我扫描单个字节,它的工作速度也很慢。我检查了memedit0x0 project 的源代码,发现它运行得非常快,而且我没有找到任何与 Boyer–Moore 字符串搜索算法相关的东西。它在函数 MemoryCell::update() 中扫描单个 value in memory 正如您所看到的,它使用与我相同的算法,但它的工作速度要快几个数量级。
  • @DrMorgan 嗯,我看不出您的代码会变慢的任何明显原因。您是否尝试过用您的代码替换 memedit0x0 中的扫描代码,看看它在那里的执行速度是否较慢。如果不是,那么差异可能是由于程序的其他方面造成的。
猜你喜欢
  • 2012-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-06
  • 2013-04-08
  • 2013-03-18
相关资源
最近更新 更多