Hi all. It’s been a while since we’ve touched on malware techniques. We’re back, and I have a neat topic to write about.
I was looking at my LinkedIn feed recently and I found this which prompted me to write this post.
If you don’t have much of an idea of what this post is saying, you may need to touch up on your malware skills. Triage will refresh you on inspecting imports.
What
You should know that when an executable utilizes functions from the Windows API, it imports the function. So what if the function wasn’t imported? Can it be invoked?
Why yes, of course. Importing a function simply acquires the memory address of the function located within a DLL for the executable to call. The function is listed in the imports table. So, in theory, if you were able to acquire the function address without importing it, then you can call it without it being in the imports table. If you don’t import a function from a library, then you don’t need to load the library into the program. Therefore, you can use the entire ws2_32 library for socket connections without it being visible to a PE explorer.
There are a few discrepancies that have to do with permissions. For example, you can’t execute certain psapi functions without having loaded the module into the program first to acquire the proper permissions for the functions. However, you can load and then immediately free user32 then call MessageBox with no problems.
Why
This is done to thwart property analysis. If done well, it can even be a form of a misdirection technique, where a would-be analyst believes that the executable is performing a different behavior than it actually is.
How
There are 2 popular methods that I know of which are a form of hidden importing.
GetProcAddress
The first is by using GetProcAddress, which retrieves the location of an exported function from a module. It is used by kernel32.dll, and is even implicitly imported. If you compile an empty program in MSVC, approximately 66 functions will be implicitly imported, and GetProcAddress is one of them.
This has a few flags to look for:
Seeing a massive amount of xrefs to GetProcAddress, especially within a single function block.
Usage of GetModuleHandle can be traced to imported functions. One has to retrieve the module that holds the desired functions, so this library is also loaded with LoadLibrary. This module can sometimes be freed immediately so it may not be caught under a tool such as Process Hacker.
And the last one I can think of is strings. You obviously need to get the functions via their name, so seeing any unencrypted strings is a telltale sign.
Manual Mapping
Another method is by manually mapping a DLL into the program space. This is wickedly advanced; I don’t even know how to do it (yet). But, I’ve seen it done before. We’re obviously going to go with the first method.
Methodology
Reverse engineer a basic program.
Discover hidden imported functionality
Requirements
A PE file explorer.
A disassembler.
You know the drill. Tor site, grab “hiddenimport.zip” under the malware folder.
There’s blank.exe, and here is its code:
#include <Windows.h>
int main(int argc, char **argv)
{
}
The reason blank.exe exists is to demonstrate what a program header looks like when no imports are used.
This is what it looks like under PeStudio:
It only imports kernel32.dll and has 66 function imports. You may be thinking “If it’s an empty program, then why does it import so many functions?”. That’s because the loader manually loads kernel32 DLL and these functions (along with every other linked DLL and their imported functions) for use.
Now, look at hiddenimports_1.exe in PeStudio:
The only difference between the PE header exploration is that hiddenimports_1 has 7 more strings than blank.
How about you run hiddenimports_1.exe? It’s not malicious, pinky promise.
A text box pops up. But wait, MessageBox uses the user32 library, which wasn’t imported. How did it execute the function? We’ll have to look deeper.
Open up IDA or another disassembler to inspect what’s happening here.
The disassembly slightly obfuscates the strings, but decompiling the main function gives us this:
If you’ve read the post on DLL injection, then you may already be familiar with LoadLibrary, as it loads a DLL for a program to use. What is happening is that the program asks kernel32.dll for the address of LoadLibraryA, then it calls LoadLibraryA with user32.dll to load the DLL into the program’s memory space. Once user32.dll is loaded, the program then asks the DLL for the address of MessageBoxA, which it then calls. It also frees the library to prevent tools such as Process Hacker from inspecting the loaded modules of a program while it’s running.
There’s another executable called hiddenimports_2.exe. Throw that one into PEStudio too.
This one also has 66 functions and a single import. It even has fewer total strings that hiddenimports_1. If you want to run it, you’ll also want to run server.py before you start it. That kind gives away the game, but I still haven’t learned WinHTTP yet 😊.
If you run it, it sends a message to the server.
And the process is the same as before, only this time the DLL used is ws2_32, the one responsible for socket connections.
I went ahead and threw a breakpoint at the bottom of the main function to capture and remnants that might be left over. Let’s debug it real quick. When the breakpoint is hit, check the modules tab.
Looks like ws2_32.dll got loaded. Also, it looks like ws2_32.dll loads mswsock.dll, which is probably the kernel-level Windows socket provider. For reference, here’s the modules that blank.exe loads.
That’s all for this post. Neat little tricks you pick up on in the field.
Have a great weekend.
Go!
-BowTiedCrawfish