上一篇简单说了一下如何使用windows native API 扫描站点广播的信号,比较浅显。这一篇简单说一下,如何获取bssid以及wifi定位的原理。
bssid是一个AP信号的唯一标示,一般通过wifi定位,就是检测设备把连接的AP信号的bssid和当前GPS位置上传到服务器,其他设备需要连接目标AP并上传GPS位置信息进行比较,如果一致就认为在范围内,就会允许签到、打卡等操作。这个思路借鉴自这篇博客https://blog.csdn.net/gupar/article/details/50854480,这里说win8.1一下的版本无法获取bssid,待验证。
通过native API获取bssid的方式现在知道两种。一种是通过处于已连接状态的信号,通过接口查询链接属性,属性里可以获取bssid,但仅限于已连接的AP。显然无法获取全部扫描到的AP信号。第二种方式可以获取到所有扫描到的AP信号的bssid,是通过另一个接口,如下
std::wstring bssid;
PWLAN_BSS_LIST ppWlanBssList;DWORD dwResult2 = WlanGetNetworkBssList(hClient, &pIfInfo->InterfaceGuid,
&pBssEntry->dot11Ssid,
pBssEntry->dot11BssType,
pBssEntry->bSecurityEnabled,
NULL,
&ppWlanBssList);
第三个参数很重要,如果是传入的某个AP的ssid则,只能获取当前AP的bssid,如果传入NULL,则会返回当前网卡扫描到的所有AP的bssid。
发现这个接口也很偶然。开始我一直关注这个接口
dwResult = WlanQueryInterface(hClient,
&pIfInfo->InterfaceGuid,
wlan_intf_opcode_current_connection,
NULL,
&connectInfoSize,
(PVOID *)&pConnectInfo,
&opCode);
但发现他只能返回【current_connection】状态的接口。后来又去MSDN去查有哪些数据结构中包含【bssid】的属性,就发现了WLAN_BSS_LIST。然后继续找,哪些接口会返回这个结构,就找到了【WlanGetNetworkBssList】,然后百度搜了一下就找到上面的链接,顺便用法也有了。后来仔细看了一下官方文档,发现如果第三个参数为NULL,则接口会默认返回所有扫到的bssid。
通过这种方式获取的bssid,会发现同一个AP会有两个bssid,也就是一个路由器有两块无线网卡。百度了很久,基本有两种说法,一是认为高级点儿路由器有两种模式,除了正常模式外,还可以开启访客模式。另外一种说现在的路由器支持2.4GHz和5.0GHz频率的信号,所以会有两块无线网卡。这个地方有点儿疑惑,最终也没太搞懂。姑且认为这两个bssid对应的是同一个AP。
这里附上源码,因为我的应用中嵌入了CEF,所以json数据结构就使用了cef的实现(所以不太纯,引入cef的依赖,仅供参考吧)。
#include<windows.h>
#include<wlanapi.h>
#include<stdio.h>
#include "include/cef_parser.h" // json func
#include <xstring>
#pragma comment(lib, "wlanapi.lib")
#pragma comment(lib, "ole32.lib")
int getWlanContent(CefString& strRes)
{
int iState(200);
CefRefPtr<CefDictionaryValue> pDictRt = CefDictionaryValue::Create();
HANDLE hClient = NULL;
DWORD dwMaxClient = 2; //
DWORD dwCurVersion = 0;
DWORD dwResult = 0;
DWORD dwRetVal = 0;
int iRet = 0;
WCHAR GuidString[39] = { 0 };
unsigned int i, j, k;
/* variables used for WlanEnumInterfaces */
PWLAN_INTERFACE_INFO_LIST pIfList = NULL;
PWLAN_INTERFACE_INFO pIfInfo = NULL;
PWLAN_AVAILABLE_NETWORK_LIST pBssList = NULL;
PWLAN_AVAILABLE_NETWORK pBssEntry = NULL;
int iRSSI = 0;
dwResult = WlanOpenHandle(dwMaxClient, NULL, &dwCurVersion, &hClient);
if (dwResult != ERROR_SUCCESS) {
wprintf(L"WlanOpenHandle failed with error: %u\n", dwResult);
iState = 251;
pDictRt->SetInt("state", iState);
pDictRt->SetDictionary("result", CefDictionaryValue::Create());
CefRefPtr<CefValue> pValue = CefValue::Create();
if (nullptr != pValue)
{
pValue->SetDictionary(pDictRt);
strRes = CefWriteJSON(pValue, JSON_WRITER_DEFAULT);
}
return iState;
// You can use FormatMessage here to find out why the function failed
}
dwResult = WlanEnumInterfaces(hClient, NULL, &pIfList);
if (dwResult != ERROR_SUCCESS) {
wprintf(L"WlanEnumInterfaces failed with error: %u\n", dwResult);
iState = 252;
pDictRt->SetInt("state", iState);
pDictRt->SetDictionary("result", CefDictionaryValue::Create());
CefRefPtr<CefValue> pValue = CefValue::Create();
if (nullptr != pValue)
{
pValue->SetDictionary(pDictRt);
strRes = CefWriteJSON(pValue, JSON_WRITER_DEFAULT);
}
return iState;
// You can use FormatMessage here to find out why the function failed
}
if ((int)pIfList->dwNumberOfItems < 1)
{
wprintf(L"WlanEnumInterfaces failed with error: %u\n", dwResult);
iState = 256;
pDictRt->SetInt("state", iState);
pDictRt->SetDictionary("result", CefDictionaryValue::Create());
CefRefPtr<CefValue> pValue = CefValue::Create();
if (nullptr != pValue)
{
pValue->SetDictionary(pDictRt);
strRes = CefWriteJSON(pValue, JSON_WRITER_DEFAULT);
}
return iState;
}
CefRefPtr<CefListValue> pGuid = CefListValue::Create();
for (i = 0; i < (int)pIfList->dwNumberOfItems; i++)
{
pIfInfo = (WLAN_INTERFACE_INFO *)&pIfList->InterfaceInfo[i];
if (nullptr == pIfInfo)
{
continue;
}
const std::wstring strGuid = L"{9245fe4a-d402-451c-b9ed-9c1a04247482}";
GUID stGuid = { 0 };
//TCHAR sz[MAX_PATH] = { 0 };
LPOLESTR streGuid;
StringFromCLSID(pIfInfo->InterfaceGuid, &streGuid);
std::wstring strTgt = OLE2T(streGuid);
// avaiable wlan
dwResult = WlanGetAvailableNetworkList(hClient,
&pIfInfo->InterfaceGuid,
0,
NULL,
&pBssList);
if (ERROR_SUCCESS != dwResult)
{
wprintf(L"WlanEnumInterfaces failed with error: %u\n", dwResult);
iState = 253;
continue;
}
CefRefPtr<CefListValue> pDictA = CefListValue::Create();
for (auto m = 0; m < pBssList->dwNumberOfItems; m++)
{
CefRefPtr<CefDictionaryValue> pItem = CefDictionaryValue::Create();
pBssEntry =(WLAN_AVAILABLE_NETWORK *)& pBssList->Network[m];
if (nullptr == pBssEntry)
{
continue;
}
std::wstring ssid;
TCHAR sz[30] = { 0 };
for (auto k = 0;
k < pBssEntry->dot11Ssid.uSSIDLength;
k++) {
swprintf_s(sz, L"%c",
(int)pBssEntry->dot11Ssid.ucSSID[k]);
ssid.append(sz);
}
ULONG lSqn = pBssEntry->wlanSignalQuality;
if (ssid.empty())
{
continue;
}
// bssid
std::wstring bssid;
PWLAN_BSS_LIST ppWlanBssList;
DWORD dwResult2 = WlanGetNetworkBssList(hClient, &pIfInfo->InterfaceGuid,
&pBssEntry->dot11Ssid,
pBssEntry->dot11BssType,
pBssEntry->bSecurityEnabled,
NULL,
&ppWlanBssList);
CefRefPtr<CefListValue> pBssidArr = CefListValue::Create();
if (dwResult2 == ERROR_SUCCESS)
{
for (int z = 0; z < ppWlanBssList->dwNumberOfItems; z++)
{
WLAN_BSS_ENTRY bssEntry = ppWlanBssList->wlanBssEntries[z];
TCHAR sz[MAX_PATH] = { 0 };
swprintf_s(sz, L"%02X:%02X:%02X:%02X:%02X:%02X",
bssEntry.dot11Bssid[0],
bssEntry.dot11Bssid[1],
bssEntry.dot11Bssid[2],
bssEntry.dot11Bssid[3],
bssEntry.dot11Bssid[4],
bssEntry.dot11Bssid[5]);
bssid = sz;
CefRefPtr<CefValue> pBidValue = CefValue::Create();
pBidValue->SetString(bssid);
pBssidArr->SetValue(z, pBidValue);
//vecBSSIDs.push_back(bssid);
}
}
if (bssid.empty() || (pBssidArr->GetSize() <1))
{
continue;
}
pItem->SetString("name", ssid);
pItem->SetInt("sglqlt", lSqn);
pItem->SetList("bssid", pBssidArr);
pItem->SetString("netid", strTgt);
pDictA->SetDictionary(m, pItem);
}
pGuid->SetList(i, pDictA);
}
CefRefPtr<CefValue> pValueT = CefValue::Create();
pValueT->SetList(pGuid);
pDictRt->SetInt("state", iState);
pDictRt->SetValue("result", pValueT);
//pDictRt->SetValue("result", pValueT);
CefRefPtr<CefValue> pValueRes = CefValue::Create();
pValueRes->SetDictionary(pDictRt);
strRes = CefWriteJSON(pValueRes, JSON_WRITER_DEFAULT);
return iState;
}
在此简单记录。