0%

实现自定义密码复杂度过滤(1)

简介

由于一些特殊的情况,操作系统提供的密码复杂度检测,不能满足我们的需求。
比如 小写字母 大写字母 数字 特殊符号 我们需要全都存在(四选四),但是操作系统只能(四选三),
又或者类似 p@ssw0rd 之类的密码,虽然复杂度要求满足,但是太常见,很容易被密码字典破解。
所以我们需要注册一个自定义的密码检测规则,在创建或修改密码时,直接进行检测和处理。

过滤器的注册

微软操作系统提供了一种 Password Filter DLL 的方式,可以在 密码保存之前 对其进行处理。
该功能需要导出三个函数,而且必须编译为DLL类型,32位系统使用32位DLL,64位使用64位DLL。

1
BOOLEAN InitializeChangeNotify(void);
1
2
3
4
5
NTSTATUS PasswordChangeNotify(
_In_ PUNICODE_STRING UserName,
_In_ ULONG RelativeId,
_In_ PUNICODE_STRING NewPassword
);
1
2
3
4
5
6
BOOLEAN PasswordFilter(
_In_ PUNICODE_STRING AccountName,
_In_ PUNICODE_STRING FullName,
_In_ PUNICODE_STRING Password,
_In_ BOOLEAN SetOperation
);

其中 InitializeChangeNotify 函数用来初始化DLL信息,返回 TRUE 表示成功,FALSE 表示失败。

其中 PasswordFilter 函数用来自定义我们的过滤规则,参数 Password 就是需要检测的密码,前两个
参数是对应的 账户名称,参数 SetOperation 为 TRUE 时,表示 密码新建 操作,反之为 密码修改 操作,
返回 TRUE 表示 允许 本次操作,返回 FALSE 表示 拒绝 本次操作。

其中 PasswordChangeNotify 函数会在所有检测都已通过,系统准备存储该密码时调用,该函数多数是
做信息记录用,而且只能返回 STATUS_SUCCESS 返回值。

注册该过滤器DLL时,我们需要先把DLL文件,复制到操作系统 System32 目录下,注意用程序复制时,
会涉及到64位系统中32位程序的目录重定向问题。然后在如下注册表路径下

1
HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Lsa

存在一个 Notification Packages 注册表值,是 REG_MULTI_SZ 类型,如果不存在,需要新建一个,
注意 不要删除 原来自带的数据,要在原字符串 后边附加 新字符串,填写DLL的文件名,不要 .dll 后缀。

所有操作完毕后重启操作系统,该DLL过滤器就会被操作系统加载起来。

过滤器的代码

如下为相关三个函数的代码,我们只处理 PasswordFilter 函数,其余两个函数直接返回成功。

1
2
3
4
BOOLEAN __stdcall InitializeChangeNotify(void)
{
return TRUE;
}
1
2
3
4
5
6
NTSTATUS __stdcall PasswordChangeNotify(
PUNICODE_STRING UserName, ULONG RelativeId,
PUNICODE_STRING NewPassword)
{
return STATUS_SUCCESS;
}
1
2
3
4
5
6
7
8
9
10
BOOLEAN __stdcall PasswordFilter(
PUNICODE_STRING AccountName, PUNICODE_STRING FullName,
PUNICODE_STRING Password, BOOLEAN SetOperation)
{
if (Password == NULL) return TRUE;
if (Password->Buffer == NULL) return TRUE;
// 检查复杂度(四选四)
if (!CheckPwdComplexStrong(Password)) return FALSE;
return TRUE;
}

这里需要注意的地方是,每个函数都需要声明 __stdcall 类型,但是这样在头文件中使用
extern "C" 导出函数时,就变成 _InitializeChangeNotify@0 的样式,导致DLL加载失败,
所以我们需要创建一个 .def 文件来代替 extern "C" 声明导出函数,文件内容如下

1
2
3
4
EXPORTS
InitializeChangeNotify
PasswordChangeNotify
PasswordFilter

然后在工程配置中,配置属性 -> 链接器 -> 输入 -> 模块定义文件,加入该 .def 文件

模块定义文件

最后如果在写过滤规则时,引入了其他的库,可以使用 多线程 (/MT) 配置,把相关函数编译到DLL中。