用API查询和设置策略
前边提到了如何用 secedit
修改密码策略,在修改之前我们应该先查询一下当前的策略是什么,
不符合预期要求的再进行修改,以减少数据的写入次数。
经过搜索,发现可以使用 NetUserModalsGet
来查询密码策略,最后需要使用 NetApiBufferFree
来释放
查询到的数据空间。还可以使用 NetUserModalsSet
可以对密码策略进行设定。
1 2 3 4 5
| NET_API_STATUS NetUserModalsGet( _In_opt_ LPCWSTR servername, _In_ DWORD level, _Out_ LPBYTE *bufptr );
|
1 2 3
| NET_API_STATUS NetApiBufferFree( _In_ LPVOID Buffer );
|
1 2 3 4 5 6
| NET_API_STATUS NetUserModalsSet( _In_ LPCWSTR servername, _In_ DWORD level, _In_ LPBYTE buf, _Out_ LPDWORD parm_err );
|
这两个函数中 level
参数决定了要处理数据的类型,可以取值的内容为
Value |
Meaning |
Structure |
0 |
Specifies global password parameters. |
USER_MODALS_INFO_0 |
1 |
Specifies logon server and domain controller information. |
USER_MODALS_INFO_1 |
2 |
Specifies the domain name and identifier. |
USER_MODALS_INFO_2 |
3 |
Specifies lockout information. |
USER_MODALS_INFO_3 |
1001 |
Specifies the minimum allowable password length. |
USER_MODALS_INFO_1001 |
1002 |
Specifies the maximum allowable password age. |
USER_MODALS_INFO_1002 |
1003 |
Specifies the minimum allowable password age. |
USER_MODALS_INFO_1003 |
1004 |
Specifies forced logoff information. |
USER_MODALS_INFO_1004 |
1005 |
Specifies the length of the password history. |
USER_MODALS_INFO_1005 |
1006 |
Specifies the role of the logon server. |
USER_MODALS_INFO_1006 |
1007 |
Specifies domain controller information. |
USER_MODALS_INFO_1007 |
如下是使用 NetUserModalsGet
查询时,我们关注的结构体信息
1 2 3 4 5 6 7
| typedef struct _USER_MODALS_INFO_0 { DWORD usrmod0_min_passwd_len; DWORD usrmod0_max_passwd_age; DWORD usrmod0_min_passwd_age; DWORD usrmod0_force_logoff; DWORD usrmod0_password_hist_len; } USER_MODALS_INFO_0, *PUSER_MODALS_INFO_0, *LPUSER_MODALS_INFO_0;
|
1 2 3 4 5
| typedef struct _USER_MODALS_INFO_3 { DWORD usrmod3_lockout_duration; DWORD usrmod3_lockout_observation_window; DWORD usrmod3_lockout_threshold; } USER_MODALS_INFO_3, *PUSER_MODALS_INFO_3, *LPUSER_MODALS_INFO_3;
|
使用 NetUserModalsSet
除了以上结构体进行整体设置外,还可以对部分参数进行单独设置
1 2 3
| typedef struct _USER_MODALS_INFO_1001 { DWORD usrmod1001_min_passwd_len; } USER_MODALS_INFO_1001, *PUSER_MODALS_INFO_1001, *LPUSER_MODALS_INFO_1001;
|
1 2 3
| typedef struct _USER_MODALS_INFO_1002 { DWORD usrmod1002_max_passwd_age; } USER_MODALS_INFO_1002, *PUSER_MODALS_INFO_1002, *LPUSER_MODALS_INFO_1002;
|
1 2 3
| typedef struct _USER_MODALS_INFO_1003 { DWORD usrmod1003_min_passwd_age; } USER_MODALS_INFO_1003, *PUSER_MODALS_INFO_1003, *LPUSER_MODALS_INFO_1003;
|
1 2 3
| typedef struct _USER_MODALS_INFO_1004 { DWORD usrmod1004_force_logoff; } USER_MODALS_INFO_1004, *PUSER_MODALS_INFO_1004, *LPUSER_MODALS_INFO_1004;
|
1 2 3
| typedef struct _USER_MODALS_INFO_1005 { DWORD usrmod1005_password_hist_len; } USER_MODALS_INFO_1005, *PUSER_MODALS_INFO_1005, *LPUSER_MODALS_INFO_1005;
|
密码复杂度的处理
看到这里是不是发现一个问题,那就是 密码必须符合复杂性要求
这项配置,查询不到。
我们可以通过直接读取 SAM
注册表的信息,来判断密码复杂度是否启用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| BOOL CheckPwdComplexPolicy() { HKEY hKey = NULL; LONG lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account", 0, KEY_ALL_ACCESS, &hKey); if (lResult != ERROR_SUCCESS) return FALSE; DWORD dwLen = 1024; BYTE pBuf[1024] = { 0 }; lResult = RegQueryValueEx(hKey, "F", NULL, NULL, pBuf, &dwLen); RegCloseKey(hKey); if (lResult != ERROR_SUCCESS) return FALSE; if (pBuf[76] != 1) return FALSE; return TRUE; }
|
普通情况下 SAM
注册表是不允许访问的,就需要我们首先修改一下访问 SAM
的权限
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| BOOL ModifySamRegPrivilege() { PACL pOldDacl = NULL; PSECURITY_DESCRIPTOR pSID = NULL; DWORD dRet = GetNamedSecurityInfo("MACHINE\\SAM\\SAM", SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, NULL, NULL, &pOldDacl, NULL, &pSID); if (dRet != ERROR_SUCCESS) { LocalFree(pSID); return FALSE; } EXPLICIT_ACCESS_A eia = { 0 }; BuildExplicitAccessWithName(&eia, "Administrators", KEY_ALL_ACCESS, SET_ACCESS, SUB_CONTAINERS_AND_OBJECTS_INHERIT); PACL pNewDacl = NULL; dRet = SetEntriesInAcl(1, &eia, pOldDacl, &pNewDacl); if (dRet != ERROR_SUCCESS) { LocalFree(pSID); return FALSE; } dRet = SetNamedSecurityInfo("MACHINE\\SAM\\SAM", SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, NULL, NULL, pNewDacl, NULL); if (dRet != ERROR_SUCCESS) { LocalFree(pNewDacl); LocalFree(pSID); return FALSE; } return TRUE; }
|