Building a Network Security Monitor

Windows NT Magazine introduces this practical, hands-on department by showing how NT APIs let you easily develop a utility to quickly check security on your networked Windows NT systems.

Michael Otey

October 31, 1996

13 Min Read
ITPro Today logo


Harness the power of the Win32 APIs to build a useful VB app without complex coding

VB Solutions is a new department in Windows NTMagazine that shows you how to use Visual Basic (VB) to solve businessproblems. In this space, we'll build a variety of solutions for specificbusiness problems ranging from network administration to integrating MicrosoftOffice/BackOffice applications with Object Linking and Embedding (OLE).

This column doesn't teach you how to write VB--instead, it focuses on usingVB to provide quick and easy-to-implement solutions. Although a workingknowledge of VB is important to understand how the utilities in this columnwork, you don't have to know VB to benefit from them. You can download thesource and executable code for all VB Solutions utilities from Windows NTMagazine's Web site at www.winntmag.com.

Network Security Monitor
This month's solution is a network administration utility--Network SecurityMonitor--that uses VB to collect and report security violations for yournetworked Windows NT systems and lets you perform a quick security check onthose systems. Network Security Monitor warns you about attempted networksecurity violations by displaying all login failures for each networked NTsystem. Repeated login failures are a telltale sign of unauthorized networkaccess attempts.

Requirements
You can use Network Security Monitor if you are running the NetBEUI protocolor NetBIOS over TCP/IP on your network. User and password definitions must matchacross all NT systems you want to monitor. For example, user MIKEO must have thesame password on all systems.

How It Works
Both NT Server and NT Workstation use an Event Log to track security-relatedevents and other system- and application-related events. If you aren't familiarwith NT's event logs, see "Windows NT Event Logs," page 153, for abrief explanation of this NT feature. Mike Reilly shows you how to audit your NTsecurity in "Find Holes in Your NT Security," October 1996.

NT also has a built-in Event Viewer that lets you select and view eventlogs for local and remote systems. However, Event Viewer lets you view only onesystem at a time, so it's too cumbersome for checking several systems regularly.Network Security Monitor solves this problem by reading event logs from multiplenetworked NT systems. Screen 1 shows the Network Security Monitorprogram's main window.

Using Network Security Monitor is easy. When the program starts, itretrieves a list of networked NT systems and displays them in the main window'sleft list box. You can monitor any or all systems in the list that appear in thelist box on the right. After choosing the systems, you simply click OK to beginreading those systems' security event logs. The Network Security Monitorhighlights each system in the list box on the right as it begins reading thatsystem's log. The program displays a progress bar as it reads through the logs.After Network Security Monitor finishes, the progress bar disappears and a ViewResults button appears. Clicking View Results displays the NetworkSecurity Monitor Results window, as shown in Screen 2.

The Network Security Monitor Results window displays a grid that containsthe server name, the user, the time stamp the Event Log generated, the event ID,and a brief description of the event. Each system appears in the order youselected it, and events appear chronologically (newest to oldest). Collectinglogin security errors with Network Security Monitor is a snap.

Inside Network Security Monitor
Considering Network Security Monitor's functionality, you might think thatbuilding it requires complicated communications coding and a knowledge of systeminternals--but that's not the case. NT provides a rich set of more than 800APIs, most of which you can call from VB. They let you access a variety ofsystem functions. Network Security Monitor takes advantage of a small set of NTAPIs to handle the trickiest parts of the program.

The first API Network Security Monitor uses, the Win32 NetBIOS API, letsthe program browse for the available networked NT systems. NT supplies this APIin the DLL netapi32.dll. It contains many functions; Network Security Monitoruses one, netserverenum, that returns a list of networked systems.

To use NetBIOS or any API functions in VB, you must declare them. Listing 1shows the VB declaration for function netserverenum.

LISTING 1: VB declaration for NetBIOS API function netserverenum

