Hacking aspSysInfo

Started by Björn and Rob, aspSysInfo is a clone of the ever popular phpSysInfo script for Microsoft IIS-based web servers written using Active Server Pages (ASP). Both of these scripts display system statistics (phpSysInfo) such as uptime, memory usage, file system resources, network information, and a device list of the host being accessed in a neatly formatted HTML page. Although aspSysInfo has been dormant for almost a year, phpSysInfo has continued on and is now compatible on Windows with IIS. It still requires (you guessed it) PHP to be installed which is overkill for–what amounts to–eye candy.

Installation for aspSysInfo is very straight forward. Unrar the file into a directory on your webserver and register two DLL files in the /components directory (i.e. ‘regsvr32 c:\path\to\file.dll’). A few options can be changed in /includes/config.inc. Everything should be ready now. The page can be viewed by pointing your web browser to the directory where aspSysInfo is installed. It should look something like this:

aspSysInfo Example (small)


Note: Unlike the example system above, I am running Windows XP Pro SP2 but aspSysInfo should work on any modern version of Windows.

Instead, I saw this page with VBScript runtime error ’800a0006′:

aspSysInfo Error (small)

A search of the various files shows cint() used in drawProgressBar() to determine the width of the progress bar. cint() only works for values up to 32,767 but int() doesn’t have that limitation. The page loaded in Firefox without throwing any runtime errors after the change, but was severely distorted due to the progress bar width of negative 2 million. It turns out that all the values are off by 2^32. The easy way out is to edit MemInfo() and adjusted the values by simply adding 2^32 anywhere a “if < 0" argument is true. This is a lazy hack but it does work. On closer examination of the numbers, the total pagefile is being incorrectly displayed as ((2 * TotalPhysicalMemory) + TotalPageFile). Although installing PHP would probably be easier than hunting down bugs at this point, there is a good chance of making this work properly without too many ugly hacks.

To the credit of the aspSysInfo authors, the source code for SystemInfo.dll is provided which should theoretically facilitate in fixing these problems. On a side note, there is no easy way to grabbing information about the memory subsystem directly through VBScript thus necessitating either a WMI or a COM-based DLL approach. There is just one problem--I have never coded Visual Basic and have no idea of what I'm up against. I found my old copy of Visual Studio 6.0 from college and installed it. Opening the SystemInfo project is straight forward and I found "Memory.cls" which is the memory class that needs to be fixed.

Microsoft (MSDN) has a lot of material online that came in handy as a reference. GlobalMemoryStatus returns the current virtual and physical memory usage. The main caveats don’t begin to show up until a system has more than 2GB of RAM and completely fails on systems with >4GB. On this system, in some fields aspSysInfo is signifying an overflow condition by returning ‘-1′. The other problem is in the MemoryStatus declaration due to the use of signed Long types which are limited to -2^31 to 2^31-1:

1
2
3
4
5
6
7
8
9
10
Private Type MEMORYSTATUS
    dwLength As Long
    dwMemoryLoad As Long
    dwTotalPhys As Long
    dwAvailPhys As Long
    dwTotalPageFile As Long
    dwAvailPageFile As Long
    dwTotalVirtual As Long
    dwAvailVirtual As Long
End Type

The material on MSDN requires moving to GlobalMemoryStatusEx and MemoryStatusEx for systems with >4GB of RAM.

The next hurdle was the fact that I’ve never used any part of the Win32 API and had to find a VB equivalent of the DWORDLONG typedef (unsigned __int64) called for in the structure definition of MemoryStatusEx. The VB Currency type is scaled int64 shifted 4 decimal places so I’d have to multiply everything by 1E4. The function declarations (see below) return String instead of Double in the original. I reimplemented the functions an fixed the error in the total physical + virtual combined memory. After VB6 finished compiling it, the new DLL was a drop in replacement on my machine. I have not tried it on systems with less than 4GB of RAM so I have no way of knowing if it will work.

