Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Linq
Imports System.Runtime.InteropServices
Imports System.Security.Principal
Imports System.Text
Imports System.Threading.Tasks
Public Class Sam_Class
Partial Public Class SamServer : Implements IDisposable
Private _handle As IntPtr
Public Sub New(ByVal name As String, ByVal access As SERVER_ACCESS_MASK)
name = name
Check(SamConnect(New UNICODE_STRING(name), _handle, access, IntPtr.Zero))
End Sub
Public ReadOnly Property Name As String
Public Sub Dispose() Implements IDisposable.Dispose
If _handle <> IntPtr.Zero Then
SamCloseHandle(_handle)
_handle = IntPtr.Zero
End If
End Sub
Public Sub SetDomainPasswordInformation(ByVal domainSid As SecurityIdentifier, ByVal passwordInformation As DOMAIN_PASSWORD_INFORMATION)
If domainSid Is Nothing Then Throw New ArgumentNullException(NameOf(domainSid))
Dim sid = New Byte(domainSid.BinaryLength - 1) {}
domainSid.GetBinaryForm(sid, 0)
Dim domain As IntPtr = Nothing
Check(SamOpenDomain(_handle, DOMAIN_ACCESS_MASK.DOMAIN_WRITE_PASSWORD_PARAMS, sid, domain))
Dim info As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(passwordInformation))
Marshal.StructureToPtr(passwordInformation, info, False)
Try
Check(SamSetInformationDomain(domain, DOMAIN_INFORMATION_CLASS.DomainPasswordInformation, info))
Finally
Marshal.FreeHGlobal(info)
SamCloseHandle(domain)
End Try
End Sub
Public Function GetDomainPasswordInformation(ByVal domainSid As SecurityIdentifier) As DOMAIN_PASSWORD_INFORMATION
If domainSid Is Nothing Then Throw New ArgumentNullException(NameOf(domainSid))
Dim sid = New Byte(domainSid.BinaryLength - 1) {}
domainSid.GetBinaryForm(sid, 0)
Dim domain As IntPtr = Nothing
Check(SamOpenDomain(_handle, DOMAIN_ACCESS_MASK.DOMAIN_READ_PASSWORD_PARAMETERS, sid, domain))
Dim info = IntPtr.Zero
Try
Check(SamQueryInformationDomain(domain, DOMAIN_INFORMATION_CLASS.DomainPasswordInformation, info))
Return CType(Marshal.PtrToStructure(info, GetType(DOMAIN_PASSWORD_INFORMATION)), DOMAIN_PASSWORD_INFORMATION)
Finally
SamFreeMemory(info)
SamCloseHandle(domain)
End Try
End Function
Public Function GetDomainSid(ByVal domain As String) As SecurityIdentifier
If domain Is Nothing Then Throw New ArgumentNullException(NameOf(domain))
Dim sid As IntPtr = Nothing
Check(SamLookupDomainInSamServer(_handle, New UNICODE_STRING(domain), sid))
Return New SecurityIdentifier(sid)
End Function
Public Iterator Function EnumerateDomains() As IEnumerable(Of String)
Dim cookie As Integer = 0
Dim info As IntPtr = Nothing, count As Integer = Nothing
While True
Dim status = SamEnumerateDomainsInSamServer(_handle, cookie, info, 1, count)
If status <> NTSTATUS.STATUS_SUCCESS AndAlso status <> NTSTATUS.STATUS_MORE_ENTRIES Then Check(status)
If count = 0 Then Exit While
Dim us = CType(Marshal.PtrToStructure(info + IntPtr.Size, GetType(UNICODE_STRING)), UNICODE_STRING)
SamFreeMemory(info)
Yield us.ToString()
us.Buffer = IntPtr.Zero
End While
End Function
Private Enum DOMAIN_INFORMATION_CLASS
DomainPasswordInformation = 1
End Enum
<Flags>
Public Enum PASSWORD_PROPERTIES
DOMAIN_PASSWORD_COMPLEX = &H1
DOMAIN_PASSWORD_NO_ANON_CHANGE = &H2
DOMAIN_PASSWORD_NO_CLEAR_CHANGE = &H4
DOMAIN_LOCKOUT_ADMINS = &H8
DOMAIN_PASSWORD_STORE_CLEARTEXT = &H10
DOMAIN_REFUSE_PASSWORD_CHANGE = &H20
End Enum
<Flags>
Private Enum DOMAIN_ACCESS_MASK
DOMAIN_READ_PASSWORD_PARAMETERS = &H1
DOMAIN_WRITE_PASSWORD_PARAMS = &H2
DOMAIN_READ_OTHER_PARAMETERS = &H4
DOMAIN_WRITE_OTHER_PARAMETERS = &H8
DOMAIN_CREATE_USER = &H10
DOMAIN_CREATE_GROUP = &H20
DOMAIN_CREATE_ALIAS = &H40
DOMAIN_GET_ALIAS_MEMBERSHIP = &H80
DOMAIN_LIST_ACCOUNTS = &H100
DOMAIN_LOOKUP = &H200
DOMAIN_ADMINISTER_SERVER = &H400
DOMAIN_ALL_ACCESS = &HF07FF
DOMAIN_READ = &H20084
DOMAIN_WRITE = &H2047A
DOMAIN_EXECUTE = &H20301
End Enum
<Flags>
Public Enum SERVER_ACCESS_MASK
SAM_SERVER_CONNECT = &H1
SAM_SERVER_SHUTDOWN = &H2
SAM_SERVER_INITIALIZE = &H4
SAM_SERVER_CREATE_DOMAIN = &H8
SAM_SERVER_ENUMERATE_DOMAINS = &H10
SAM_SERVER_LOOKUP_DOMAIN = &H20
SAM_SERVER_ALL_ACCESS = &HF003F
SAM_SERVER_READ = &H20010
SAM_SERVER_WRITE = &H2000E
SAM_SERVER_EXECUTE = &H20021
End Enum
<StructLayout(LayoutKind.Sequential)>
Public Structure DOMAIN_PASSWORD_INFORMATION
Public MinPasswordLength As Short
Public PasswordHistoryLength As Short
Public PasswordProperties As PASSWORD_PROPERTIES
Private _maxPasswordAge As Long
Private _minPasswordAge As Long
Public Property MaxPasswordAge As TimeSpan
Get
Return -New TimeSpan(_maxPasswordAge)
End Get
Set(ByVal value As TimeSpan)
_maxPasswordAge = value.Ticks
End Set
End Property
Public Property MinPasswordAge As TimeSpan
Get
Return -New TimeSpan(_minPasswordAge)
End Get
Set(ByVal value As TimeSpan)
_minPasswordAge = value.Ticks
End Set
End Property
End Structure
<StructLayout(LayoutKind.Sequential)>
Private Class UNICODE_STRING
'Inherits IDisposable
Public Length As UShort
Public MaximumLength As UShort
Public Buffer As IntPtr
Public Sub New()
Me.New(Nothing)
End Sub
Public Sub New(ByVal s As String)
If s IsNot Nothing Then
Length = CUShort((s.Length * 2))
MaximumLength = CUShort((Length + 2))
Buffer = Marshal.StringToHGlobalUni(s)
End If
End Sub
Public Overrides Function ToString() As String
Return If(Buffer <> IntPtr.Zero, Marshal.PtrToStringUni(Buffer), Nothing)
End Function
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
If Buffer <> IntPtr.Zero Then
Marshal.FreeHGlobal(Buffer)
Buffer = IntPtr.Zero
End If
End Sub
Protected Overrides Sub Finalize()
Dispose(False)
End Sub
Public Sub Dispose()
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
End Class
Private Shared Sub Check(ByVal err As NTSTATUS)
If err = NTSTATUS.STATUS_SUCCESS Then Return
Throw New Win32Exception("Error " & err & " (0x" & (CInt(err)).ToString("X8") & ")")
End Sub
Private Enum NTSTATUS
STATUS_SUCCESS = &H0
STATUS_MORE_ENTRIES = &H105
STATUS_INVALID_HANDLE = &HC0000008
STATUS_INVALID_PARAMETER = &HC000000D
STATUS_ACCESS_DENIED = &HC0000022
STATUS_OBJECT_TYPE_MISMATCH = &HC0000024
STATUS_NO_SUCH_DOMAIN = &HC00000DF
End Enum
<DllImport("samlib.dll", CharSet:=CharSet.Unicode)>
Private Shared Function SamConnect(ByVal ServerName As UNICODE_STRING, <Out> ByRef ServerHandle As IntPtr, ByVal DesiredAccess As SERVER_ACCESS_MASK, ByVal ObjectAttributes As IntPtr) As NTSTATUS
End Function
<DllImport("samlib.dll", CharSet:=CharSet.Unicode)>
Private Shared Function SamCloseHandle(ByVal ServerHandle As IntPtr) As NTSTATUS
End Function
<DllImport("samlib.dll", CharSet:=CharSet.Unicode)>
Private Shared Function SamFreeMemory(ByVal Handle As IntPtr) As NTSTATUS
End Function
<DllImport("samlib.dll", CharSet:=CharSet.Unicode)>
Private Shared Function SamOpenDomain(ByVal ServerHandle As IntPtr, ByVal DesiredAccess As DOMAIN_ACCESS_MASK, ByVal DomainId As Byte(), <Out> ByRef DomainHandle As IntPtr) As NTSTATUS
End Function
<DllImport("samlib.dll", CharSet:=CharSet.Unicode)>
Private Shared Function SamLookupDomainInSamServer(ByVal ServerHandle As IntPtr, ByVal name As UNICODE_STRING, <Out> ByRef DomainId As IntPtr) As NTSTATUS
End Function
<DllImport("samlib.dll", CharSet:=CharSet.Unicode)>
Private Shared Function SamQueryInformationDomain(ByVal DomainHandle As IntPtr, ByVal DomainInformationClass As DOMAIN_INFORMATION_CLASS, <Out> ByRef Buffer As IntPtr) As NTSTATUS
End Function
<DllImport("samlib.dll", CharSet:=CharSet.Unicode)>
Private Shared Function SamSetInformationDomain(ByVal DomainHandle As IntPtr, ByVal DomainInformationClass As DOMAIN_INFORMATION_CLASS, ByVal Buffer As IntPtr) As NTSTATUS
End Function
<DllImport("samlib.dll", CharSet:=CharSet.Unicode)>
Private Shared Function SamEnumerateDomainsInSamServer(ByVal ServerHandle As IntPtr, ByRef EnumerationContext As Integer, <Out> ByRef EnumerationBuffer As IntPtr, ByVal PreferedMaximumLength As Integer, <Out> ByRef CountReturned As Integer) As NTSTATUS
End Function
End Class
End Class
메인 Form
Public Sub chk_SAM()
Dim chk_LoginAccount As String
chk_LoginAccount = System.Environment.UserName
Using server As SamServer = New SamServer(Nothing,
SamServer.SERVER_ACCESS_MASK.SAM_SERVER_ENUMERATE_DOMAINS _
Or SamServer.SERVER_ACCESS_MASK.SAM_SERVER_LOOKUP_DOMAIN)
For Each domain As String In server.EnumerateDomains
If chk_LoginAccount <> Nothing Then
If chk_LoginAccount.ToUpper() = domain Then
Dim sid = server.GetDomainSid(domain)
Dim pi = server.GetDomainPasswordInformation(sid)
Dim msg As String
msg = $"계정 : {chk_LoginAccount}{vbNewLine}"
msg += $"패스워드 속성 : {pi.PasswordProperties}{vbNewLine}"
msg += $"최근 암호 기억 : {pi.PasswordHistoryLength}{vbNewLine}"
msg += $"최대 암호 사용기간 : {pi.MaxPasswordAge}{vbNewLine}"
msg += $"최소 암호 길이 : {pi.MinPasswordLength}{vbNewLine}"
msg += $"최소 암호 사용기간 : {pi.MinPasswordAge}{vbNewLine}"
MsgBox(msg)
End If
End If
Next
End Using
End Sub
'VB.net' 카테고리의 다른 글
x86, x64 모두 사용 가능한 cmd (0) | 2021.03.31 |
---|---|
Key입력(VirtureKey and HardwareKey) (0) | 2021.03.31 |
이벤트 Retry(재시도) 로직 - 문자발송 포함 (0) | 2020.09.28 |
윤달 체크 로직 (0) | 2020.09.28 |
전역 오류 확인 및 전달 로직 (0) | 2020.09.24 |