< Day Day Up > |
Since kernel structures change between major versions of the operating system and, in rare cases, between service packs, a rootkit developer must be aware of the system version on which the rootkit will run. The authors of this book believe it is poor form to use hard-coded addresses, or even offsets. Instead, your code should adapt to its surroundings. The goal: Compile once, or at most twice, but run everywhere! If your rootkit has a user-mode portion, you can determine the operating system version in a userland process using the Win32 APIs. Alternatively, you can determine the system version in the kernel. Obviously, the former is much easier than the latter. User-Mode Self-DeterminationWith the Win32 API, it is very easy to determine what version of the operating system your rootkit is installed upon. The structure used to retrieve this information is called OSVERSIONINFO or OSVERSIONINFOEX. It contains information about the major and minor versions of the operating system. The EX version also specifies the major and minor versions of service-pack level.
The definition of the OSVERSIONINFOEX structure follows: typedef struct _OSVERSIONINFOEX { DWORD dwOSVersionInfoSize; DWORD dwMajorVersion; DWORD dwMinorVersion; DWORD dwBuildNumber; DWORD dwPlatformId; TCHAR szCSDVersion[128]; WORD wServicePackMajor; WORD wServicePackMinor; WORD wSuiteMask; BYTE wProductType; BYTE wReserved; } OSVERSIONINFOEX, *POSVERSIONINFOEX, *LPOSVERSIONINFOEX; Declare a structure of this type in your code and pass a pointer to this structure when you call the GetVersionEx function. Here is the function prototype for GetVersionEx: BOOL GetVersionEx( LPOSVERSIONINFO lpVersionInfo ); After you have made this call, you should have identified the version of the operating system executing your code. The following code uses the OSVERSIONINFOEX in the call to GetVersionEx to retrieve the major version of the operating system and its service pack level: void DetermineOSVersion() { OSVERSIONINFOEX osvi; // Setup the size of the structure osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if (GetVersionEx((OSVERSIONINFO *) &osvi)) { switch (osvi.dwPlatformId) { // Tests for Windows NT product family. case VER_PLATFORM_WIN32_NT: // Test for the product. if ( osvi.dwMajorVersion == 4 && \ osvi.dwMinorVersion == 0) { fprintf(stderr, "Microsoft Windows NT 4.0 "); //... } else if ( osvi.dwMajorVersion == 5 && \ osvi.dwMinorVersion == 0 && \ osvi.wServicePackMajor == 3) { fprintf(stderr, "Microsoft Windows 2000 SP 3 "); //... } break; } } } Once you know the version of the operating system your rootkit is running on, you can adjust the offsets you will use with DKOM. The importance of this will become evident in the next section. Kernel-Mode Self-DeterminationThe user-mode APIs discussed in the preceding section are not the only way to find out the operating-system version. The kernel also contains an API that provides access to version information. On older Windows systems, you must call PsGetVersion and parse the UNICODE string to obtain service-pack information. Its function prototype follows: BOOLEAN PsGetVersion( PULONG MajorVersion OPTIONAL, PULONG MinorVersion OPTIONAL, PULONG BuildNumber OPTIONAL, PUNICODE_STRING CSDVersion OPTIONAL ); Newer versions of the operating system, such as Windows XP and Windows 2003, support the API function RtlGetVersion. It takes as a parameter a pointer to an OSVERSIONINFOW or OSVERSIONINFOEXW, similar to the user-mode Win32 call discussed in the preceding section. The function prototype of RtlGetVersion is almost exactly the same as the Win32 version. It is defined as: NTSTATUS RtlGetVersion( IN OUT PRTL_OSVERSIONINFOW lpVersionInformation ); Querying the Operating System Version in the RegistryThe Windows Registry holds a great deal of valuable information. In fact, you can use it to find the version of the operating system on which your rootkit is installed. You can do this from user mode, or in the kernel driver itself. Please note that if you decide to query the Registry in your device driver, part of the Registry may not be available if your driver loads and attempts to query the Registry early in the boot process. Here are the important keys to query:
From user mode, you can query these keys once you have the appropriate handle by calling RegQueryValue or RegQueryValueEx. The following code illustrates how to query these Registry keys from a device driver: // Query the Registry to get the operating system version. RTL_QUERY_REGISTRY_TABLE paramTable[3]; UNICODE_STRING ac_csdVersion; UNICODE_STRING ac_currentVersion; // Initialize the variables. RtlZeroMemory(paramTable, sizeof(paramTable)); RtlZeroMemory(&ac_currentVersion, sizeof(ac_currentVersion)); RtlZeroMemory(&ac_csdVersion, sizeof(ac_csdVersion)); paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[0].Name = L"CurrentVersion"; paramTable[0].EntryContext = &ac_currentVersion; paramTable[0].DefaultType = REG_SZ; paramTable[0].DefaultData = &ac_currentVersion; paramTable[0].DefaultLength = sizeof(ac_currentVersion); paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[1].Name = L"CSDVersion"; paramTable[1].EntryContext = &ac_csdVersion; paramTable[1].DefaultType = REG_SZ; paramTable[1].DefaultData = &ac_csdVersion; paramTable[1].DefaultLength = sizeof(ac_csdVersion); // Query the Registry. RtlQueryRegistryValues( RTL_REGISTRY_WINDOWS_NT, NULL, paramTable, NULL, NULL ); // Do something with the data here if the query is successful. // This might include initializing some global variables to // store the service pack number, etc. // Free the UNICODE_STRINGs created by the query. RtlFreeUnicodeString(&ac_currentVersion); RtlFreeUnicodeString(&ac_csdVersion); As you can see, you can determine the version of the operating system in many different ways. The method you choose will depend on what type of rootkit you implement. In the next section, we will show you how to communicate information such as version numbers from a userland process to a driver. |
< Day Day Up > |