Here is the code for Memory.cls:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "Memory"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
 
‘ Reimplemented Memory.cls in SystemInfo.dll for >4GB systems using aspSysInfo
‘ by HishamR
 
Option Explicit
 
Private Declare Function GlobalMemoryStatusEx Lib "Kernel32.dll" (ByRef lpBuffer As MemoryStatusEx) As Long
 
Private Type MemoryStatusEx
    dwLength As Long
    dwMemoryLoad As Long
    ullTotalPhys As Currency
    ullAvailPhys As Currency
    ullTotalPageFile As Currency
    ullAvailPageFile As Currency
    ullTotalVirtual As Currency
    ullAvailVirtual As Currency
    ullAvailExtendedVirtual As Currency
End Type
Private MemStat As MemoryStatusEx
Private Const CurMult As Long = 10000
Private Const OneKb As Long = 1024
 
Private Declare Function GetTickCount Lib "Kernel32.dll" () As Long
 
Public Function AvailablePhysicalMemory() As String
    Dim AvailPhys As String
    MemStat.dwLength = Len(MemStat)
    GlobalMemoryStatusEx MemStat
    AvailPhys = CStr((CDec(MemStat.ullAvailPhys) * CurMult))
    AvailablePhysicalMemory = AvailPhys
End Function
 
Public Function TotalPhysicalMemory() As String
    Dim TotalPhys As String
    MemStat.dwLength = Len(MemStat)
    GlobalMemoryStatusEx MemStat
    TotalPhys = CStr((CDec(MemStat.ullTotalPhys) * CurMult))
    TotalPhysicalMemory = TotalPhys
End Function
 
 
Public Function AvailablePageFile() As String
    Dim AvailPage As String
    MemStat.dwLength = Len(MemStat)
    GlobalMemoryStatusEx MemStat
    AvailPage = CStr((CDec(MemStat.ullAvailPageFile) * CurMult))
    AvailablePageFile = AvailPage
End Function
 
Public Function PageFileSize() As String
    Dim PageFileS As String
    MemStat.dwLength = Len(MemStat)
    GlobalMemoryStatusEx MemStat
    PageFileS = CStr(((CDec(MemStat.ullTotalPageFile - MemStat.ullTotalPhys)) * CurMult))
    PageFileSize = PageFileS
End Function
 
Public Function AvailableMemory() As String
    Dim AvailMem As String
    MemStat.dwLength = Len(MemStat)
    GlobalMemoryStatusEx MemStat
    AvailMem = CStr((CDec(MemStat.ullAvailPageFile + MemStat.ullAvailPhys)) * CurMult)
    AvailableMemory = AvailMem
End Function
 
Public Function TotalMemory() As String
    Dim TotalMem As String
    MemStat.dwLength = Len(MemStat)
    GlobalMemoryStatusEx MemStat
    TotalMem = CStr((CDec(MemStat.ullTotalPageFile) * CurMult))
    TotalMemory = TotalMem
End Function
 
Public Function GetUptime() As Long
    GetUptime = GetTickCount \ 1000
End Function

Lastly, aspSysInfo has some problems in its networking code. After taking a look at the netInfo class, I was able to correct the missing arguments. Change displaynicinfo to 1 in config.inc to enable it after modifying the following code in index.asp):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Function Netinfo()
        if displaynicinfo = 0 then
                Set net = Server.CreateObject("SystemInfo.Netinfo")
                output = device & "§" & received & "§" & sent & "§" & errors & "<§>"
                for a = 1 to net.getNicCount
                        if net.getnictype(a) <> 24 then
                                if net.getNicOctIn(a) < 0 then
                                        netIn = net.getNicOctIn(a) + (4294966*1024)
                                else
                                        netIn = net.getNicOctIn(a)
                                end if
                                if net.getNicOctOut(a)  < 0 then
                                        netOut = net.getNicOctOut(a) + (4294966*1024)
                                else
                                        netOut = net.getNicOctOut(a)
                                end if
                                output = output & net.getNicName(a)  & "§" & formatSize(netIn) & "§" & formatSize(netOut) & "§" & net.getNicErrors(a)  & "/" & net.getNicDrops(a)  & "<§>"
                        end if
                next
                NetInfo = left(output, (len(output)-3))
        else
                NetInfo = "Disabled in config"
        end if
