Threat Hunting with Function Imports

What are Function Imports

Function Imports are a collection of all of the functions a particular executable file depends upon that exist in other executables and are not implemented in the executable itself. If you have ever developed C or C++ applications for Windows systems, you are probably very familiar with the concept. If that applies to you, then you can probably skip to the next section.

How do Function Imports Work?

For those of you who are not familiar with funtion imports, function imports are defined by the application developer based on what functionality the application needs. The developer references header files where the functions are defined and library files where the functions are implemented. At compile time, the compiler will create a section in the PE header called an Import Address Table (IAT). This table is referenced by the Windows Loader at runtime in order to map the necessary DLL dependencies into the virtual address space of the application so that the application can call the necessary exported functions in those DLLs.

Figure 1: How the Import Address Table (IAT) is built and used.

Application developers will typically chose to depend upon other libraries for several reasons:

  1. To make application development easier. There is no need to reinvent the wheel. If the necessary functionality already exists in a library somewhere, then there is no reason to duplicate that work.
  2. To reduce the size of the compiled application. Generally speaking, application developers want to make their software small so that it is quick and easy to download. If there are functions that exist inside of DLLs that are already on the target system, then you don’t have to include all of that compiled code inside of your binary. Simply reference the DLL that exists on the target system.

There are several other reasons for application developers to reference DLLs, but the two reasons above are the primary reasons malware developers also reference functions that are native to the target. The key takeaway is that both application developers and malware authors rely on code native to the target OS in order to make development easier and their tools smaller. The real question is, are there certain functions that are more closely associated with malware than legitimate applications?

Methods for Importing Functions

In the Windows environment, there are two different ways to import functions from another library: function name or ordinal. When importing by function name, the loader will search through all of the exported functions in the target library for corresponding function name. That function’s virtual address is then mapped to the application, so that it knows where to pass execution to. When importing by ordinal, the loader will map the function by its non-zero based index in the export table of the target library.

The image below explains the structure of the IAT. For each import on the table, there is an unsigned 2-byte integer called a “hint” that is the non-zero based index into the target libraries export table. The next field is a null terminated array of ASCII characters called the “name” which is used to lookup the corresponding function in the target library’s export table. The “hint” is used to speed up the process by letting the loader know which function in the export table is likely the right one. The loader will check the function at the index first for a match. If successful, a lot of time was saved by not checking through the entire table. If not, then the loader will search the entire export table.

typedef struct IMAGE_IMPORT_DESCRIPTOR {
    DWORD OriginalImportAddressTableRVA; //RVA to an array of PIMAGE_IMPORT_BY_NAME or IMAGE_THUNK_DATA structures.
    DWORD TimeStamp; //Another TimeStamp for when the module was built.
    DWORD ForwarderChainRVA; //Seldom used.
    DWORD LibraryNameRVA; //RVA to a null-terminated ASCII string.
    DWORD ImportAddressTableRVA; //RVA to an array of PIMAGE_IMPORT_BY_NAME or IMAGE_THUNK_DATA structures.
};

/*
NOTE:The contents pointed to by OriginalImportAddressTableRVA and ImportAddressTableRVA are typically
mirrors of each other, but the ImportAddressTableRVA is modified by the loader at runtime to have the
correct virtual address of the loaded function in memory.
*/
typedef struct IMAGE_IMPORT_BY_NAME {
     WORD Hint; //Index of the function in the export table of the target library.
     char[] Name; //A null terminated ASCII string that is the functions n name.
}
  1. Load the target library based off of the IMAGE_IMPORT_DESCRIPTOR::LibraryNameRVA field. Windows will follow the DLL search path discussed here.
  2. Get the list of exported functions from the target libraries export table.
  3. For each “thunk” in the IMAGE_IMPORT_DESCRIPTOR::ImportAddressTableRVA field:
    1. Look at the exported function located at the index from the “hint” field.
    2. If the “name” field from the IAT matches the “name” field from the export table, then map that function. Of the “name” field is empty, then the import will be mapped by index only.
    3. Else, loop through the entire export table and compare the “name” field from the IAT to each “name” field from the export table until the function is found or the end of the table is reached.
    4. If the end of the table is reached, then that library is not the correct library, so Windows will continue searching for a library that matches.

The “hint” field can also provide useful information to help identify malware. Most legitimate applications are compiled in such a way that each import in the IAT has a valid “hint” field, or at least one that is non-zero. Some malware follows that same standard, but there are many that don’t. For example, nearly 78% of all malware references kernel32::GetProcAddress while only 24% of legitimate files reference that same function (Figure 2). By ignoring the “Hint” field, we are missing out on a lot of information. By creating a histogram of “Hints”, we can see that nearly 33% of malware has a zero for the hint, while only 0.4% of legitimate files have a zero for the Hint. This is a much better indicator to look at than just a reference to GetProcAddress by itself. The downside is that it is very difficult to look at this information across multiple function imports as there are potentially hundreds of imports for a single file, but looking at the hint value can be a useful feature to feed into a machine learning model.

Figure 2: The percentage whitelist and blacklist samples that reference kernel32::GetProcAddress.
Figure 3: The percentage of whitelist and blacklist samples that reference kernel32::GetProcAddress broken out by the “Hint” value.

Example Import Address Table

