Malware that detects Wine and infects both Windows and Linux?
If you are a slightly above-average user – and you certainly are; otherwise, you wouldn’t be reading this article =D – you’ve probably come across the eternal Linux vs. Windows debate. One point that always comes up is: Linux doesn’t get viruses. We know that’s not true. But indeed, most malware is made for Windows. That’s because, in general, malware developers focus on the most used system to increase the chances of successful infection. Therefore, the primary target has always been Windows, as it holds the largest market share in desktops (while in the mobile market, Android holds the largest share – and is the main target for most malware in that segment).
However, Linux malware has been growing year after year! A 2006 report showed that the number of malware created for Linux had doubled the previous year¹. Shane Coursen, senior technical consultant at Kaspersky at the time, said that many Windows users were migrating to Linux “thinking that Linux would provide them with more security”². As Linux usage in desktops was growing (and still is), so was the number of malware for this system (and still is)! As Shane Coursen said back in 2006: “The use of an Operating System is directly related to malware writers’ interest in developing malware for that Operating System”³.
Ok…, but why am I talking about all this? Let’s jump to a malware analysis I did this week.
Grandoreiro – Detecting if It’s Running in Wine or Not
I was analyzing a malware titled “Grandoreiro” – a famous banking malware from Latin America (SHA256 3687ce1b0865f1c821d471d3f85b8403b5cfd127f807bb1f3aa9cd2fef7db4d9) and noticed something quite interesting: it has a detection mechanism to check if it’s running in “wine.”
This piece of code is quite simple. “Translating” it to C, it would basically be this line of code:
If (GetProcAddress(GetModuleHandle(“ntdll.dll”), “wine_get_version”)) return(1); return(0);
Thus, the malware will attempt to locate the address of the “wine_get_version” function in the “ntdll.dll” library in memory. If the returned address is non-zero, it returns true; otherwise, it means the function was not found, and it returns false.
This piece of code has been used in several malware to detect if they are running on Linux or not. Generally, when they detect that they are running in wine, they terminate their operation because, in this case, they are not running in the target environment (Windows).
Considering everything I’ve written so far, I want to demonstrate a few points:
- Demonstrate that running an infected Windows executable on Linux may not be safe;
- It is possible to create malware that works on both Windows and Linux;
- Wine does not offer operating system isolation (and that was never its goal).
To demonstrate these points, I first need to explain what Wine is.
Wine – Wine Is Not an Emulator
According to the official website, “Wine (originally an acronym for ‘Wine Is Not an Emulator’) is a compatibility layer capable of running Windows applications on several POSIX-compliant operating systems such as Linux, MacOS, and BSD”4.
The website further explains briefly how Wine works, and this part is crucial to understand: “Instead of simulating the internal Windows logic like a virtual machine or emulator, Wine translates Windows API calls into POSIX in real time, eliminating the performance and memory penalties of other methods and allowing you to integrate Windows applications into your desktop cleanly”5.
The point here is to understand that Wine “translates Windows API calls into POSIX in real time.” Let’s see how this works. But first, I need to download Wine’s source code. On the main page, click on the link for the latest stable version:
Then click on “Source code” to download the source code.
Unzip the file, and it’s ready.
Understanding the Translation from Windows API to POSIX – CreateProcessA
Now let’s understand how this translation works. To do this, I’ll take a typical Windows function: CreateProcessA, which is declared in KERNEL32.DLL on Windows.
Wine, of course, reproduces Windows libraries and functions. Therefore, the CreateProcessA function will also be in KERNEL32.DLL. Going to the “dlls/kernel32” folder, you’ll find these files:
It makes sense to see the implementation of the function in “process.c.” In this file, the implementation of “CreateProcessA” is as follows:
Therefore, CreateProcessA just calls “CreateProcessInternalA.” Analyzing the implementation of CreateProcessInternalA, in the end, it only passes to “CreateProcessInternalW.” “CreateProcessInternalW” checks if the process is a command line process (.cmd, .bat, etc.), an old process (e.g., 16-bit), etc. The “normal” is to be a common executable. So this function will end up calling “create_nt_process.” “create_nt_process” calls the function “NtCreateUserProcess.”
Finally, in “NtCreateUserProcess,” we begin to see some POSIX-compliant system implementations. For example, we see that there is a “wine_server,” and communication with it is based on sockets. The sockets are opened with the “socketpair” function, which does not have a native implementation for Windows, and UNIX SOCKETS (PF_UNIX) are used in the domain – which was only implemented in Windows with Windows 10 1803.
It is possible to see some more Unix-specific features, such as the function “get_unix_curdir” to get the current Unix directory where the user is executing the new process. Eventually, we reach the “spawn_process” function, which will finally create the child process.
The implementation of the “spawn_process” function is this:
In the end, CreateProcessA will just call a classic fork! It’s exactly what the documentation said: it’s a “translation” to Unix (in my case, Linux). Just that.
Let’s move on to another example.
Translating GetSystemInfo
The function GetSystemInfo “retrieves information about the current system”6. This function can be used to obtain hardware information, such as the OEM identifier, processor type, number of processors, etc. Look at how interesting part of the implementation for retrieving processor information from the system is:
What’s interesting is that the implementation will be different depending on whether Wine is running on an Apple system, Linux, or FreeBSD. Of course! After all, system information will be in different locations!
Therefore, if running on an Apple system, it will call the “host_processor_info” function to retrieve processor information. If running on a Linux system, Wine will fetch the information from /proc/stat. If running on a FreeBSD system, it will have the respective code to fetch information for that system.
Creating an Executable that Infects Both Windows and Linux?
From this, we realize that it is possible to create malware that infects both systems! In 2018, an interesting study was published in the “Journal of Computer Virology and Hacking Techniques,” revealing that 5 malware out of 30 samples were able to “escape” from Wine and infect the system6. It’s a low number, it’s true. But this shows us that:
- It’s possible; and
- There is a real risk of this happening.
So, to demonstrate this possibility, I want to actually create an executable in this article that identifies if it’s running in Wine and, if it is, infects Linux. If not, infects Windows. Let’s start!
Finding Functions that Identify Wine
Earlier in this article, we saw that Grandoreiro uses the function “wine_get_version” exported by Wine’s “ntdll.dll” to identify the “translator,” as this function only exists in it. Are there other ways to detect it?
In the official Wine forum, in a 2009 thread, they also mention the function “wine_get_unix_file_name”7. They also mention some registry keys, but that’s not a good solution because registry keys can be easily modified.
In a question raised on StackOverflow, they mention the already known “wine_get_version” function and also mention the “wine_get_host_version” function, also exported by Wine’s “ntdll.dll”8.
In another StackOverflow question, they include code that tries to grab the address of the “wine_get_version” function for Wine detection9 (code similar to Grandoreiro’s, by the way). Besides this code, they mention the registry keys from the official Wine forum thread. However, for the reason explained above, they reject the solution.
Finally, there is a C code on GitHub for Wine detection based on these two functions already mentioned above10.
It’s true that most malware will end up using these two functions. But stealthier malware will need to discover some less “known” or less “monitored” function. Therefore, the first thing I want to do is look for some functions exported only by Wine that are different from these two.
There are a few ways to do this. One of them is to read the source code and find the functions that are known not to exist in Windows! See: after downloading the source code of the latest version of the software (at the time of writing, the latest stable version is 9.0), I went to the implementation of the “CreateProcessA” function, for example. I realized that several functions do not exist in Windows DLLs. Just one example: the function “wine_server_handle_to_fd.” Therefore, reading the source code is an efficient method to find these functions. But it certainly won’t be the fastest method.
A faster method is to look at the “specification” files (files with the “.spec” extension). In the root directory of the source code, there is a folder titled “dlls.” Inside this folder, there is a folder for each known Windows DLL (kernel32, ntdll, etc.). Inside each of these folders, there is a file with a “.spec” extension. These files contain a list of all the functions exported by their respective DLLs. But the interesting thing is that for Wine-exclusive functions, the file has the following written:
Therefore, a simple way to find all functions exported “exclusively” by Wine is to search for the string “for internal function” in all “.spec” files. This is easy to do with a grep! Let’s go! =D
Great. Now I have files that contain several functions exclusive to Wine for internal use and are exported! Therefore, they can be used for Wine identification! Here are a few, in addition to the already known “wine_get_version” and “wine_get_host_version”:
wine_get_build_id – exported by ntdll.dll
wine_nt_to_unix_file_name – exported by ntdll.dll
wine_unix_to_nt_file_name – exported by ntdll.dll
wine_server_fd_to_handle – exported by ntdll.dll
wine_server_handle_to_fd – exported by ntdll.dll
wine_server_call – exported by ntdll.dll
__wine_unix_call – exported by ntdll.dll
__wine_unix_spawnvp – exported by ntdll.dll
__wine_syscall_dispatcher – exported by ntdll.dll
__wine_unix_call_dispatcher – exported by ntdll.dll
__wine_unixlib_handle – exported by ntdll.dll
__wine_dbg_write – exported by ntdll.dll
__wine_dbg_get_channel_flags – exported by ntdll.dll
__wine_dbg_header – exported by ntdll.dll
__wine_dbg_output – exported by ntdll.dll
__wine_dbg_strdup – exported by ntdll.dll
wine_get_unix_file_name – exported by kernel32.dll
wine_get_dos_file_name – exported by kernel32.dll
Among others!
A third way to identify Wine would be to look for functions that are no longer exported by Windows DLLs but are still present in Wine. For example, functions that were exported in kernel32.dll on Windows 95 and are no longer exported in current versions of Windows but are still present in Wine (these functions are well documented in the “.spec” file in the kernel32 folder)!
There you go. We already have a list of functions that will identify Wine. Now let’s create an executable that simulates an infection on both Windows and Linux!
Achieving Persistence
Now that we have identified some different functions to detect Wine, we need to determine some method of persistence.
Persistence, according to the Online Portuguese Dictionary11, is the “act or effect of persisting; the quality of something that lasts.” This definition is excellent. That’s exactly what we want to achieve! We want to ensure that regardless of the system, whenever the system restarts, our little “malware” will restart with the system!
Persistence is a very extensive topic and completely outside the scope of this article. Therefore, I will only use two classic methods: .bashrc for Linux and the “Startup” folder for Windows.
The .bashrc file contains information for configuring the bash shell environment. Every time a shell session starts, what is in .bashrc is executed.
This file contains shell configurations such as colors to be used, aliases for commands, how many commands should be saved in the history, etc. Therefore, I will use this file to achieve persistence. So, every time an infected user’s shell session starts, the malware will be executed.
For Windows, I will use the user’s Startup folder. This folder is well known to malware developers in general because everything in it is executed as soon as the user logs into the machine.
This folder is located at C
\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup.
I won’t explain all the code because it’s beyond the scope of the article. But basically, there are three executables in one. One of them is the executable that runs on Wine (or outside of it). This executable acts as a “dropper.” It has two resources: an executable for Linux (which is my “malware” for Linux) and also another executable for Windows (which is the “malware” for Windows). The “dropper,” as soon as it runs, will try to locate the memory address of the function “wine_get_unix_file_name” in the “kernel32.dll” library. Obviously, this function only exists in Wine’s DLL, so if it is found, the executable is running on Linux through Wine.
For the more attentive reader, I am assuming that the user will be running on Linux! But Wine can run on any POSIX-compliant system, as I mentioned above. So nothing would prevent an attacker from developing malware with identification for several different operating systems and infecting them all. But here, I will focus only on Windows and Linux.
Returning to the code, when it detects that it’s running on Linux, my dropper will use a combo of the GetTempPathW and wine_get_unix_file_name functions from Wine. In Wine’s source code, we read that the wine_get_unix_file_name function takes a directory path in “wide char” format and returns the corresponding Unix path for that directory! That was perfect for me because, in one go, I could easily get to “.bashrc.” But first, I needed to find some directory path that I was sure existed to pass as an argument to wine_get_unix_file_name in “wide char” format.
“Wine” reproduces Windows directory standards. So I could try directories like “C
” and other known ones. But I preferred to use the GetTempPathW function because it “returns the path of the directory designated for temporary files”12 and thus it wouldn’t be a “guess.” Wine itself would reveal a valid path. And better: it would already return it in “wide char” format! So everything works out perfectly! =D
The GetTempPathW function on my test machine returns C
. Of course, on each person’s machine, this value would be different. So this value goes to the wine_get_unix_file_name function, and this function, in turn, returns the Unix path for this folder – here on my test machine, it’s /home/Rafael/.wine/dosdevices/C:/users/Rafael/Temp/
This will be Wine’s default path: %user%/.wine/dosdevices/blablabla. Now the rest is easy. My dropper cuts the Unix path at “.wine,” adds “.bashrc,” and now has the user’s .bashrc path.
Now that my “dropper” also knows the real “Unix” path of the user, it writes the “malware” (the ELF executable that is in the “resource” of the “dropper”) in %user%/.config/wMal. After that, the “dropper” will write the following in the last line of %user%/.bashrc:
chmod +x /home/user/.config/wMal;/home/user/.config/wMal
Of course, the path will be adjusted according to the infected user.
And that’s it! Now, every time the user starts bash, my malware will be executed.
And what does it do? It just creates a file called “hi.txt” in the user’s home directory with the text “Infected by RafaMalware! HAHAHA! (Read with Evil Laugh).”
Well, as proof of concept, that’s good enough, right? Haha!
And if the “dropper” detects that it’s running on Windows, it will grab the “APPDATA” environment variable, add “\Microsoft\Windows\Start Menu\Programs\Startup\w.exe” to the APPDATA path, and save the Windows “malware” that is in the “dropper’s” resources. Now, every time the user logs into the machine, the “malware” will be executed.
And what does this malware do? It just shows the message box below =D
Conclusion
You can never be too careful! Search the internet, and you’ll find hundreds of Linux forums with users asking if it’s possible to get infected through Wine. Many believe that as long as they are using Linux, they are free from any infection. But we have seen that this is not quite the case =D
That’s why in Wine’s FAQ, it’s explicitly stated that “just because Wine runs on a different OS than Windows doesn’t mean you’re protected from viruses, trojans, and other forms of malware”12.
If you want to test the dropper, download it here: https://u.pcloud.link/publink/show?code=XZc64C0ZORVgIgfNaTLiVpWq4tG3e5fmiEly
I promise it’s safe! Haha! Afterward, just undo the modifications I mentioned, and you’re clean 😉
Hugs and see you next time!
- https://www.internetnews.com/security/linux-malware-on-the-rise/
- Ibid
- Ibid
- https://www.winehq.org/
- Ibid
- Journal of Computer Virology and Hacking Techniques. 15 (1): 39–60. doi:10.1007/s11416-018-0319-9. ISSN 2263-8733
- https://forum.winehq.org/viewtopic.php?t=4988
- https://stackoverflow.com/questions/56277858/how-to-detect-is-wine-running-from-linux-or-from-mac-os-environment-in-c
- https://stackoverflow.com/questions/7372388/determine-whether-a-program-is-running-under-wine-at-runtime
- https://gist.github.com/klange/282e62fdd1c2cae6a210443ccbc5b12f
- https://www.dicio.com.br/persistencia/
- https://wiki.winehq.org/FAQ#Is_Wine_malware-compatible?