End Function

Note: I’ve added argument (a) to functions getnictype, getNicOctIn, getNicOctOut, getNicName, getNicErrors and getNicDrops.

There is still a problem in this code that can be seen with “if function() < 0". Once getNicOctIn and getNicOctOut reach 2^32-1, the values either return an overflow or wrap around to a negative number. Although I’m sure there are ways to change this to keep track of the data transferred beyond 4GB, I haven’t bothered to try yet. If you have any ideas, then shoot them my way, to the original authors, or make the necessary changes so we can all benefit.

Disclaimer: There is no warranty or promise that it will work. Also If you decide to experiment with my adaptation, please remember that I have zero experience with DLLs, Win32 API, and VB so I’m sure my explanations and implementation are riddled with errors, hacks, cruft, and is pretty much worthless to anyone but me so, again, you are on your own and I’m not responsible for any damage or downtime that occurs by using this code. By the way, I’m not a lawyer so that is the best disclaimer I can grab off the top of my head.

Popularity: 5% [?]

Comments

6 Responses to “Hacking aspSysInfo”

  1. blog.towel.nu on June 21st, 2006 12:42 am

    Expressions…

    Hacking aspSysInfo at Hisham’s Blog Hey would you look at that? Seems like my old code is still alive. This guy tried to contact me on irc the other day, but time-zone differences made me unable to reply before he……

  2. Björn Gustafsson on June 21st, 2006 12:46 am

    Good stuff! I wrote a little entry in my blog about it, and well – if you’re interested I’d be more than happy to grant you access to the sourceforge-repository so you can upload the changes you’ve made (i don’t think i have a vb-compiler around anymore for one) :)

  3. Hisham on June 22nd, 2006 3:49 am

    Björn: Nice to hear from you. I’m hesitant to integrate this code into the official project because of how little testing and feedback this code has been through. Furthermore, the new DLL only works on systems that have GlobalMemoryStatusEx available (Windows 2000, XP, 2003, and Vista) but I’ve only tested on XP. Although I think one possible to make it compatible with Win95 to Vista would be to add a check for the presence of GlobalMemoryStatusEx and then running the appropriate code.

    On the networking side I haven’t been as lucky. I think using PDH.DLL is a fairly easy solution to grab data instantaneous data. However, there is no solution I could find for the looping of the network data information. The problem is that both dwInOctets and dwOutOctets under MID_IFROW (iphlpapi.dll) are of type DWORD so they return data as unsigned 32-bit integers. Every time their value goes above 2^32, they loop. It also doesn’t help they are passed to VB data type Long which has a max value of 2^31-1. The only way to make this work is by running a service in the background that polls the data frequently enough to both record data and to increment a counter for each rollover. Then the data could be correctly displayed by running dwInOctets (loop counter * 2 ^ 32). The sad part is if you run “netstat -e” from the command line in Windows XP and transfer a large file, it too will reset to 0 once crossing the 4gig (2^32) limit.

  4. jake on July 4th, 2006 11:38 am

    i dont think you need to use the globalmemorystatusex on win95-win98 since they can only address

  5. Command Line Warriors on November 4th, 2006 5:42 am

    This Week on the Command Line: Laptop Backups and apt-get for Windows…

    It has been a while since last time, so lets do this. ‘This week on the command line’ is my look at what I have read online in the past seven days. If you have read or written anything cool then leave a comment below and tell me about it.
    P…

  6. x0ver on November 16th, 2006 7:53 am

    Is there anything that needs to be enabled in order to get this working on a new IIS install?