共用方式為


應用程式驗證器 - 應用程式驗證器內的測試

基本概念

您至少應該執行已選取 [基本] 設定的應用程式驗證器。 每一個都會測試會導致損毀或其他負面案例的區域,這些案例對客戶體驗有直接且有跡象性的影響。

  • 例外狀況 - 確保應用程式不會使用結構化例外狀況處理隱藏存取違規
  • 句柄 - 測試以確保應用程式未嘗試使用無效的句柄
  • 堆積 - 檢查堆積 中的記憶體損毀問題
  • 流失 - 追蹤 dll 卸除時未釋放的 dll 所建立的資源,以偵測流失
  • 鎖定 - 驗證重要區段的正確使用方式
  • 記憶體 - 確保正確使用虛擬空間操作的 API(例如 VirtualAlloc、MapViewOfFile)
  • SRWLock - 驗證精簡讀取器/寫入器鎖定的正確使用方式。
  • Threadpool - 確保線程集 區 API 的正確使用方式,並在回呼之後強制執行背景工作線程狀態的一致性檢查,例如髒線程集區線程和其他線程集區相關問題。
  • TLS - 確保正確使用線程本機 儲存體 API

如需這些測試所產生的停止程式代碼例外狀況資訊,請參閱 應用程式驗證器 - 停止程式代碼和定義。 如需偵錯這些失敗的資訊,請參閱 應用程式驗證器 - 偵錯應用程式驗證器停止

相容性

相容性驗證層測試有助於識別可能有 Microsoft Windows 作業系統問題的應用程式。 其中許多檢查也可以用來測試標誌/認證需求。

如需這些測試所產生的停止程式代碼例外狀況資訊,請參閱 應用程式驗證器 - 停止程式代碼和定義

HighVersionLie - 識別 Windows 中一些最常見應用程式相容性問題的問題。 不正確地偵測操作系統的版本或使用硬式編碼版本資訊,可能會導致應用程式在稍後的作業系統上失敗。

克魯茲

並行模糊 (Cuzz) 驗證層會偵測並行錯誤和數據競爭狀況。 Cuzz 會插入應用程式程式代碼中關鍵點的隨機延遲,藉以調整線程排程。 下列案例說明 Cuzz 驗證層可以偵測到的並行錯誤類型。

應用程式具有父線程和子線程。 父線程會啟動子線程,然後配置結構的記憶體。

// Parent Thread
StartChildThread(...);
g_pointer = ... malloc(...);

子線程會取值指標。

//Child Thread
LONG value = g_pointer->someMember;

上述程式代碼有並行錯誤。 如果子線程嘗試在父線程配置記憶體之前取值指標,則指標將會無效。 Bug 不太可能自行顯示,因為在大多數情況下,父線程會在子線程啟動之前配置記憶體。 但在罕見的情況下,子線程可以開始,並嘗試在父線程配置記憶體之前取值指標。

Cuzz 驗證層會增加尋找並行錯誤的可能性,例如上述範例所示的錯誤。 除了插入延遲之外,Cuzz 不會執行任何其他檢查。 因此,沒有直接與 Cuzz 相關聯的驗證停止。 不過,如果啟用 Cuzz 會導致並行錯誤本身顯現出來,其他驗證層將受益。 例如,如果競爭條件導致堆積溢位,除非競爭條件在運行時間顯示本身,否則堆積驗證層將不會找到錯誤。 藉由增加發生競爭條件的機率,Cuzz 可改善堆積層在識別錯誤時的有效性。

若要取得 Cuzz 的最大優點,請在盡可能多的測試上啟用 Cuzz,並多次重複相同的測試。 您可以在所有測試上啟用 Cuzz,包括手動測試、功能測試和壓力測試。 此外,請儘可能啟用盡可能多的應用程式驗證器驗證層。

您可以藉由為 Cuzz 提供相同的隨機種子來增加重現 Bug 的機率(請參閱屬性)。

Cuzz 只會在 Win32 同步處理 API 呼叫上插入延遲。

Cuzz 屬性

下列屬性適用於 Cuzz 驗證層。 若要設定屬性,請選取應用程式驗證器使用者介面中的 Cuzz 層,然後開啟 [屬性視窗]。

屬性 說明
FuzzingLevel 控制 Cuzz 的模糊層級。 針對時間關鍵性應用程式將此設定為 1,而一般應用程式則設定為 4。
RandomSeed Cuzz 一開始使用的隨機種子。 如果您將此設定為 0,Cuzz 會產生以時間為基礎的隨機種子。

低資源模擬

低資源模擬會嘗試在低資源下模擬環境,例如記憶體不足。 此模擬會識別記憶體不足狀況中發生的 Bug。 這也稱為錯誤插入。 您可以在低資源下模擬環境,其中您可以定義數位(0–100),指出錯誤機率呼叫:

  • 等候 (例如,WaitForXXXX API)。
  • Heap_Alloc (堆積配置 API)。
  • Virtual_Alloc (虛擬記憶體配置 API)。
  • 登錄 (登錄 API)。
  • 檔案 (檔案 API,例如 CreateFile)。
  • 事件 (事件 API,例如 CreateEvent)。
  • MapView (MapView API,例如 CreateMapView)。
  • Ole_Alloc (Ole API,例如 SysAllocString)。

低資源模擬(也稱為錯誤插入)會嘗試在低資源下模擬環境,例如記憶體不足。 這會識別記憶體不足狀況內的 Bug。

低資源模擬屬性

若要編輯屬性,請在 [測試] 區域中核取 [低資源模擬] 複選框,以滑鼠右鍵按下並選取屬性:

