Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Querying Wireless Settings and Decrypting Wireless Key

4.89/5 (18 votes)
14 Apr 2013CPOL5 min read 60.1K   5.1K  
This article explains how to get the wireless settings and decrypting the wireless key using the Crypto APIs.

Introduction    

The attached sample application queries and displays the wireless settings of the connected wireless interface and lists the available networks. The code itself is self-explanatory. The only area that needs some explanation is the part that decrypt's the key cipher text. If you are in Windows 7 or above, then you will be getting the wireless key as plain text and no need to decrypt.  If you are in Windows Vista, you will be getting the wireless key as a cipher text and needs to decrypt.  For the decryption to work, it is good to run as administrator. 

Image 1

Background

This sample uses Native Wifi API’s. The Native Wifi application programming interface (API) functions have two purposes: to manage wireless network profiles and to manage wireless network connections. The API elements  are exposed by the Auto Configuration Module (ACM). The Native Wi-Fi API contains functions, structures, and enumerations that support wireless network connectivity and wireless profile management. The API can be used for both infrastructure and ad hoc networks. 

XP Users: 

A subset of the Native Wi-Fi API functionality is supported on Windows XP with Service Pack 2 (SP2) and Windows XP with Service Pack 3(SP3). This functionality is included in Windows XP with SP3 by default.

In Windows XP with SP2, this functionality can be added by applying a hot fix, which is known as Wireless LAN API for Windows XP with Service Pack 2 (SP2). For more information or to download the hotfix, see Knowledge Base at http://support.microsoft.com/kb/918997.  

Using the code

Querying the Wireless Settings  

The application links to wlan library and connects to the server using WlanOpenHandle and after that enumertates the wireless interfaces and query the wireless settings from the first connected interface.  

C++
#pragma comment(lib, "wlanapi.lib") // WLAN
dwResult = WlanOpenHandle (WLAN_API_MAKE_VERSION(2,0), NULL, &dwWLANVersion , &hClient); 

In XP (SP2/SP3) WlanOpenHandle will return an error message if Wireless Zero Configuration (WCZ) service is not running or not responsive.

The WlanEnumInterfaces function enumerates all of the wireless LAN interfaces currently enabled on the local computer. This function returns a pointer WLAN_INTERFACE_INFO_LIST  if succeeded. From this list WLAN_INTERFACE_INFO represents  each detected interface.

C++
dwResult =  WlanEnumInterfaces (hClient, NULL, &pWInfoList);
for (int i = 0; i < (int)pWInfoList->dwNumberOfItems; i++)
{
    WLAN_INTERFACE_INFO *pInfo = &pWInfoList->InterfaceInfo[i];
    StringFromGUID2 (pInfo->InterfaceGuid, (LPOLESTR) strGuidString, 39);
    wifi.Set_InterfaceGUID (strGuidString);
    wifi.Set_Adapter (pInfo->strInterfaceDescription);
    .....
} 

The WlanEnumInterfacesz function allocates memory for the list of returned interfaces that is returned in the buffer pointed to by the ppInterfaceList parameter when the function succeeds. The memory used  should be released by calling the WlanFreeMemory function after the buffer is no longer needed. 

Once we got a connected interface, we can use WlanQueryInterface to query various parameters of a specified interface. In this example I am only interested in querying the Connection Attributes of the specified interface. From Connection Attributes, we will get Profile, SSID, Signal Strength, Security type and key. There are lot of other useful attributes of the interface you can query. (Radio state, Channel numbers, statistics, etc..)   

C++
dwResult = WlanQueryInterface (hClient, &pInfo->InterfaceGuid, 
                   wlan_intf_opcode_current_connection, 
                   NULL, &dwSize, (PVOID*)&pConnectionAttributes, NULL);
