Introduction

A common structure in malware, and also in many legitimate software is to recognize what is the underlying operating system (OS). Depending on the version of it, the virus may want to take different route to execute its activities. This functionality can be reused across different programs and is therefore useful to create and optimize. In this post, we coded a function to detect the version of the Windows OS on which the program is currently running.

Windows Versions

The Windows OS is known to have a confusing version system, which is often the result of marketing pressure. As such, while all software usually follow a version number specifying the major version, the minor version and the revision (and sometimes the build), Windows swings between years of release (ex. Windows 2000), fancy names (Windows Vista) or some designated number (Windows 7). However, in the underlying machinery of Windows are defined standard version numbers, ex. 5.1. Below is the correspondance between Windows operating systems and standard versions.

+------------------------+----------------+----------------+
|                        | dwVersionMajor | dwVersionMinor |
+------------------------+----------------+----------------+
| Windows 10             | 6              | 4              |
+------------------------+----------------+----------------+
| Windows 8.1            | 6              | 3              |
+------------------------+----------------+----------------+
| Windows Server 2012 R2 | 6              | 3              |
+------------------------+----------------+----------------+
| Windows 8              | 6              | 2              |
+------------------------+----------------+----------------+
| Windows Server 2012    | 6              | 2              |
+------------------------+----------------+----------------+
| Windows 7              | 6              | 1              |
+------------------------+----------------+----------------+
| Windows Server 2008 R2 | 6              | 1              |
+------------------------+----------------+----------------+
| Windows Server 2008    | 6              | 0              |
+------------------------+----------------+----------------+
| Windows Vista          | 6              | 0              |
+------------------------+----------------+----------------+
| Windows Server 2003 R2 | 5              | 2              |
+------------------------+----------------+----------------+
| Windows Server 2003    | 5              | 2              |
+------------------------+----------------+----------------+
| Windows XP 64-Bit      | 5              | 2              |
+------------------------+----------------+----------------+
| Windows XP             | 5              | 1              |
+------------------------+----------------+----------------+
| Windows 2000           | 5              | 0              |
+------------------------+----------------+----------------+

When programming in C/C++, the version number of Windows can be obtained via the OSVERSIONINFO structure (or OSVERSIONINFOEX) and the GetVersionEx Windows API function:

OSVERSIONINFOEX VersionInformation;
result = GetVersionExA(VersionInformation);

The version information described in the table above is stored in the dwVersionMajor and dwVersionMinor of the OSVERSIONINFO structure. All version of Windows in the table above will store the value VER_PLATFORM_WIN32_NT (0x2) in the variable dwPlatformId of the structure.

Branching C++ Code Based on Version

Based on the description above, we can create a reusable function to detect the version of the Windows OS:

int GetWindowsVersionInfo() {
//Structure holding the information about
//the operating system.
OSVERSIONINFOEX VersionInformation;

//Variable to hold the return value of
//function calls
BOOL result = false;

SecureZeroMemory(&VersionInformation, sizeof(OSVERSIONINFOEX));
VersionInformation.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

// Call to Windows API function
result = GetVersionEx(&VersionInformation);

if (result == 0) {
    return PROGRAM_ERROR;
}

if (VersionInformation.dwMajorVersion == 5) {
    switch(VersionInformation.dwMinorVersion) {
         case 4:
             //Windows 10
             break;
         case 3:
            //Windows 8.1 or Windows Server 2012 R2
            break;
         case 2:
            //Windows 8 or Windows Server 2012
            break;
         case 1:
            //Windows 7 or Windows Server 2008 R2
            break;
         case 0:
            //Windows Vista or Windows Server 2008
            break;
         default:
            //Unknown Value
            return ERROR_UNEXPECTED_VALUE;
    }
} else if (VersionInformation.dwMajorVersion == 6) {
    switch(VersionInformation.dwMinorVersion) {
         case 2:
            //Windows XP (64bit), Windows Server 2003 or Windows Server 2003 R2
            break;
         case 1:
            //Windows XP
            break;
         case 0:
            //Windows 2000
            break;
         default:
            //Unknown Value
            return ERROR_UNEXPECTED_VALUE;
    }
} else {
    //Unknown version.
    return ERROR_UNEXPECTED_VALUE;
}
return SUCCESS;
} // end function

Note that in many cases, we won’t know if the OS is the workstation version of Windows or the server version of it. Also, there is no details for other versions of Windows, i.e. Windows CE, Windows 95 (…should it still be used somewhere..). That’s where OSVERSIONINFOEX is useful. The wProductType variable will be set to VER_NT_WORKSTATION if the current OS is the workstation version. Furthermore, you can retrieve service packs numbers and edition information with wServicePackMajorwServicePackMinor and wSuiteMask. So if you need more details about the Windows OS, you can include these as well:

//If there is a suite, add it after the version
//of Windows
if (Suite) {
    switch (*Suite) {
        case 0x001:
        strcat_s(WindowsDescription, BufferSize, " Small Business");
        break;
    case 0x002:
        strcat_s(WindowsDescription, BufferSize, " Enterprise");
        break;
    case 0x004:
        strcat_s(WindowsDescription, BufferSize, " BackOffice");
    break;
[...]

For a full version of the procedure, visit this GitHub page, you’ll find the C/C++ code.

Conclusion

Practically any malware at some point will need to check the version of the operating system being infected in order to enable specific functions or exploit certain vulnerability. Rarely will this check go as far as getting the suite, but Remote Access Tool (RAT) and bots will report the operating system, the version and the service pack. If you are a malware analyst, expeect this function to be present in whatever piece of code you’re analyzing.

Leave a Reply

Your email address will not be published. Required fields are marked *