屬性 說明
包括​​ 限制錯誤只會發生在指定的 dll 中。 每個數據列沒有路徑的一個 dll 名稱。 如果指定了 『*』,所有模組都會發生錯誤。
排除 排除指定模組的錯誤。 每個數據列沒有路徑的一個 dll 名稱。
Timeout 在進程初始化時沒有錯誤時,請提供時間位置(以毫秒為單位)。
等候 數位 [0 – 1000000] 表示 WaitForXXXX API 的錯誤機率。
Heap_Alloc 數位 [0 – 1000000] 表示堆積配置 API 的錯誤機率。
Virtual_Alloc 數位 [0 – 10000000] ,表示虛擬記憶體配置 API 的錯誤機率。
登錄 數位 [0 – 1000000] 表示登錄 API 的錯誤機率。
檔案 數位 [0 – 10000000] 表示檔案 API 的錯誤機率,例如 CreateFile。
活動 數位 [0 – 1000000] 表示事件 API 的錯誤機率,例如 CreateEvent
MapView 數位 [0 – 1000000] 表示 MapView API 的錯誤機率,例如 CreateMapView。
Ole_Alloc 數位 [0 – 1000000] 表示 Ole API 的錯誤機率,例如 SysAllocString。
堆疊 每個 Windows 應用程式線程都會以堆疊保留和堆疊認可大小開始。 在一般使用量下,每當堆疊上需要更多空間時,堆疊認可就會成長。 如需詳細資訊,請參閱 建立線程線程堆疊大小 。 如果系統遇到記憶體不足的情況,堆疊認可成長可能會失敗。 無法成長其堆疊的線程,而且整個應用程式很可能會當機。 對於重要的系統進程來說,這種當機是不能接受的(例如服務)。 Stacks 檢查會停用正在驗證之應用程式的任何堆疊成長,因此它會模擬堆疊成長失敗,而不需要模擬整個系統的記憶體不足狀況。 當應用程式嘗試展開堆疊時,將會擲回例外狀況。 這個不會產生任何驗證器停止。

LuaPriv

有限的使用者帳戶許可權預測器 (LuaPriv) 測試既是預測性和診斷性,也能夠呈現與以系統管理許可權執行應用程式相關的問題,以及如果以較少的許可權執行該應用程式,該應用程式是否也能夠運作(通常是一般使用者)。

也稱為UAC檢查,有限用戶帳戶許可權預測工具 (LuaPriv) 有兩個主要目標:

  • 預測性:以系統管理許可權執行應用程式時,如果以較少的許可權執行,則預測該應用程式是否正常運作(通常是一般使用者)。 例如,如果應用程式寫入只允許存取 管理員 istrators 的檔案,則當以非系統管理員身分執行時,該應用程式將無法寫入相同的檔案。

  • 診斷:以非系統管理員許可權執行時,找出目前執行可能已經存在的潛在問題。 繼續上述範例,如果應用程式嘗試寫入僅授與 管理員 istrator 群組成員存取權的檔案,應用程式將會收到ACCESS_DENIED錯誤。 如果應用程式無法正確運作,此作業可能是罪魁禍首。

LuaPriv 可識別下列類型的問題:

潛在問題 說明
受限制的命名空間 在沒有命名空間的情況下建立具名同步處理物件 (Event、Semaphore、Mutex 等),可能會使某些操作系統在沒有許可權的情況下執行時複雜,因為操作系統可能會選擇將物件放在受限制的命名空間中。 在受限制的命名空間中建立這類物件(例如全域命名空間)需要只授與系統管理員的 SeCreateGlobalPrivilege。
如果 LuaPriv 偵測到這些問題,則會標幟這兩個問題。
硬式 管理員 istrator 檢查 有些應用程式會詢問使用者的安全性令牌,以瞭解他/她擁有多少許可權。 在這些情況下,應用程式可能會根據其認為用戶擁有多少權力來變更其行為。
LuaPriv 旗標會傳回這項資訊的 API 呼叫。
要求許可權 在執行需要安全性的作業之前,應用程式可能會嘗試啟用安全性相關許可權(例如 SeTcbPrivilege 或 SeSecurityPrivilege)。
LuaPriv 旗標嘗試啟用安全性相關許可權。
遺漏許可權 如果應用程式嘗試啟用使用者沒有的許可權,它可能會發出應用程式預期許可權的訊號,這可能會造成行為差異。
LuaPriv 旗標失敗的許可權要求。
INI-File 作業 嘗試寫入對應的 INI 檔案(WritePrivateProfileSection 和類似的 API)可能會因為非系統管理員用戶而失敗。
LuaPriv 會標幟這類作業。
拒絕存取 如果應用程式嘗試存取物件(檔案、登錄機碼等),但嘗試因為存取不足而失敗,則應用程式可能會預期執行的許可權比它擁有的更多許可權。
LuaPriv 會標幟對象開啟嘗試,但失敗並出現ACCESS_DENIED和類似的錯誤。
拒絕 ACE 如果物件在其 DACL 中有 Deny ACE,則會明確拒絕存取特定實體。
這是罕見的,並使得預測變得困難,因此 LuaPriv 旗標拒絕 ACE 時發現它們。
存取受限 如果應用程式嘗試針對未授與一般使用者的許可權開啟物件(例如,嘗試寫入只有系統管理員可寫入的檔案),則當以一般使用者身分執行時,應用程式可能無法運作。
LuaPriv 會標幟這類作業。
MAXIMUM_ALLOWED 如果應用程式開啟MAXIMUM_ALLOWED的物件,則對象的實際存取檢查將會發生在其他地方。 執行這項作業的大部分程式代碼都無法正確運作,而且在不使用許可權執行時,幾乎肯定會以不同的方式運作。
因此,LuaPriv 會標幟所有MAXIMUM_ALLOWED事件。

其他

通常忽略的問題會在其他測試中擷取。

  • 危險 API - 追蹤應用程式是否使用下列不安全動作:
    • TerminateThread 的危險呼叫。
    • 記憶體不足時的潛在堆疊溢位。
    • 在多個線程仍在執行時呼叫的結束進程。
    • LoadLibrary 會在 DllMain 期間呼叫。
    • FreeLibrary 會在 DllMain 期間呼叫。
  • 髒堆疊會以記憶體模式填滿堆疊未使用的部分(定期)。 這可協助偵測該線程內容中未來函數調用中未初始化的變數。
  • TimeRollOver 會強制 GetTickCount 和 TimeGetTime API 的變換速度比平常快。 這可讓應用程式更輕鬆地測試其處理時間變換。

