【问题标题】:Decrypt WEP wlan profile key using CryptUnprotectData使用 CryptUnprotectData 解密 WEP wlan 配置文件密钥
【发布时间】:2012-05-26 11:36:48
【问题描述】:

我正在尝试使用CryptUnprotectData 解密 WEP 配置文件的密钥。我获取配置文件密钥的方法是使用 netsh 导出配置文件。

netsh wlan export profile name="MyWEP" folder="./"

现在,我手动将 netsh 命令生成的 .xml 文件中的密钥材料复制到我的程序中。我正在解密的方式是-

DATA_BLOB DataOut, DataVerify;
DataOut.cbData = encryptData.length();
DataOut.pbData = (BYTE*)("I_Manually_Copy_The_WEP_Key_Here");

if (CryptUnprotectData( &DataOut,
                        NULL,
                        NULL,
                        NULL,
                        NULL,
                        0,
                        &DataVerify))
{
    printf("The decrypted data is: %s\n", DataVerify.pbData);
}
else
{
    printf("Failed. Error Code: %d", GetLastError());
}

但我收到错误代码 13 引用无效数据。我究竟做错了什么 ?在 Win 7 及更高版本上,我可以直接使用 WlanGetProfile 和参数 WLAN_PROFILE_GET_PLAINTEXT_KEY 。但我在 Vista 上有 NO 选项,而不是使用 CryptUnprotectData 功能。我看过类似的帖子herehere,但没有得到太多有用的信息。此外,我正在使用具有相同用户登录凭据的同一系统。有人可以建议我如何进行吗?

PS:我已经在 Windows Desktop SDK 论坛上发布了同样的问题,但还没有得到回复。试试我的运气。

