Previous thread: https://malwaretips.com/threads/malware-analysis-1-introduction.61972/

Hello and welcome to my thread on Malware Analysis. :)

I have intentionally set the font size to 3 because this thread is very long and with the normal font size it may actually be even more of a pain to go through... I highly recommend you zoom in once in your browser, and it will appear much nicer to read the text (as opposed to reading it without zooming in once or as it is by default).

If you find any false information/mistakes in this thread then please let me know so I can fix them.

In this tutorial we will be performing static analysis: checking the dynamically linked imports of a PE (Portable Executable). For this tutorial I will be using IDA (from Hex Rays) however since not everyone may want to jump into using IDA due to its complexity to start off, I will also be making an additional part to this tutorial which will demonstrate how to check the imports using different tools. This tutorial will have four parts – the first part will be theory based, the second part will be about explaining how we actually check for the imported functions (whilst using a clean test sample) whereas the third part will be doing it again but with a malicious sample. The fourth (additional) part will be explaining how to use other tools to check for the imports for future reference, should anyone run into trouble with using other tools for it.

All images will be placed within a SPOILER (spoiler tags).
Below the information regarding the requirements to carry out this tutorial you will find a part under the title “Theory”.

What you will need to follow this tutorial (prerequisites):
- An environment for performing malware analysis (a secure, working and set-up Virtual Machine will do just fine).
- IDA (for the Part 2 & 3 of this tutorial - the free version should be fine if you do not have the pro version).
- FileAlyzer/PE Explorer (for the Part 4 of this tutorial).
- Samples to practise on (check below for the download links).

