為什麼 WinInet 的 FindNextUrlCacheEntry() 回傳 ERROR_FILE_NOT_FOUND=2 而不是 ERROR_NO_MORE_ITEMS=295

為什麼 WinInet 的 FindNextUrlCacheEntry() 回傳 ERROR_FILE_NOT_FOUND=2 而不是 ERROR_NO_MORE_ITEMS=295

描述

根據標題,WinInet 的FindNextUrlCacheEntry()結果是未記錄的錯誤代碼 2 (ERROR_FILE_NOT_FOUND),而不是 ERROR_NO_MORE_ITEMS=295。有人可以解釋原因或建議其他調查方法嗎?

環境

  • Windows 10
  • 瀏覽器11
  • VisualStudio 2006(但可以在 VS2010 上重建)

概要

詳細資訊如下,但問題的關鍵是在循環內,此程式碼片段:

/ Get the next entry
if (!FindNextUrlCacheEntry(hCache, *plpCacheEntry, &bufSize)) 
{
    DWORD d = GetLastError();
    ATLTRACE(_T("getNextEntry - FindNExtUrlCacheEntry returned %d: %s ==> %s"), d, (*plpCacheEntry)->lpszSourceUrlName, (*plpCacheEntry)->lpszLocalFileName);
    // Are we done?
            // <.... more error handling logic ....>

導致此追蹤輸出:

>>[12536] getNextEntry - FindNExtUrlCacheEntry returned 2: (null) ==> (null)

如果我們修改程式碼以類似 ERROR_NO_MORE_ITEMS 的方式處理這種情況,事情就會如預期進行。如果我們忽略這一點並繼續嘗試獲取更多條目,我們就會陷入無限循環。

概述

注意:在這篇文章中,我將錯誤代碼 2 視為 ERROR_FILE_NOT_FOUND 的同義詞,但由於它是相關 API 呼叫的未記錄代碼,因此 2 實際上可能來自其他內容。

我們有一個大約在 VisualStudio 2006 中運行的 C++ ActiveX 控件,可以在 IE 中運行。其目的是對與我們的應用程式伺服器主機名稱相符的檔案進行部分 IE 快取清理。 (此操作是透過將來自伺服器的請求 cookie 值與用戶端上最後儲存的版本號進行比較來控制的。僅當偵測到版本不符時才會進行刪除。)

ActiveX 控制項多年來一直很好地完成其工作。但最近開始失敗,如上所述。我們不知道這是否是由於 KB Windows 更新或最近的安全性舉措或其他原因造成的。我們的程式碼正確處理 ERROR_NO_MORE_ITEMS 情況並終止其工作。但它將 2 視為錯誤,並且應用程式不會繼續。

FindNextUrlCacheEntry() 的文件在這裡: https://docs.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-findnexturlcacheentrya 此呼叫傳回 true/false 並將錯誤代碼設為 ERROR_NO_MORE_ITEMS 或 ERROR_INSUFFICIENT_BUFFER。

我們的邏輯:

我可以提供詳細信息,但猶豫是否要發布專有代碼。簡而言之,邏輯遵循為此推薦的步驟。一個例子在這裡https://support.microsoft.com/en-us/help/815718/how-to-clear-the-cache-when-your-application-hosts-a-webbrowser-contro只是我們不處理「快取組」。您可以捲動執行評論後開始的程式碼

// Start to delete URLs that do not belong to any group.

無論如何,我們的邏輯如下:

  1. 透過 取得快取句柄FindFirstUrlCacheEntry(),根據條目緩衝區的需要重新分配內存,並檢查GetNextError()是否成功
  2. 透過循環遍歷此條目和後續條目FindNextUrlCacheEntry(),根據條目緩衝區的需要重新分配內存,檢查 true/false 返回值
  3. DeleteUrlCacheEntry()如果為 true:如果主機名稱相符且條目不是類型:COOKIE_CACHE_ENTRY或,則呼叫HISTORY_CACHE_ENTRY。否則跳過該條目
  4. 如果為 false:我們檢查 的值GetNextError()並執行以下操作:
  5. ERROR_NO_MORE_ITEMS(=259):終止循環並成功退出ActiveX入口點
  6. 將 ActiveX 控制項入口點的回傳程式碼設定為錯誤代碼(在我們的範例中為 2)。 JavaScript 呼叫程式測試 0 並停止錯誤的使用者的alert(),並指示致電支援人員

解決方法:

允許應用程式正常運行的解決方法是讓使用者刪除整個 IE 快取。此時,FindFirstUrlCacheEntry() 找不到任何內容,並且永遠不會進入刪除循環。 (實際上,這是猜測,因為我們無法明確測試這種情況。請參閱下面的調試說明。)無論如何,錯誤都不會發生

調試工作:

我們可以透過在 JavaScript 程式碼中設定斷點並在比較完成之前手動修改版本字串來強制 cookie 版本不符。這「欺騙」程式碼呼叫 ActiveX 控制項刪除入口點。

我們這裡的條件極為有限。我們只能偶爾訪問出現問題的一台 PC。透過 msvsmon.exe 遠端附加 VisualStudio 偵錯器的努力允許我們看到 ATLTRACE(又稱 printf)輸出,但我們無法觸發斷點。相反,我們簡化為 ATLTRACE() 和 Sysinternals 的 DebugView。

我們看到的是 ERROR_FILE_NOT_FOUND 的行為類似 ERROR_NO_MORE_FILES。也就是說,如果我們破解 C++ 程式碼以在任一條件下終止查找下一個/刪除循環,它總是會刪除一些預期的檔案並完成而不會出現錯誤。然而,很難知道是否真的沒有更多文件:手動詢問 IE 快取充其量是很棘手的,而我們 PC 上的安全措施則更是如此。例如,子資料夾C:\Users\<usr>\AppData\Local\Microsoft\Windows\INetCache\Low\IE\...不會顯示在 cmd 或 power-shell dir 或 ls 指令中。儘管如此,我們在客戶端中點擊的應用程式頁面越多,ActiveX 控制項在完成之前清除的檔案就越多。 DebugView 中的一個工作案例如下所示:

[12536] DeleteCacheEntries(10.3.31.28:9080) BEGIN: hr=0 pErrCode=0
[12536] deleteCacheEntriesFor(10.3.31.28:9080) BEGIN
[12536] deleteCacheEntriesFor(10.3.31.28:9080) - getFirstEntry returned 0
[12536] deleteCacheEntryIf - (lpdErrCd now=0) deleting: http://10.3.31.28:9080/images/our.jpg ==> C:\Users\<user>\AppData\Local\Microsoft\Windows\INetCache\Low\IE\Q6HU0E35\our[1].jpg
....<lots more "deleting" ...>
[12536] getNextEntry - FindNExtUrlCacheEntry returned 2: (null) ==> (null)
[12536] getNextEntry - ERROR=2: (null) ==> (null)
[12536] deleteCacheEntriesFor(10.3.31.28:9080) - getNextEntry returned 2. Breaking out of loop!
[12536] deleteCacheEntriesFor(10.3.31.28:9080) - Complete. Closing URL cache handle current. errCd=0
[12536] deleteCacheEntriesFor(10.3.31.28:9080) - Done. Returning 0

我們也嘗試了一種駭客攻擊,假設 ERROR_FILE_NOT_FOUND 只是意味著那裡有一個快取條目,但不知何故我們被阻止看到它。也就是說,我們不是跳出循環,而是繼續希望遇到其他「可找到的」條目來可能刪除。但在這種情況下,循環永遠不會終止。這裡的「從不」是指幾分鐘和超過 200 萬個案例:

[5712] getNextEntry - FindNExtUrlCacheEntry returned 2: (null) ==> (null)
[5712] deleteCacheEntriesFor(10.3.31.28:9080) - getNextEntry returned 2. CONTINUING!
...<forever>...

推測:

以下是對可能發生的事情的一些無可否認的薄弱猜測

  • 某些安全軟體或 Windows KB 修補程式會幹預 C++ 執行並導致呼叫 SetLastError(2),從而破壞 FindNextUrlCacheEntry() 設定的記錄值
  • 我們的程式碼連結到舊版本的 VisualStudio SDK,並且我們遇到了一些程式碼偏差
  • 我們考慮過堆疊或暫存器損壞,但這種行為似乎太容易重現了。仍然...?
  • FindNextUrlCacheEntry() 確實有一些東西,但安全性以某種方式阻止它可見,並且它在快取中的「遊標」永遠不會前進

答案1

事實證明,這是由於 Microsoft WinINet API 中的缺陷造成的。完整的詳細資訊可以在他們的 devcommunity/visualstudio 論壇上的線程中找到這裡。 (答案在我最後的帖子底部。)

相關內容