Declare Function NetServerEnum Lib "Netapi32" _(vComputerName As Any, ByVal lLevel As Long, vBuffer As Any, lPreferedMaxLen AsLong, lEntriesRead As Long, lTotalEntries As Long, vServerType As Any, ByValsDomain As String, vResume As Any) As Long

When Network Security Monitor first starts, it calls subroutinegetserverinfo in the main form's form_load event, which in turn calls functionnet serverenum. Listing 2 shows a partial listing of the getserverinfosubroutine. (You can obtain the complete code for getserverinfo and the other VBprograms for Network Security Monitor from www.winntmag.com.)

LISTING 2: Subroutine getserverinfo (partial listing)

'Clear all sComputerName. sComputerName = ""vServerType = SV_TYPE_NT

A

'Call NetServerEnum to get a list of servers.

lReturnCode = NetServerEnum(ByVal 0&, 101, lSeverInfo101,lPreferedMaxLen, lEntriesRead, lTotalEntries, vServerType, ByVal sDomain,vResume)

' NetServerEnum index is 1 based. x = 1 lSeverInfo101StructPtr = lSeverInfo101

B

Do While x <= lTotalEntries

RtlMoveMemory tSeverInfo101, ByVal lSeverInfo101StructPtr,Len(tSeverInfo101)

lstrcpyW bBuffer(0), tSeverInfo101.wki101_servername 'Get every other byte from Unicode string. i = 0 Do While bBuffer(i) <> 0 sComputerName = sComputerName & Chr(bBuffer(i))i = i + 2 Loop

List_AllServers.AddItem sComputerName

'Debug.Print "Found server: " &sComputerName sComputerName = ""x = x + 1

lSeverInfo101StructPtr = lSeverInfo101StructPtr + Len(tSeverInfo101)

Loop

lReturnCode = NetApiBufferFree(lSeverInfo101)