You can download the clean test sample which I made myself for the purpose of this tutorial from the following link: Private file (remember that this is a clean test sample although running it is really pointless - it's all setup to have some imported functions for the main tutorial part - there is no password to the archive since it is 100% clean).

You can download the malicious sample we will be using for the second part of the tutorial from the following link: hxxp://www78.zippyshare.com/v/dBUabsTF/file.html/ (the sample is not packed) (warning: this download is malicious and you should only download it from within your secure analysis environment – replaced http with hxxp for security purposes & the archive password is infected).

The link in the custom fields for this thread will lead to the malicious sample (and the VT analysis will be for the malicious sample of course).

PE stands for Portable Executable and you can identify a Portable Executable based on the first few bytes in the binary (and through checking for information regarding DOS in the bytes of the binary). As well as this, you can identify a PE based on its extension (*.exe), however even if the extension is not for a PE this does not mean it is not really a PE. For example, you can have the extension of a Portable Executable different to that of *.exe, even though you won’t be able to open it up normally (but if it is really a PE you can find out through the bytes in the binary – first bytes should be “MZ” and following all this near the top should be information about DOS). If the extension is not *.exe but the binary is indeed a real PE file, it can actually be loaded and executed (regardless of the extension change – but not done through clicking the file to open it for example).

The hexadecimal representation for the start of the PE format (“MZ”) would be 4D 5A. Underneath this should be bytes for things relating to DOS (e.g. “This program cannot be run in DOS mode” which in hex would be 54 68 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 6D 6F 64 65). There is also the PE File Signature which is explained further down.

Every Portable Executable has parts/sections and there are many of these with different purposes.

The first parts to a PE is the MZ Header (as discussed above) and a stub for MS-DOS (which is 38 bytes in size). You may be asking yourself why there is a stub with a size of 38 bytes for MS-DOS and the reason behind this is because there needs to be some sort of backwards compatibility so if someone attempts to run a program on MS-DOS it will let them know that it is not supported (which is why there is the error message text “This program cannot be run in DOS mode” found – this is from the MS-DOS stub and the MZ header is also for MS-DOS).

The next part to a PE file is the PE File Header which is very important. The PE File Header contains information such as the PE File Signature (which is for the Windows Loader – the signature is PE00 (50 45 00 00)), information regarding machines the Portable Executable can be run on (e.g. Intel processors), information regarding the size of the sections table, information regarding the size of the optional header, a flag for characteristic information and then the optional header (includes information like the PEP (Program Entry Point) and OS version along with some other information).

In the PE File Header there is a Virtual Address (VA for short) and a Relative Virtual Address (RVA for short). The RVA is an address of an item after it has been loaded into memory however for it to be the RVA you must subtract the base address of the image file from it. Whereas the VA is an address of an item after it has been loaded into memory without subtracting the base address. Each process will have its own VA.

Within the Portable Executable there will be many sections however we will only talk about a few of them for now: .text, .bss, .rdata, .data and the .idata section. I may make an entire reference thread which will have much more detail regarding the Portable Executable format (the internals of it) however for now this is not necessary.

The purpose of the .text section is to hold the actual instructions for the executable. In other words, it is the code (heart) of the program.

The purpose of the .bss section is to hold static/dynamic variables (which are uninitialized). If you are not aware of what an uninitialized variable actually is, it is essentially a variable which has been declared in the code but was not given a definite value before it was used.

The purpose of the .data section is to hold data which was defined within the compiled code (in the program). For example, if you created a variable and set its value as you created it, then this is defining data and it would be stored within this section.

The purpose of the .rdata section is to hold read-only data which was defined within the compiled code (in the program). An example of this would be a constant variable which should not have its value changed (therefore since it is read-only it is placed in this section). Basically it is the .data section but read-only.

The purpose of the .idata section is for containment of information regarding the imports for the program. This is also where the IAT (Import Address Table) resides (which I explain below since I feel it deserves its own paragraph explanation due to it being very important and being a key part to this tutorial).

An imported function is where a program makes use of a function which is from another DLL. In Windows there are many import libraries which Microsoft provides and there are many functions within these available libraries which are officially documented (at MSDN for example). It is important that functions from libraries provided by Microsoft are documented because if you are performing analysis and find that a function from a Windows library is being used you need to understand what it does to a reasonable extent to understand what the malware is actually doing. However, not all functions within these libraries are documented for specific reasons of their own (which I will explain a bit about below) and in fact, not all libraries are documented for the purpose of Microsoft not wanting them being used at all.

When you run a Portable Executable, the Windows Loader will make sure that the correct DLLs (Dynamic Link Libraries) are imported if a function from them are being used. If a program makes use of a function from a Windows Library then the DLL which holds this function being called will be imported into the address space of the process (when you run a Portable Executable, the Windows Loader will have this PE loaded into memory and therefore it receives its own process which is built up of “threads”).

The Import Address Table (IAT) is key to this thread and very important in Portable Executables. The purpose of the IAT is to hold addresses (pointer addresses) which will reside to the function imported (located within the imported library) in the PE. It is a table which holds functions in a list and each entry to the table has its own pointer address which can be accessed (e.g. via a JMP instruction). When a function is imported the Windows Loader will make sure that the function has a place in the IAT and corresponds to a pointer address which will trail to the function within the imported library (DLL). When this imported function is called it is looked up in the IAT and then it performs an instruction to go to that address for the function call (e.g. CALL/JMP instruction). The IAT was made to prevent having to statically link all the functions (adding the function code within the PE itself) but instead it is dynamically-linked since the PE will import the required modules containing the functions and then work with a function pointer to call/jmp (jump) to the called function.

Part 2
For this second part of the thread we will actually start the tutorial for checking the imports of a sample. However, we will be using a test sample for this part of the tutorial. This means that there will be no malicious usage of functions within the sample, all the executing code will be clean. Although this is a static analysis tutorial therefore there is no need to actually run the sample. You can find the download to the test sample at the beginning of the thread. You will also require IDA (the free version should be fine) for this part of the tutorial.

To get started you will need to open up IDA (I will be using the Pro version). You need to open up the correct version (there is a version for x86 binaries and another for x64 – both sample downloads will be for x86 for people who only have the Free version since the Free version doesn’t support x64 binaries).

Once you open up IDA you will be presented with the Quick Start dialog (depending on the configuration settings). This dialog will have some options for you to get started with working in IDA: ‘New’, ‘Go’ and ‘Previous’. The first option is ‘New’ which will take you into the IDA environment and then present you with an open file dialog (to select the file you will be working with). The second option which is ‘Go’ will take you to the work space environment for IDA but will not give you an open file dialog (you will have to manually open the file into the environment). The third option which is ‘Previous’ will open the most recently open file (with any saved data if any from your recent work on it with IDA). At the bottom after the options there will be a list of previously opened files and double clicking on this will open them up in IDA again.

Since we will be working with the clean test sample for this part of the tutorial we will be using the ‘New’ option. Select this option and when prompted use the dialog to open the clean test sample.

After opening the test file (Portable Executable) you should have another dialog show up. By default, it should have the first option selected as “Portable Executable for… (PE) […]” and all the other settings should be left the same. If you want to make sure that the settings on the dialog are the same as mine, you can check the below screenshot of the dialog within the spoiler (excluding settings for things which require an additional dialog since as long as you leave those settings un-touched you won’t need to compare at all for whatever reason). Just hit OK.

After sending confirmation that you are happy with the settings by hitting OK you will be presented with some more dialog's which hopefully should speed through and auto-close. Eventually you will get a dialog which requires user-interaction which will be about a *.pdb file. This file is actually used for things relating to debugging however just ignore this and choose No. You can even tick the box to not show it again (as it won’t be required for now at least). You’ll get a bunch more dialog's after this which should speed through, the last one will be user-interaction based but all you’ll have to do is click OK.

Now you should be presented with the main work space of IDA with the Portable Executable successfully loaded. Everything might look quite complex at first glance but after you learn to use the environment then it won’t be as bad as it looks, trust me!

By default, IDA will attempt to locate the start function of the program (most likely by signature detection) and leave it there at the ‘IDA View-A’ tab (you may have already noticed that there are main tabs for the work space next to the functions window which is placed next to it on the left by default). The functions window aside to the main tabs is very important however that is for another tutorial – for now just ignore all the windows and tabs there, except the tab with the title ‘Imports’ (check the screenshot in the below screenshot if you are not sure of where it is located).

If you haven’t caught on yet, we are interested in this tab for this tutorial and this part of the tutorial will strictly evolve around it. We do not require to use the other tabs for things such as checking the exports, looking at the actual instructions at certain functions within the program and converting this to a more appropriate and readable code (since reading ASM may not always be convenient depending on the circumstances), and so on. Simply navigate to this Imports tab and then follow on!

Once you have selected the tab it should look something like this (if you have the same test sample loaded, otherwise the Imports will be different):

Once you are viewing this tab you should see 4 columns (Address, Ordinal, Name and Library).

The value in the address is the location in memory as to where the function was defined since we reference it within the program (if you double click the item it will take you to that address – feel free to do so and don’t be shy, just go back to the Imports tab after and you’re back to where you started… Or press Esc and it’ll take you back to where you started on the IDA View-A tab before it moved to the address from the Imports tab).

The value in the Name column (string) will be the function name which is from the library. As an example, if the program uses a function called “GiveWaveOneMillionPounds” from a library called “Wave32.dll” then the Name column will have an entry with the value “GiveWaveOneMillionPounds” and the Library column will have the value (string also) of “Wave32.dll” corresponding to that entry in the list.

My test sample was created with a Win32 template (Console Application) with Visual Studio, which is making use of functions from a library called MSVCP140 therefore we can ignore this for now (and the VCRUNTIME140/api-ms-win-crt-runtime-l1-1-0 library). However, we should take interest in the imports from KERNEL32.DLL and USER32.DLL.

Straight off the bat from the top of the list entries we can see some imported functions from KERNEL32: CreateFileW, SetUnhandledExceptionFilter, GetCurrentProcess, TerminateProcess, IsProcessorFeaturePresent, QueryPerformanceCounter, GetCurrentProcessId, GetCurrentThreadId, GetSystemTimeAsFileTime, InitializeSListHead, IsDebuggerPresent, GetModuleHandleW and UnhandledExceptionFilter.

Firstly, not all these functions were imported by me. Some of these functions were imported automatically for functions which are generated for functions like the ExitProcess function (which makes use of functions like TerminateProcess to end the process) which are called when you exit the program.

Secondly, without further analysis we cannot pick out functions like TerminateProcess and say that it will attempt to terminate an external process (since this function is being used by default for the actual program to terminate itself when the program is closed). The same goes for the function IsDebuggerPresent – we cannot just assume that the sample is checking if it is being debugged or not to avoid the correct instruction execution/to close if it detects a debugger because this had nothing to do with my code but it was relating to the auto-generated code which was built with the compiler for the pre-after code execution of my own code. Therefore, should you encounter these functions in future analysis, you can perform other static analysis techniques (and dynamic analysis) to further investigate the purpose being the import of these functions to make sure you don’t accidentally jump to conclusions with false facts.

The functions we need to be looking at is kernel32.dll!CreateFileW, User32!MessageBoxA and User32!MessageBoxW. These functions won’t be added in automatically by the compiler therefore it must have been added by the programmer (in the case of this clean test sample… Me (Wave))!

The reason I wrote “kernel32.dll!CreateFileW” instead of “CreateFileW from kernel32.dll” is because they both mean the same things and it is much shorter to use the first format which I used. It is also more commonly used by programmers.

I suspect that you won’t know (unless you have a programming background with the Win32 API) about what these functions I pin-pointed out are/do.

The first one, CreateFileW, is a Win32 API function (as we know – we also know it is from kernel32 library) which has the ability to create files given the correct information. This tells us that the program will create a file at some point during execution flow.

The second and third one are actually more-or-less the same. MessageBoxA and MessageBoxW are the same however one is for Ascii (the one with the ‘A’ at the end) and the other is for Unicode. I won’t go into my own zone talking about the differences between Ascii and Unicode in this thread as it’s not really necessary however it is relating to data types for the passed in information to the function parameters. In fact, it is quite often to find a Win32 API with a version for both the Ascii and Unicode version. Since there are both functions imported, this tells us that at some point during program execution flow, it will call both of them (maybe not one after the other, we would need to perform further analysis to discover when the calls are made, etc).

If you are not familiar with the Win32 API but would like to know how a function from the Win32 API works which is used within a sample you are checking the imports on, you can try looking up the function at MSDN (Microsoft Developer Network).


I should note that there are many libraries within the Win32 API; not just KERNEL32.DLL and USER32.DLL.

Hopefully you as a reader are still with me and have understood so far… If you are just spectating to learn, I highly recommend trying this out yourself. If you try to follow this tutorial practically then you may catch a much better understanding. I believe this is the end of Part 2, therefore feel free to move onto Part 3 if you are still interested and I haven’t scared you off!

Part 3
Since Part 2 was the actual tutorial of checking the imports of a PE we will not be going over everything again. You can scroll up to read above information from the main tutorial in Part 2 to help you with this part if you get stuck.

All I will be doing in this part is talking about some of the imported functions within the malicious sample. Of course you can do further analysis to practise your analysis skills (if you have previous experience) then feel free to do so. Just make sure you are in your secure analysis environment.

The malicious sample we will be using for the purpose of this Part 3 is typically being detected as “spyware” (Zeus Bot) by most (if not all) vendors which detect the sample according to some online scan results. You can view the VirusTotal report at the following link: https://virustotal.com/en/file/05afbcd2f3801ac0b4954ad9b38e0817fa3914838059008b6b095cb0e632b9c4/analysis/

MD5 hash of the sample: b38cddd0fa7713d0033f5db5186a9d98
SHA256 hash of the sample: 05afbcd2f3801ac0b4954ad9b38e0817fa3914838059008b6b095cb0e632b9c4

NOTE: I have not done any further analysis other than what is taught in this tutorial. I have done this so my knowledge on the sample is more-or-less the same as the readers (through checking imports and based on the detections from existing vendors). For all we know, the detections from the vendors are maybe incorrect (and then a bunch of vendors may have copied the incorrect detection name), but I will assume that the existing detections are indeed correct for now and base my opinions around the existing evidence which other analysts have already found.

I still start by opening up the malicious sample in IDA (with the default settings) and navigate to the Imports tab. You can see a screenshot of my Imports tab below:

Straight off the bat you can see that there are many imports within this malicious sample and therefore they do not all fit inside of one screenshot. I have used the Copy All (Ctrl + Shift + Ins) to copy all of the information in the Imports tab have posted it below for you.

Address  Ordinal Name  Library

-------  ------- ----  -------
00401000  InitiateSystemShutdownExW  ADVAPI32
00401004  CryptCreateHash  ADVAPI32
00401008  AllocateAndInitializeSid  ADVAPI32
0040100C  LookupPrivilegeValueW  ADVAPI32
00401010  SetNamedSecurityInfoW  ADVAPI32
00401014  SetSecurityDescriptorDacl   ADVAPI32
00401018  InitializeSecurityDescriptor  ADVAPI32
0040101C  CryptReleaseContext  ADVAPI32
00401020  RegCreateKeyExW  ADVAPI32
00401024  GetTokenInformation  ADVAPI32
00401028  GetSidSubAuthorityCount  ADVAPI32
0040102C  OpenThreadToken  ADVAPI32
00401030  CryptAcquireContextW  ADVAPI32
00401034  GetSidSubAuthority  ADVAPI32
00401038  OpenProcessToken  ADVAPI32
0040103C  CryptGetHashParam   ADVAPI32
00401040  IsWellKnownSid  ADVAPI32
00401044  RegCloseKey  ADVAPI32
00401048  RegEnumValueW   ADVAPI32
0040104C  RegDeleteValueW  ADVAPI32
00401050  RegQueryInfoKeyW  ADVAPI32
00401054  RegCreateKeyW  ADVAPI32
00401058  EqualSid  ADVAPI32
0040105C  RegQueryValueExW  ADVAPI32
00401060  RegQueryValueExA  ADVAPI32
00401064   ConvertSidToStringSidW  ADVAPI32
00401068  GetLengthSid  ADVAPI32
0040106C  CreateProcessAsUserW  ADVAPI32
00401070  CreateProcessAsUserA  ADVAPI32
00401074  SetSecurityInfo  ADVAPI32
00401078  FreeSid  ADVAPI32
0040107C  RegOpenKeyExW   ADVAPI32
00401080  GetSecurityDescriptorSacl  ADVAPI32
00401084  CheckTokenMembership  ADVAPI32
00401088  SetSecurityDescriptorSacl  ADVAPI32
0040108C  CryptDestroyHash  ADVAPI32
00401090  AdjustTokenPrivileges  ADVAPI32
00401094  RegSetValueExW  ADVAPI32
00401098  GetSecurityDescriptorDacl  ADVAPI32
0040109C  CryptHashData  ADVAPI32
004010A0  RegEnumKeyExW  ADVAPI32
004010A4  RegEnumKeyW  ADVAPI32
004010A8  ConvertStringSecurityDescriptorToSecurityDescriptorW ADVAPI32
004010B0  PFXImportCertStore  CRYPT32
004010B4  CertDeleteCertificateFromStore  CRYPT32
004010B8  CryptUnprotectData  CRYPT32
004010BC  CertCloseStore  CRYPT32
004010C0  CertEnumCertificatesInStore   CRYPT32
004010C4  CertDuplicateCertificateContext  CRYPT32
004010C8  PFXExportCertStoreEx  CRYPT32
004010CC  CertOpenSystemStoreW  CRYPT32
004010D4  GetDeviceCaps  GDI32
004010DC  GetModuleHandleA  KERNEL32
004010E0  FindFirstFileW  KERNEL32
004010E4   GetCurrentThread  KERNEL32
004010E8  FileTimeToSystemTime  KERNEL32
004010EC  SetThreadPriority  KERNEL32
004010F0  FindClose   KERNEL32
004010F4  FindNextFileW  KERNEL32
004010F8  GetWindowsDirectoryW  KERNEL32
004010FC  GetCommandLineW   KERNEL32
00401100  CreateDirectoryW  KERNEL32
00401104  LoadLibraryW  KERNEL32
00401108  lstrcmpiA  KERNEL32
0040110C  WTSGetActiveConsoleSessionId  KERNEL32
00401110  MoveFileExW  KERNEL32
00401114  SetFilePointer  KERNEL32
00401118  SetEndOfFile  KERNEL32
0040111C  WriteFile  KERNEL32
00401120  ExpandEnvironmentStringsW  KERNEL32
00401124  GetPrivateProfileStringW  KERNEL32
00401128  FlushFileBuffers  KERNEL32
0040112C  GetPrivateProfileIntW  KERNEL32
00401130  GetUserDefaultUILanguage  KERNEL32
00401134  CreateMutexW  KERNEL32
00401138  SetErrorMode  KERNEL32
0040113C  GetComputerNameW   KERNEL32
00401140  TerminateThread  KERNEL32
00401144  WriteProcessMemory  KERNEL32
00401148  CreateThread  KERNEL32
0040114C  SetHandleInformation  KERNEL32
00401150  GetExitCodeProcess  KERNEL32
00401154  ReadFile  KERNEL32
00401158   GetExitCodeThread  KERNEL32
0040115C  CreatePipe  KERNEL32
00401160  GetEnvironmentVariableW  KERNEL32
00401164  FileTimeToDosDateTime  KERNEL32
00401168  GetTempFileNameW  KERNEL32
0040116C  VirtualFree  KERNEL32
00401170  GetTickCount   KERNEL32
00401174  SystemTimeToFileTime  KERNEL32
00401178  SetFilePointerEx  KERNEL32
0040117C  GetLogicalDriveStringsW  KERNEL32
00401180  HeapFree  KERNEL32
00401184  GetProcessHeap  KERNEL32
00401188  SetFileTime  KERNEL32
0040118C  VirtualQueryEx  KERNEL32
00401190  Thread32First  KERNEL32
00401194  WideCharToMultiByte  KERNEL32
00401198  ReadProcessMemory  KERNEL32
0040119C  HeapDestroy  KERNEL32
004011A0  HeapCreate  KERNEL32
004011A4  lstrcpynW   KERNEL32
004011A8  Thread32Next  KERNEL32
004011AC  GetTimeZoneInformation  KERNEL32
004011B0  MultiByteToWideChar   KERNEL32
004011B4  lstrlenW  KERNEL32
004011B8  GetTempPathW  KERNEL32
004011BC  GetFileSizeEx  KERNEL32
004011C0  OpenMutexW  KERNEL32
004011C4  VirtualProtectEx  KERNEL32
004011C8  VirtualAllocEx  KERNEL32
004011CC   RemoveDirectoryW  KERNEL32
004011D0  QueryDosDeviceW  KERNEL32
004011D4  GetFileTime  KERNEL32
004011D8  ReleaseMutex   KERNEL32
004011DC  FileTimeToLocalFileTime  KERNEL32
004011E0  GetVolumeNameForVolumeMountPointW  KERNEL32
004011E4  GetFileInformationByHandle   KERNEL32
004011E8  GetSystemTime  KERNEL32
004011EC  InterlockedExchange  KERNEL32
004011F0  GetLocalTime  KERNEL32
004011F4  ResetEvent  KERNEL32
004011F8  SetLastError  KERNEL32
004011FC  GetLastError  KERNEL32
00401200  CreateEventA  KERNEL32
00401204  SetFileAttributesW  KERNEL32
00401208  GetLogicalDrives  KERNEL32
0040120C  GetDriveTypeW  KERNEL32
00401210  lstrcmpiW  KERNEL32
00401214  LoadLibraryA  KERNEL32
00401218  FreeLibrary   KERNEL32
0040121C  GetFileAttributesW  KERNEL32
00401220  GlobalUnlock  KERNEL32
00401224  GlobalLock   KERNEL32
00401228  GetCurrentProcessId  KERNEL32
0040122C  HeapReAlloc  KERNEL32
00401230  OpenEventW  KERNEL32
00401234  SetEvent  KERNEL32
00401238  LocalFree  KERNEL32
0040123C  GetVersionExW  KERNEL32
00401240   GetNativeSystemInfo  KERNEL32
00401244  WaitForMultipleObjects  KERNEL32
00401248  CreateEventW  KERNEL32
0040124C  Sleep   KERNEL32
00401250  ResumeThread  KERNEL32
00401254  DeleteFileW  KERNEL32
00401258  DuplicateHandle   KERNEL32
0040125C  CreateToolhelp32Snapshot  KERNEL32
00401260  VirtualProtect  KERNEL32
00401264  Process32NextW  KERNEL32
00401268  CreateFileMappingA  KERNEL32
0040126C  Process32FirstW  KERNEL32
00401270  GetProcAddress  KERNEL32
00401274   CreateFileW  KERNEL32
00401278  TerminateProcess  KERNEL32
0040127C  CopyFileW  KERNEL32
00401280  OpenProcess   KERNEL32
00401284  CreateRemoteThread  KERNEL32
00401288  IsBadReadPtr  KERNEL32
0040128C  GetModuleHandleW   KERNEL32
00401290  GetCurrentProcess  KERNEL32
00401294  CreateProcessW  KERNEL32
00401298  WaitForSingleObject  KERNEL32
0040129C  UnmapViewOfFile  KERNEL32
004012A0  MapViewOfFile  KERNEL32
004012A4  GetFileSize  KERNEL32
004012A8   CloseHandle  KERNEL32
004012AC  GetFileAttributesExW  KERNEL32
004012B0  GetProcessId  KERNEL32
004012B4  EnterCriticalSection  KERNEL32
004012B8  VirtualAlloc  KERNEL32
004012BC  LeaveCriticalSection  KERNEL32
004012C0  VirtualFreeEx   KERNEL32
004012C4  InitializeCriticalSection  KERNEL32
004012C8  SetThreadContext  KERNEL32
004012CC  GetThreadContext   KERNEL32
004012D0  ExitProcess  KERNEL32
004012D4  ExitThread  KERNEL32
004012D8  GetModuleFileNameW  KERNEL32
004012DC  HeapAlloc  KERNEL32
004012E4  NetApiBufferFree  NETAPI32
004012E8  NetUserEnum  NETAPI32
004012EC  NetUserGetInfo  NETAPI32
004012F4 2  SysAllocString  OLEAUT32
004012F8 9  VariantClear  OLEAUT32
004012FC 8  VariantInit   OLEAUT32
00401300 6  SysFreeString  OLEAUT32
00401308  CommandLineToArgvW  SHELL32
0040130C  ShellExecuteW   SHELL32
00401310  SHGetFolderPathW  SHELL32
00401314  ShellExecuteExW  SHELL32
0040131C  PathUnquoteSpacesW  SHLWAPI
00401320  PathRemoveBackslashW  SHLWAPI
00401324  PathQuoteSpacesW  SHLWAPI
00401328  PathRenameExtensionW  SHLWAPI
0040132C   StrCmpNIA  SHLWAPI
00401330  UrlUnescapeA  SHLWAPI
00401334  wvnsprintfW  SHLWAPI
00401338  PathIsDirectoryW  SHLWAPI
0040133C  PathAddBackslashW  SHLWAPI
00401340  SHDeleteValueW  SHLWAPI
00401344  PathSkipRootW   SHLWAPI
00401348  SHDeleteKeyW  SHLWAPI
0040134C  PathCombineW  SHLWAPI
00401350  PathAddExtensionW  SHLWAPI
00401354  PathMatchSpecW  SHLWAPI
00401358  wvnsprintfA  SHLWAPI
0040135C  StrStrIA  SHLWAPI
00401360   StrStrIW  SHLWAPI
00401364  StrCmpNIW  SHLWAPI
00401368  PathRemoveFileSpecW  SHLWAPI
0040136C  PathFindFileNameW  SHLWAPI
00401370  PathIsURLW  SHLWAPI
00401378  GetUserNameExW  Secur32
00401380  CharUpperW   USER32
00401384  CharLowerA  USER32
00401388  GetDC  USER32
0040138C  CharLowerW   USER32
00401390  MsgWaitForMultipleObjects  USER32
00401394  LoadImageW  USER32
00401398  ToUnicode  USER32
0040139C  PeekMessageW  USER32
004013A0  DispatchMessageW  USER32
004013A4  GetForegroundWindow  USER32
004013A8   CharLowerBuffA  USER32
004013AC  GetKeyboardState  USER32
004013B0  TranslateMessage  USER32
004013B4  GetMessageW   USER32
004013B8  GetCursorPos  USER32
004013BC  GetIconInfo  USER32
004013C0  DrawIcon   USER32
004013C4  MessageBoxA  USER32
004013C8  CharToOemW  USER32
004013CC  ExitWindowsEx  USER32
004013D0  GetClipboardData  USER32
004013D8  GetFileVersionInfoSizeW  VERSION
004013DC  VerQueryValueW  VERSION
004013E0   GetFileVersionInfoW  VERSION
004013E8  HttpQueryInfoA  WININET
004013EC  InternetSetStatusCallbackA  WININET
004013F0  InternetOpenA  WININET
004013F4  InternetSetOptionA  WININET
004013F8  InternetCrackUrlW  WININET
004013FC  InternetCrackUrlA   WININET
00401400  InternetQueryOptionW  WININET
00401404  InternetConnectA  WININET
00401408  InternetQueryOptionA   WININET
0040140C  InternetCloseHandle  WININET
00401410  HttpEndRequestW  WININET
00401414  HttpSendRequestA  WININET
00401418  HttpAddRequestHeadersA  WININET
0040141C  HttpEndRequestA  WININET
00401420  InternetSetFilePointer  WININET
00401424  InternetGetCookieA  WININET
00401428  HttpOpenRequestW  WININET
0040142C  HttpOpenRequestA  WININET
00401430  HttpAddRequestHeadersW   WININET
00401434  InternetSetStatusCallbackW  WININET
00401438  GetUrlCacheEntryInfoW  WININET
0040143C  HttpSendRequestW   WININET
00401440  InternetReadFile  WININET
00401444  InternetReadFileExA  WININET
00401448  InternetQueryDataAvailable  WININET
0040144C  HttpSendRequestExW  WININET
00401450  HttpSendRequestExA  WININET
00401458  WSAGetOverlappedResult  WS2_32
0040145C  WSARecv  WS2_32
00401460  WSAEventSelect  WS2_32
00401464  WSAEnumNetworkEvents  WS2_32
00401468  WSAConnect  WS2_32
0040146C 111  WSAGetLastError  WS2_32
00401470 11  inet_addr  WS2_32
00401474 52  gethostbyname   WS2_32
00401478 5  getpeername  WS2_32
0040147C 17  recvfrom  WS2_32
00401480  WSAIoctl   WS2_32
00401484 4  connect  WS2_32
00401488  WSAAddressToStringW  WS2_32
0040148C 115  WSAStartup  WS2_32
00401490  getaddrinfo  WS2_32
00401494  WSASocketA  WS2_32
00401498 22  shutdown  WS2_32
0040149C 21  setsockopt  WS2_32
004014A0 20  sendto  WS2_32
004014A4 16  recv  WS2_32
004014A8 2  bind   WS2_32
004014AC 23  socket  WS2_32
004014B0  freeaddrinfo  WS2_32
004014B4 112  WSASetLastError   WS2_32
004014B8 19  send  WS2_32
004014BC 13  listen  WS2_32
004014C0  WSAWaitForMultipleEvents  WS2_32
004014C4 6  getsockname  WS2_32
004014C8 1  accept  WS2_32
004014CC  WSACloseEvent  WS2_32
004014D0  WSAResetEvent  WS2_32
004014D4 3  closesocket  WS2_32
004014D8 18  select  WS2_32
004014DC  WSACreateEvent  WS2_32
004014E0  WSASend  WS2_32
004014E8  NtQueryKey  ntdll
004014F0  StringFromGUID2   ole32
004014F4  CoInitializeEx  ole32
004014F8  CoInitialize  ole32
004014FC  CoGetObject   ole32
00401500  CLSIDFromString  ole32
00401504  CoUninitialize  ole32
00401508  CoSetProxyBlanket  ole32
0040150C  CoCreateInstance  ole32
00401510  CoInitializeSecurity  ole32

I will not be going through every single individual import since this would take much more time than what is necessary however what I will be doing is picking out some of the imported functions from the list which are more interesting and have more relevance to malware/the detections from AV vendors.

The first entry of the imports table is Advapi.dll!InitiateSystemShutdownExW (W = Unicode). This function will allow the sample to shut down the system. For more information: InitiateSystemShutdownEx function (Windows)

We can see that there are many registry functions imported: RegCreateKeyExW, RegCloseKey, RegEnumValueW, RegDeleteValueW, RegQueryInfoKeyW, RegCreateKeyW, RegSetValueExW and others. This tells us that the sample most likely interacts with the registry for whatever purpose at some point during execution flow.

The functions RegCreateKeyExW/RegCreateKeyW (from Advapi32.dll library) will allow the sample to create a new key in the registry. Based on first view I would suspect that the sample would be using this to allow itself to auto-start at boot, although without further analysis I can only assume. Based on some of the functions I listed above with relevance to registry operations, we know that the sample is capable of: deleting registry keys, querying info from registry keys, modifying the value of existing registry keys, and closing the handle to a registry key it had opened. You can find more information about Windows API registry functions at the following page: Registry Functions (Windows)

The function GetWindowsDirectoryW (from Kernel32.dll) will return the path of the Windows folder. For more information check the following page: GetWindowsDirectory function (Windows)

The functions Process32FirstW and CreateToolhelp32Snapshot (both present in Kernel32.dll) which caught my eye whilst looking at the imported functions tells me that the malicious sample will most likely enumerate through all the processes to find a specific process (preferably passing a process name (string) and then receiving the PID if a process with that name was found). I actually assume this due to experience of using these functions and using them for a function which will do exactly this, although I can still be wrong. This is just an assumption based on the imports.

I think that it is worth mentioning that the sample has the ability to inject into external processes should it have a need too for whatever purpose. It can accomplish through using the common DLL injection method by using the CreateRemoteThread function it imports (which will force the target process to call LoadLibraryA/LoadLibraryW on the target DLL to be injected, thus it becomes loaded in the address space of the target process and therefore the malicious code within the DLL becomes executed from within that targeted process). The usage to do this also evolves around using functions such as WriteProcessMemory to modify the memory of an external process prior to calling the CreateRemoteThread function to set things up, therefore for me this is enough evidence that the sample will attempt to inject into an external process at some point during execution flow. Maybe my opinion on this is a bit far-fetched, though.

Interestingly, the sample actually imports functions from the CryptoAPI (Crypt32.dll). Another thing I found interesting about the sample is how it imported ntdll!NtQueryKey (ntdll.dll will be covered in a theory thread therefore be sure to watch out for when that thread arrives sometime in distant future).

Pointing out different functions imported by the sample and giving a very brief explanation of what the function is for/what the sample may be doing with it can go on forever with the amount of imports this sample has, however to put it short, the sample has the ability to do many things such as (but not limited to): shutting down the local system (or systems on a network); modifying the security descriptor (DACL) – this can actually be done to protect a process from termination against non-admin processes; registry modification, file modification, modifying process tokens, modifying the priority of threads within a process, performing injection attacks into external processes, enumerating through running processes/threads, duplicate handles, process creation/termination, perform network activity (e.g. send/open HTTP requests – network activity could be used to send the required info about the user/system back to the attacker and receive back instructions to be performed. However, network activity could also be used maliciously to perform network attacks to targeted services such as DDoS attacks), obtain net user information, obtain clipboard data, work with the CryptoAPI, call NTAPI functions (instead of just using the Windows API equivalent and having it point to that direction anyway), and more.

I believe that sufficient information was presented in this part 3 therefore we shall move onto part 4 (for anyone who needs it). The purpose of this part 3 was not to be another tutorial embedded into this tutorial, but to do a quick analysis of a malicious sample based on the import checking we did in the main part of this thread (part 1).

Part 4
The purpose of this part 4 is to quickly overview checking the imports of a Portable Executable via two other pieces of software (which can be freely obtained – or at least there is a free version which will be good enough).

Before we can get started you will need to have downloaded and installed FileAlyzer. You can download FileAlyzer from the official website here.

After you have downloaded and installed FileAlyzer you need to open it up (you do not need to necessarily run it with admin rights, it can run with standard rights right now).

When you open it up at first, you should be granted with an open file dialog. You can actually exit this dialog and it won’t close the program – you’ll be taken to the normal overview work space area however if you haven’t opened anything to analyse then it will all be empty.

Open up the Portable Executable you wish to analyse. For the purpose of this section (part 4) I will be using the malicious sample we used in part 3. After opening it up, the work space should change to something like this:

In my opinion it looks very simple to read. The General tab will provide you details on the path of the sample, the file size, version (if available), hash checksums (CRC-32, MD5, SHA1), file attribute information (e.g. Read only, Hidden, System file, etc), and creation/access/write information. For now, you can ignore all of this and all of the tabs except the “PE Imports” tab. Simply navigate to this tab.

Once you have navigated to the PE Imports tab it should look something like this:

FileAlyzer will actually group each import to the library it’s from. As an example, all Advapi32.dll imports were grouped into their own section, whereas the imports from Kernel32.dll were placed within their own group. The + and – symbols with the green/red boxes are for if the imported function is available on a specific OS (I believe). If you right click you can search for more information for a specific function – this will search for information on the function using your default web browser on Google but filter for MSDN results only.

NOTE: The PE Imports tab may be very “laggy” for you. It normally is for me at least whilst dealing with a PE containing many imports.

Now you’re done! That is all you have to do to view the imports of a PE with FileAlyzer.

PE Explorer:
Before we can get started you will need to have downloaded and installed PE Explorer (free edition will be just fine). You can download PE Explorer from the official website here.

After you have downloaded and installed PE Explorer you need to open it up (you do not need to necessarily run it with admin rights, it can run with standard rights right now).

When you first open it up you should be presented with a quick splash screen however this will close and load the main GUI speedily. You should see something like below:

It looks quite simple, right? The next thing you’ll need to do is actually open up the target file (I will be using the malicious sample from earlier – you can drag it onto the GUI or use the quick-open toolbar icon… Or go to File > Open File (Ctrl + O)).

After opening up the PE it should take you to the “Headers info” tab by default. All you need to do is click the “Import” tab button on the toolbar to be taken to the Imports tab. Check the screenshots in the below spoiler:

Now the useful feature of the Imports tab with this software which I like is that it allows you to go through the different libraries which functions are imported from and then it displays the functions from that library which were imported in a separate list as opposed to mixing it all up, and it does this quite quickly. Nor do I experience any sort of “lag” whilst navigating the Imports tab GUI, unlike with other software.

Another good feature is that it provides a feature known as “Syntax Details” with a box containing text elements which gives some information on the function (e.g. parameters).

NOTE: The free version is actually a 30-day evaluation of the paid version. Although there is nothing wrong with experimenting with it for analysis of malware during this evaluation!

Now you’re done! That is all you have to do to view the imports of a PE with PE Explorer.

Part four was just a bonus addition to not only improve the quality of this thread but to potentially help anyone who does actually find themselves stuck attempting to check the imports with another tool – when you are starting out it can be very easy to become confused and stuck and this can be very stressful. Therefore, even though it should be fairly obvious on using another tool for checking the imports (to some people), it may not be as simple for others and this was the reason I added in part four.

IDA is a very sophisticated piece of software for reverse engineering and has a very large feature set. Due to this, I intentionally did not cover a lot of things and strictly tried to stay on topic and base it down to what was needed to be mentioned. Other things will be covered with IDA in future tutorials (both static and dynamic tutorials).


Thank you for reading and hopefully this tutorial helped you. ;)