其他屬性

危險 API 檢查有一個可以改變的屬性:

DllMainCheck - 檢查 DllMain 使用中時呼叫的 LoadLibrary/FreeLibrary。

網路

網路測試會尋找不當使用 WinSock API。 例如,如果在成功 WSAStartup() 之前呼叫網路 API,或在平衡成功的 WSACleanup() 呼叫之後呼叫。 如需 WinSock 的詳細資訊,請參閱 winsock.h 標頭Windows Sockets 2

屬性

下列屬性適用於 Net 驗證層。 若要設定屬性,請在應用程式驗證器使用者介面中選取 [網络提供者],然後開啟 [屬性視窗]。

屬性 說明
FragmentsEnabled 啟用 TCP IPv4 和 IPv6 套接字所接收數據流的片段。
FragmentSize 指定傳回緩衝區給任何 Winsock 接收 API 呼叫的最大位元元組數目。

FragmentsEnabled 屬性可讓網路驗證器提供者中的功能,協助測試和驗證應用程式從網路剖析 TCP 數據流。 啟用之後,所有呼叫 Winsock 來接收數據只會接收最多 FragmentSize 位元組,除非應用程式在傳回之前需要填滿整個緩衝區(由MSG_WAITALL旗標控制)。 由於 TCP 通訊協定和 Winsock 都未提供任何關於可能傳回至緩衝區之位元組數目的任何保證,因此啟用這項檢查可協助確認程式碼剖析網路外數據流的程式代碼會正確進行,而與每次呼叫 Winsock 所收到的位元組數目無關。 串流剖析器中的問題一直是高調 Bug 的來源,而且會提供這些屬性來簡化正確性的驗證,因為這特別難以測試。 注意:這不會變更傳回的數據,只會以特定速率降低數據的速度:應用程式的行為應該與啟用或停用的方式完全相同。

下列命令行可讓所有傳入 TCP 數據流分散到在 myApp.exe 中建立的所有 TCP IPv4 和 IPv6 套接字,以及由 myApp.exe 載入的所有二進位檔。

appverif -enable Networking -for myApp.exe -with Networking.FragmentsEnabled=True Networking.FragmentSize=10

!avrf 調試程序擴充功能

!avrf -net -socket count - 顯示開啟和關閉的套接字句柄計數

!avrf -net -socket dump [-v] [HANDLE] - 顯示套接字句柄(s),詳細或不顯示。

!avrf -net -wsastacks - 顯示 WSAStartup/WSACleanup 的目前 WSA init 計數和堆疊追蹤的時間順序列表。

!avrf -net -wsastacks count - 顯示目前的 WSA init 計數。

!avrf -net -socket count - 此命令會提供正在追蹤的套接字句柄總數,無論是開啟還是關閉。 請注意,這些會追蹤在迴圈佇列中,因此追蹤總數的上限。 當其中一個配置套接字句柄的 Winsock API 呼叫時,套接字會新增至開啟的清單。 例如,socket()、WSASocket()、accept()。 當該套接字句柄上呼叫 closesocket() 函式時,套接字會從開啟的清單移至關閉清單。

!avrf -net -socket dump [-v] [HANDLE] - 此命令會列舉套接字句柄。 “-socket dump” 會依其 SOCKET 值列出所有追蹤開啟和關閉的套接字句柄。 選擇性 -v 旗標會在列印每個 SOCKET 值之後,立即列印開啟或關閉呼叫堆疊。 選擇性的 HANDLE 字段只會列出指定的 SOCKET 句柄及其開啟或關閉呼叫堆疊。

以下是各種 -socket 使用選項的範例:

0:008> !avrf -net -socket count
Number of open socket handles   = 16
Number of closed socket handles = 12
 
0:008> !avrf -net -socket dump
CLOSED SOCKET HANDLE - 0x47c
CLOSED SOCKET HANDLE - 0x2cc
CLOSED SOCKET HANDLE - 0x8c4
CLOSED SOCKET HANDLE - 0x6bc
CLOSED SOCKET HANDLE - 0x44c
CLOSED SOCKET HANDLE - 0x578
CLOSED SOCKET HANDLE - 0x6f4
CLOSED SOCKET HANDLE - 0x5b4
CLOSED SOCKET HANDLE - 0x4d8
CLOSED SOCKET HANDLE - 0x3cc
CLOSED SOCKET HANDLE - 0x4fc
CLOSED SOCKET HANDLE - 0x4e0
OPEN SOCKET HANDLE - 0xfd4
OPEN SOCKET HANDLE - 0x7d8
OPEN SOCKET HANDLE - 0xf8c
OPEN SOCKET HANDLE - 0xf88
OPEN SOCKET HANDLE - 0xae0
OPEN SOCKET HANDLE - 0xe58
OPEN SOCKET HANDLE - 0xdfc
OPEN SOCKET HANDLE - 0xcf8
OPEN SOCKET HANDLE - 0xa18
OPEN SOCKET HANDLE - 0x7a0
OPEN SOCKET HANDLE - 0x7b0
OPEN SOCKET HANDLE - 0x534
OPEN SOCKET HANDLE - 0xcdc
OPEN SOCKET HANDLE - 0x1f0
OPEN SOCKET HANDLE - 0x444
OPEN SOCKET HANDLE - 0x8bc
 
0:008> !avrf -net -socket dump -v 0x47c
 
The socket handle is closed
 
