自定義·關於Windows登錄(其一)
說來慚愧,準備了好久最後寫出來的東西少得可憐…不過也算在這個過程中摸清楚了一點好玩的東西吧…先把一些階段性成果拿出來,寫成這麼一篇文章。
在上次寫了CP(Credential Provider)的文章之後,我就一直在考慮,我們有沒有辦法,對一台計算機實現各種奇怪的驗證方式。
最開始的時候,我寄希望於Custom Authentication Package以完成這個目的。
然而,事實就是,在修仙若干天/在各種奇怪的社區提問/LSA崩潰幾十次之後,我依然只能實現預期功能的一小部分。然後,在絕望中,我把目光轉向了SubAuthentication Packages(以下簡稱SubAuth)
Subauthentication PackagesSubAuth是一個非常簡單的限制登陸方式。然而其受到限制也頗多:只有在正常登錄成功的情況下系統才會考慮SubAuth的意見。而在本文中,我們的SubAuth會尋找附近的藍牙設備,判斷是否有特定地址設備的存在,如果有,則完成登錄。
從上面的介紹中也可以看出我們採用的是一個非常不安全的驗證方法…因此僅供惡作劇使用,需要安全性的時候請考慮更加嚴謹的驗證方式。(實際上參考代碼中是留有後門的。為了避免測試過程中無法開機的尷尬情況。)
(另外,如果寫出來了…分享給我一份唄QwQ)
好了,閑話說到這裡,我們開始工作。
首先我們構造一個檢索特定藍牙設備的工具。
這裡我們利用Bluetooth APIs中的函數。首先打開一個Radio,正常來說我們應該遍歷所有Radio的,但是因為第一,我們希望登錄速度較快。第二,在登錄時兩台設備距離應該比較近…總之在我的測試機這裡所有Radio都能檢索到目標設備,因此我們只取第一個Radio來搜索。
void getRadio(HANDLE *pRadio,HBLUETOOTH_RADIO_FIND *pRadioFind) {
BLUETOOTH_FIND_RADIO_PARAMS parFinder;
parFinder.dwSize=sizeof(BLUETOOTH_FIND_RADIO_PARAMS);
*pRadioFind=BluetoothFindFirstRadio(&parFinder,pRadio);
BLUETOOTH_RADIO_INFO bri;
bri.dwSize=sizeof(BLUETOOTH_RADIO_INFO);
if (BluetoothGetRadioInfo(*pRadio, &bri)!=ERROR_SUCCESS) {
CloseHandle(*pRadio);
BluetoothFindRadioClose(*pRadioFind);
*pRadio=NULL;
*pRadioFind=NULL;
}
return;
}
當然,在使用完畢時要記得關閉Radio
CloseHandle(Radio);
BluetoothFindRadioClose(RadioFinder);
接下來在這兩段代碼中間,就是對設備的枚舉了。
首先構造用於枚舉設備的BLUETOOTH_DEVICE_SEARCH_PARAMS結構體
BLUETOOTH_DEVICE_SEARCH_PARAMS par;
par.dwSize=sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS);
par.hRadio = Radio;
par.fReturnAuthenticated = TRUE;
par.fReturnConnected = TRUE;
par.fReturnRemembered = TRUE;
par.fReturnUnknown = TRUE;
par.fIssueInquiry=TRUE;
par.cTimeoutMultiplier = 3;
接下來,我們再聲明一個BLUETOOTH_DEVICE_INFO結構體用於接收枚舉到的設備信息。
BLUETOOTH_DEVICE_INFO info;
info.dwSize=sizeof(BLUETOOTH_DEVICE_INFO);
然後就可以開始愉快地枚舉了
bool found=false;
HBLUETOOTH_DEVICE_FIND Finder=BluetoothFindFirstDevice(&par, &info);
bool cont;
if(NULL==Finder)
cont=false;
else
cont=true;
while (cont)
{
if(info.Address.ullLong==addr){
found=true;
break;
}
cont=BluetoothFindNextDevice(Finder, &info);
}
BluetoothFindDeviceClose(Finder);
最後得到的found變數就記錄了是否存在地址為addr的設備。
接下來進入SubAuth的正題。我們要實現的是一個msv1_0 SubAuth.在這裡,我們需要實現兩個函數,分別是Msv1_0SubAuthenticationRoutine和Msv1_0SubAuthenticationFilter。定義如下。
NTSTATUS Msv1_0SubAuthenticationRoutine(
IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
IN PVOID LogonInformation,
IN ULONG Flags,
IN PUSER_ALL_INFORMATION UserAll,
OUT PULONG WhichFields,
OUT PULONG UserFlags,
OUT PBOOLEAN Authoritative,
OUT PLARGE_INTEGER LogoffTime,
OUT PLARGE_INTEGER KickoffTime
);
NTSTATUS Msv1_0SubAuthenticationFilter(
IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
IN PVOID LogonInformation,
IN ULONG Flags,
IN PUSER_ALL_INFORMATION UserAll,
OUT PULONG WhichFields,
OUT PULONG UserFlags,
OUT PBOOLEAN Authoritative,
OUT PLARGE_INTEGER LogoffTime,
OUT PLARGE_INTEGER KickoffTime
);
出於懶癌,我們把這兩個函數定義成同樣的處理流程。
NTSTATUS
NTAPI
Msv1_0SubAuthenticationFilter (
IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
IN PVOID LogonInformation,
IN ULONG Flags,
IN PUSER_ALL_INFORMATION UserAll,
OUT PULONG WhichFields,
OUT PULONG UserFlags,
OUT PBOOLEAN Authoritative,
OUT PLARGE_INTEGER LogoffTime,
OUT PLARGE_INTEGER KickoffTime
)
{
return Msv1_0SubAuthenticationRoutine(
LogonLevel,
LogonInformation,
Flags,
UserAll,
WhichFields,
UserFlags,
Authoritative,
LogoffTime,
KickoffTime
);
}
至於二者實際上的區別…在MSDN上也有相關的描述。
TheMsv1_0SubAuthenticationRoutinefunction performs client/server-specific authentication.
TheMsv1_0SubAuthenticationFilterfunction performs user logon authentication that is specific to domain controllers.
有興趣的話可以打log看一看直接從winlogon登錄走的是那個函數哦
反正我的log里顯示是走的Msv1_0SubAuthenticationRoutine
關於函數的第一個參數,MSDN裡面沒有太多介紹。根據參數的類型檢索,我們得到了下面的頁面。
NETLOGON_LOGON_INFO_CLASS
typedef enum _NETLOGON_LOGON_INFO_CLASS
{
NetlogonInteractiveInformation = 1,
NetlogonNetworkInformation = 2,
NetlogonServiceInformation = 3,
NetlogonGenericInformation = 4,
NetlogonInteractiveTransitiveInformation = 5,
NetlogonNetworkTransitiveInformation = 6,
NetlogonServiceTransitiveInformation = 7
} NETLOGON_LOGON_INFO_CLASS;
然而這個參數(至少在本文中)幾乎不會用到,我們先跳過。
第二個參數則是如下結構體。
typedef struct _NETLOGON_LOGON_IDENTITY_INFO {
UNICODE_STRING LogonDomainName;
ULONG ParameterControl;
OLD_LARGE_INTEGER LogonId;
UNICODE_STRING UserName;
UNICODE_STRING Workstation;
} NETLOGON_LOGON_IDENTITY_INFO, *PNETLOGON_LOGON_IDENTITY_INFO;
需要注意的是UserName項目。我們可以用它來給不同用戶指定不同的驗證方式。
當然USER_ALL_INFORMATION包含了更多的信息…雙倍的快…咳咳。
不過UserAll中的信息也是可以修改的哦。
然後WhichFields…這裡就是決定我們要不要將UserAll改動記錄進LSA資料庫的時刻了…如果要記錄,它應當設為USER_ALL_PARAMETERS,否則直接NULL就好。
UserFlags…一如既往地NULL…
畢竟LOGON_GUEST和LOGON_NOENCRYPTION都不太對是吧…
Authoritative的話…如果要好好地返回一個確定的結果的話…還是設置成true比較有強制力吧…要不然再換一個DomainController嘗試也太麻煩了啊。
LogoffTime和KickoffTime總之設置到那遙遠的未來就好了啦。
最後給出返回值就好了啦。最後給出返回值就好了啦。
STATUS_SUCCESS成功登錄,STATUS_WRONG_PASSWORD密碼錯誤…
最後是關於安裝。
我們首先在HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlLsaMSV1_0下建立REG_SZ類型鍵值Auth0。(儘管MSDN建議沒有申請編號的開發者使用255,但是除了0之外的數字似乎都不能成功註冊…原因不詳…)並把它的值設置為SubAuth.
然後把我們寫好的dll拷貝到%SystemRoot%System32下,並且命名為SubAuth.dll.
最後一步,重啟您的計算機。
遊戲開始了。
(你知道登錄一個沒有密碼的賬戶時提示密碼錯誤,然後機主回來一點就進去的感覺嗎啊哈哈)
正式使用之前記得先配置好需要使用的藍牙地址。
最後一如既往Demo地址
6ziv/Custom-Samples推薦閱讀:
TAG:MicrosoftWindows | 科技 | 操作系統 |