In this post, we will discuss various ways you can analyze malware and understand infections by analyzing the atom tables. You'll be surprised that creating window classes, registering window messages, injecting DLLs with message hooks or event hooks all result in the creation of atoms in kernel memory. Thus today's MoVP 2.1 plugins are atomscan and atoms.
Atoms are strings that can easily be shared
between processes in the same session. However, instead of passing the actual
string value, or performing string comparison operations, atom tables provide a
simpler, faster implementation. In summary, one process adds an atom to an atom table by
passing a string to a function such as AddAtom
or GlobalAddAtom (or indirectly via one the APIs discussed shortly). The *AddAtom APIs return an
integer identifier that the process or other processes can use to retrieve the
string. An atom table is a hash bucket that contains the integer to string mappings.
Atom tables are extremely interesting from a forensic
perspective, because many Windows API functions create atoms without explicitly
saying so. Malware authors use these APIs, but are not aware of the
artifacts they leave on the system, thus they cannot try and cover
up the tracks. Atoms even have a tendency to "stick around" longer than sometimes intended. For example, atoms have a reference count that is incremented and
decremented automatically when they are added and deleted from the table.
According to Microsoft’s About Atom Tables, string atoms remain in the atom
table as long as the reference count is greater than zero, even after the
application that placed it in the table terminates.
Data Structures
Atom tables are represented as _RTL_ATOM_TABLE structures. As noted in Identifying Global Atom Table Leaks, the format of the structure is different in user mode than in kernel mode. When analyzing the session atom table (win32k!UserAtomTableHandle) and window station atom table (tagWINDOWSTATION.pGlobalAtomTable), we must be sure to use the kernel mode definition.
The session atoms are accessible to all processes in the session. Processes can also create local atom tables accessible to only that process, but this post will not discuss the local tables. Below, the atom table and atom table entry structures for Windows 7 64-bit are shown.
>>> dt("_RTL_ATOM_TABLE")
'_RTL_ATOM_TABLE' (112 bytes)
0x0 : Signature ['unsigned long']
0x8 : CriticalSection ['_RTL_CRITICAL_SECTION']
0x18 : NumBuckets ['unsigned long']
0x20 : Buckets ['array', <function <lambda> at 0x10054c9b0>, ['pointer', ['_RTL_ATOM_TABLE_ENTRY']]]
>>> dt("_RTL_ATOM_TABLE_ENTRY")
'_RTL_ATOM_TABLE_ENTRY' (24 bytes)
0x0 : HashLink ['pointer64', ['_RTL_ATOM_TABLE_ENTRY']]
0x8 : HandleIndex ['unsigned short']
0xa : Atom ['unsigned short']
0xc : ReferenceCount ['unsigned short']
0xe : Flags ['unsigned char']
0xf : NameLength ['unsigned char']
0x10 : Name ['String', {'length': <function <lambda> at 0x10428b0c8>, 'encoding': 'utf16'}]
Key Points
- Signature for an atom table is 0x6d6f7441 (Atom) and the structures exist in pools with tag AtmT. Together they provide a good initial pattern to scan for in physical memory.
- NumBuckets specifies the number of _RTL_ATOM_TABLE_ENTRY structures in the Buckets array.
- HashLink can be used to enumerate all atom entries in the bucket.
- Atom is the integer identifier for the atom table entry. This is the value returned by functions such as AddAtom and FindAtom.
- ReferenceCount is incremented each time the specified string is added to the atom table. Likewise, it is decremented each time it is deleted.
- Name is the string name of the atom.
The AtomScan Plugin
The atomscan plugin locates atom tables in physical memory by searching for the pool tags and then performing sanity checks on the Signature and NameLength fields. The downside to this method is that there is no clear way to link an atom table to its owning session or window station (thus a second plugin, atoms, is also provided which is able to perform the association). Atoms are reported the order in which they were found, unless you specify --sort-by=atom (sorts by atom ID) or --sort-by=refcount (sorts by number of references to the atom).
Here are some examples of how you can leverage atoms in your malware analysis and forensic investigations
1. Window class names. When applications call RegisterClassEx, win32k.sys creates an atom from the WNDCLASSEX.lpszClassName parameter. Some malware tries to be cute and creates blank window class names (" ") or uses non-ascii characters thinking no one will ever see them. You can see this happening in the disassembly below from a sample of Mutihack.
The output of the atomscan command shows a blank atom name:
$ python vol.py -f mutihack.vmem atomscan
AtomOfs(V) Atom
Refs Pinned Name
---------- ---------- ------ ------ ----
[snip]
0xe179d850 0xc038 1
1 OleMainThreadWndClass
0xe17a7e40 0xc094 2
0 Shell_TrayWnd
0xe17c34b8 0xc0c4 2
0 UnityAppbarWindowClass
0xe17c7678 0xc006 1
1 FileName
0xe17d40a0 0xc0ff
2 0
0xe17d4128 0xc027 1
1 SysCH
0xe17e78f0 0xc01c 1
1 ComboBox
0xe17e9070 0xc065 26
0 6.0.2600.6028!Combobox
0xe17ec350 0xc13e 1
0 Xaml
0xe18119c0 0xc08c
5 0 OM_POST_WM_COMMAND
[snip]
2. Registered window messages. Using the RegisterWindowMessage API results in the creation of an atom based on the string passed as an argument to the function. A classic example is shown below from a disassembly of the Clod malware. Registering the WM_HTML_GETOBJECT window message is
one of the required steps to obtaining a fully marshaled IHTMLDocument2interface from an HWND. In other words, this
is how Clod gains scriptable control over Internet Explorer – such as the
ability to add/remove/modify DOM elements or inspect form variables upon
submission (like credentials to banking sites).
3. Injected DLLs. Malware that uses SetWindowsHookEx or SetWinEventHook to inject a DLL into other processes will unknowingly create new atoms that identify the full path to the malicious library. It makes sense if you think about. Assume a malware process calls one of the hooking APIs and specifies "C:\windows\bad.dll" and then terminates. How does the system "remember" the DLL path when it comes time to load it in the other processes? Easy - the hooking APIs call through from user32.dll into win32k.sys and the kernel driver saves the string into the atom table. Take the example below from the Laqma malware:
As you can see in the disassembly, the HINSTANCE parameter to SetWindowsHookEx is a handle to the library C:\WINDOWS\system32\Dll.dll. Guess what shows up in the atom table?
$ python vol.py –f
laqma.vmem atomscan
AtomOfs(V) Atom
Refs Pinned Name
---------- ---------- ------ ------ ----
0xe1000d10 0xc001 1
1 USER32
0xe155e958 0xc002 1
1 ObjectLink
0xe100a308 0xc003 1
1 OwnerLink
0xe1518c00 0xc004 1
1 Native
0xe1b2aa88 0xc1b2 2
0 __axelsvc
0xe4bcb888 0xc1be 2
0 ShImgVw:CPreview
0xe11b0250 0xc1c1 2
0 __srvmgr32
0xe1f8bc30 0xc1c3 1
0 C:\WINDOWS\system32\psbase.dll
0xe28ed818 0xc1c7 1
0 BCGP_TEXT
0xe2950c98 0xc19f 1
0 ControlOfs01420000000000FC
0xe11d6290 0xc1a0 1
0 C:\WINDOWS\system32\Dll.dll
0xe1106380 0xc1a1 1
0 BCGM_ONCHANGE_ACTIVE_TAB
0xe11a5090 0xc1a2 1
0 ControlOfs01EE0000000003C8
[snip]
4. System presence marking. If you're not familiar with the importance of mutexes, see Lenny Zelter's Looking at Mutex Objects for Malware Discovery. Most likely though you've never heard of using atoms for malware discovery. Well guess what, now you have...and Volatility is the only only tool I know about (for live systems *or* physical memory analysis) that carves such structures. Below, you'll see a disassembly showing how Tigger uses atoms instead of mutexes. First, it calls GlobalFindAtomA to check if an
atom named ~Sun Nov 16 15:46:54 2008~ exists on the machine. If so, the
malware terminates, assuming it has already completed the infection. Otherwise,
it calls GlobalAddAtomA to mark the
machine infected.
The next function enters a loop that
looks for an atom named putas38 every 200 milliseconds. It will sleep forever
if the atom is never created, so surely another thread creates the atom, which
causes the code below to exit the loop. At that time, the malware proceeds to
hook various API functions.
Conclusion
Atoms are inevitable artifacts of many USER and GDI API functions. They're created both directly and indirectly by malware more often than you realize. In the case when they're created indirectly (by creating class names, installing hooks, etc) there's a pretty good chance the malware authors have never heard of an atom - so this post should give you a competitive advantage in detection. For more information on atoms, see Extraordinary String Based Attacks: Smashing the Atom by Tarjei Mandt.
More information on the atomscan and atoms plugins and their uses in forensic investigations will be presented at Open Memory Forensics Workshop (OMFW) 2012.
More information on the atomscan and atoms plugins and their uses in forensic investigations will be presented at Open Memory Forensics Workshop (OMFW) 2012.
No comments:
Post a Comment