if (dwResult == ERROR_SUCCESS)
{
    wifi.Set_Profile (pConnectionAttributes->strProfileName);
    wifi.Set_SSID (GenerateSSID(pConnectionAttributes->wlanAssociationAttributes.dot11Ssid.ucSSID));
    wifi.Set_Strength (pConnectionAttributes->wlanAssociationAttributes.wlanSignalQuality);
    wifi.Set_Type (pConnectionAttributes->wlanAssociationAttributes.dot11BssType ==
                    2 ? L"Adhoc" : L"Infrastructure");
    ....
    ....
}

Once we got the wireless profile from connection attributes, we can get all other information's regarding that  profile using the WlanGetProfile API. If the WlanGetProfile function succeeds, the wireless profile is returned  in the buffer pointed to by the pstrProfileXml parameter. The buffer contains a string that is the XML representation of the queried profile.  

Image 2

Getting the Wireless Key  (Run the app as administrator) 

Windows 7 and above :  

The keyMaterial element returned in the profile schema pointed to by the pstrProfileXml may be requested as plaintext if the WlanGetProfile function is called with the WLAN_PROFILE_GET_PLAINTEXT_KEY flag set in the value pointed to by the pdwFlags parameter on input.   

For the WlanGetProfile call to return the plain text key, the wlan_secure_get_plaintext_key  permissions from the WLAN_SECURABLE_OBJECT enumerated type must be set on the calling thread. The DACL must also contain an ACE that grants WLAN_READ_ACCESS permission to the access   token of the calling thread. By default, the permissions for retrieving the plain text key is  allowed only to the members of the Administrators group on a local machine.  

If the calling thread lacks the required permissions, the WlanGetProfile function returns the encrypted key in the keyMaterial element of the profile returned in the buffer pointed to by the pstrProfileXml parameter. No error is returned if the calling thread lacks  the required permissions. 

C++
 if(dwOSMajorVersion > 5 && dwOSMinorVersion > 1) //Win7 and above
{
   dwResult = WlanGetProfile (hClient, &pInfo->InterfaceGuid,wifi.Get_Profile().c_str(), 
              NULL, &pProfileXML, &dwFlags, &dwGranted);                           
    if(dwResult == ERROR_SUCCESS)
      {
    wifi.Set_Security(ReadValuefromXML(pProfileXML, L"authentication"));
    wifi.Set_Password(ReadValuefromXML(pProfileXML, L"keyMaterial"));
      }
}

Windows Server 2008 and Windows Vista:    

The keyMaterial element returned in the profile schema pointed to by the pstrProfileXml is always encrypted. If your process runs in the context of the LocalSystem account,  then you can unencrypt key material by calling the CryptUnprotectData function. 

C++
dwResult = WlanGetProfile (hClient, &pInfo->InterfaceGuid,wifi.Get_Profile().c_str(), 
                           NULL, &pProfileXML, 0, 0);
if (dwResult == ERROR_SUCCESS)
{
    wifi.Set_Security(ReadValuefromXML(pProfileXML, L"authentication"));
    std::wstring strKey  = ReadValuefromXML(pProfileXML, L"keyMaterial");
    std::wstring strPWD = DecryptData(strKey);
    wifi.Set_Password(strPWD);
} 

Windows XP with SP3 and Wireless LAN API for Windows XP with SP2:   Its not encrypted.

Decrypting the Key

So to be able to decrypt the key we have to call CryptUnprotectData in LocalSystem security context. If your program is already running under LocalSystem context you can do this directly. If it's not so, but you have administrative rights or you have at least Debug privilege, you can borrow the LocalSystem token from some other process running on the computer. In this example I am getting the process token of "winlogon.exe" process and impersonate it. 

Windows Added Integrity Levels to Process and Secured Objects (Resources) to control access to resource and provide a second level security. (Vista onwards) Each process/Resource is assigned an integrity level (LOW, MEDIUM, HIGH, SYSTEM). When a process is accessing a resource its integrity level is compared with the Resource's integrity level. If integrity level of process is less than resources , access is denied. By default a process launched by administrator has an integrity level of medium, if administrator runs the process in an elevated mode, the integrity level of that process becomes high. But there are certain system resources, process cannot access even its running on high integrity level. (E.g.: Crypto APIs) For that we need to elevate the integrity level of that particular thread to system integrity level. So if SeDebugPrivilege is set on elevated process, the process/thread can access System integrity level resources. This functions shows how to set SeDebugPrivilege. Privilege is stored in Access Token. So we need to add new privilege to the Token as shown below.  

