A Simple Software/Hardware Scanner For Windows Machines


Home Page | Comments | Articles | Faq | Documents | Search | Archive | Tales from the Machine Room | Contribute
The Need For Informations Sometimes ago, I had to perform an 'hardware and software inventory', meaning that I needed to go around the office and take down informations about the hardware (machines, processors, memory and the like) and the kind of software installed on each machine.

Of course, doing this by hand wasn't exactly my idea of 'fun'. So I thought about a simple system to perform the scanning trought the network.

Currently, Windows can give a lot of informations trought SNMP, but snmp must be enabled on the single machine and the data retrived aren't exactly what I needed.

So, after a while, I sat down at the keyboard and wrote this simple 'scanner' that search the registry and report the informations into a simple MySQL database. I choosed to store the data in MySQL so I could display and mangle the informations later at my leasure.

To compile the source you need Visual C++ version 5 or better, this is due to the fact that the MySQL libraries are compiled for Visual C++ (thanks a lot MySQL!), so if you don't have Visual C or don't want to install it you're left with two choices: recompile the source code of the library from scratch or use a different database.

If you decide to recompile the MySQL library please let me know, since it took me 3 days and then I gave up with it. Addendum: Marco Nava solved the riddle by calling directly each and any functions using the GetProcAddress, you'll find his solution at the end of the page.

ATTENTION: when I started this thing was 4 years after my last encounter with C language, so don't bust my ball about the fact that the code stinks or the fact that there are 'better way' to do certain things. I know it. I don't give a damn! Ok?

If you want to compile the code you need to tell Visual C to use wsock32.lib and mysqlclient.lib in addition to the other libraries, you also need the MySQL includes and the aforementioned libraries. You can found the lib in the MySQL distribution and the includes in the source distribution.

Scanning The Registry The registry isn't exactly the perfect place where to store informations, it's bloathed, huge, complicated and doesn't work very well. But this can be said about the entire Windows environment so...

Anyway, the core functions to read the registry are a couples of Windows API: RegOpenKey, RegEnumValue, RegEnumKeys and RegQueryInfoKey. Those are used to access a key, get the subkeys, get the values and get the informations related to a single key.

The whole process can be explained as follow:

  1. Open a key
  2. Read the values
  3. Store the values in the database
  4. Read the subkeys
  5. For each subkey repeat from (1)

This if you are interested in the subkeys, sometimes I don't care about the subkeys, so I just read the values of a specific key and that's it.

After a while of checking things in the registry I decide that the keys I was interested into were:

  • SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName
    Contains the machine name, this is usefull to distinguish the various machine in a network.
  • SOFTWARE\Microsoft\Windows NT\CurrentVersion
    This actually contains the informations related to the version of Windows installed on the machine. I know that it works for Windows NT/2000/XP/2003, don't know if it works for other versions.
  • HARDWARE\DESCRIPTION\System\CentralProcessor\0
    It contains informations about the processor, since in my network there are only single-processor machines (so far), I don't need to check all the possible keys but just this one.
  • HARDWARE\DEVICEMAP\Scsi
    This contains informations about the disks installed on the machine.
  • HARDWARE\DEVICEMAP\VIDEO
    Should I say something about it?
  • SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
    This key and all the subkeys, are a complete list of the software that is installed in the machine.

Only two things I couldn't figure out how to get from the registry: the physical ram installed and the size of the disks. So I cheated and used GlobalMemoryStatus for the Ram and DeviceIoControl for the disks.

I decided that I didn't want to change the program if I wanted to add a key to the list, so I wrote a very simple piece of code to read the keys list from a configuration file.

Storing The Data When you get the data out of the registry they aren't really 'clean'. A 'standard' key or a value can be something quite uggly to see and quite useless, so I had two choices: clean the data before inserting them in the database or clean the data after. I decided to do both. I 'clean' the registry key before inserting the data and don't insert data I don't want, then I process the info when I display them from the database in my user interface. This allow me to keep the C code very slim and efficient, and at the same time I keep all the informations I could need available.

To store the data I use MySQL, besides the fact that they distribute the libraries only for Visual C (again: Thanks A Lot!), the API of MySQL are really easy to use and very 'to the point'.