【问题讨论】:

    标签: c windows-vista encryption wlanapi wep


    【解决方案1】:

    我喜欢有关 Windows 安全的问题。所以如果我偶尔看到这样的,我会尝试解决它。​​

    在您的情况下,您已经通过使用netsh.exe wlan export profile ... 将数据从 WLAN 配置文件导出到 XML 文件中。该文件包含<keyMaterial> 元素。元素内部的数据是编码为十六进制的二进制数据:(类似于01000000D08C9DDF0115D1118C7A00C0...)。

    所以你首先需要做的就是将字符串解码为二进制数据。您可以使用CryptStringToBinaryCRYPT_STRING_HEX 参数将字符串解码为二进制。

    下一步将用二进制数据填充DATA_BLOB 并调用CryptUnprotectData 以获取结果,但是...有一个小问题。如何在WlanGetProfile的文档中阅读以下内容

    默认情况下,配置文件中返回的 keyMaterial 元素指向 由 pstrProfileXml 加密。如果您的进程在 LocalSystem 帐户在同一台​​计算机上的上下文,然后您可以 通过调用 CryptUnprotectData 函数对密钥材料进行解密。

    Windows Server 2008 和 Windows VistakeyMaterial 元素 在 pstrProfileXml 指向的配置文件模式中返回的是 始终加密。如果您的进程在 LocalSystem 帐户,然后您可以通过调用 CryptUnprotectData 函数。

    为了能够解密密钥,我们必须在 LocalSystem 安全上下文中调用 CryptUnprotectData。如果您的程序已经在 LocalSystem 上下文中运行,您可以直接执行此操作。如果不是这样,但您具有管理权限或至少具有调试权限,您可以从计算机上运行的其他进程“借用”LocalSystem 令牌。例如,可以获取“winlogon.exe”进程的进程令牌并模拟它。

    以下演示程序使用我个人更喜欢的NtQuerySystemInformation 方法(参见我的old answer)枚举进程。可以使用EnumProcesses 或其他众所周知的方式来做同样的事情。这是对我有用的代码

    #include <Windows.h>
    #include <tchar.h>
    #include <stdio.h>
    #pragma comment (lib, "Crypt32.lib")
    
    #define STATUS_SUCCESS               ((NTSTATUS)0x00000000L)
    #define STATUS_INFO_LENGTH_MISMATCH  ((NTSTATUS)0xC0000004L)
    
    typedef enum _SYSTEM_INFORMATION_CLASS {
        SystemProcessInformation = 5
    } SYSTEM_INFORMATION_CLASS;
    
    typedef struct _UNICODE_STRING {
        USHORT Length;
        USHORT MaximumLength;
        PWSTR  Buffer;
    } UNICODE_STRING;
    
    typedef LONG KPRIORITY; // Thread priority
    
    typedef struct _SYSTEM_PROCESS_INFORMATION_DETAILD {
        ULONG NextEntryOffset;
        ULONG NumberOfThreads;
        LARGE_INTEGER SpareLi1;
        LARGE_INTEGER SpareLi2;
        LARGE_INTEGER SpareLi3;
        LARGE_INTEGER CreateTime;
        LARGE_INTEGER UserTime;
        LARGE_INTEGER KernelTime;
        UNICODE_STRING ImageName;
        KPRIORITY BasePriority;
        HANDLE UniqueProcessId;
        ULONG InheritedFromUniqueProcessId;
        ULONG HandleCount;
        BYTE Reserved4[4];
        PVOID Reserved5[11];
        SIZE_T PeakPagefileUsage;
        SIZE_T PrivatePageCount;
        LARGE_INTEGER Reserved6[6];
    } SYSTEM_PROCESS_INFORMATION_DETAILD, *PSYSTEM_PROCESS_INFORMATION_DETAILD;
    
    typedef NTSTATUS (WINAPI *PFN_NT_QUERY_SYSTEM_INFORMATION)(
      IN       SYSTEM_INFORMATION_CLASS SystemInformationClass,
      IN OUT   PVOID SystemInformation,
      IN       ULONG SystemInformationLength,
      OUT OPTIONAL  PULONG ReturnLength
    );
    
    //
    // The function changes a privilege named pszPrivilege for
    // the current process. If bEnablePrivilege is FALSE, the privilege
    // will be disabled, otherwise it will be enabled.
    //
    BOOL SetCurrentPrivilege (LPCTSTR pszPrivilege,   // Privilege to enable/disable
                              BOOL bEnablePrivilege)  // to enable or disable privilege
    {
        HANDLE hToken;
        TOKEN_PRIVILEGES tp;
        LUID luid;
        TOKEN_PRIVILEGES tpPrevious;
        DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES);
        BOOL bSuccess = FALSE;
    
        if (!LookupPrivilegeValue(NULL, pszPrivilege, &luid)) return FALSE;
    
        if (!OpenProcessToken (GetCurrentProcess(),
                               TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
                               &hToken
                              )) return FALSE;
    
        //
        // first pass.  get current privilege setting
        //
        tp.PrivilegeCount           = 1;
        tp.Privileges[0].Luid       = luid;
        tp.Privileges[0].Attributes = 0;
    
        AdjustTokenPrivileges(
                hToken,
                FALSE,
                &tp,
                sizeof(TOKEN_PRIVILEGES),
                &tpPrevious,
                &cbPrevious);
    
        if (GetLastError() == ERROR_SUCCESS) {
            //
            // second pass.  set privilege based on previous setting
            //
            tpPrevious.PrivilegeCount     = 1;
            tpPrevious.Privileges[0].Luid = luid;
    
            if(bEnablePrivilege)
                tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
            else
                tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED &
                    tpPrevious.Privileges[0].Attributes);
    
            AdjustTokenPrivileges(
                    hToken,
                    FALSE,
                    &tpPrevious,
                    cbPrevious,
                    NULL,
                    NULL);
    
            if (GetLastError() == ERROR_SUCCESS) bSuccess=TRUE;
    
            CloseHandle(hToken);
        }
        else {
            DWORD dwErrorCode = GetLastError();
    
            CloseHandle(hToken);
            SetLastError(dwErrorCode);
        }
    
        return bSuccess;
    }
    
    DWORD GetProcessIdByProcessName (LPCWSTR pszProcessName)
    {
        SIZE_T bufferSize = 1024*sizeof(SYSTEM_PROCESS_INFORMATION_DETAILD);
        PSYSTEM_PROCESS_INFORMATION_DETAILD pspid = NULL;
        HANDLE hHeap = GetProcessHeap();
        PBYTE pBuffer = NULL;
        ULONG ReturnLength;
        PFN_NT_QUERY_SYSTEM_INFORMATION pfnNtQuerySystemInformation = (PFN_NT_QUERY_SYSTEM_INFORMATION)
            GetProcAddress (GetModuleHandle(TEXT("ntdll.dll")), "NtQuerySystemInformation");
        NTSTATUS status;
        int uLen = lstrlenW(pszProcessName)*sizeof(WCHAR);
    
        __try {
            pBuffer = (PBYTE) HeapAlloc (hHeap, 0, bufferSize);
    #pragma warning(disable: 4127)
            while (TRUE) {
    #pragma warning(default: 4127)
                status = pfnNtQuerySystemInformation (SystemProcessInformation, (PVOID)pBuffer,
                                                      bufferSize, &ReturnLength);
                if (status == STATUS_SUCCESS)
                    break;
                else if (status != STATUS_INFO_LENGTH_MISMATCH) { // 0xC0000004L
                    _tprintf (TEXT("ERROR 0x%X\n"), status);
                    return 1;   // error
                }
    
                bufferSize *= 2;
                pBuffer = (PBYTE) HeapReAlloc (hHeap, 0, (PVOID)pBuffer, bufferSize);
            }
    
            for (pspid = (PSYSTEM_PROCESS_INFORMATION_DETAILD)pBuffer; ;
                 pspid = (PSYSTEM_PROCESS_INFORMATION_DETAILD)(pspid->NextEntryOffset + (PBYTE)pspid)) {
    
                if (pspid->ImageName.Length == uLen && lstrcmpiW(pspid->ImageName.Buffer, pszProcessName) == 0)
                    return (DWORD)pspid->UniqueProcessId;
    
                if (pspid->NextEntryOffset == 0) break;
            }
        }
        __finally {
            pBuffer = (PBYTE) HeapFree (hHeap, 0, pBuffer);
        }
        return 0;
    }
    
    int _tmain()
    {
        BOOL bIsSuccess, bImpersonated = FALSE;
        HANDLE hProcess = NULL, hProcessToken = NULL;
        DATA_BLOB DataOut, DataVerify;
        // !!! in the next line you should copy the string from <keyMaterial>
        WCHAR szKey[] = L"01000000D08C9DDF0115D1118C7....";
        BYTE byKey[1024];
        DWORD cbBinary, dwFlags, dwSkip;
        DWORD dwProcessId = GetProcessIdByProcessName(L"winlogon.exe");
        if (dwProcessId == 0) return 1;
    
        bIsSuccess = SetCurrentPrivilege(SE_DEBUG_NAME, TRUE);
        if (!bIsSuccess) return GetLastError();
    
        __try {
            hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, dwProcessId);
            if (!hProcess) __leave;
            bIsSuccess = OpenProcessToken (hProcess, MAXIMUM_ALLOWED, &hProcessToken);
            if (!bIsSuccess) __leave;
            bIsSuccess = ImpersonateLoggedOnUser(hProcessToken);
            if (!bIsSuccess) __leave;
            bImpersonated = TRUE;
    
            cbBinary = sizeof(byKey);
            bIsSuccess = CryptStringToBinary (szKey, lstrlenW(szKey), CRYPT_STRING_HEX, // CRYPT_STRING_HEX_ANY
                byKey, &cbBinary, &dwSkip, &dwFlags);
            if (!bIsSuccess) __leave;
            DataOut.cbData = cbBinary;
            DataOut.pbData = (BYTE*)byKey;
    
            if (CryptUnprotectData (&DataOut, NULL, NULL, NULL, NULL, 0, &DataVerify)) {
                _tprintf(TEXT("The decrypted data is: %hs\n"), DataVerify.pbData);
            }
        }
        __finally {
            if (bImpersonated)
                RevertToSelf();
            if (hProcess)
                CloseHandle(hProcess);
            if (hProcessToken)
                CloseHandle(hProcessToken);
        }
    
        return 0;
    }
    

    【讨论】:

    • 非常感谢您的详细解释。该代码适用于所有网络,部分适用于 WEP 网络。在获取WEP网络的key时,DataVerify.pbData末尾有垃圾值(LLLMEM+,这个值是一致的)。但是,DataVerify.cbData 正确显示了密钥的长度。您能否检查一下您的机器上是否也发生了同样的情况?我正在使用 VS 2010。
    • 我在网络和共享中心 -> 管理无线网络手动添加了一个 WEP 类型的网络。再次,非常感谢:)
    • 我从不关心 LocalSystem 这个词,因为我是以 Administrator 的身份运行的,并且认为 Administrator 是更像一个超级用户,可以做任何事情。
    • @Mahesh:你是对的! DataVerify.pbData 中的数据不能以 NULL 结尾。因此,应该将_tprintf(TEXT("The decrypted data is: %hs\n"), DataVerify.pbData); 行修复为仅显示DataVerify.cbData 字节的数据。例如,可以使用_tprintf(TEXT("The decrypted data is: %*hs\n"), DataVerify.cbData, DataVerify.pbData);,或者只是复制字符串数据并在其中附加\0
    • 好的。非常感谢。你完成了我的工作。感谢您的代码:)
    猜你喜欢
    • 2014-08-29
    • 1970-01-01
    • 2023-01-31
    • 2014-10-24
    • 1970-01-01
    • 2021-02-17
    • 2017-08-18
    • 2019-11-18
    • 2012-04-11
    相关资源
    最近更新 更多