Let’s take a look at some source code and what function imports are created in the IAT as dependencies to make this application work. The code below is source code from a DLL injector, written by SaEeD (source), that uses Reflective DLL Injection to inject a malicious DLL into the virtual address space of another process. Reflective DLL Injection is a common technique used by malware authors to help evade detection. It relies on API calls that are less common in legitimate applications because very few need to inject code into other processes.

/*
Written by: SaEeD
Description: Injecting DLL to Target process using Process Id or Process name
*/
#include "pch.h"
#include <iostream>
#include <string>
#include <ctype.h>
#include <Windows.h>
#include <tlhelp32.h>
#include <Shlwapi.h>
//Library needed by Linker to check file existance
#pragma comment(lib, "Shlwapi.lib")

using namespace std;

int getProcID(const string& p_name);
bool InjectDLL(const int &pid, const string &DLL_Path);
void usage();

int main(int argc, char ** argv)
{
	if (argc != 3)
	{
		usage();
		return EXIT_FAILURE;
	}
	if (PathFileExists(argv[2]) == FALSE)
	{
		cerr << "[!]DLL file does NOT exist!" << endl;
		return EXIT_FAILURE;
	}

	if (isdigit(argv[1][0]))
	{
		cout << "[+]Input Process ID: " << atoi(argv[1]) << endl;
		InjectDLL(atoi(argv[1]), argv[2]);
	}
	else {
		InjectDLL(getProcID(argv[1]), argv[2]);
	}


	return EXIT_SUCCESS;
}
//-----------------------------------------------------------
// Get Process ID by its name
//-----------------------------------------------------------
int getProcID(const string& p_name)
{
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	PROCESSENTRY32 structprocsnapshot = { 0 };

	structprocsnapshot.dwSize = sizeof(PROCESSENTRY32);

	if (snapshot == INVALID_HANDLE_VALUE)return 0;
	if (Process32First(snapshot, &structprocsnapshot) == FALSE)return 0;

	while (Process32Next(snapshot, &structprocsnapshot))
	{
		if (!strcmp(structprocsnapshot.szExeFile, p_name.c_str()))
		{
			CloseHandle(snapshot);
			cout << "[+]Process name is: " << p_name << "\n[+]Process ID: " << structprocsnapshot.th32ProcessID << endl;
			return structprocsnapshot.th32ProcessID;
		}
	}
	CloseHandle(snapshot);
	cerr << "[!]Unable to find Process ID" << endl;
	return 0;
}
//-----------------------------------------------------------
// Inject DLL to target process
//-----------------------------------------------------------
bool InjectDLL(const int &pid, const string &DLL_Path)
{
	long dll_size = DLL_Path.length() + 1;
	HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

	if (hProc == NULL)
	{
		cerr << "[!]Fail to open target process!" << endl;
		return false;
	}
	cout << "[+]Opening Target Process..." << endl;

	LPVOID MyAlloc = VirtualAllocEx(hProc, NULL, dll_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	if (MyAlloc == NULL)
	{
		cerr << "[!]Fail to allocate memory in Target Process." << endl;
		return false;
	}

	cout << "[+]Allocating memory in Targer Process." << endl;
	int IsWriteOK = WriteProcessMemory(hProc, MyAlloc, DLL_Path.c_str(), dll_size, 0);
	if (IsWriteOK == 0)
	{
		cerr << "[!]Fail to write in Target Process memory." << endl;
		return false;
	}
	cout << "[+]Creating Remote Thread in Target Process" << endl;

	DWORD dWord;
	LPTHREAD_START_ROUTINE addrLoadLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(LoadLibrary("kernel32"), "LoadLibraryA");
	HANDLE ThreadReturn = CreateRemoteThread(hProc, NULL, 0, addrLoadLibrary, MyAlloc, 0, &dWord);
	if (ThreadReturn == NULL)
	{
		cerr << "[!]Fail to create Remote Thread" << endl;
		return false;
	}

	if ((hProc != NULL) && (MyAlloc != NULL) && (IsWriteOK != ERROR_INVALID_HANDLE) && (ThreadReturn != NULL))
	{
		cout << "[+]DLL Successfully Injected :)" << endl;
		return true;
	}

	return false;
}
//-----------------------------------------------------------
// Usage help
//-----------------------------------------------------------
void usage()
{
	cout << "Usage: DLL_Injector.exe <Process name | Process ID> <DLL Path to Inject>" << endl;
}

The code above generates the IAT listed below. The table itself includes the DLL name and the function name. Each entry has been enriched with statistics from the Malware Analysis Center database. The Whitelist and Blacklist columns shows the percentage of samples in each list that contained references to each of the functions in their respective rows (e.g. ExitProcess is referenced by 13.48% of legitimate files and 63.9% of malicious files in the database.

DLLNameWhitelistBlacklistGain
kernel32.dllexitprocess13.48%63.90%0.199
kernel32.dllgetprocaddress24.32%78.71%0.166
kernel32.dllloadlibrarya15.31%56.98%0.138
kernel32.dllwritefile17.20%50.85%0.091
kernel32.dllgetstdhandle12.56%42.05%0.079
kernel32.dllgetcommandlinea10.08%37.26%0.075
kernel32.dllrtlunwind12.07%40.02%0.073
kernel32.dllreadfile13.09%40.90%0.071
kernel32.dllmultibytetowidechar19.71%49.09%0.069
kernel32.dllgetcpinfo11.42%37.95%0.069
kernel32.dllclosehandle23.90%53.65%0.067
kernel32.dlltlsgetvalue12.53%38.79%0.065
kernel32.dlltlssetvalue12.30%38.36%0.065
kernel32.dllgetlasterror25.63%55.06%0.064
kernel32.dllgetacp13.60%39.77%0.063
kernel32.dllgetfiletype12.38%37.74%0.062
kernel32.dllfindclose9.61%32.16%0.056
kernel32.dllwidechartomultibyte18.87%44.61%0.055
kernel32.dllheapalloc18.01%40.37%0.043
kernel32.dllgetcommandlinew6.96%24.66%0.043
kernel32.dllgetstringtypew11.17%30.92%0.042
kernel32.dllgetenvironmentstringsw11.09%30.78%0.042
kernel32.dllgetoemcp11.28%31.03%0.042
kernel32.dlllcmapstringw11.30%30.90%0.041
kernel32.dllfreeenvironmentstringsw11.06%30.51%0.041
kernel32.dllheapfree18.20%39.89%0.041
kernel32.dllraiseexception14.95%35.28%0.039
kernel32.dllleavecriticalsection20.71%42.21%0.038
kernel32.dllentercriticalsection20.68%42.14%0.038
kernel32.dlldeletecriticalsection20.86%41.49%0.035
kernel32.dllheapsize11.90%29.91%0.035
kernel32.dllheaprealloc13.74%32.19%0.034
kernel32.dlltlsalloc12.34%30.09%0.034
kernel32.dllcreatefilew13.02%30.52%0.032
kernel32.dllsetlasterror17.87%36.66%0.032
kernel32.dllgetmodulefilenamew14.37%31.46%0.029
kernel32.dllgetcurrentthreadid27.35%46.57%0.028
kernel32.dllgetcurrentprocess27.00%45.27%0.026
kernel32.dllunhandledexceptionfilter25.42%43.02%0.024
kernel32.dllsetstdhandle9.85%22.26%0.020
kernel32.dllgetstartupinfow7.62%19.07%0.020
kernel32.dllisvalidcodepage8.96%20.84%0.020
kernel32.dllfreelibrary20.87%35.82%0.020
kernel32.dllgetconsolecp8.22%19.51%0.019
kernel32.dlltlsfree11.82%24.34%0.019
kernel32.dllgetconsolemode8.68%19.90%0.018
kernel32.dllwriteconsolew8.62%19.66%0.018
kernel32.dllsetfilepointerex2.82%10.61%0.018
kernel32.dllflushfilebuffers11.85%23.49%0.017
kernel32.dllenumsystemlocalesw0.83%6.24%0.016
kernel32.dllterminateprocess25.92%40.09%0.016
kernel32.dllgetprocessheap15.04%27.21%0.016
kernel32.dllinitializecriticalsectionandspincount9.45%19.92%0.016
kernel32.dllgetmodulehandlew16.05%28.34%0.015
kernel32.dllreadconsolew1.28%6.50%0.014
kernel32.dllisvalidlocale4.22%11.42%0.013
kernel32.dllgetcurrentprocessid26.18%38.84%0.013
kernel32.dllgetmodulehandleexw3.33%9.90%0.013
kernel32.dllcreatetoolhelp32snapshot1.83%6.81%0.011
kernel32.dllisprocessorfeaturepresent7.21%14.86%0.011
kernel32.dllgetuserdefaultlcid5.67%12.18%0.009
kernel32.dllsetunhandledexceptionfilter25.05%35.22%0.009
kernel32.dllgetlocaleinfow6.67%13.01%0.008
kernel32.dllfindnextfilew4.85%10.39%0.008
kernel32.dllvirtualallocex0.37%2.79%0.007
kernel32.dllinitializeslisthead1.54%4.82%0.006
kernel32.dllopenprocess6.10%11.26%0.006
kernel32.dllwriteprocessmemory0.50%2.68%0.006
kernel32.dlldecodepointer7.43%12.79%0.006
kernel32.dllisdebuggerpresent15.88%22.83%0.005
kernel32.dllloadlibraryexw9.07%14.72%0.005
kernel32.dllprocess32first0.68%2.91%0.005
kernel32.dllprocess32next0.70%2.93%0.005
kernel32.dllencodepointer7.31%12.27%0.005
kernel32.dllcomparestringw8.25%13.23%0.005
kernel32.dllfindfirstfileexw0.97%3.22%0.004
shlwapi.dllpathfileexistsa0.44%2.14%0.004
kernel32.dllswitchtothread1.01%3.22%0.004
kernel32.dllcreateremotethread0.20%1.12%0.002
kernel32.dllqueryperformancecounter26.14%31.16%0.002
kernel32.dllgetsystemtimeasfiletime26.00%30.71%0.002
kernel32.dllgetfilesizeex2.50%2.01%0.000
kernel32.dllsetenvironmentvariablew2.03%2.18%0.000

A few of the functions that really stand out are:

  • Kernel32.dll:VirtualAllocEx – used to allocate memory in a another process.
  • Kernel32.dll:WriteProcessMemory – used to copy memory from one process to another.
  • Kernel32.dll:CreateRemoteThread – used to create a thread in the target process to run the malicious code that was just copied into that process.

The functions listed above are found in less than 0.5% of legitimate executables, and most are not found together. Additionally, these imports are specifically used to implement the DLL Injection technique. Those imports are located lower in the list when sorted by the information gain, but are really the most important piece to implementing Reflective DLL Injection. I was surprised to see that only 2% of malware samples contained references to those functions in the IAT. This may be due to other types of malware dynamically referencing those functions via calls to GetProcAddress.

Other imports have a higher information gain because a larger percentage of samples reference those imports. For example, ExitProcess is a common import found in 63.9% of malware, but it is only found in 13.5% of legitimate samples. I was surprised to see that ExitProcess was the biggest differentiator among samples because I generally don’t think of that function as being malicious in and of itself, nor can I think of any malicious techniques that use that explicitly depend upon ExitProcess in the same way that Reflective DLL Injection relies upon VirtualAllocEx. The code itself also does not reference ExitProcess, which led me to investigate other reasons why ExitProcess is even found in the IAT.

Compiler Artifacts

Another thing that led me to investigate other sources for function imports is the shear number of imports compared to the number of functions actually referenced in the code itself. The table below lists all 11 of the functions called by the actual code. All of the other imports are dependencies of the Windows runtime that are added by the Visual C++ compiler.

DLLName
kernel32.dllclosehandle
kernel32.dllcreateremotethread
kernel32.dllcreatetoolhelp32snapshot
kernel32.dllgetprocaddress
kernel32.dllloadlibrarya
kernel32.dllopenprocess
kernel32.dllprocess32first
kernel32.dllprocess32next
kernel32.dllvirtualallocex
kernel32.dllwriteprocessmemory
shlwapi.dllpathfileexistsa

Essentially, any function import that is not in the list above is added as a result of the Visual C++ runtime, and it is a very extensive list. This is something to keep in mind when statically analyzing a file. Even though the IAT may contain references to API calls such as kernel32:createfilew or kernel32:isdebuggerpresent, that does not mean the application will ever be creating files or checking to see if the debugger is present as part of the core logic of the application. It could be something that a piece of malware will use, or it could just be an artifact of the Visual C++ runtime.

One last thing of interest with the DLL injector is that it only two dependencies: shlwapi.dll and kernel32.dll, both of which are found on nearly all Windows systems by default. Keep this fact in mind in the next section.

DLL Whitelisting

Another technique to distinguish between legitimate and malicious files is to use a whitelist of DLL dependencies that are not used by malware authors. In general, most malware is developed to run on as many systems as possible to give them the largest possible attack surface. To ensure their malware will run, malware authors build the malware to only depend upon DLLs that are present on nearly every system. This also simplifies their operations because the operators don’t have to worry about having to download additional dependencies if their malware can be a standalone product. Sometimes, an adversary only has the opportunity to get a user to download a single file either as a drive by download or as the result of an exploit. Having to download multiple files greatly complicates those scenarios and reduces the probability of success.

On the other hand, most legitimate software does not have the same requirement to not have any non-native dependencies. If you look at any program installed on a Windows system, it most likely has several DLLs located in the same directory that the application depends upon that were placed their by the installer. Legitimate software developers don’t have to worry as much about making their products standalone because the user is actively trying to install the software, and is therefore more willing to wait for larger downloads or to even install the pre-requisites themselves.

As a result, there are thousands of DLLs that legitimate software depends upon that are never referenced by malware because the malware authors cannot guarantee that the DLL will be present on the target system.

In this dataset, there are a total of 5,367 unique DLL names that are never referenced by any of the 114,815 malware samples. on the other hand, 21% of the 86,810 legitimate samples referenced at least one of those DLLs. Given that information, part of your triage process can include looking for non-native DLL dependencies. If any are found, then the sample is most likely legitimate. That analysis will eliminate 21% of the possible files you may need to triage.

Import Hash Blacklisting

With the previous technique of DLL whitelisting, we only concerned ourselves with one import at a time. Essentially, if an unknown PE file was identified for triage, we would check to see if any of it’s imports references a DLL on the whitelist. Of it did, then we would have increased confidence that the sample was non-malicious.

Import Hash blacklisting takes a more wholeistic approach by looking at all of the imports of a particular sample by grouping samples by their ImpHash.

What is an ImpHash?

An ImpHash is a MD5 hash of specific data from a PE file’s IAT. It is designed to yield a unique value for a given set of import functions. This allows researchers to easily compare one string and determine whether or not two binaries share the same set of import functions. Although I cannot find a source for the original inventor, the technique of ImpHashing was popularized by FireEye in 2014. Since then, the hash has been added into most major malware analysis tools including VirusTotal, Hybrid Analysis, Cuckoo Sandbox, and even Sysinternals.

The algorithm goes through the following steps to generate an ImpHash:

  1. Extract list of imports from IAT
  2. Convert all DLL and function names to lowercase
  3. Combine DLL without extension and function name with a period in between
  4. Convert to list to a byte array
  5. MD5 hash the array

Blacklisting

The way I use ImpHash for triage is by querying a database of samples that are labeled either Whitelist, Blacklist, Graylist, or Unknown. This yields a count of whitelist and blacklist samples that share the same set of import functions. From there, I normalize the count by the total number of whitelisted and blacklisted samples in order to have a meaningful comparison between the two. In the database, there are actually way more blacklisted samples than whitelisted samples, so directly comparing the raw count would not be accurate. Once I have a percentage of whitelist and blacklist samples that share the same ImpHash, I generate a final metric which is:

Figure 4: Percentage of normalized true positives.

The equation above yields a value between 0 and 100 that says what is the percentage of normalized true positives would be given a perfectly even distribution of samples (i.e. 50/50 malicious/non-malicious). A count of all samples with an ImpHash with a greater than 95% FP rate results in firing on 42.69% of the malicious files in the set while only firing on 0.15% of the total legitimate files in the set.

Conclusion

Function imports provide a wealth of knowledge when statically analyzing files to determine if they are legitimate or malicious. The key take-aways are:

  • All of the functions you will see in an IAT were created for legitimate use, and are likely being used somewhere legitimately.
  • There are some functions that are used by malware more often than legitimate software.
  • Just because a function is not in the IAT does not mean the application won’t call that function. Malware can dynamically load libraries or call functions in those libraries.
  • Just because a function is in the IAT does not mean that the application will call that function. Sometimes, certain imports are an artifact of the compiler.
  • There are a lot of DLLs that malware does not reference. This is likely because many of those DLLs are not guaranteed to be on a system, and malware authors need to ensure their malware will run with minimal dependencies. As a result, DLL whitelisting is an effective technique to determine if a file is legitimate or not, accounting for 21% of all legitimate files.
  • While looking at an individual import may not yield a lot of information, looking at a combination of all imports of a file can uniquely identify a file as legitimate or malicious. Looking at a reputation score associated with a given ImpHash can be a very good indicator a file is legitimate or malicious.

Statistics

The table below lists some other functions commonly used by malware or functions that had a strong association with malware.

DLLNameWhitelistBlacklistGainDescription
advapi32.dllAdjustTokenPrivileges2.17%0.19%0.007326This function is used to enable or disable specific access privileges. In a process injection attack, this function is used by malware to gain additional permissions.
advapi32.dllcontrolservice6.32%2.28%0.007454These functions are used to create, start, stop, modify, or send a signal to a running service. If malware is using its own malicious service, code needs to be analyzed that implements the service in order to determine the purpose of the call.
advapi32.dllcreateservicew3.45%1.02%0.005209
advapi32.dllcreateservicea2.52%1.31%0.001416
advapi32.dllcryptacquirecontextw3.16%4.76%0.00119These functions are often used by malware to initialize the use of Windows encryption.
advapi32.dllcryptacquirecontexta1.58%1.13%0.000275
api-ms-win-core-toolhelp-l1-1-0.dllcreatetoolhelp32snapshot0.08%0.00%0.000434This function is used to enumerate running processes, but most software typically references the kernel32.dll version, but I have recently seen a lot of legitimate software using the api-ms-win-core-*.dll assemblies for some of these core library functions. I have not looked into why these functions are referenced versus kernel32.dll.
crypt32.dllcertopensystemstorea0.19%0.03%0.00049These functions are used to access the certificates stored on the local system. Malware authors may use these functions to load the own certificates in order to run code that must be digitally signed such as drivers.
crypt32.dllcertopensystemstorew0.10%0.07%2.37E-05
gdi32.dllbitblt3.51%15.76%0.031875Used for capturing screenshots.
iphlpapi.dllicmpcreatefile0.16%0.47%0.000539The IcmpCreateFile function opens a handle on which IPv4 ICMP echo requests can be issued.
iphlpapi.dllgetadaptersinfo0.65%2.24%0.003217The GetAdaptersInfo function retrieves adapter information for the local computer. This is a call some malware will make when first fingerprinting a new system.
kernel32.dllenumcalendarinfoa0.08%5.35%0.023052Enumerates calendar information for a specified locale.
kernel32.dllgetprofileinta0.21%3.06%0.010232Retrieves an integer from a key in the specified section of the Win.ini file.
kernel32.dllgetmodulehandlea22.20%46.89%0.048124These functions are used to obtain a handle to an already loaded module. Malware may use GetModuleHandle to locate and modify code in a loaded module or to search for a good location to inject code.
kernel32.dllgetmodulehandleexa0.98%0.18%0.002263
kernel32.dllgetmodulehandleexw8.02%10.02%0.000856
kernel32.dllgetmodulehandlew31.27%28.76%0.000531
kernel32.dllgetprocaddress42.76%72.51%0.065566Gets the address of a procedure in a specified DLL. This is used by malware to dynamically lookup additional functions to call in order to inhibit static analysis of the malware.
kernel32.dllloadlibrarya22.57%57.69%0.092586These functions are used to dynamically load a DLL at runtime. These functions are typically used in conjunction with calls to GetProcAddress in order to dynamically find and call other functions. Malware will often use these functions as a defense evasion technique in order to slow down static analysis and reverse engineering of the malware.
kernel32.dllloadlibraryexw17.88%14.92%0.001133
kernel32.dllloadlibraryexa9.95%12.41%0.001075
kernel32.dllloadlibraryw23.11%21.19%0.000382
kernel32.dllgetmodulefilenamea22.25%39.83%0.025618
kernel32.dllgetmodulefilenamew26.33%31.84%0.002605
kernel32.dllconnectnamedpipe2.57%1.73%0.000602Named pipes are a stream based communication mechanism in Windows systems that allows interprocess communication within a single system or across systems. Some malware will use named pipes for communication within a target network.
kernel32.dlldisconnectnamedpipe2.38%0.84%0.00281
kernel32.dllcreatefilea15.36%31.58%0.026051These functions are used to create files, but are also an artifact of the Microsoft Visual C++ compiler.
kernel32.dllcreatefilew24.35%30.87%0.003749
kernel32.dllcreatefilemappingw7.49%6.35%0.000359
kernel32.dllcreatefilemappinga4.65%3.93%0.00022
kernel32.dllcreatemutexw8.80%5.74%0.002485Mutexes are used for thread synchronization within processes or across multiple processes. Each mutex is global on a single system and has a unique name. Malware will use mutexes to prevent multiple re-infections on the same system.
kernel32.dllcreatemutexa7.07%6.91%6.66E-06
kernel32.dllcreateprocessa6.05%16.75%0.020405
kernel32.dllcreateprocessw8.62%6.53%0.001116
kernel32.dllcreateremotethread0.34%1.16%0.001672This function is often used by malware to execute code in another thread such as with Reflective DLL Injection used by Meterpreter.
kernel32.dllcreatetoolhelp32snapshot2.95%6.94%0.00603This function is used to enumerate running processes.
kernel32.dlldeviceiocontrol7.78%3.78%0.005368Sends a control code directly to a specified device driver, causing the corresponding device to perform the corresponding operation. This can be used by malware to communicate with a rootkit or other low level kernel mode driver that is used by the malware.
kernel32.dllfindfirstfilea5.37%20.33%0.036526These functions are used to look through files in a directory.
kernel32.dllfindfirstfilew9.99%14.70%0.003624
kernel32.dllfindfirstfileexw1.71%3.24%0.001717
kernel32.dllfindfirstfileexa0.87%0.76%2.76E-05
kernel32.dllfindnextfilea4.30%14.52%0.022216
kernel32.dllfindnextfilew8.60%10.52%0.00075
kernel32.dllfindresourcea4.79%11.99%0.012083These functions are used to access resources contained within a portable executable. Common resources include icons, strings, configuration files, and dependencies. Malware will sometimes store packed, obfuscated, or encypted code in resource sections. At runtime, these functions will be used to extract and run the code. These functions are often combined with VirtualAllocEx to allocate virtual memory with R/W/E permissions to write the unpacked code into memory and execute it.
kernel32.dllfindresourcew10.37%13.85%0.002007
kernel32.dllfindresourceexa1.05%0.82%9.75E-05
kernel32.dllfindresourceexw6.55%6.83%2.26E-05
kernel32.dllgetstartupinfoa12.68%30.40%0.033227These functions are used to retrieve a structure containing details about how the current process was configured to run, such handles to the standard in, out, and error.
kernel32.dllgetstartupinfow14.93%19.42%0.002501
kernel32.dllgetsystemdefaultlangid2.45%1.00%0.002296This function returns the default language settings for the system. These settings may be used by malwares designed for region-based attacks. It may also just be used as initial survey information.
kernel32.dllgettemppatha3.54%17.18%0.03721These functions return the temporary file path. Malware may use this to download additional malicious files onto the system or to move the malware to a different location for long-term persistence.
kernel32.dllgettemppathw7.16%11.82%0.004456
kernel32.dllgetthreadcontext1.25%2.89%0.002382This function returns the context structure of a given thread. The context for a thread stores all the thread information, such as the register values and current state.
kernel32.dllgetversion9.47%22.26%0.021904This function returns information about which version of Windows is currently running. This can be used as part of an initial victim survey, or to select between different offsets for undocumented structures that have changed between different versions of Windows.
kernel32.dllgetversionexa17.38%24.60%0.00556
kernel32.dllgetversionexw13.62%10.16%0.002041
kernel32.dllgetwindowsdirectorya3.73%13.74%0.022973Used to get the path to the Windows directory. Malware may use the result of this call to place additional malicious files.
kernel32.dllgetwindowsdirectoryw4.32%4.28%4.7E-07
kernel32.dllvirtualallocex0.37%2.79%0.007291This function is a memory-allocation routine that can allocate memory in a remote process with specific memory permissions (e.g. RWE). Malware sometimes uses VirtualAllocEx as part of process injection.
mpr.dllwnetuseconnectionw0.02%0.84%0.003471
mscoree.dllcorbindtoruntimeex0.11%2.24%0.008005
msvbvm60.dll0.16%7.19%0.02999The Microsoft Visual Basic Virtual Machine (msvbvm) version 60 library. Malware authors leverage this DLL to build combined malware written in Visual Basic or it can be used to dynamically execute Visual Basic code.
msvbvm60.dll__vbaexcepthandler0.16%6.84%0.028333
msvbvm60.dllevent_sink_queryinterface0.15%6.45%0.026578
msvbvm60.dllevent_sink_release0.15%6.45%0.026574
msvbvm60.dllevent_sink_addref0.15%6.45%0.026574
msvbvm60.dlldllfunctioncall0.14%6.28%0.026041
msvbvm60.dllproccallengine0.03%3.63%0.016204
msvbvm60.dllmethcallengine0.03%3.56%0.015838
msvbvm60.dll_cicos0.13%2.98%0.011019
msvbvm60.dll__vbafreevar0.12%2.94%0.010952
msvbvm60.dll_adj_fptan0.13%2.97%0.010945
msvbvm60.dll_adj_fdiv_m640.13%2.97%0.010945
msvbvm60.dll_cisin0.13%2.96%0.010914
msvbvm60.dll_cilog0.13%2.96%0.010914
msvbvm60.dll__vbachkstk0.13%2.96%0.010914
msvbvm60.dll_citan0.13%2.96%0.01091
msvbvm60.dll_allmul0.13%2.96%0.01091
msvbvm60.dll_adj_fprem10.13%2.96%0.01091
msvbvm60.dll_adj_fdivr_m32i0.13%2.96%0.01091
msvbvm60.dll_adj_fdivr_m16i0.13%2.96%0.01091
msvbvm60.dll_adj_fdiv_m32i0.13%2.96%0.01091
msvbvm60.dll_adj_fdiv_m320.13%2.96%0.01091
msvbvm60.dll_adj_fdiv_m16i0.13%2.96%0.01091
msvbvm60.dll_cisqrt0.13%2.96%0.010906
msvbvm60.dll_ciatan0.13%2.96%0.010906
msvbvm60.dll_adj_fprem0.13%2.96%0.010906
msvbvm60.dll_adj_fpatan0.13%2.96%0.010906
msvbvm60.dll_adj_fdivr_m640.13%2.96%0.010906
msvbvm60.dll_adj_fdivr_m320.13%2.96%0.010906
msvbvm60.dll_adj_fdiv_r0.13%2.96%0.010906
msvbvm60.dll__vbafpexception0.13%2.96%0.010906
msvbvm60.dll_ciexp0.13%2.96%0.010902
msvbvm60.dll__vbastrmove0.12%2.89%0.010726
msvbvm60.dll__vbafreestr0.13%2.89%0.010662
msvbvm60.dll__vbafreevarlist0.13%2.88%0.010626
msvbvm60.dll__vbahresultcheckobj0.13%2.84%0.010416
msvbvm60.dll__vbasetsystemerror0.12%2.71%0.010049
msvbvm60.dll__vbanew20.13%2.74%0.010007
msvbvm60.dll__vbafreestrlist0.12%2.64%0.009647
msvbvm60.dll__vbastrcopy0.12%2.62%0.009547
msvbvm60.dll__vbafreeobj0.13%2.61%0.009378
msvbvm60.dll__vbavarmove0.10%2.48%0.009261
msvbvm60.dll__vbastrcat0.12%2.55%0.009222
msvbvm60.dll__vbavardup0.12%2.45%0.008805
msvbvm60.dll__vbastrvarmove0.12%2.31%0.008198
msvbvm60.dll__vbaarydestruct0.10%2.23%0.008191
netapi32.dllnetwkstagetinfo0.10%2.59%0.009815The NetWkstaGetInfo function returns information about the configuration of a workstation.
oleaut32.dllsysfreestring0.11%8.57%0.037727The oleaut32.dll is often used to leverage the Component Object Model system to execute commands or code from another host process.
oleaut32.dllvariantinit0.04%8.04%0.036922
oleaut32.dllvariantcopy0.05%7.84%0.035631
oleaut32.dllsysallocstringlen0.09%7.72%0.034084
oleaut32.dllvariantclear0.10%7.26%0.031655
oleaut32.dllsysreallocstringlen0.08%7.05%0.031127
oleaut32.dllsafearrayptrofindex0.05%6.89%0.030939
oleaut32.dllsafearraycreate0.05%6.39%0.028728
oleaut32.dllsafearraygetubound0.04%6.34%0.028588
oleaut32.dllsafearraygetlbound0.04%6.31%0.02853
oleaut32.dllvariantchangetype0.04%5.99%0.027029
oleaut32.dllgeterrorinfo0.04%2.81%0.011876
oleaut32.dllvariantcopyind0.06%2.28%0.009106
psapi.dllenumprocesses1.15%1.45%0.000129Used to enumerate running processes. This information may be used by malware to detect the presence of AV, detect sandboxing, or to look for applications of interest.
psapi.dllenumprocessmodules1.54%1.50%2.01E-06This function is used to enumerate the loaded modules (executables and DLLs) for a given process. Some malware may enumerate through modules when performing some type of process injection. It may also be used to detect the presence of AV or sandboxing.
urlmon.dllurldownloadtofilea0.03%1.54%0.006426Used by droppers to download additional files, payloads, or commands to the malware.
urlmon.dllurldownloadtofilew0.11%0.56%0.001119
user32.dllfindwindowexa0.53%4.01%0.010452These functions are used to search for an open window on the desktop. Sometimes this function is used as an anti-debugging technique.
user32.dllfindwindowa2.99%7.63%0.007679
user32.dllfindwindowexw1.56%3.65%0.003057
user32.dllfindwindoww3.38%3.50%7.71E-06
user32.dllgetasynckeystate1.88%2.86%0.000725Used for keylogging.
user32.dllgetdc9.64%27.97%0.039624These functions return a handle to a device context for a window or the whole screen. Spyware that takes screen captures often uses this function.
user32.dllgetdcex0.52%6.75%0.022144
user32.dllgetforegroundwindow4.88%12.74%0.01377This function returns a handle to the window currently in the foreground of the desktop. Keyloggers commonly use this function to determine in which window the user is entering his keystrokes.
user32.dllgetkeystate4.96%11.25%0.009483Used for keylogging.
winhttp.dllwinhttpconnect0.69%2.13%0.002694Used by droppers to download the next payload stage or by malware for command and control of the implant.
winhttp.dllwinhttpopen0.75%2.70%0.004073
winhttp.dllwinhttpreceiveresponse0.64%2.08%0.002796
winhttp.dllwinhttpconnect0.69%2.13%0.002694
winhttp.dllwinhttpsendrequest0.69%2.11%0.002614
winhttp.dllwinhttpopenrequest0.69%2.10%0.002614
winhttp.dllwinhttpclosehandle0.75%2.13%0.002431
winhttp.dllwinhttpquerydataavailable0.44%1.38%0.001751
winhttp.dllwinhttpqueryheaders0.55%1.39%0.001322
winhttp.dllwinhttpreaddata0.62%1.48%0.001288
wininet.dllinternetconnectw0.60%3.80%0.009101
wininet.dllinternetconnecta0.35%2.66%0.006918
wininet.dllinternetfindnextfilea0.00%0.06%0.000215
wininet.dllinternetfindnextfilew0.00%0.01%6.08E-06
wininet.dllinternetconnecta0.30%2.64%0.007311
wininet.dllinternetopenw0.70%5.36%0.014196
wininet.dllinternetopena0.43%4.48%0.013563
wininet.dllinternetreadfile0.94%5.66%0.013173
wininet.dllinternetconnectw0.60%3.80%0.009101
wininet.dllhttpsendrequestw0.55%3.66%0.008989
wininet.dllhttpopenrequestw0.63%3.76%0.008605
wininet.dllinternetopenurla0.08%2.00%0.007437
wininet.dllinternetconnecta0.35%2.66%0.006918
wininet.dllhttpqueryinfoa0.51%2.99%0.006732
wininet.dllhttpopenrequesta0.36%2.58%0.006543
wininet.dllhttpsendrequesta0.34%2.46%0.006268
wininet.dllinternetcrackurlw0.52%2.77%0.005901
wininet.dllinternetopenurlw0.30%1.76%0.00398
wininet.dllinternetquerydataavailable0.40%1.89%0.003657
wininet.dllinternetclosehandle1.12%8.60%0.023291
wininet.dllftpfindfirstfilea0.00%0.12%0.000471Can by used by malware for command and control, discovery, or exfiltration via FTP.
wininet.dllftpopenfilew0.03%0.72%0.002649
wininet.dllftpputfilea0.01%0.30%0.001239
wininet.dllftpgetfilesize0.14%0.61%0.001099
wininet.dllftpopenfilea0.01%0.22%0.000766
wininet.dllftpfindfirstfilea0.00%0.12%0.000471
wininet.dllftpcreatedirectorya0.00%0.10%0.000344
wininet.dllftpdeletefilea0.00%0.09%0.000336
wininet.dllftpgetfilea0.00%0.08%0.00029
wininet.dllftpputfilew0.01%0.09%0.000286
wininet.dllftpgetcurrentdirectorya0.01%0.07%0.000207
wininet.dllftpcreatedirectoryw0.00%0.04%0.000134
wininet.dllftpsetcurrentdirectorya0.01%0.05%0.000124
wininet.dllftpsetcurrentdirectoryw0.01%0.04%9.5E-05
wininet.dllftpcommandw0.00%0.02%9.28E-05
wininet.dllftpremovedirectorya0.00%0.03%8.41E-05
wininet.dllftprenamefilea0.00%0.02%6.05E-05
wininet.dllftpgetfilew0.00%0.02%5.39E-05
wininet.dllftpgetcurrentdirectoryw0.00%0.02%3.58E-05
wininet.dllftpcommanda0.01%0.02%2.55E-05
wininet.dllinternetcrackurla0.38%1.77%0.00334Cracks a URL into its component parts.
ws2_32.dllgethostbyname0.05%0.14%0.000154These functions are used to perform a DNS lookup on a particular hostname prior to making an IP connection to a remote host. This may be employed by malware to resolve an IP address of a command and control server.
ws2_32.dllwsaasyncgethostbyname0.00%0.03%0.000121
wsock32.dllgethostbyname0.01%0.70%0.002994
wsock32.dllwsaasyncgethostbyname0.00%0.30%0.001358
wsock32.dllgethostname0.01%0.55%0.002383
ws2_32.dllgethostname0.06%0.15%0.000155This function is used to retrieve the hostname of the computer the software is running on. Backdoors sometimes use gethostname in information gathering phase of the victim machine.
ws2_32.dllinet_addr0.03%0.09%0.00011Converts a IP address string into a binary format for use with other API calls.
wsock32.dllinet_addr0.01%0.81%0.003493
wsock32.dllaccept0.01%0.32%0.001295These functions are used to create raw socket connections. The accept function indicates that the program will listen for incoming connections on a socket. These functions can be used by malware to communicate with their Command and Communication server.
wsock32.dllbind0.01%0.37%0.001503
wsock32.dllconnect0.01%0.72%0.003062

Downloads

I’ve included the results of my statistical analysis as excel documents to help aid in any analysis you may need to conduct.

DLL Name Histogram

A frequency chart of DLL dependencies broken down by malicious vs legitimate files. This will give you an idea of how often malware will statically reference a particular DLL name.

Import Name Histogram

A frequency chart of import function names broken down by malicious vs legitimate files. This will give you an idea of how often malware will statically reference a particular function name.

2 thoughts on “Threat Hunting with Function Imports”

  1. Pingback: Malware Triage: Dissecting Threats to Your Security - Hurricane Labs

Leave a Comment

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

Scroll to Top