vfNet!VfHookclosesocket
WININET!ICSocket::_UnSafeCloseSocket
WININET!ICSocket::Dereference
WININET!CFsm_GetConnection::RunSM
WININET!CFsm::Run
WININET!DoFsm
WININET!HTTP_REQUEST_HANDLE_OBJECT::OpenConnection_Fsm
WININET!CFsm_OpenConnection::RunSM
WININET!CFsm::Run
WININET!DoFsm
WININET!HTTP_REQUEST_HANDLE_OBJECT::OpenConnection
WININET!HTTP_REQUEST_HANDLE_OBJECT::MakeConnection_Fsm
WININET!CFsm_MakeConnection::RunSM
WININET!CFsm::Run
WININET!DoFsm
WININET!HTTP_REQUEST_HANDLE_OBJECT::SendRequest_Fsm
WININET!CFsm_SendRequest::RunSM
WININET!CFsm::Run
WININET!DoFsm
WININET!HTTP_REQUEST_HANDLE_OBJECT::HttpSendRequest_Start
WININET!CFsm_HttpSendRequest::RunSM
WININET!CFsm::Run
WININET!CFsm::RunWorkItem
SHLWAPI!ExecuteWorkItemThreadProc
vfbasics!AVrfpRtlWorkerCallback
ntdll!RtlpTpWorkCallback
ntdll!TppWorkerThread
kernel32!BaseThreadInitThunk
ntdll!__RtlUserThreadStart
ntdll!_RtlUserThreadStart

!avrf -net -wsastacks [count]

Winsock 要求應用程式開發人員至少呼叫 WSAStartup() 一次,再進行任何 Winsock 呼叫。 這會由 Winsock 進程追蹤。 初始參考計數會指示 Winsock 連結庫 (ws2_32.dll) 初始化和載入 Winsock 目錄和提供者。 對 WSAStartup 的進一步呼叫會遞增該參考計數。 Winsock 也需要應用程式開發人員在呼叫 Winsock 時呼叫 WSACleanup()。 WSACleanup 的呼叫必須正確地與先前對 WSAStartup() 的呼叫配對。 對 WSACleanup() 的呼叫會遞減整個進程的參考計數。 當參考計數降至零時,Winsock 會釋放其資源,並卸除 Winsock 目錄和提供者。

此命令會提供目前 「WSAStartup」 初始化例程的整體參考計數值,並列出呼叫程式中對 WSAStartup 和 WSACleanup 呼叫的呼叫堆棧。 請注意,這會在固定的迴圈佇列中維護,因此不保證會完成 -- 只有 N 個最近的呼叫。

以下是各種 -wsastacks 使用選項的範例:

0:008> !avrf -net -wsastacks count
 
Current WSARefCount: 1 (WSAStartup call count minus WSACleanup call count for the target process)
 
 
0:008> !avrf -net -wsastacks
 
Current WSARefCount: 1 (WSAStartup call count minus WSACleanup call count for the target process)
 
 
THREAD ID: 0xe4c called WSAStartup
vfNet!WSAInitStacks<NetAllocatorViaPrivateHeap>::AddWSAStackTrace
vfNet!VfHookWSAStartup
WININET!LoadWinsock
WININET!GlobalDataInitialize
WININET!InternetSetOptionA
WININET!InternetSetOptionW
IEFRAME!LCIEUpdateSessionStartTime
IEFRAME!LCIETab_ThreadProc
iertutil!_IsoThreadProc
vfbasics!AVrfpStandardThreadFunction
kernel32!BaseThreadInitThunk
ntdll!__RtlUserThreadStart
ntdll!_RtlUserThreadStart

NTLM

此應用程式驗證器外掛程式會監視個別進程對驗證 API AcquireCredentialsHandle 和 InitializeSecurityContext 的呼叫,以偵測 NTLM 通訊協定的使用方式。 NTLM 是過時的驗證通訊協定,有可能會危害應用程式和操作系統的安全性,因此不應使用。

NTLM 驗證風險

過時 NTLM 驗證通訊協定最重要的缺點是缺少伺服器驗證,這可讓攻擊者誘使用戶聯機到詐騙伺服器。 作為遺漏伺服器驗證的必然結果,使用NTLM的應用程式也可能容易受到稱為「反映」攻擊的攻擊類型。 後者可讓攻擊者將使用者的驗證交談劫持至合法伺服器,並用它來向使用者的計算機驗證攻擊者。 NTLM 的弱點和利用方式是安全性社群中增加研究活動的目標。

雖然 Kerberos 已可供使用多年,但許多應用程式仍會撰寫為僅使用 NTLM。 這不需要減少應用程式的安全性。 不過,Kerberos 在所有案例中都無法取代 NTLM,主要是用戶端需要向未加入網域的系統進行驗證的案例(家庭網路可能是其中最常見的網路)。 交涉安全性套件允許回溯相容入侵,盡可能使用 Kerberos,而且只有在沒有其他選項時才會還原為 NTLM。 切換程式代碼以使用 Negotiate,而不是 NTLM,將會大幅提升客戶的安全性,同時引進少數或沒有應用程式相容性。 交涉本身不是銀子彈– 在某些情況下,攻擊者可以強制降級為NTLM,但這些攻擊會更加難以惡意探索。 不過,一個立即的改進是,撰寫成正確使用Negotiate的應用程式會自動不受NTLM反映攻擊的影響。

藉由謹慎使用NTLM的最後一句話:在Windows中,可以停用在操作系統層級使用NTLM。 如果應用程式與 NTLM 有硬式相依性,在停用 NTLM 時,它們就根本無法進行驗證。

哪些因素會導致應用程式中的NTLM「硬式編碼」?

有兩個因素會造成NTLM的硬式相依性。 第一個是明確選取NTLM作為應用程式要使用的驗證套件。 對於某些通訊協定和 API,NTLM 的選擇是顯而易見的,例如在 API AcquireCredentialsHandle 的呼叫中。 對於其他通訊協定,可能不太明顯。 例如,RPC 的預設驗證套件 (RPC_C_AUTHN_DEFAULT) 實際上是NTLM的別名,當 RPC 透過網路使用,甚至明確旗標來選取NTLM時,它中的任何位置都沒有NTLM縮寫(RPC_C_AUTH_WINNT)。 這種建構可讓您更輕鬆地選取NTLM,而不需要知道您已這麼做。