C++
bool CWirelessHelper::SetPrivilege (std::wstring strPrivilege, HANDLE hToken, bool bEnable)
{
    BOOL bReturn = false;
    LUID luid;
    bReturn =  LookupPrivilegeValue (NULL, strPrivilege.c_str(), &luid); // This is the val
    if (bReturn == false) return false;

    TOKEN_PRIVILEGES tp;
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    tp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;

    if (AdjustTokenPrivileges (hToken, FALSE, &tp, 0, NULL, NULL) != ERROR_SUCCESS)
    {
        return false;
    }
    return true;
} 

In this sample I am using WMI to enumerate all running process and find the process ID of winlogon.exe, whose impersonate token is what I am using to adjust the privilege of this thread's token. you can use any other mechanism to enumerate process (like NtQuerySystemInformation, Enumprocess, Toolhelp APIs). 

C++
bool CWirelessHelper :: ImpersonateToLoginUser()
{
     HANDLE hProcess;
     HANDLE hToken;
     BOOL bSuccess;

    DWORD dwProcID = GetProcessID (L"winlogon.exe");

    if (dwProcID < 1) return false;

    hProcess = OpenProcess (MAXIMUM_ALLOWED, FALSE, dwProcID);

    if (hProcess == INVALID_HANDLE_VALUE)
        return false;
            
    bSuccess = OpenProcessToken (hProcess, MAXIMUM_ALLOWED, &hToken);
    if (bSuccess)
    {

        SetPrivilege (SE_DEBUG_NAME, hToken, true); // 
         bSuccess = ImpersonateLoggedOnUser (hToken);
    }
    
    if (hProcess)
    CloseHandle (hProcess);
    if (hToken)
    CloseHandle (hToken);
    
    return bSuccess;
}

Once your process got enough privilege for using the Crypt API's you can decrypt the cipher text as shown below.

C++
std::wstring CWirelessHelper:: DecryptData(const std::wstring& strKey)
{
    std::wstring strPWD = L"";
    BYTE byteKey[1024] = {0};
    DWORD dwLength = 1024;
    DATA_BLOB DataOut, DataVerify;

    ImpersonateToLoginUser();

    BOOL bReturn = CryptStringToBinary (strKey.c_str(), strKey.length(), 
                           CRYPT_STRING_HEX, byteKey, &dwLength, 0, 0);
    if(bReturn)
    {
        DataOut.cbData = dwLength;
        DataOut.pbData = (BYTE*) byteKey;

    }
    if ( CryptUnprotectData (&DataOut, NULL, NULL, NULL, NULL, 0, &DataVerify))
    {
        CHAR str[MAX_PATH] = {0};
        sprintf_s(str, "%hs", DataVerify.pbData);
        std::string strData(str);
        std::wstring strPassword(strData.begin(), strData.end());
        strPWD = strPassword;
    }

    RevertToSelf();

    return strPWD;
}

Listing the Available Networks 

C++
WLAN_AVAILABLE_NETWORK_LIST *pWnwList;
dwResult =  WlanGetAvailableNetworkList(hClient, &pInfo->InterfaceGuid, 0, NULL, &pWnwList);
if(dwResult == ERROR_SUCCESS)
{
   for (int i = 0; i < (int) pWnwList->dwNumberOfItems; i++)
   {
    WLAN_AVAILABLE_NETWORK  *pNetwok = &pWnwList->Network[i];
    wifi.Set_Available_NW(GenerateSSID(pNetwok->dot11Ssid.ucSSID));
   }
}

Conclusion

The sample attached is developed using VS2012 and Windows8. 

Native Wi-Fi provides a vast capabilities than what I have mentioned here. MSDN is the right place to explore more features that it supports. Have fun!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)