我用 pascal 编写了一个程序,使用 sam api 启用/禁用“密码必须满足复杂性要求”。每个函数都返回 STATUS_SUCCESS,SamSetInformationDomain 除外,它返回 STATUS_INVALID_PARAMETER。
这是我尝试编写的完成工作的代码:
program projet;
{$apptype gui} {$linklib libprojet1.a} {$linklib libadvapi32.a}
uses windows,
sysutils, jwawinnt, jwawinbase, jwaaccctrl, jwaaclapi, jwasddl, jwantsecapi,
jwanative, jwalmaccess, jwauserenv, jwaprofinfo, jwawinternl, jwantstatus;
type NTSTATUS = LongInt;
const ACCOUNT_VIEW = 1;
SECURITY_ACCESS_BATCH_LOGON : ULONG = $00000004;
SECURITY_ACCESS_SERVICE_LOGON : ULONG = $00000010;
ACCOUNT_ADJUST_PRIVILEGES = 2;
ACCOUNT_ADJUST_QUOTAS = 4;
ACCOUNT_ADJUST_SYSTEM_ACCESS = 8;
ACCOUNT_EXECUTE = STANDARD_RIGHTS_EXECUTE;
ACCOUNT_ALL_ACCESS =
STANDARD_RIGHTS_REQUIRED OR ACCOUNT_VIEW OR ACCOUNT_ADJUST_PRIVILEGES OR
ACCOUNT_ADJUST_QUOTAS OR ACCOUNT_ADJUST_SYSTEM_ACCESS;
ACCOUNT_READ = STANDARD_RIGHTS_READ OR ACCOUNT_VIEW;
ACCOUNT_WRITE = STANDARD_RIGHTS_WRITE OR ACCOUNT_ADJUST_PRIVILEGES OR
ACCOUNT_ADJUST_QUOTAS OR ACCOUNT_ADJUST_SYSTEM_ACCESS;
SAM_SERVER_CONNECT : ULONG = $00000001;
SAM_SERVER_LOOKUP_DOMAIN : ULONG = $00000020;
DOMAIN_LOOKUP : ULONG = $00000200;
USER_READ_GENERAL : ULONG = $00000001;
USER_READ_PREFERENCES : ULONG = $00000002;
USER_WRITE_PREFERENCES : ULONG = $00000004;
USER_READ_LOGON : ULONG = $00000008;
USER_READ_ACCOUNT : ULONG = $00000010;
USER_WRITE_ACCOUNT : ULONG = $00000020;
USER_CHANGE_PASSWORD : ULONG = $00000040;
USER_FORCE_PASSWORD_CHANGE : ULONG = $00000080;
USER_LIST_GROUPS : ULONG = $00000100;
USER_READ_GROUP_INFORMATION : ULONG = $00000200;
USER_WRITE_GROUP_INFORMATION : ULONG = $00000400;
USER_ALL_ACCESS : ULONG = $000F07FF;
USER_READ : ULONG = $0002031A;
USER_WRITE : ULONG = $00020044;
USER_EXECUTE : ULONG = $00020041;
ALIAS_ADD_MEMBER : ULONG = $00000001;
ALIAS_REMOVE_MEMBER : ULONG = $00000002;
ALIAS_LIST_MEMBER : ULONG = $00000004;
ALIAS_READ_INFORMATION : ULONG = $00000008;
ALIAS_WRITE_ACCOUNT : ULONG = $00000010;
ALIAS_ALL_ACCESS : ULONG = $000F001F;
ALIAS_READ : ULONG = $00020004;
ALIAS_WRITE : ULONG = $00020013;
ALIAS_EXECUTE : ULONG = $00020008;
DOMAIN_PASSWORD_NO_CLEAR_CHANGE : DWORD = $00000004;
DOMAIN_PASSWORD_STORE_CLEARTEXT : DWORD = $00000010;
DOMAIN_WRITE_PASSWORD_PARAMS : DWORD = $00000002;
DOMAIN_WRITE_OTHER_PARAMETERS : DWORD = $00000008;
DOMAIN_ALL_ACCESS : DWORD = $000F07FF;
DOMAIN_READ : DWORD = $00020084;
DOMAIN_WRITE : DWORD = $0002047A;
SAM_SERVER_ALL_ACCESS : DWORD = $000F003F;
type OLD_LARGE_INTEGER = record LowPart : DWORD;
HighPart : Longint;
end;
type DOMAIN_PASSWORD_INFORMATION = record MinPasswordLength : Word;
PasswordHistoryLength : Word;
PasswordProperties : DWORD;
MaxPasswordAge : OLD_LARGE_INTEGER;
MinPasswordAge : OLD_LARGE_INTEGER;
end;
type DOMAIN_INFORMATION_CLASS =
(DomainPasswordInformation = 1, DomainGeneralInformation,
DomainLogoffInformation, DomainOemInformation, DomainNameInformation,
DomainReplicationInformation, DomainServerRoleInformation,
DomainModifiedInformation, DomainStateInformation, DomainUasInformation,
DomainGeneralInformation2, DomainLockoutInformation,
DomainModifiedInformation2);
function GetOtherDomainSid() : PSID cdecl;
external;
function LsaCreateAccount(p
: LSA_HANDLE;
a
: PSID;
d
: ACCESS_MASK;
h
: PLSA_HANDLE)
: NTSTATUS;
stdcall;
external 'advapi32.dll';
function LsaOpenAccount(p
: LSA_HANDLE;
a
: PSID;
d
: ACCESS_MASK;
h
: PLSA_HANDLE)
: NTSTATUS;
stdcall;
external 'advapi32.dll';
function LsaSetSecurityObject(ObjectHandle
: LSA_HANDLE;
SecurityInfo
: SECURITY_INFORMATION;
SecD
: PSECURITY_DESCRIPTOR)
: NTSTATUS;
stdcall;
external 'advapi32.dll';
function LsaSetSystemAccessAccount(p
: LSA_HANDLE;
SystemAccess
: ULONG)
: NTSTATUS;
stdcall;
external 'advapi32.dll';
function SamConnect(ServerName
: Pointer;
ServerHandle
: PHANDLE;
DesiredAccess
: ACCESS_MASK;
oa
: POBJECT_ATTRIBUTES)
: NTSTATUS;
stdcall;
external 'samlib.dll';
function SamOpenUser(DomainHandle
: HANDLE;
DesiredAccess
: ACCESS_MASK;
userId
: ULONG;
UserHandle
: PHANDLE)
: NTSTATUS;
stdcall;
external 'samlib.dll';
function SamSetSecurityObject(ObjectHandle
: HANDLE;
secinfo
: SECURITY_INFORMATION;
secdesc
: PSECURITY_DESCRIPTOR)
: NTSTATUS;
stdcall;
external 'samlib.dll';
function SamOpenDomain(ServerHandle
: HANDLE;
DesiredAccess
: ACCESS_MASK;
DomainId
: PSID;
DomainHandle
: PHANDLE)
: NTSTATUS;
stdcall;
external 'samlib.dll';
function SamLookupNamesInDomain(DomainHandle
: HANDLE;
Count
: ULONG;
Names
: PUNICODE_STRING;
&RelativeIds
: PULONG;
&Use
: PSID_NAME_USE)
: NTSTATUS;
stdcall;
external 'samlib.dll';
function SamCloseHandle(b : HANDLE) : NTSTATUS;
stdcall;
external 'samlib.dll';
function SamSetInformationDomain(DomainHandle
: HANDLE;
DomainInfoClass
: DOMAIN_INFORMATION_CLASS;
PInfo
: LPVOID)
: NTSTATUS;
stdcall;
external 'samlib.dll';
function SamQueryInformationDomain(DomainHandle
: HANDLE;
DomainInfoClass
: DWORD;
PInfo
: PVOID)
: NTSTATUS;
stdcall;
external 'samlib.dll';
function SamOpenAlias(DomainHandle
: HANDLE;
DesiredAccess
: ACCESS_MASK;
AliasId
: ULONG;
AliasHandle
: PHANDLE)
: NTSTATUS;
stdcall;
external 'samlib.dll';
function IntToOldLarge(value : Int64) : OLD_LARGE_INTEGER;
begin Result.LowPart : = DWORD(value);
Result.HighPart : = Longint(value shr 32);
end;
function NTSetPrivilege(sPrivilege : string; bEnabled : Boolean) : Boolean;
var hToken : THandle;
TokenPriv : TOKEN_PRIVILEGES;
PrevTokenPriv : TOKEN_PRIVILEGES;
ReturnLength : Cardinal;
begin
Result : = True;
// Only for Windows NT/2000/XP and later.
if
not(Win32Platform = VER_PLATFORM_WIN32_NT) then Exit;
Result : = False;
// obtain the processes token
if OpenProcessToken (GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken)
then begin try
// Get the locally unique identifier (LUID) .
if LookupPrivilegeValue (nil, PChar(sPrivilege),
TokenPriv.Privileges[0].Luid)
then begin TokenPriv.PrivilegeCount : = 1; // one privilege to set
case bEnabled of True:
TokenPriv.Privileges[0].Attributes : = SE_PRIVILEGE_ENABLED;
False : TokenPriv.Privileges[0].Attributes : = 0;
end;
ReturnLength : = 0; // replaces a var parameter
PrevTokenPriv : = TokenPriv;
// enable or disable the privilege
AdjustTokenPrivileges(hToken, False, @TokenPriv, SizeOf(PrevTokenPriv),
@PrevTokenPriv, @ReturnLength);
end;
finally CloseHandle(hToken);
end;
end;
// test the return value of AdjustTokenPrivileges.
Result : = GetLastError = ERROR_SUCCESS;
end;
function TakeOwnSam(Domain2 : HANDLE) : Boolean;
var Sid : PSID;
SidAdmin : PSID;
peUse : DWORD;
cchDomain : DWORD;
cchName : DWORD;
Name : array of Char;
Domain : array of Char;
pDACL : PACL;
pDACL3 : PACL;
pEA : PEXPLICIT_ACCESS_A;
dwDisposition : DWORD;
Key : hKey;
secdesc : SECURITY_DESCRIPTOR;
foldername : String; // Temp to hardcode
begin pDACL3 : = nil;
Sid : = nil;
ConvertStringSidToSidA(PChar('S-1-1-0'), Sid);
ConvertStringSidToSidA(PChar('SYSTEM'), SidAdmin);
cchName : = 0;
cchDomain : = 0;
// Get Length
NTSetPrivilege('SeTakeOwnershipPrivilege', True);
NTSetPrivilege('SeRestorePrivilege', True);
if (not LookupAccountSid(nil, Sid, nil, cchName, nil, cchDomain, peUse))
and(GetLastError = ERROR_INSUFFICIENT_BUFFER) then begin
SetLength(Name, cchName);
SetLength(Domain, cchDomain);
if LookupAccountSid (nil, Sid, @Name[0], cchName, @Domain[0], cchDomain,
peUse)
then begin pEA : = AllocMem(SizeOf(EXPLICIT_ACCESS));
BuildExplicitAccessWithName(@pEA[0], PChar(Name), DOMAIN_ALL_ACCESS,
SET_ACCESS,
SUB_CONTAINERS_AND_OBJECTS_INHERIT{
NO_INHERITANCE});
Sleep(10);
SetEntriesInAcl(1, pEA, nil, pDACL);
InitializeSecurityDescriptor(@secdesc, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(@secdesc, TRUE, pDACL, TRUE);
SetSecurityDescriptorOwner(@secdesc, Sid, FALSE);
SamSetSecurityObject(Domain2, DACL_SECURITY_INFORMATION, @secdesc);
LocalFree(Cardinal(pDACL));
end;
end;
end;
function EnablePasswordComplexity(enable : Boolean) : Boolean;
var Attr : LSA_OBJECT_ATTRIBUTES;
Quality : SECURITY_QUALITY_OF_SERVICE;
hPolicy, account, account2 : LSA_HANDLE;
Sid, Sid4, Sid5 : PSID;
sidsize : DWORD;
peUse : DWORD;
jaaj : OLD_LARGE_INTEGER;
dom : DOMAIN_PASSWORD_INFORMATION;
cchDomain : DWORD;
cchName : DWORD;
Name : array of Char;
Domain : array of Char;
pDACL : PACL;
pEA : PEXPLICIT_ACCESS_A;
secdesc : SECURITY_DESCRIPTOR;
AccountDomainInfo : PPOLICY_ACCOUNT_DOMAIN_INFO;
NtAuthority : SID_IDENTIFIER_AUTHORITY;
ServerHandle, DomainHandle, DomainHandle2, UserHandle : HANDLE;
Ptr : PULONG;
point : PVOID;
successhandle : DWORD;
RelativeIds : PULONG;
Use : PSID_NAME_USE;
userna : UNICODE_STRING;
stat : NTSTATUS;
po : PChar;
begin Attr.Length : = sizeof(Attr);
Attr.RootDirectory : = 0;
Attr.ObjectName : = Nil;
Attr.Attributes : = 0;
Attr.SecurityDescriptor : = Nil;
Attr.SecurityQualityOfService : = @Quality;
Quality.Length : = sizeof(Quality);
Quality.ImpersonationLevel : = SecurityImpersonation;
Quality.ContextTrackingMode : = SECURITY_DYNAMIC_TRACKING;
Quality.EffectiveOnly : = FALSE;
if (LsaOpenPolicy(Nil, Attr, POLICY_VIEW_LOCAL_INFORMATION, hPolicy) =
STATUS_SUCCESS)
then begin
end;
point : = PVOID(@AccountDomainInfo);
stat : = LsaQueryInformationPolicy(hPolicy, PolicyAccountDomainInformation,
point);
If(stat = STATUS_SUCCESS) then begin
end else begin
MessageBoxA(0, PChar(IntToStr(LsaNtStatusToWinError(stat))), 'Error', 0);
ExitProcess(0);
end;
Sid5 : = GetOtherDomainSid();
NtAuthority : = SECURITY_NT_AUTHORITY;
Sid4 : = nil;
AllocateAndInitializeSid(@NtAuthority, 1, SECURITY_BUILTIN_DOMAIN_RID, 0, 0,
0, 0, 0, 0, 0, Sid4);
ConvertStringSidToSidA(PChar('S-1-1-0'), Sid);
cchName : = 0;
cchDomain : = 0;
// Get Length
if (not LookupAccountSid(nil, Sid, nil, cchName, nil, cchDomain, peUse))
and(GetLastError = ERROR_INSUFFICIENT_BUFFER) then begin
SetLength(Name, cchName);
SetLength(Domain, cchDomain);
if LookupAccountSid (nil, Sid, @Name[0], cchName, @Domain[0], cchDomain,
peUse)
then begin pEA : = AllocMem(SizeOf(EXPLICIT_ACCESS));
ZeroMemory(@dom, sizeof(dom));
successhandle : = 0;
InitializeSecurityDescriptor(@secdesc, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(@secdesc, TRUE, pDACL, TRUE);
if (SamConnect(nil, @ServerHandle, SAM_SERVER_ALL_ACCESS, nil) =
STATUS_SUCCESS)
then begin
end;
stat : = SamOpenDomain(ServerHandle, WRITE_DAC, Sid4, @DomainHandle);
if (stat = STATUS_SUCCESS)
then begin MessageBoxA(0, 'opening domain success', 'success', 0);
TakeOwnSam(DomainHandle);
SamCloseHandle(DomainHandle);
Sleep(20);
if (SamOpenDomain(ServerHandle, DOMAIN_ALL_ACCESS, Sid4, @DomainHandle) =
STATUS_SUCCESS)
then begin
MessageBoxA(0, 'taking domain ownership successful', 'takeown success',
0);
end;
// stat := SamQueryInformationDomain(DomainHandle, 1, @dom);
// MessageBoxA(0, PChar(IntToStr(LsaNtStatusToWinError(stat))), 'Error In
// Querying Domain Info', 0);
MessageBoxA(0, 'query domain success2',
PChar(IntToStr(dom.MinPasswordLength)), 0);
dom.MinPasswordLength : = 0;
dom.PasswordHistoryLength : = 0;
dom.MaxPasswordAge : = IntToOldLarge(0);
dom.MinPasswordAge : = IntToOldLarge(0);
if (enable = True)
then
begin dom.PasswordProperties
: = DOMAIN_PASSWORD_STORE_CLEARTEXT OR DOMAIN_PASSWORD_COMPLEX;
end else begin dom.PasswordProperties : = DOMAIN_PASSWORD_STORE_CLEARTEXT;
end;
stat : = SamSetInformationDomain(DomainHandle, DomainPasswordInformation,
@dom);
MessageBoxA(0, PChar(IntToStr(LsaNtStatusToWinError(stat))),
'Error In Setting Domain Info', 0);
end else begin MessageBoxA(0, PChar(IntToStr(LsaNtStatusToWinError(stat))),
'Error In Domain Opening', 0);
end;
stat : = SamOpenDomain(ServerHandle, WRITE_DAC, Sid5, @DomainHandle2);
if (stat = STATUS_SUCCESS)
then begin MessageBoxA(0, 'opening domain success', 'success', 0);
TakeOwnSam(DomainHandle2);
SamCloseHandle(DomainHandle2);
Sleep(20);
if (SamOpenDomain(ServerHandle, DOMAIN_ALL_ACCESS, Sid5, @DomainHandle2) =
STATUS_SUCCESS)
then begin
MessageBoxA(0, 'taking domain ownership successful', 'takeown success',
0);
end;
// stat := SamQueryInformationDomain(DomainHandle2, 1, @dom);
// MessageBoxA(0, PChar(IntToStr(LsaNtStatusToWinError(stat))), 'Error In
// Querying Domain Info', 0);
MessageBoxA(0, 'query domain success2',
PChar(IntToStr(dom.MinPasswordLength)), 0);
dom.MinPasswordLength : = 0;
dom.PasswordHistoryLength : = 0;
dom.MaxPasswordAge : = IntToOldLarge(0);
dom.MinPasswordAge : = IntToOldLarge(0);
if (enable = True)
then begin dom.PasswordProperties
: = DOMAIN_PASSWORD_STORE_CLEARTEXT OR DOMAIN_PASSWORD_COMPLEX;
end else begin dom.PasswordProperties : = DOMAIN_PASSWORD_STORE_CLEARTEXT;
end;
stat : = SamSetInformationDomain(DomainHandle2, DomainPasswordInformation,
@dom);
MessageBoxA(0, PChar(IntToStr(LsaNtStatusToWinError(stat))),
'Error In Setting Domain Info', 0);
end else begin MessageBoxA(0, PChar(IntToStr(LsaNtStatusToWinError(stat))),
'Error In Domain Opening', 0);
end;
SamCloseHandle(DomainHandle);
SamCloseHandle(UserHandle);
SamCloseHandle(ServerHandle);
// LsaSetSecurityObject(account2, DACL_SECURITY_INFORMATION, @secdesc);
end;
LocalFree(Cardinal(pDACL));
end;
end;
function IsRunAsAdministrator() : BOOL;
var fIsRunAsAdmin : BOOL;
dwError : DWORD;
pAdministratorsGroup : PSID;
NtAuthority : SID_IDENTIFIER_AUTHORITY;
label Cleanup;
begin
fIsRunAsAdmin : = LongBool(0);
dwError : = ERROR_SUCCESS;
pAdministratorsGroup : = nil;
NtAuthority : = SECURITY_NT_AUTHORITY;
if
not AllocateAndInitializeSid(@NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0,
pAdministratorsGroup) then begin dwError
: = GetLastError();
goto Cleanup;
end;
if
not CheckTokenMembership(0, pAdministratorsGroup,
&fIsRunAsAdmin) then begin dwError
: = GetLastError();
goto Cleanup;
end;
Cleanup : if pAdministratorsGroup<> nil then begin
FreeSid(pAdministratorsGroup);
pAdministratorsGroup : = nil;
end;
if ERROR_SUCCESS
<> dwError then begin
end;
begin result : = fIsRunAsAdmin;
exit;
end;
end;
begin try
SetErrorMode(SEM_FAILCRITICALERRORS OR SEM_NOGPFAULTERRORBOX);
if (IsRunAsAdministrator() = True)
then begin
NTSetPrivilege('SeRestorePrivilege', True);
NTSetPrivilege('SeTakeOwnershipPrivilege', True);
NTSetPrivilege('SeBackupPrivilege', True);
EnablePasswordComplexity(False);
end else begin ShellExecute(0, 'runas', PChar(ParamStr(0)), '', '',
SW_SHOWNORMAL);
end;
except ExitProcess(0);
end;
end.
我检查了 SamSetInformationDomain 函数的声明,它的声明方式应该与代码中的完全相同。我假设这都是指针错误,但我不太确定。该代码在Windows 10和11上进行了测试,在两者上的效果完全相同。 以及让这段代码工作的想法?