Basically to store the data you need to do very few steps:

  1. Open a connection to the database
  2. Create a query to insert the data
  3. Run the query
  4. Close the connection

That's it...

The Database I decided to keep the database really simple, so I designed one single table to receive all the data, this is the structure:

fieldname type length description
computer varchar 50 is the machine name, actually the lenght of a name is limited to 15 character on Windows 2000, maybe is going to change in future.
keyid varchar 200 is the name of the key
valid varchar 150 is the 'name' or description of the value
value varchar 150 is the real value

Now, To The Code! You can get the whole source code here.

Some explanation could be usefull...

readconfig is the function used to read the configuration file (an example configuration file is in the .zip file), the configuration file is read and an array of 'keys' (they are strings) is created, the array is then processed and each key is scanned. The configuration file also contains the informations required to connect to the database server (username,password,database and hostname/ip).

scansubkeys is used to retrive a list of all the subkey of a specific key.

scanvalues is used to scan all the values of a specific key.

scanallofthem is the function that actually perform the job, it uses the other two functions to scan the whole registry.

scanakey this function is recursive, it scan a key for all the subkeys and values.

getcomputername this function is used to collect the machine name before we start the whole process. Note: the key that allow me to retrive the computer name is the only key that is not read from the configuration file but is hardcoded.

getthedisks it calls DeviceIoControl to recover informations about every hard disks installed and store the data as fake registry keys in the database.

getthefuckingmemory did I mentioned that I was quite stressed when I wrote this?

storedata well... it store the data in the database...

strtran is used to remove certain characters from the values and keys before inserting them in the database, character like '\' and ' can be nasty to handle. So I change them in '/' and '(space)'.

Running The Program The program require one parameter on the command line, that is the configuration file path/name. If he can't find the configuration file it complains and stop. If you don't give him a configuration file it doesn't do anything.

I kept the error checking at a minimum, so if you get some kind of error during the process I won't be so surprised.

The idea is to have this running from the network during the normal startup of the machine, so he can scan the machine and store the data in the database.

The User Interface The 'User Interface' of the entire program is... well, I wrote it in PHP because I wanted it like that, you're free to rewrite it how you like. So I'm not going to tell you how to write it.

Addendum Marco Nava says:

Like you I found some... trouble in making MySQL cooperating with GCC, at least after 5 or 6 tryes dlltool didn't make a dll or an .a file that was useful. I do know, however, that it can be done altering the .def file by hand, I know that because I did it once, but I don't remember anymore how I did it. So I thought of circumventing the problem by calling directly the functions from libmysql.dll using LoadLibrary and GetProcAddress.

The code, compiled with MiGW, works like a charm. To do so I had to redefine all the prototypes for MySQL functions. For all the versions from 3.23 to 5.1 the prototype is the same, so it should work regardless of the version (if the functions are the same in all the DLLs). I've tested it with 5.0 and 4.1. I've also added two entry to the configuration file to specify the table where to store the data and one for the DLL path.

And here you'll find his source.

Addendum 2 Walter Lain and Fabio Pinton modified the source to use simple plain text files and Walter also wrote some PHP code to display the infos. Walter said that The PHP code sucks, but this is my first real project beyond 'phpinfo()' so bear with me.

Modified source coude
PHP client


Comments

Max length of comments: 1000 chars.

nessun commento.

Add a comment (max 1000 chars)

Comment from:
Comment:


Author Davide Bianchi, works as Unix/Linux administrator for a "network security" company of Haarlem.
Contacts: mail: davide AT onlyforfun.net , ICQ: 268751033, Jabber: davideyeahsure AT gmail.com Skype: davideyahsure

Contribuire Volete contribuire? Leggete come!

Copyright This site is made by me with blood, sweat and gunpowder, if you want to republish or redistribute any part of it, please drop me (or the author of the article if is not me) a mail.

This site was composed with VIM, now is composed with VIM and the (in)famous CMS FdT.

This site isn't optimized for vision with any specific browser, nor it requires special fonts or resolution.
You're free to see it as you wish.
Web Interoperability Pleadge is this a valid html document?

Last Update: 07/10/2008