Once malware gains access to a system, it often looks to be there for a long time. This behavior is known as persistence. If the persistence mechanism is unique enough, it can even serve as a great way to fingerprint a given piece of malware.
In this section, we begin with a discussion of the most commonly achieved method of persistence: modification of the system’s registry. Next, we review how malware modifies files for persistence through a process known as trojanizing binaries. Finally, we discuss a method that achieves persistence without modifying the registry or files, known as DLL load-order hijacking.
When we discussed the Windows registry in Chapter 7, we noted that it is common for malware to access the registry to store configuration information, gather information about the system, and install itself persistently. You have seen in labs and throughout the book that the following registry key is a popular place for malware to install itself:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
There are many other persistence locations in the registry, but we won’t list all of them, because memorizing them and then searching for each entry manually would be tedious and inefficient. There are tools that can search for persistent registries for you, like the Autoruns program by Sysinternals, which points you to all the programs that automatically run on your system. Tools like ProcMon can monitor for registry modification while performing basic dynamic analysis.
Although we covered registry analysis earlier in the book, there are a couple popular registry entries that are worth expanding on further that we haven’t discussed yet: AppInit_DLLs, Winlogon, and SvcHost DLLs.
Malware authors can gain persistence for their DLLs though a special registry location called AppInit_DLL. AppInit_DLLs are loaded into every process that loads User32.dll, and a simple insertion into the registry will make AppInit_DLLs persistent.
The AppInit_DLLs
value is stored in the following Windows
registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
The AppInit_DLLs
value is of type REG_SZ
and consists of a space-delimited string of DLLs. Most processes
load User32.dll, and all of those processes also load the AppInit_DLLs. Malware
authors often target individual processes, but AppInit_DLLs will be loaded into many processes.
Therefore, malware authors must check to see in which process the DLL is running before executing
their payload. This check is often performed in DllMain
of the
malicious DLL.
Malware authors can hook malware to a particular Winlogon event, such as logon, logoff,
startup, shutdown, and lock screen. This can even allow the malware to load in safe mode. The
registry entry consists of the Notify
value in the following
registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\
When winlogon.exe generates an event, Windows checks the Notify
registry key for a DLL that will handle it.
As discussed in Chapter 7, all services persist in the registry, and if they’re removed from the registry, the service won’t start. Malware is often installed as a Windows service, but typically uses an executable. Installing malware for persistence as an svchost.exe DLL makes the malware blend into the process list and the registry better than a standard service.
Svchost.exe is a generic host process for services that run from DLLs, and Windows systems often have many instances of svchost.exe running at once. Each instance of svchost.exe contains a group of services that makes development, testing, and service group management easier. The groups are defined at the following registry location (each value represents a different group):
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost
Services are defined in the registry at the following location:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\ServiceName
Windows services contain many registry values, most of which provide information about the
service, such as DisplayName
and Description
. Malware authors often set values that help the malware blend in, such as
NetWareMan
, which “Provides access to file and print
resources on NetWare networks.” Another service registry value is ImagePath
, which contains the location of the service executable. In the case of an
svchost.exe DLL, this value contains %SystemRoot%/System32/svchost.exe –k GroupName
.
All svchost.exe DLLs contain a Parameters
key with a ServiceDLL
value, which the
malware author sets to the location of the malicious DLL. The Start
value, also under the Parameters
key, determines when
the service is started (malware is typically set to launch during system boot).
Windows has a set number of service groups predefined, so malware will typically not create a
new group, since that would be easy to detect. Instead, most malware will add itself to a
preexisting group or overwrite a nonvital service—often a rarely used service from the
netsvcs
service group. To identify this technique, monitor the
Windows registry using dynamic analysis, or look for service functions such as CreateServiceA
in the disassembly. If malware is modifying these registry
keys, you’ll know that it’s using this persistence technique.
Another way that malware gains persistence is by trojanizing system binaries. With this technique, the malware patches bytes of a system binary to force the system to execute the malware the next time the infected binary is run or loaded. Malware authors typically target a system binary that is used frequently in normal Windows operation. DLLs are a popular target.
A system binary is typically modified by patching the entry function so that it jumps to the malicious code. The patch overwrites the very beginning of the function or some other code that is not required for the trojanized DLL to operate properly. The malicious code is added to an empty section of the binary, so that it will not impact normal operation. The inserted code typically loads malware and will function no matter where it’s inserted in the infected DLL. After the code loads the malware, it jumps back to the original DLL code, so that everything still operates as it did prior to the patch.
While examining one infected system, we noticed that the system binary
rtutils.dll did not have the expected MD5 hash, so we investigated further. We
loaded the suspect version of rtutils.dll, along with a clean version, into IDA
Pro. The comparison between their DllEntryPoint
functions is
shown in Table 11-1. The difference is obvious: the
trojanized version jumps to another location.
Example 11-5 shows the malicious code that was inserted into the infected rtutils.dll.
Example 11-5. Malicious patch of code inserted into a system DLL
76E8A660 DllEntryPoint_0 76E8A660 pusha 76E8A661 call sub_76E8A667 ❶ 76E8A666 nop 76E8A667 sub_76E8A667 76E8A667 pop ecx 76E8A668 mov eax, ecx 76E8A66A add eax, 24h 76E8A66D push eax 76E8A66E add ecx, 0FFFF69E2h 76E8A674 mov eax, [ecx] 76E8A677 add eax, 0FFF00D7Bh 76E8A67C call eax ; LoadLibraryA 76E8A67E popa 76E8A67F mov edi, edi ❷ 76E8A681 push ebp 76E8A682 mov ebp, esp 76E8A684 jmp loc_76E81BB2 ... 76E8A68A aMsconf32_dll db 'msconf32.dll',0 ❸
As you can see, the function labeled DLLEntryPoint_0
does a pusha
, which is commonly used in malicious code to save
the initial state of the register so that it can do a popa
to
restore it when the malicious process completes. Next, the code calls sub_76E8A667
at ❶, and the function is
executed. Notice that it starts with a pop ecx
, which will put
the return address into the ECX register (since the pop comes immediately after a call). The code
then adds 0x24 to this return address (0x76E8A666 + 0x24 = 0x76E8A68A) and pushes it on the stack.
The location 0x76E8A68A contains the string 'msconf32.dll'
, as
seen at ❸. The call to LoadLibraryA
causes the patch to load msconf32.dll. This means that
msconf32.dll will be run and loaded by any process that loads
rtutils.dll as a module, which includes svchost.exe,
explorer.exe, and winlogon.exe.
After the call to LoadLibraryA
, the patch executes the
instruction popa
, thus restoring the system state that was saved
with the original pusha
instruction. After the popa
are three instructions (starting at ❷) that are identical to the first three instructions in the clean
rtutils.dll
DllEntryPoint
, shown in Table 11-1. After these instructions is a jmp
back to the original DllEntryPoint
method.
DLL load-order hijacking is a simple, covert technique that allows malware authors to create persistent, malicious DLLs without the need for a registry entry or trojanized binary. This technique does not even require a separate malicious loader, as it capitalizes on the way DLLs are loaded by Windows.
The default search order for loading DLLs on Windows XP is as follows:
The directory from which the application loaded
The current directory
The system directory (the GetSystemDirectory
function is
used to get the path, such as .../Windows/System32/)
The 16-bit system directory (such as .../Windows/System/)
The Windows directory (the GetWindowsDirectory
function is
used to get the path, such as .../Windows/)
The directories listed in the PATH
environment
variable
Under Windows XP, the DLL loading process can be skipped by utilizing the KnownDLLs
registry key, which contains a list of specific DLL locations,
typically located in .../Windows/System32/. The KnownDLLs
mechanism is designed to improve security (malicious DLLs can’t be placed
higher in the load order) and speed (Windows does not need to conduct the default search in the
preceding list), but it contains only a short list of the most important DLLs.
DLL load-order hijacking can be used on binaries in directories other than
/System32 that load DLLs in /System32 that are not
protected by KnownDLLs
. For example,
explorer.exe in the /Windows directory loads
ntshrui.dll found in /System32. Because
ntshrui.dll is not a known DLL, the default search is followed, and the
/Windows directory is checked before /System32. If a
malicious DLL named ntshrui.dll is placed in /Windows, it
will be loaded in place of the legitimate DLL. The malicious DLL can then load the real DLL to
ensure that the system continues to run properly.
Any startup binary not found in /System32 is vulnerable to this attack, and explorer.exe has roughly 50 vulnerable DLLs. Additionally, known DLLs are not fully protected due to recursive imports, and because many DLLs load other DLLs, which follow the default search order.