At A in Listing 2, you can see the call to netserverenum. The firstparameter identifies the name of the NT system that will run functionnetserverenum. A null value causes this function to execute on the local system.Specifying a remote server name causes the function to run on a remote system.The second parameter controls the layout of the returned information. Thisparameter accepts a constant value of 100 or 101; 101 returns only the mostbasic remote system information. The third parameter is a pointer to a bufferthat holds the returned list of networked computers. The fourth parameterspecifies the preferred length of data to read. The fifth and sixth parametersreturn the number of systems read and the total number of systems, respectively.The seventh parameter is a filter for the type of system to include in the list.This parameter accepts several constant values. This example needs the returnedlist of system names to include just NT workstations and servers because only NTsystems maintain security event logs. To get this information, you enter theconstant &H1000 in this parameter, which sets the filter to include only NTsystems. (At the top of Listing 2, the constant SV_TYPE_NT represents the value&H1000. It is set in another part of the code.) The eighth parameter is afilter for the domain to search. If this parameter is null as it is here, theprimary domain is searched. You can specify another domain name (typically foranother network or a different workgroup). The last parameter is not used, butit must be null. (The fact that these parameters are set to null is not obviousbased on the variable name, vResume, but vResume is never givena value; it's simply declared and not used.)

Once the call to netserverenum succeeds, an array of structures is in thevbuffer parameter. Each element in the structure contains information about aparticular server. Table 1shows the layout of the serverstructure that function netserverenum returns.

The code at B in Listing 2 traverses the returned array of structures,extracting the computer names and adding them to the list box on NetworkSecurity Monitor's main form. After subroutine getserverinfo is complete,Network Security Monitor displays the main window with the system list.

After you select the networked systems to monitor and you click OK, NetworkSecurity Monitor loops through the list of selected NT systems. For eachselected system, the program calls subroutine readservereventlog to retrievethat system's security Event Log.

Luckily, you don't need in-depth system internals information to access NTevent logs. As you do to obtain the system names, you use a set of system APIfunctions to open and read each system's Event Log. These functions, which arein advapi32.dll (the Registry API), include openeventlog, which opens an EventLog on a remote or local system; getnumberofeventlogrecords, which returns thenumber of entries in the log; and readeventlog, which reads the Event Logentries. Listing 3 shows the three functions' declarations. Subroutinereadservereventlog contains calls to each of the three functions; Listing 4 is apartial listing of this subroutine.

LISTING 3: Declarations for functions openeventlog, getnumberofeventlogrecords,and readeventlog

Declare Function OpenEventLog Lib "advapi32" Alias"OpenEventLogA" (ByVal lpUNCServerName As String,ByVal lpSourceName As String) As Long

Declare Function GetNumberOfEventLogRecords Lib "advapi32.dll"(ByVal hEventLog As Long, NumberOfRecords As Long) As Long

Declare Function ReadEventLog Lib "advapi32.dll"Alias "ReadEventLogA" (ByVal hEventLog As Long, ByValdwReadFlags As Long, ByVal dwRecordOffset As Long, lpBuffer As Any, ByValnNumberOfBytesToRead As Long, pnBytesRead As Long, pnMinNumberOfBytesNeeded As Long) As Long

For each selected NT system, Network Security Monitor opens the Event Logby calling function openeventlog (A in Listing 4) and reading the securityevents. The first parameter of openeventlog contains the server name from thelist box of selected servers on the main form. The second parameter is a stringthat specifies the type of Event Log to open. Because Network Security Monitorreads security event logs only, the stringsEventLogType contains the value security.Although Network Security Monitor reads only security event logs, you can openand read the application and system event logs by specifying the value applicationor system, respectively. When function openeventlog completessuccessfully, it returns a handle, a unique identifier for the open Event Log.

LISTING 4: Subroutine readservereventlog (partial listing)

' Open the event log and get the number of records. lEventLogHwd = OpenEventLog(sServerName, sEventLogType)

A

If GetNumberOfEventLogRecords(lEventLogHwd, lRecordNumber) <> FalseThen 'Set up the progress bar. ProgressBar1.Max = lRecordNumber ProgressBar1.Value = 1

B

DoEvents Else MsgBox "Error getting number of Event Log Records: "& Err.LastDllError Exit Function End If

If lRecordNumber <= 0 Then MsgBox "No records in event log"Exit Function End If

' Now set up the read flags. lReadFlags = EVENTLOG_BACKWARDS_READ Or EVENTLOG_SEEK_READ

' Loop through the record numbers. For x = 1 To lRecordNumber ' Increment the progress bar as we read the event log. ProgressBar1.Value = x lReadRecordOffset = x

lNumBytesToRead = 56 ReDim bBuffer(0 To lNumBytesToRead - 1)

TryAgain: lReturnCode = ReadEventLog(lEventLogHwd, lReadFlags, lReadRecordOffset,_bBuffer(0), lNumBytesToRead, lBytesRead, lMinNumBytesNeeded)

If lReturnCode = False Then If Err.LastDllError = ERROR_INSUFFICIENT_BUFFER Then lNumBytesToRead = lMinNumBytesNeeded ReDim bBuffer(0 To lNumBytesToRead - 1) GoTo TryAgain ElseIf Err.LastDllError = ERROR_HANDLE_EOF Then Exit For Else MsgBox "Error reading the Event log on: " &sServerName Exit For End If End If

C

' Copy the data from the pointer to a string buffer. sBuffer = Space$(lBytesRead) RtlMoveMemory ByVal sBuffer, bBuffer(0), lBytesRead

Next, Network Security Monitor calls function getnumberofeventlogrecords toretrieve the number of entries in the log. The code that accomplishes this callis at B. The handle returned by openeventlog is passed as the first parameter.The second parameter returns the number of records in the open Event Log. Theprogram uses this number to set up a progress bar that shows relatively how muchof the Event Log you have read. If no records appear in the Event Log, an erroroccurs, and the program displays a message box.

The code that reads through the event logs is at C. A loop reads thesecurity Event Log for the number of entries retrieved previously with functiongetnumberofeventlogrecords. The program increments the progress bar each timethe read loop is executed. The program calls function readeventlog, whichretrieves data from the security Event Log. readeventlog's first parameter takesthe Event Log handle that the call to openeventlog obtained earlier. The secondparameter controls whether the program reads the Event Log forward or backward.To display the most current event first, the program must read the securityEvent Log backward (i.e., from most recent to least recent), so you use theeventlog_backwards_read flag with the eventlog_seek_read flag, which shows wherethe read will begin. In this case, the flag is at the beginning of the filebecause of the value of the lreadrecordoffset parameter. The program uses thethird parameter with the eventlog_seek_read flag, and the third parameterspecifies where in the security Event Log the readeventlog function startsreading. (If you set this parameter to 0, readeventlog starts reading from thebeginning of the log.) The fourth parameter is a pointer to a buffer thatcontains data that readeventlog returns.

readeventlog's fifth parameter sets the number of bytes to read, and thesixth parameter returns the number of bytes that the program actually read. Youcan read more than one security Event Log entry per call, but for simplicity,this example reads only one record at a time. However, there's a gotcha inreading one entry at a time. Event Log records vary in length, so you can't usea fixed value for the number of bytes. Therefore, the program attempts the firstread using an event entry's minimum length. If the return code shows that thecall failed due to insufficient buffer space, the program resizes the bufferaccording to the value returned in the sixth parameter and reattempts the callto function readeventlog. If the call is successful, subroutinereadservereventlog calls the Win32 rtlmovememory function to copy the Event Logdata from the buffer to a VB string. This API is part of the NT kernel. Itspurpose is to copy data from one memory location to another. For convenience,I've declared the rtlmovememory API in both the netapi.bas file and theeventlog.bas file. This configuration lets you easily apply the rtlmovememoryfunction in other projects that use either the NetBIOS APIs or the Event LogAPIs.

The data returned by readeventlog follows a predefined layout or structure,as shown in Table 2. The first part of the returned datauses a fixed format. In contrast, the second portion of the returned data variesin length. The offset data in the beginning of the structure identifies thelocation of the variable data. The returned data includes the time stamp, EventID, username, computer name, and string data that may contain an informationalmessage.

One point is important to know about reading the Event Log string data thatfunction readeventlog returns: The function doesn't return the same string datayou see in NT's Event Viewer. Instead, the string data returned by the APIcontains substitution parameters that you must convert and reformat before theyare meaningful. The substitution parameters don't affect Network SecurityMonitor because it looks for a specific type of event with known string data.However, if you want to write a more sophisticated application that interpretsand displays more types of Event Log data than Network Security Monitor, yourprogram must read the Services key of the NT Registry to retrieve the name ofthe eventmessages library. It contains the formatmessage function. Then yourprogram must load that library and call the formatmessage function to fill inthe substitution parameters for each string entry. (This extra step accommodatesinternationalization of the string data.)

Powerful APIs
As you've seen, creating a useful VB utility is easy with just a few Win32APIs. I didn't have to master complex system internals or network coding todevelop Network Security Monitor--only call some Win32 API functions. Althoughusing the APIs isn't hard, I suggest you use the Microsoft Developer Network CDto learn about the API functions--as I did.

Although I wrote Network Security Monitor to search for one type of EventLog entry, you can easily modify this program to look for different Event Logentries or to read through the other event logs. Enjoy using Network SecurityMonitor--and stay tuned for more VB Solutions!

Editor's Note: For the complete source and executable code for thissolution, go to the Windows NT Magazine site, www.winntmag.com. WindowsNT Magazine wants to publish your VB solutions. If you've created someinteresting and useful VB solutions for your business' problems, send them tous! If we agree that your VB solutions are interesting and useful to ourreaders, we'll publish your code and pay you $100. You can send contributions orideas for VB solutions to me at [email protected].

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like