A Simple Software/Hardware Scanner For Windows Machines
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.
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:
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:
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.
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:
I decided to keep the database really simple, so I designed one single table to receive all the data, this is the structure:
|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|
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)'.
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' 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.
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.
Comments are added when and more important if I have the time to review them and after removing Spam, Crap, Phishing and the like. So don't hold your breath. And if your comment doesn't appear, is probably becuase it wasn't worth it.
By Davide Bianchi - posted 10/09/2009 08:43
@ Davide Bianchi
By Anonymous coward - posted 10/09/2009 09:15
@ Anonymous coward
By Davide Bianchi - posted 10/09/2009 09:42
By Sandman - posted 25/02/2011 14:42
Gran bella pensata, sul serio!
>L'idea e' di avere sto coso in un qualche script di login, cosi' che venga eseguito >direttamente dalla macchina client al login, senza richiedere niente installato sul client >stesso.
e usare smbclient per iniettare il software e winexe per eseguirlo dalla tua console
By Shikky - posted 09/07/2012 11:43
OCS inventory invece ti sembra un'emerita stronzata oppure, semplicemente, non lo conoscevi?
Davide Bianchi, works as Unix/Linux administrator for an hosting provider in The Netherlands.
Do you want to contribute?
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.