為了取代NTLM,開發人員應該使用其他驗證方法,例如交涉套件(這有時也稱為SPNEGO或SNEGO套件)。 套件選取必須符合客戶端和伺服器元件,才能讓 Negotiate 能夠嘗試使用 Kerberos - 因此應用程式用戶端和伺服器元件都需要使用 Negotiate。 如果任一方都使用NTLM(如同舊版的情況),交涉仍可運作,但一律會還原為NTLM。 如何告知您的應用程式使用Negotiate會因通訊協定而異。 一些最常見的通訊協定(RPC、LDAP、DCOM、HTTP)稍後會在主題 5000 - Application Has Explicitly Selected NTLM Package 中詳細說明。

導致 NTLM 使用的第二個因素是用戶端未提供有效的伺服器目標名稱給驗證程式時。 在支援或需要相互驗證的通訊協定中,目標名稱是用來達成相互驗證的目標名稱。 驗證 API(例如 InitializeSecurityContext)採用選擇性參數,通常稱為“TargetName”、“PrincipalName” 或 “ServerPrincipalName”。 這是域控制器用來選取正確的網域帳戶,以取得目標服務的認證。 由於 NTLM 沒有伺服器驗證的概念,因此 NTLM 不需要此參數才能成功驗證。 另一方面,Kerberos 要求用戶端取得服務票證,該票證對用戶端正在驗證的服務有效。 如果未指定目標名稱或無效的目標名稱,Kerberos 驗證一律會失敗。 選取 Negotiate 做為套件時,未提供任何目標名稱(或無效的目標名稱)會導致完全略過 Kerberos,且使用 NTLM。 如果 NULL 沒有錯誤,大部分的驗證 API 都會以選擇性參數的形式接受目標名稱。 除非開發人員覆寫此專案,而且會提供明確的目標名稱NTLM(以及可反映的NTLM)是結果。

NTLM 外掛程式的運作方式

驗證器外掛程式會偵測到下列錯誤:

  • NTLM 套件直接在對 AcquireCredentialsHandle 的呼叫中指定(或更高層級的包裝函式 API)。

  • InitializeSecurityContext 呼叫中的目標名稱為 NULL。 在此情況下,Negotiate 會直接回復為NTLM。

  • 呼叫 InitializeSecurityContext 中的目標名稱不是格式正確的 SPN、UPN 或 NetBIOS 樣式功能變數名稱。 在此情況下,域控制器會傳回「找不到主體」錯誤,這會導致 Negotiate 回復為 NTLM。

外掛程式也會在偵測降級為NTLM時記錄警告;例如,域控制器找不到SPN時。 這些只會記錄為警告,因為它們通常是合法的案例,例如,向未加入網域的系統進行驗證時。

設定外掛程式停止選項

根據預設,分類為 Error 的所有事件都會設定為造成偵錯中斷。 所有警告事件都會設定為只記錄事件詳細數據。

錯誤事件造成停止/中斷:

  • 5000 – 應用程式已明確選取 NTLM 套件

  • 5001 – 交涉套件清單僅包含 NTLM

  • 5002 – 交涉套件列表錯誤的 NTLM 排除

  • 5003 – 伺服器沒有目標名稱或格式不正確的目標名稱

記錄的警告事件:

  • 5010 – 降級至偵測到 NTLM

NTLM 停止

5000 – 應用程式已明確選取 NTLM 套件

嚴重性 – 錯誤

應用程式或子系統會在呼叫 AcquireCredentialsHandle 時明確選取 NTLM,而不是 Negotiate。 即使客戶端和伺服器可能可以使用 Kerberos 進行驗證,但 NTLM 的明確選取還是會防止這種情況發生。

如何修正此錯誤

此錯誤的修正方式是選取交涉套件來取代NTLM。 完成此作業的方式將取決於客戶端或伺服器所使用的特定網路子系統。 以下提供一些範例。 您應該參閱您所使用之特定連結庫或 API 集合的檔案。

應用程式所使用的 API(參數 ) 不正確的值 正確值 備註
AcquireCredentialsHandle (pszPackage) “NTLM” NEGOSSP_NAME或「交涉」
RPC 用戶端:RPCBindingSetAuthInfoEx RPCBindingSetAuthInfoEx (AuthnSv) RPC 伺服器:RPCServerRegisterAuthInfo(AuthnSvc) RPC_C_AUTHN_WINNT或RPC_C_AUTH_DEFAULT RPC_C_AUTH_GSS_NEGOTIATE RPC 伺服器註冊 NTLM/WINNT 套件並非錯誤。 這通常需要支援僅支援NTLM的舊版用戶端。 如果只註冊 NTLM 套件,就會發生錯誤,因為這會強制所有用戶端使用 NTLM,即使它們能夠使用 Kerberos 也一樣。
DCOM:SetBlanket CoSetProxyBlanket (dwAuthnSvc) CoCreateInstanceEx (傳遞為 COAUTHINFO 結構的 dwAuthnSvc 成員,其本身是傳遞至 API 的 COSERVERINFO 結構成員) RPC_C_AUTHN_WINNT RPC_C_AUTHN_DEFAULT或RPC_C_AUTHN_GSS_NEGOTIATE 只有當通訊一律在網路上發生時,才應該使用交涉。 如果在相同電腦上的用戶端和伺服器之間發生 DCOM 呼叫,您必須使用 DEFAULT 並允許 DCOM 選擇要使用的正確套件。
LDAP:ldap_bind_s (方法) LDAP_AUTH_NTLM LDAP_AUTH_NEGOTIATE
HTTP WinHTTPSetCredentials (AuthScheme) WINHTTP_AUTH_SCHEME_NTLM WINHTTP_AUTH_SCHEME_NEGOTIATE

5001 – 交涉套件清單僅包含 NTLM

嚴重性 – 錯誤

使用 AcquireCredentialsHandle 時,可能會提供交涉所要使用的或忽略套件清單。 視指定的清單而定,這可能會覆寫 Negotiate 內建的邏輯,以選擇最適當且安全的驗證套件。 如果套件清單只包含 NTLM 或排除 Kerberos,則結果與完全略過 Negotiate 相同,並明確選取 NTLM SSP 套件。

只有在直接呼叫 AcquireCredentialsHandle 時,才能指定子套件清單,因為大多數較高層 API(例如 RPC)都不允許呼叫者控制交涉套件清單。

Microsoft 不建議應用程式嘗試以這種方式操作交涉套件清單。

如何修正此錯誤

使用 Negotiate 套件而不指定子套件清單,或確保包含 Kerberos。

應用程式所使用的 API(參數 ) 不正確的值 正確值
AcquireCredentialsHandle (傳遞為 pAuthData 參數之SEC_WINNT_AUTH_IDENTITY_EX結構的 PackageList 成員) "!Kerberos“ 或 ”NTLM” NULL 或 “Kerberos、NTLM” 或 “Kerberos, !NTLM“ 或 ”!NTLM”

5002 – 交涉套件列表錯誤的 NTLM 排除

嚴重性 - 警告

呼叫 AcquireCredentialsHandle 時,應用程式嘗試從 Negotiate 支援的套件清單中排除 NTLM。 不過,錯誤語法已用來排除NTLM,因此會保留在清單中。

如何修正此錯誤:使用下列語法將 NTLM 套件從 Negotiate 中排除:

應用程式所使用的 API(參數 ) 不正確的值 正確值
AcquireCredentialsHandle (傳遞為 pAuthData 參數之SEC_WINNT_AUTH_IDENTITY_EX結構的 PackageList 成員) “-NTLM” "!NTLM”

5003 – 伺服器沒有目標名稱或格式不正確的目標名稱

嚴重性 – 錯誤

使用 Negotiate 套件時,提供 Null 或無效的目標名稱(有時稱為主體名稱)會導致 Kerberos 失敗,且 NTLM 會用於其位置。 進行驗證呼叫時,您應該一律指定有效的目標名稱。 目標名稱是唯一標識元,可讓域控制器取得應用程式嘗試驗證之伺服器的帳戶詳細數據。 域控制器擁有這項資訊后,就可以建置用戶端和伺服器可瞭解的適當 Kerberos 票證(可解密)。

如何修正此錯誤

目標名稱可以用三種不同的格式指定,每個格式都可以由域控制器用來找出正確的伺服器帳戶物件。 這些格式為服務主體名稱 (SPN)、用戶主體名稱 (UPN) 和 NetBIOS 兩部分的網域帳戶名稱。 SPN 是最常見的形式,與其他 Kerberos 實作互通。 SPN 的完整討論超出本文件的範圍,但最簡單的且最常見的SPN表單有兩個部分 – 服務類別和主機名。 服務類別會識別伺服器應用程式的類型(例如 HTTP 或ldap或泛型作為主機的特定應用程式類型)。第二個部分是伺服器的完整功能變數名稱或一般 (NetBIOS) 名稱。 Windows 用戶端和伺服器會自動為 FQDN 和一般名稱註冊“host” 的 SPN。 域控制器也會將大約 40 個應用程式特定服務類別對應至 “host” SPN,例如 “HTTP”、“ldap”、“rpc”、“tapi” 等專案。

o 指定伺服器操作系統內容中執行之應用程式的目標名稱(例如localsystem、network service 或localservice)用戶端應用程式可以使用自動註冊的「主機」SPN 或其別名之一。 若要向網域用戶帳戶內容中執行的應用程式進行驗證,您必須註冊該帳戶的SPN。

針對用戶帳戶,您也可以使用從用戶帳戶名稱和帳戶所在的網域建置的隱含 UPN 表單: useraccountname@domain.dom。 雖然您可以為用戶帳戶建立額外的 UPN(使用每個網域可以建立的 UPN 後綴),但這些都無法做為 Kerberos 目標名稱 -- 只有對應到實際登入賬戶名稱和實際網域的 UPN,帳戶所在的實際網域才能使用。

最後,您仍然可以使用NT4樣式網域\用戶名稱(或網域\計算機名稱,以本機系統、網路服務或localservice執行的服務)。 這適用於在網域用戶帳戶或計算機帳戶內容中執行的目標。

應用程式所使用的 API(參數 ) 設定目標名稱的參數 備註
InitializeSecurityContext pszTargetName
RPC 用戶端:RPCBindingSetAuthInfoEx RPCBindingSetAuthInfoEx (AuthnSv) ServerPrincipalName 這應該是伺服器/服務執行所在的帳戶目標名稱。 它不必與 RPCServerRegisterAuthInfo 中設定的值相同
DCOM:SetBlanket CoSetProxyBlanket (dwAuthnSvc) CoCreateInstanceEx (傳遞為 COAUTHINFO 結構的 dwAuthnSvc 成員,其本身是傳遞至 API 的 COSERVERINFO 結構成員) pServerPrincName 可以使用COLE_DEFAULT_PRINCIPAL讓 COM 自動從系結資訊中選取名稱
LDAP:無 LDAP 用戶端自動產生。
HTTP 無 WinHTTP 和 WinInet 會從 URL 伺服器名稱提供 targetname

5010 – 降級至偵測到 NTLM

嚴重性 - 警告

即使應用程式特定的 Negotiate 並使用正確格式化的目標名稱,也會導致 Negotiate 降級為 NTLM。 視情況而定,這可能表示錯誤或預期的行為。 例如,當計算機不是網域的一部分,或正在使用域控制器無法存取的位置時,預期 Negotiate 會以無訊息方式降級,讓應用程式使用 NTLM 進行驗證。 不過,如果域控制器可供使用,而且您通常會預期 Kerberos 會使用,這幾乎肯定會指出發生錯誤。

如何修正此錯誤

假設您已判斷 Kerberos 應該已使用,而不是在此情況下使用 NTLM,則有許多可能性會導致降級:

• 目標名稱,即使其格式可能正確,但不存在於定義域(或樹系)。

o 您應該檢查您要在用戶端應用程式中建置正確的目標名稱。 服務類別是否正確? 主機名是否正確?

o 這是在電腦或其他網域帳戶內容中執行的伺服器進程。 在先前的案例中,SPN 會自動註冊,在後者中,您可能必須註冊 SPN 或使用替代形式,例如隱含 UPN 或一般名稱。

o 是否有網路連線問題導致無法與域控制器或 DNS 伺服器通訊?

o 目標 SPN 是否在多個帳戶上註冊? 這會導致域控制器拒絕驗證嘗試。

列印

列印驗證器可協助尋找並針對應用程式呼叫列印子系統時可能造成的問題進行疑難解答。 列印驗證器的目標是列印子系統的兩個層:PrintAPI 層和 PrintDriver 層。

列印 API 層

列印驗證程式會測試程式與 Winspool.drv 之間的介面,並prntvpt.dll測試這些 DLL 的介面。 您可以在 MSDN 說明區段中檢閱 winspool.drv 和 prntvpt.dll 匯出之 API 的呼叫函式規則。

列印驅動程式層

列印驗證器也會測試核心列印驅動程式之間的介面,例如UNIDRV.DLL、UNIDRUI.DLL、PSCRIPT5.DLL、PS5UI.DLL或MXDWDRV.DLL,以及列印驅動程式外掛程式外掛程式。您可以在 MSDN 和 WDK 中找到這個介面的相關信息。

一般而言,只有偵錯版本會執行應用程式驗證器,因此效能通常不是問題。 如果使用此檢查或任何其他應用程式驗證程式檢查時發生效能問題,請一次執行一次檢查,直到您執行所有必要的檢查為止。

WebServices

Windows Webservices API (WWSAPI) 驗證層

WWSAPI 外掛程式可讓開發人員擷取下列實例:

  • 所呼叫的 WWSAPI 會參考無效的內部 WWSAPI 物件。

  • 所呼叫的 WWSAPI 會參考已經在使用中的單個線程物件。

  • 使用異步呼叫暫止釋放的內部物件。

  • 從短線程呼叫封鎖 API。

此外,此外掛程式:

  • 追蹤物件的使用,從具現化到刪除。

  • 強制以異步方式完成的呼叫,以同步方式完成。 這是為了防止應用程式根據呼叫採取WS_ASYNC_CONTEXT的特定行為。

  • 當 API 傳遞不正確的物件時,或物件正在使用 !avrf –ws –obj 調試程式延伸模組時,提供人類可讀取的描述(如下所示)

  • 針對通道、服務 Proxy 和服務主機,每個追蹤的呼叫都會顯示物件的目前狀態。

根據預設,會啟用下列檢查程式:

屬性名稱DescriptionValidateObjectValidates,內部物件有效TrackObjectTrack 會在其存留期內使用物件

可透過屬性 UI 在此提供者中啟用的其他檢查程式如下:

屬性 NameDescriptionCheckTimeoutValidates,異步函式會在逾時內完成,指定為 TimeoutValForceSyncForce,這是將WS_ASYNC_CONTEXT內容提供給 API 時要採取的同步路徑。

提供調試程式延伸模組 (!avrf –ws –obj),其會顯示已開啟和關閉的內部 WWSAPI 物件。 如果這個延伸模組是對象後綴,則會顯示此物件使用方式的詳細資訊。

!avrf -ws –obj

此命令會顯示正在追蹤的內部 WWSAPI 物件,這些物件會同時建立和關閉。 請注意,封閉的物件會儲存在迴圈佇列中,因此追蹤的物件總數有上限。

成功完成下列 API 時會新增物件:WsCreateChannel()、WsCreateChannelForListener()、WsCreateServiceHost()、WsCreateServiceProxy()、WsCreateServiceProxyFromTemplate()、WsCreateError()、WsCreateHeap()、WsCreateHeap()、 WsCreateListener(), WsCreateMetadata(), WsCreateMessage(), WsCreateMessageForChannel(), WsCreateReader(), WsCreateWriter(), WsCreateXmlBuffer(), WsReadXmlBuffer(), WsReadXmlBufferFromBytes()

當呼叫並完成對應的 WsFree*() 函式時,物件會從建立的 移至釋放清單。

!avrf –ws –obj [OBJECT]

此命令會顯示內部 WWSAPI 物件的使用方式。 使用方式資訊會在建立、使用和釋放物件時包含堆疊。 如果對像是通道、服務主機或服務 Proxy,則會在使用 物件呼叫 API 之前顯示物件的狀態。

以下是 !avrf –ws –obj 使用選項的範例:

0:001> !avrf -ws -obj
Objects dependent on internal objects allocated:


Objects currently allocated:

 0x00000000048566C0 (Type=Heap, Thread=0x000001bc, Pending Operations=0)
 0x0000000001BE6780 (Type=Error, Thread=0x000001bc, Pending Operations=0)
 0x0000000001C13580 (Type=Service Proxy, Thread=0x000001bc, Pending Operations=0)

Freed objects:

 0x0000000001C17170 (Type=Service Proxy, Thread=0x000001bc)
 0x0000000004856730 (Type=Heap, Thread=0x000001bc)
 0x0000000001BE6820 (Type=Error, Thread=0x000001bc)

0:001> !avrf -ws -obj 0x0000000001C13580

Object @ 0x0000000001C13580
        Type = Service Proxy
        Thread = 0x000001bc
        Internal Reference = 0x00000000026C5E80

Created stack:
  vfnws!VfHookWsCreateServiceProxy+0x00aa
  BLUESTONE!WST_WebServices::WsCreateServiceProxy+0x00d8
  BLUESTONE!ServiceProxy::Connect+0x0116
  BLUESTONE!ServiceModel_SimpleTest::SimpleClient+0x0607
  BLUESTONE!ServiceModelTestGroup_Simple_Test02_Run+0x0041
  BLUESTONE!Fnshell2::FnshellConfiguration::RunTest+0x002e
  BLUESTONE!Fnshell2::TESTCASE::Run+0x00d6
  BLUESTONE!fnsMsgProc+0x02d6
  BLUESTONE!fnsRunTestsWorkerThread+0x085f
  KERNEL32!BaseThreadInitThunk+0x000d
  ntdll!RtlUserThreadStart+0x001d

Last 4 operations

Operation #1 created in thread 0x00000000000001BC

Service proxy state before operation = Created

Callstack:
  vfnws!VfHookWsGetServiceProxyProperty+0x0053
  BLUESTONE!WST_WebServices::WsGetServiceProxyProperty+0x009b
  BLUESTONE!ServiceProxy::GetState+0x004b
  BLUESTONE!ServiceProxy::VerifyState+0x001c
  BLUESTONE!ServiceProxy::Connect+0x01c7
  BLUESTONE!ServiceModel_SimpleTest::SimpleClient+0x0607
  BLUESTONE!ServiceModelTestGroup_Simple_Test02_Run+0x0041
  BLUESTONE!Fnshell2::FnshellConfiguration::RunTest+0x002e
  BLUESTONE!Fnshell2::TESTCASE::Run+0x00d6
  BLUESTONE!fnsMsgProc+0x02d6
  BLUESTONE!fnsRunTestsWorkerThread+0x085f
  KERNEL32!BaseThreadInitThunk+0x000d
  ntdll!RtlUserThreadStart+0x001d

Operation #2 created in thread 0x00000000000001BC

Service proxy state before operation = Created

Callstack:
  vfnws!VfHookWsOpenServiceProxy+0x0079
  BLUESTONE!WST_WebServices::WsOpenServiceProxy+0x0092
  BLUESTONE!ServiceProxy::Connect+0x03d3
  BLUESTONE!ServiceModel_SimpleTest::SimpleClient+0x0607
  BLUESTONE!ServiceModelTestGroup_Simple_Test02_Run+0x0041
  BLUESTONE!Fnshell2::FnshellConfiguration::RunTest+0x002e
  BLUESTONE!Fnshell2::TESTCASE::Run+0x00d6
  BLUESTONE!fnsMsgProc+0x02d6
  BLUESTONE!fnsRunTestsWorkerThread+0x085f
  KERNEL32!BaseThreadInitThunk+0x000d
  ntdll!RtlUserThreadStart+0x001d

Operation #3 created in thread 0x00000000000001BC

Service proxy state before operation = Open

Callstack:
  vfnws!VfHookWsGetServiceProxyProperty+0x0053
  BLUESTONE!WST_WebServices::WsGetServiceProxyProperty+0x009b
  BLUESTONE!ServiceProxy::GetState+0x004b
  BLUESTONE!ServiceProxy::VerifyState+0x001c
  BLUESTONE!ServiceProxy::Connect+0x0484
  BLUESTONE!ServiceModel_SimpleTest::SimpleClient+0x0607
  BLUESTONE!ServiceModelTestGroup_Simple_Test02_Run+0x0041
  BLUESTONE!Fnshell2::FnshellConfiguration::RunTest+0x002e
  BLUESTONE!Fnshell2::TESTCASE::Run+0x00d6
  BLUESTONE!fnsMsgProc+0x02d6
  BLUESTONE!fnsRunTestsWorkerThread+0x085f
  KERNEL32!BaseThreadInitThunk+0x000d
  ntdll!RtlUserThreadStart+0x001d

Operation #4 created in thread 0x00000000000001BC

Service proxy state before operation = Open

Callstack:
  vfnws!VfHookWsCall+0x00a6
  BLUESTONE!DefaultBinding_ICalculator_Add+0x008b
  BLUESTONE!ServiceModelTestGroup_Simple_Function+0x010a
  BLUESTONE!ServiceModel_SimpleTest::SimpleClient+0x069a
  BLUESTONE!ServiceModelTestGroup_Simple_Test02_Run+0x0041
  BLUESTONE!Fnshell2::FnshellConfiguration::RunTest+0x002e
  BLUESTONE!Fnshell2::TESTCASE::Run+0x00d6
  BLUESTONE!fnsMsgProc+0x02d6
  BLUESTONE!fnsRunTestsWorkerThread+0x085f
  KERNEL32!BaseThreadInitThunk+0x000d
  ntdll!RtlUserThreadStart+0x001d

Asynchronous Callback = BLUESTONE!ServiceModelTestGroup_Simple_Callback
Asynchronous CallbackState = 0x0000000005EBDC30

Completed asynchronously with HRESULT=0x00000000 in thread 0x00000000000001BC

Asynchronous callback stack:
  vfnws!VfHookWsCall+0x00e3
  BLUESTONE!DefaultBinding_ICalculator_Add+0x008b
  BLUESTONE!ServiceModelTestGroup_Simple_Function+0x010a
  BLUESTONE!ServiceModel_SimpleTest::SimpleClient+0x069a
  BLUESTONE!ServiceModelTestGroup_Simple_Test02_Run+0x0041
  BLUESTONE!Fnshell2::FnshellConfiguration::RunTest+0x002e
  BLUESTONE!Fnshell2::TESTCASE::Run+0x00d6
  BLUESTONE!fnsMsgProc+0x02d6
  BLUESTONE!fnsRunTestsWorkerThread+0x085f
  KERNEL32!BaseThreadInitThunk+0x000d
  ntdll!RtlUserThreadStart+0x001d


Closed stack:

0:001>

服務

服務會測試,檢查是否適當地使用 Windows 服務。 例如,服務正在啟動並正確停止。 如需這些測試所產生的停止程式代碼例外狀況資訊,請參閱 應用程式驗證器 - 停止程式代碼 - 服務

效能

Perf 測試會檢查對影響系統效能和耗用量的 API 有效率的使用,例如呼叫使用不正確等候週期的 Windows 函式。 如需這些測試所產生的停止程式代碼例外狀況資訊,請參閱 應用程式驗證器 - 停止代碼 - Perf

Hangs 會測試使用導致系統沒有回應的 API,例如 DllMain 線程正在等候另一個封鎖的線程。 如需這些測試所產生的停止程式代碼例外狀況資訊,請參閱 應用程式驗證器 - 停止代碼 - 停止回應

ARM64EC支援

下列提供者不支援ARM64EC,因此會在 ARM64EC 中執行的程序當機:

另請參閱

應用程式驗證器 - 概觀

應用程式驗證器 - 功能

應用程式驗證器 -測試應用程式

應用程式驗證器 - 停止程式代碼和定義

應用程式驗證器 - 偵錯應用程式驗證器停止

應用程式驗證器 - 常見問題