Level 26
Malware Hunter

Thanks for this post <3

First, very well written, I liked it :)
Secondly, BIG Work done !
third : very interesting :)

I'm anxious to read another post from you, already know I will enjoy it :)

(asm, win32 api, ida, I love them all :oops:)
Last edited:


Good investigation about the most
interesting parts that are for sure the PE header and the Section Table that can contain code or data.
About the PE header, it is interesting to note the entry point, that describes the point from which begins the code, the image base, which specifies the address where the file will be memory-mapped.
The entry of the structure IMAGE_DATA_DIRECTORY contained in the optional header, are just as interesting that between these entries, is present IMAGE_DIRECTORY_ENTRY_IMPORT, or Import Table.


I'm anxious to read another post from you, already know I will enjoy it :)
Thank you & glad you liked it! :)

I don't know how often my malware analysis threads will be and due to time I cannot predict the time arrivals, there could be one sometime near the end of this month or there could be a dozen next month! It really depends on how much time I get to work on them.


@Wave great thread and explanation.

I would add also that if a malware is obfuscated it uses usually the library "GetProcAddress".
Thanks for sharing this.
IAT is a" table of external pointers" to functions the application is using. The location in memory of the dll, however, is dynamic. So the linker cannot know where a certain function is, in memory. The table is then created in a dynamic way, through the name of the function and the dll. The loader then overwrites the IAT, with the leases obtained. Each time a function is invoked, it is used the pointer to the IAT, the method used can change depending from the compiler (if the address of the procedure is not obtained at runtime, for example through GetProcAddress).
Last edited by a moderator: