在连接到WiFi网络时,如果选择保存凭证,这些凭证不仅可以被Windows系统获取,任何知道如何查找的人都可以获取。开发了一个名为GetWifiData的小工具,用于显示任何已存储的WiFi数据。将维护这个工具,并在专门的网站上发布更新。
过去(在Windows XP中),凭证存储在注册表中,但从Windows 7开始,凭证存储在单独的XML文件中。原生WiFi提供了一个更好的方式访问WiFi凭证,因为它是任何API调用的前端,用于自动配置组件,连接或断开WiFi网络。此外,Windows原生WiFi可以以XML文档的形式存储与其交互的网络的配置文件。
当连接到一个WiFi网络并选择保存凭证时,这些凭证可以被Windows系统或其他任何人在知道如何查找的情况下获取。不同版本的Windows中存储凭证的位置是不同的。
在WindowsXP中,位置如下:
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“原生WiFi”提供了一种更好的方式访问WiFi凭证,因为它是任何API调用的前端,用于自动配置组件,连接或断开WiFi网络。此外,Windows原生WiFi可以以XML文档的形式存储与其交互的网络的配置文件。
让以一个示例凭证文件为例。文件名为:
C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces\{9D0E4D68-B83A-4745-8021-DE4381E509BF}\{5094B710-D0BF-473A-BC7D-A51D4885F681}.xml
如上图所示,该文件包含名为Villa_Carriagehouse的WiFi网络的凭证,这是一个位于Indianapolis的优秀B&B酒店。几个月前住在那里(顺便说一句,这是一家很棒的酒店),所以凭证存储在PC中,可以被获取。
当WiFi凭证被加密时,可以使用Windows加密技术进行解密。微软的加密技术包括CryptoAPI、加密服务提供商(CSP)、CryptoAPI工具、CAPICOM、WinTrust、发行和管理证书以及开发可定制的公钥基础设施。
首先,需要定位一个名为keyMaterial的元素,将其隔离到一个字符串变量(strKey)中。给定strKey是一个CString变量,包含一个加密的WiFi凭证密钥(密码),以下代码块使用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现在将包含给定WiFi网络的解密密码。
现在,所需要做的就是遍历每个WiFi/网络接口,对于每个接口,遍历每个XML配置文件,并获取该配置文件的WiFi凭证,全部显示在屏幕上的报告中,并在文件中。
为此,以下是在Secured Globe, Inc中使用的一些辅助函数。
使用日志记录有多种目的,在大多数情况下,希望在控制台窗口(即使是基于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)。
此类程序需要管理员权限。首先可以通过强制用户提升来实现。
转到项目的属性。然后转到Linker -> Manifest File -> UAC Execution Level,并将其值设置为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);