How I connected computers and users with the ports of network devices in the program for monitoring Network MACMonitor
In the process of developing the program, a task arose: to determine which computers the users work with and to associate this information with the ports of network devices. In this article I want to write how I managed to do it.
I started with a simple argument: in order to connect a user to a network device port, you first need to associate the computer the user is working on with this port. Since the program Network MACMonitor allows you to find mac addresses on network device ports, it was decided to connect computers with ports using mac addresses. Next, you need to associate users with computers. This information can be obtained if you interrogate computers in any way.
I saw two options for solving this problem:
- Write a Windows agent and poll it using Network MACMonitor;
- Use Windows Management Instrumentation (WMI).
The Windows agent version has a number of drawbacks, which for me were significant:
- development of a secure protocol for network interaction between the Windows agent and the Network MACMonitor program;
- the need to pre-install the agent on computers;
- using another programming language (I am writing in Java), as I consider Java not suitable for writing an agent: due to the relatively large consumption of virtual memory and the need to install JRE on all computers.
Because of all the above minuses, I decided to dwell on the version using WMI.
WMI client development
Since the Network MACMonitor program is written in Java, I tried to find a ready-made cross-platform Java library that implements the WMI client functionality. And here I was disappointed - there is no such library. All existing libraries are either wrappers over Windows utilities, or (j-Interop library) require additional registry manipulation (change of ownership and permissions to registry branches) to activate WMI via a remote registry. Since Java was not a fully working library, I decided to find a library or a WMI client written in any other programming language. And I found one WMI client for Linux. Having downloaded and checked his work, I realized that polling Windows computers from under Linux is possible.
Since this is possible, I decided to write my own library in pure Java, which would allow to poll the computer via WMI.
To write the library, clear documentation was needed on how the WMI protocol works. It turned out that such documentation is available and it is freely available.
I started preparing for writing the library by looking at the WMI network protocol stack.
|Windows Management Instrumentation (WMI)||MS-WMI, MS-WMIO|
|Distributed Component Object Model (DCOM)||MS-DCOM|
|Remote Procedure Call (RPC)||MS-RPCE|
|Transmission Control Protocol (TCP)||-|
|Internet Protocol (IP)||-|
For WMI to work correctly, it is necessary that all levels of the stack be implemented.
Since WMI is not implemented in Java, I switched to the next protocol in the stack - DCOM. And then I was lucky. Although the aforementioned j-Interop library does not implement WMI functionality, DCOM functionality is implemented in it. So it remains to write the implementation of the WMI protocol, that is, to write the implementation of the MS-WMI and MS-WMIO specifications.
I started with the implementation of the MS-WMIO specification, which is responsible for the data encoding format in the network packets of the WMI protocol. From the specification, I learned that the encoding of Backus-Naur syntax (ABNF, RFC 5234) is used when encoding data. The MS-WMIO specification fully describes the encoding format using ABNF. It is known that if there is a grammar described in ABNF, then it is possible to create a parser for this grammar. On the Internet, I found an ABNF parser generator for Java and, at the entrance, gave it a grammar taken from the specification. Since the generated parser worked with strings, and MS-WMIO describes the binary encoding format, the idea was simply to replace the generated parser with strings of arrays of bytes and characters of bytes. But looking at the number of files where replacement was necessary, as well as having learned from the MS-WMIO specification, that sometimes you need to work with bats, I realized that it would be very difficult to fix the generated parser, and decided to abandon this idea. I thought that writing a parser from scratch would be faster. And the parser was ready.
But how to verify that the parser is written correctly if the MS-WMI specification, which is responsible for the operation of the WMI protocol, is not yet implemented? Here Wireshark, a network traffic analyzer, helped me. Having made WMI requests using standard Windows tools (wbemtest), having previously turned off encryption, I received network packets and saved them into binary files. These files could already be used as test data for the parser.
When the parser was tested and the errors found were fixed, I proceeded to implement the MS-WMI specification, which describes the operation of the WMI protocol.
The MS-WMI specification is divided into server and client. I have partially implemented the client part, in the amount necessary for polling the computer via WMI. In this part, I also needed Wireshark, but already to analyze the sequence of network packets during a WMI survey.
Attempting to retrieve necessary data using WMI
After writing the WMI library, it became the task of using it in the Network MACMonitor program. The question arose: what data should be received from computers? I thought that I need to get the computer name, domain, operating system, power-up time, mac addresses, ip addresses, active users who work at the computer.
But a very important problem arose: how to uniquely identify a computer during a WMI survey? I considered the following options:
- mac address, change possible, non-unique possible;
- computer name and domain (workgroup), it is possible to change, non-uniqueness (for a workgroup);
- the serial number of the hard disk where the operating system is installed, administrator rights are required during the WMI survey, did not check the uniqueness, but I suspect that non-uniqueness is possible;
- serial number of the motherboard, non-uniqueity is possible, and often enough;
- computer system identifier ( UUID property of WMI class Win32_ComputerSystemProduct ), non - uniqueness is possible, and quite often;
- installation time of the operating system, the best of all options, but non-uniqueness is possible when cloning a system, or when unrolling from an image.
No option allows you to uniquely identify a computer, so I stopped at the computer identification by three parameters:
- the serial number of the motherboard,
- computer system identifier
- installation time of the operating system.
Of course, these three parameters may be the same for different computers, but less often than one of them.
An attempt was also made to get active users using the standard WMI class: Win32_LogonSession . Then the first problem appeared: it turned out that Win32_LogonSession shows all user sessions, even those that have already ended. I began to think how to filter active sessions from terminated. I found that this can be done using the Win32_SessionProcess class , which binds instances of the Win32_LogonSession classes to Win32_Process . If the link to the session is in the list of Win32_SessionProcess class instances(there is at least one process with the identifier of this session), then it is active. Next came the question of how to associate a session with a user. This can be done using the Win32_LoggedOnUser class , which binds instances of the Win32_LogonSession and Win32_UserAccount classes . It remains only to obtain instances of the Win32_UserAccount class , which provide detailed information about the user.
But here I was disappointed. When using WMI remotely, it turned out that when trying to get instances of the Win32_UserAccount class , it is possible to get only local users of the computer. That is, it turned out that using standard WMI tools, it is impossible to know which users are active on the computer.
Development of a WMI provider.
Due to the inability to uniquely identify computers and the inability to obtain information about active users using standard WMI classes, it was decided to extend the functionality of WMI. You can do this by describing your WMI classes in a MOF file and writing a WMI provider to get instances of these classes.
Two new WMI classes were described: NMBY_InstallInfo - to identify the computer and NMBY_LogonSession - to determine the active users of the computer.
Then the WMI provider was written with which you can get instances of these classes.
Additional requirements were set for the provider:
- work on a system without .NET;
- work on the operating system Windows XP and above;
- possibility of obtaining information using a non-administrative account.
Therefore, the provider was written in C ++ using WinApi.
In the process of writing the provider, difficulties arose due to the small amount and quality of documentation on this topic, but despite this the provider was successfully written.
The written provider is available on the download page . It can be installed and used for free.
As a result, it became possible with the help of the Network MACMonitor program:
- connect users with computers;
- connect computers to network device ports;
- associate network device ports with computers and users;
- view the registration history of users on computers.