65.9K
CodeProject 正在变化。 阅读更多。
Home

Wi-Fi 凭据的秘密

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.99/5 (41投票s)

2017 年 1 月 9 日

CPOL

5分钟阅读

viewsIcon

41719

downloadIcon

2349

如何获取和解密 Wi-Fi 存储的凭据

引言

当您连接到 Wi-Fi 网络并选择保存凭证时,这些凭证不仅可以被 Windows 检索,也可以被任何知道在哪里以及如何查找它们的人检索。我们为此开发了一个名为 GetWifiData小型实用工具,用于显示所有已存储的 Wi-Fi 数据。我们将维护此工具,并在一个专用网站上发布更新。另请参阅如何获取 Skype 账户详情

文章目的

过去(Windows XP),凭证存储在注册表中,但自 Windows 7 起,凭证存储在单独的 XML 文件中。原生 Wi-Fi 还提供了一种集中访问凭证的方式,而 Windows 加密则提供了解密加密密钥的必要功能。关于 Wi-Fi 凭证已经发表了一些文章,但其中一些已经过时,因此我写了这篇文章并创建了该实用工具来显示所有已存储的 Wi-Fi 凭证。

Wi-Fi 凭证的存储方式

当您连接到 Wi-Fi 网络并选择保存凭证时,这些凭证不仅可以被 Windows 检索,也可以被任何知道在哪里以及如何查找它们的人检索。

不同 Windows 版本的位置不同。

在 Windows XP 上,该位置位于以下注册表位置

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WZCSVC\Parameters\Interfaces\

在 Windows Vista、7、8、8.1 和 10 上,该位置在文件中(不在注册表中)

C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces\
   {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\{Random-GUID}.xml

在每个接口下,您可以找到每个存储网络的单独文件。

Windows 原生 Wi-Fi

较新的 Windows “原生 Wi-Fi”提供了一种更好的方式来访问 Wi-Fi 凭证,因为它是任何 API 调用(用于自动配置组件、连接或断开 Wi-Fi 网络)的前端。此外,Windows 原生 Wi-Fi 可以 XML 文档的形式存储其交互的网络配置文件。

本文的源代码使用一种简单的方法从这些 XML 文件中提取每个元素,并生成所有已存储 Wi-Fi 凭证的报告。

我们按如下方式初始化程序

WLAN_INTERFACE_INFO_LIST* pWirelessAdapterList = NULL;
dwResult = WlanEnumInterfaces(hWlan, NULL, &pWirelessAdapterList);
if (dwResult != ERROR_SUCCESS)
{
    WlanCloseHandle(hWlan, NULL);
    return result;
}

int nResCount = 1;
WLAN_INTERFACE_INFO* pWirelessAdapterInfo = NULL;

XML 凭证文件的解释

我们以一个示例凭证文件为例。文件名是

C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces\
{9D0E4D68-B83A-4745-8021-DE4381E509BF}\{5094B710-D0BF-473A-BC7D-A51D4885F681}.xml

如您在上面照片中看到的,该文件包含来自印第安纳波利斯一家很棒的 B&B 酒店的名为 Villa_Carriagehouse 的 Wi-Fi 网络的凭证。几个月前我们住在那儿(顺便说一句,那是一家很棒的酒店),所以凭证存储在我的电脑上,并且可以像您所见那样被检索。

回到我们的主题...

Windows 使用 WLAN_profile Schema 来定义每个 WLAN 的配置文件,使用以下 XML 元素

  • SSID - 可以找到明文和十六进制版本,并包含无线局域网的 SSID。
  • name - 'name' 元素是明文形式的 SSID
  • authentication - 'authentication' 元素指定要使用的认证方法。
  • encryption - 'encryption' 元素指定要使用的数据加密类型。
  • keyMaterial - 'keyMaterial' 元素包含网络密钥或密码短语。如果 protected 元素的值为 TRUE,则此密钥材料已加密;否则,密钥材料未加密。加密的密钥材料以十六进制形式表示。

Wi-Fi 凭证如何解密

当 Wi-Fi 凭证被加密时,可以使用 Windows 加密来解密它们。Microsoft 加密技术包括 CryptoAPI、加密服务提供程序 (CSP)、CryptoAPI 工具、CAPICOM、WinTrust、证书的发布和管理以及可自定义公钥基础设施的开发。

首先,我们需要定位一个名为 keyMaterial 的元素,并将其隔离到一个字符串变量(strKey)中。

给定 strKey 是一个包含加密的 Wi-Fi 凭证密钥(密码)的 CString 变量,以下代码块使用 CryptUnprotectedData 将其解密。

BYTE byteKey[1024] = { 0 };
DWORD dwLength = 1024;
DATA_BLOB dataOut, dataVerify;

BOOL bRes = CryptStringToBinary
(strKey, strKey.GetLength(), CRYPT_STRING_HEX, byteKey, &dwLength, 0, 0);

if (bRes)
{
        dataOut.cbData = dwLength;
        dataOut.pbData = (BYTE*)byteKey;

        if (CryptUnprotectData(&dataOut, NULL, NULL, NULL, NULL, 0, &dataVerify))
        {
                TCHAR str[MAX_PATH] = { 0 };
                wsprintf(str, L"%hs", dataVerify.pbData);
                strKey = str;
        }
}

结果,strKey 现在将包含给定 Wi-Fi 网络的解密密码。

总结

现在,我们需要做的就是遍历每个 Wi-Fi/网络接口,然后对于每个接口,遍历每个 XML 配置文件,并将该配置文件的 Wi-Fi 凭证提取到一个报告中,显示在屏幕上并保存到文件中。

为此,这里有一些我们在 Secured Globe, Inc. 使用的辅助函数。

使用 WriteStatus() 进行日志记录

我们出于多种目的使用日志记录,在大多数情况下,我们希望在控制台窗口(即使是基于 UI 的应用程序)以及文本文件(“log”)中看到任何重要事件,以便稍后使用。

void WriteStatus(LPCTSTR lpText, ...)
{
    FILE *fp;
    CTime Today = CTime::GetCurrentTime();
    CString sMsg;
    CString sLine;
    va_list ptr;
    va_start(ptr, lpText);
    sMsg.FormatV(lpText, ptr);

    sLine.Format(L"%s",(LPCTSTR)sMsg);
    _wfopen_s(&fp, utilsLogFilename, L"a");
    if (fp)
    {
        fwprintf(fp, L"%s", sLine);
        fclose(fp);
    }
    wprintf(L"%s", sMsg);
}

基本上,您只需调用 WriteStatus 并提供与 printf(或 wprintf)相同的参数。

GetWifiData 程序的输出就是使用此函数生成的。

确保管理员权限

此类程序需要管理员权限。这可以通过首先强制用户提升权限来实现。

转到项目的属性。然后转到 **链接器** -> **清单文件** -> **UAC 执行级别**,并将值设置为 **requireAdministrator (/level='requireAdministrator')**。

此外,还有一种方法可以在运行时检测执行级别。我们使用以下函数

BOOL IsElevated()
{
    DWORD dwSize = 0;
    HANDLE hToken = NULL;
    BOOL bReturn = FALSE;

    TOKEN_ELEVATION tokenInformation;

    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
        return FALSE;

    if (GetTokenInformation(hToken, TokenElevation, 
        &tokenInformation, sizeof(TOKEN_ELEVATION), &dwSize))
    {
        bReturn = (BOOL)tokenInformation.TokenIsElevated;
    }

    CloseHandle(hToken);
    return bReturn;
}

然后,如果程序未“以管理员身份”运行,您可以警告用户,但这对于本文中描述的项目设置来说并非真正需要,但为了描述 IsElevated() 函数,我在我们的源代码中放置了以下行

    if (!IsElevated()) WriteStatus(L"[!] Running without administrative rights\n");

使用漂亮的控制台窗口显示输出

为了在运行时显示程序的输出,而无需等待日志文件,我们使用以下函数和功能

为了获取桌面度量值,以便将控制台调整到尽可能大的尺寸(尺寸越大 = 要显示的数据越多),我们使用以下函数

void GetDesktopResolution(int& horizontal, int& vertical)
{
    RECT desktop;
    // Get a handle to the desktop window
    const HWND hDesktop = GetDesktopWindow();
    // Get the size of screen to the variable desktop
    GetWindowRect(hDesktop, &desktop);
    // The top left corner will have coordinates (0,0)
    // and the bottom right corner will have coordinates
    // (horizontal, vertical)
    horizontal = desktop.right;
    vertical = desktop.bottom;
}

然后我们使用以下函数控制文本颜色

#define LOG_COLOR_WHITE 7
#define LOG_COLOR_GREEN 10
#define LOG_COLOR_YELLOW 14 
#define LOG_COLOR_MAGENTA 13
#define LOG_COLOR_CIAN 11

void SetColor(int ForgC)
{
    WORD wColor;
    static int LastColor = -1;
    if (LastColor == ForgC) return;
    LastColor = ForgC;
    //This handle is needed to get the current background attribute

    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    //csbi is used for wAttributes word

    if (GetConsoleScreenBufferInfo(hStdOut, &csbi))
    {
        //To mask out all but the background attribute, and to add the color
        wColor = (csbi.wAttributes & 0xF0) + (ForgC & 0x0F);
        SetConsoleTextAttribute(hStdOut, wColor);
    }
    return;
}

当然,这只是一个示例,您可以定义其他颜色并设置更多前景色和背景色的组合以满足您的需求。

然后将所有内容组合在一起,这里有一个示例

SetColor(LOG_COLOR_CIAN);
if (!IsElevated()) WriteStatus(L"[!] Running without administrative rights\n");
WriteStatus(L"WiFi Stored Credentials Report\nproduced by GetWifiData, 
            by Secured Globe, Inc.\n\n");
SetColor(LOG_COLOR_MAGENTA);
WriteStatus(L"http://www.securedglobe.com\n\n\n");
SetColor(LOG_COLOR_CIAN);

源代码

本文附带一个 Visual Studio 2013 Ultimate、C++ 项目。源代码是独立的,可以生成一个可独立运行的可执行文件,无需外部 DLL 且无需预先安装。

历史

  • 2017 年 1 月 9 日:初始版本
© . All rights reserved.