xmark.svg
email

Demander une démo gratuite

Prêt à commencer? Nous sommes là pour vous aider. Remplissez vos informations d'entreprise et nous vous contacterons dès que possible.

img-form.svg
xmark.svg
email

Contacter le Partenaire

Prêt à commencer? Nous sommes là pour vous aider. Remplissez vos informations d'entreprise et nous vous contacterons dès que possible.

img-form.svg
xmark.svg

Compromis!

Nos enregistrements montrent que des informations d'identification ont été divulguées en raison d'une violation de données.


Ne vous inquiétez pas, nous sommes là pour vous aider. Demandez une démo ci-dessous et nous vous aiderons à identifier et à suivre la violation.

img-form.svg
xmark.svg

Compromis!

Nos enregistrements montrent que des informations d'identification ont été divulguées en raison d'une violation de données.


Ne vous inquiétez pas, nous sommes là pour vous aider. Demandez une démo ci-dessous et nous vous aiderons à identifier et à suivre la violation.

img-form.svg
xmark.svg

Non Trouvé!

Aucune violation exposée liée à votre entreprise, pour l'instant!


Our comprehensive feeds are updated twice a day, which means every day is a possibility of capturing data related to your organization. We recommend to request a demo for detailed explanation of our services and how we can help you prevent data breaches in advance.

img-form.svg
xmark.svg
email
xmark.svg

Sur Invitation Seulement

Nous fonctionnons uniquement sur invitation. Veuillez demander une démo pour pouvoir vous inscrire/vous connecter.

email
xmark.svg

Merci pour votre abonnement!

Nous vous enverrons un email pour toutes les mises à jour, les articles de blog, les nouvelles recherches et autres!





[ PART 2 ] How C2 Works In-Depth

Par Hossam Ehab

Dernière mise à jour Nov 21, 2024 - 22 Minutes de lecture

How C2 Works In-depth

Today's Table of Content [ Part 2 ]

Second Part :

  • Reflective Loading & Reflective DLL Injection ( RDI )
  • What it's happening in the RDI Code - Walkthrough 
  • Important Notes About the RDI
  • How payload loaded into victim's memory
  • How is the ReflectiveLoader Function Called in a DLL

Reflective Loading

Introduction to Reflective Loading :-

"Malicious software and DLLs as soon as saved on computer hard drives made it clean for Endpoint Detection and Response ( EDR ) systems to locate them. Stephen Fewer introduced Reflective DLL Injection ( RDI ) around 2010, converting how those threats function. RDI allows DLLs to inject into a program's memory without touching the tough force, making it tougher for EDR structures to spot the intrusion. This approach has drastically impacted cybersecurity, forcing both defenders and attackers to evolve their strategies. Stephen Fewer's work on RDI has been pivotal in advancing digital security measures. Now, that specialize in Command and Control ( C2 ) operations, let's explore how something like Meterpreter executes a DLL with out detection."

Reflective DLL loading is a technique used by attackers to load a dynamic-link library ( DLL ) directly from memory instead of loading it from the disk. Unlike conventional DLL loading, where Windows handles the loading process, reflective DLL injection involves bypassing standard Windows functions, potentially allowing attackers to evade detection.

Source code: https://github.com/rapid7/ReflectiveDLLInjection/

This image will make us understand correctly the Reflective Loading explanation.

image

 

The process of reflective DLL injection unfolds as follows:

  • Accessing the Target Process: The attacker gains access to the target process with read-write-execute permissions. This access enables the attacker to manipulate the process's memory.

    Why do we use the OpenProcess API for it !? This API is used to obtain a handle to the target process, which is required to perform operations such as memory allocation and thread creation within that process.

    MSDN Signature:

    HANDLE OpenProcess(
            [in] DWORD dwDesiredAccess,
            [in] BOOL  bInheritHandle,
            [in] DWORD dwProcessId
          );
          

    Explanation

    • dwDesiredAccess: This defines the type of access to the process. It's checked against the process's security settings. You can use different access rights here. If you have the SeDebugPrivilege privilege, you get access no matter what the security settings say.
    • bInheritHandle: When TRUE, new processes created by this one inherit the handle. If FALSE, they don't.
    • dwProcessId: This is the ID of the process you want to open. Opening the System Idle Process ( 0x00000000 ) fails with ERROR_INVALID_PARAMETER. Trying to open the System process or a Client Server Run-Time Subsystem ( CSRSS ) process fails with ERROR_ACCESS_DENIED due to their high security.
  • Memory Allocation: Within the target process, the attacker allocates a section of memory that is spacious enough to accommodate the entire DLL. This allocated memory space serves as a staging area for the malicious code.

    Why do we use the VirtuallAllocEx API !? To create a buffer within the target process's address space, which will be used to store the DLL before it's executed.

    MSDN Signature:

    LPVOID VirtualAllocEx(
            [in] HANDLE hProcess,
            [in] LPVOID lpAddress,
            [in] SIZE_T dwSize,
            [in] DWORD  flAllocationType,
            [in] DWORD  flProtect
          );
          


    Explanation

    • hProcess: Handle to the process where memory will be allocated. It requires PROCESS_VM_OPERATION access. Details on access rights can be found in Process Security and Access Rights documentation.

    • lpAddress ( optional ): Suggests a preferred starting address for memory allocation.

      • For reserving memory, the function adjusts this address to the nearest allocation granularity.
      • For committing reserved memory, it adjusts to the nearest page boundary.
      • Use GetSystemInfo to find page size and allocation granularity.
      • If NULL, the function chooses the allocation region.
      • Inside an uninitialized enclave, allocates a zeroed page if uncommitted; fails with ERROR_INVALID_ADDRESS in initialized enclaves without dynamic memory management. SGX2 enclaves allow allocation, requiring post-allocation acceptance.
    • dwSize: Defines the memory region size to allocate in bytes. Rounds up to the next page boundary if lpAddress is NULL. If not, it allocates pages covering any part of the range lpAddress to lpAddress+dwSize, potentially including entire pages for small ranges that cross page boundaries.

    • flAllocationType: Specifies the memory allocation type. Must be one of the predefined values

  • Copying the DLL: The attacker proceeds to copy the malicious DLL into the allocated memory space within the target process. This step places the malicious code directly into the memory of the process.

    Why do we use the WriteProcessMemory API !? This API writes data to the memory area within the target process, which is essential for placing the DLL into the allocated space.

    MSDN Signature

    BOOL WriteProcessMemory(
            [in]  HANDLE  hProcess,
            [in]  LPVOID  lpBaseAddress,
            [in]  LPCVOID lpBuffer,
            [in]  SIZE_T  nSize,
            [out] SIZE_T  *lpNumberOfBytesWritten
          );
          


    Explanation

    • hProcess: This is the process's handle where memory will be changed. The handle needs PROCESS_VM_WRITE and PROCESS_VM_OPERATION permissions.

    • lpBaseAddress: Points to the start address in the target process where data will be written. The function checks if this area can be written to; if not, it won't proceed.

    • lpBuffer: Points to the data source to be copied into the target process's space.

    • nSize: Specifies how much data ( in bytes ) will be written to the target process.

    • lpNumberOfBytesWritten ( output ): Points to a variable that will store the count of bytes successfully copied. If not needed, setting it to NULL skips this output.

  • Locating the Reflective Loader: The attacker calculates the memory offset within the loaded DLL to find the export responsible for reflective loading. This offset serves as the entry point for the injection process.

    This step involves internal logic within the DLL, typically not using a standard API but rather a custom algorithm to locate the Reflective Loader function within the injected DLL memory space.

  • Thread Creation: Using functions like CreateRemoteThread or, in some cases, undocumented APIs like RtlCreateUserThread the attacker initiates a new thread within the remote process. The reflective loader function's offset address within the DLL is used as the starting point for execution.

    Why do we use the CreateRemteThread API !? It creates a thread that runs within the virtual address space of the target process, which is important for initiating the execution of the injected DLL code

    MSDN Signature:

    HANDLE CreateRemoteThread(
            [in] HANDLE                 hProcess,
            [in] LPSECURITY_ATTRIBUTES  lpThreadAttributes,
            [in] SIZE_T                 dwStackSize,
            [in] LPTHREAD_START_ROUTINE lpStartAddress,
            [in] LPVOID                 lpParameter,
            [in] DWORD                  dwCreationFlags,
            [out] LPDWORD               lpThreadId
          );
          


    Explanation

    • hProcess: This is the process handle where the thread will be created. It requires PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ access rights. Missing rights may cause failure on some platforms. More details are available in Process Security and Access Rights.

    • lpThreadAttributes: Points to a SECURITY_ATTRIBUTES structure that defines the new thread's security and inheritance capability. If NULL, the thread has a default security descriptor and the handle is not inheritable. The default security descriptor's ACLs are derived from the creator's primary token.

      • Windows XP Note: Previously, ACLs were taken from the primary or impersonation token of the creator, changing with Windows XP SP2 and Windows Server 2003.
    • dwStackSize: Specifies the initial stack size in bytes, rounded to the nearest page. If 0, the thread uses the executable's default stack size. Additional information on Thread Stack Size is available.

    • lpStartAddress: A pointer to the function to be executed by the thread, marking the thread's starting address in the remote process. The function must be present in the remote process. Further information can be found under ThreadProc.

    • lpParameter: A pointer to a variable that will be passed to the thread function.

    • dwCreationFlags: Flags for thread creation.

      • 0: The thread starts immediately after being created.
      • CREATE_SUSPENDED ( 0x00000004 ): The thread is created in a suspended state and needs ResumeThread to start.
      • STACK_SIZE_PARAM_IS_A_RESERVATION ( 0x00010000 ): dwStackSize becomes the initial reserve size of the stack. Without this flag, dwStackSize is the commit size.
    • lpThreadId ( output ): Points to a variable that will receive the thread identifier. If NULL, the identifier is not returned.

  • Loader Function's Execution: The reflective loader function, once executed within the target process, begins by locating the Process Environment Block (PEB) wich is accessed via the fs:[30h] or gs:[60h] register on x86/x64 architecturesspecific to that process. It leverages CPU registers to accomplish this. With the PEB, it then identifies the memory addresses of crucial system libraries, including kernel32.dll.

  • API Function Retrieval: The reflective loader parses the exports directory of kernel32.dll to identify the memory addresses of essential API functions such as LoadLibraryA, GetProcAddress, and VirtualAlloc. These functions are pivotal for the subsequent loading of the DLL.

  • DLL Loading: With the API functions' addresses in hand, the reflective loader utilizes them to properly load the DLL into the memory of the target process. This self-loading process ensures that the malicious DLL is brought into the process's address space without traditional Windows loading mechanisms.

  • Executing the DLL

    Finally, the Reflective Loader calls the DLL's entry point function ( DllMain ) to execute the injected code. This is handled internally by the Reflective Loader logic, not by a separate API.

So basically here is a simple steps recap of how the Meterpreter is using the RDI:

1.      Locate the image in memory

2.      Find libraries/functions

3.      Prepare memory for new image

4.      Process sections

5.      Process Imported lib/functions

6.      Process relocations

7.      Call the Entry point

How it's happening in the RDI C Code - Walkthrough RDI

Reflective DLL Injection involves two main steps. First, the DLL is written into the target process's memory. Second, it's loaded in a way that ensures it functions correctly, like resolving its dependencies and placing it properly in memory. These steps assume that you’ve already managed to run some code within the target process, usually through exploiting a vulnerability.

Once the DLL is placed in memory, the Reflective DLL Injection process works like this:

  1. Starting the ReflectiveLoader:
    • A small piece of bootstrap code runs and hands over control to a special function within the DLL called the ReflectiveLoader 
    • The ReflectiveLoader figures out where it’s currently located in memory so it can correctly handle the DLL's data structures
    • It then looks up important functions in the system (like LoadLibraryA, GetProcAddress, and VirtualAlloc) that it will need to fully load and run the DLL
    • The ReflectiveLoader creates a new memory space to load the DLL, ensuring it can be properly relocated if necessary
    • It processes the DLL’s headers and sections to set everything up in the new memory space
    • The ReflectiveLoader then resolves any additional libraries and functions the DLL needs to run
    • Finally, the ReflectiveLoader triggers the DLL’s main entry point (DllMain), completing the injection process

 

- Step 0: Calculating the Current Image's Base Address

Imagine you're in a library, and you need to find a specific book. However, all the books are placed in a way that you can't just go to a section labeled "Books" and find it. You need a clever way to figure out where you are in the library and where that book might be.

See the code from here: Rapid7 - ReflectiveDLLInjection- ReflectiveLoader.c

 

image-2

 

  1. Calling caller() Function: In this step, you're asking the librarian ( represented by the caller() function ) for information about your current location. They hand you a small note with a location on it.

     
    uiLibraryAddress = caller();
     
  2. Searching for the MZ/PE Header: Now, you're like a detective with a magnifying glass, and you start walking backward in the library. You're looking for a specific sign that says "Library Start." This sign is like a special marker that tells you where the library begins.

     
      // loop through memory backwards searching for our images base address
              // we dont need SEH style search as we shouldnt generate any access violations with this
              while( TRUE )
              {
                if( ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE )
                {
                  uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
                  // some x64 dll's can trigger a bogus signature (IMAGE_DOS_SIGNATURE == 'POP r10'),
                  // we sanity check the e_lfanew with an upper threshold value of 1024 to avoid problems.
                  if( uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024 )
                  {
                    uiHeaderValue += uiLibraryAddress;
                    // break if we have found a valid MZ/PE header
                    if( ((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE )
                      break;
                  }
                }
                uiLibraryAddress--;
              }
     
  3. Identifying the PE Header: Once you find that "Library Start" sign, you spot a map (represented by the e_lfanew field in the DOS header) on the wall that shows you where the different sections of the library are. This map helps you find the book you're looking for.

     
    uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
     
  4. Validating PE Header: To make sure you're on the right track, you check if this map on the wall is valid by making sure it's not too small or too large. You also make sure it has the right label ("PE Header").

     
    if (uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024) {
                uiHeaderValue += uiLibraryAddress;
                if (((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE) {
                    // Valid PE header found.
                    break;
                }
            }
     

Now, you know where the library starts, and you have access to the map that will help you find the book.

- Step 1: Processing the Kernel's Exports

Think of this step as you needing a special tool to open locked doors in the library. You know that there's a workshop ( library maintenance room ) where you can get these tools.

  1. Retrieve Process Environment Block ( PEB ) : You need to know how to get to the workshop, so you ask the librarian for directions. The librarian tells you to go through a secret passage to reach the workshop.

    uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr;
     
  2. Iterating Through Loaded Modules: You enter the secret passage, and it leads you to a corridor filled with rooms. Each room represents a loaded module (like "kernel32.dll" or "ntdll.dll"). You need to find the workshop room ( the one with the tools ).

    while (uiValueA) {
                // ...
            }
     
  3. Computing Hash Values: To recognize the workshop room without peeking inside, you write down a code (hash) for its name. When you pass by each room, you check the code you wrote against the room nameplate.

     
    dwHashValue = _hash((char *)(uiBaseAddress + DEREF_32(uiNameArray)));
     
  4. Identifying Kernel Functions: You wrote down the codes for the names of specific tools you need (like "LoadLibraryA" and "GetProcAddress"). When you see the nameplates on the workshop room doors, you compare them with your list to identify the right rooms.

    if (dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || 
                dwHashValue == VIRTUALALLOC_HASH || dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH) {
                // Store function addresses.
            }

     

Now, you know where to find the tools you need.

- Step 2: Loading the Image into Memory

Imagine you're about to build a model airplane. You need a clear workspace, so you reserve a big table and gather all your materials there.

  1. Memory Allocation: In this case, you're reserving a large, clean table to build your model airplane. You specify the size and permissions for the table.

    uiBaseAddress = (ULONG_PTR)pVirtualAlloc(NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

 

Now, you have a clean workspace ready to load the DLL.

Step 3: Loading Sections of the Image

Imagine you have a blueprint for your model airplane that's divided into different parts. To build it, you need to copy each part from your materials collection to the table.

  1. Copying Headers: First, you take out the blueprint and copy it to the table. The blueprint includes instructions for how to assemble the model airplane.

    while (uiValueA--) {
                *(BYTE *)uiValueC++ = *(BYTE *)uiValueB++;
            }
     
  2. Copying Sections: Now, you start copying each part of the model airplane from your materials collection to the table. Each part has a specific place in the blueprint, and you make sure they fit together perfectly.

This process of copying sections ensures that all the pieces of the DLL are in the right place in memory, just like assembling a model airplane.

This is where we've left off in the explanation. If you'd like to continue with the next steps, please let me know!

- Step 4: Relocating the Image ( Optional )

Sometimes, the table where you're building your model airplane might be smaller than the blueprint. You'd need to adjust and reposition some parts to make them fit correctly. Similarly, in memory, you may need to adjust memory addresses to accommodate the loaded DLL.

  1. Checking for Relocation: You look at the blueprint and see if any notes indicate you need to adjust parts. In our case, you check if the DLL has a relocation section.

    uiValueD = (ULONG_PTR)uiHeaderValue + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +
                      ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader;
     
  2. Performing Relocation: If there are relocation instructions in the DLL, it's like getting a set of specific guidelines for moving parts around. You carefully follow these instructions to ensure everything fits properly in memory.

    if (((PIMAGE_OPTIONAL_HEADER)uiValueD)->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) {
                // Perform relocation.
            }
     

This step is optional and may not always be necessary, just as not all model airplanes require adjustments.

- Step 5: Resolving Import Address Table ( IAT )

Imagine you have a list of suppliers for your model airplane parts, and you need to contact them to get the necessary pieces. You'll call each supplier and ask for the specific parts you need.

  1. Finding Import Table: You refer to your blueprint and find a list of suppliers ( like a phone book ) known as the Import Address Table (IAT). These suppliers (functions from other DLLs) are essential for your DLL to work correctly.

    pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)(uiBaseAddress + RvaToVa((PIMAGE_NT_HEADERS)uiHeaderValue, ((PIMAGE_OPTIONAL_HEADER)uiValueD)->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));
     
  2. Iterating Through Suppliers: You start calling each supplier ( function ) from the list to get the parts you need. You use the tools you found earlier (like LoadLibraryA and GetProcAddress) to make these calls.

    while (pImportDesc->Name) {
                HMODULE hModule = pLoadLibraryA((LPCSTR)(uiBaseAddress + RvaToVa((PIMAGE_NT_HEADERS)uiHeaderValue, pImportDesc->Name)));
                // Iterate through functions for this supplier.
                // Use GetProcAddress to get function addresses.
                pImportDesc++;
            }
     

This way, you're making sure you have all the necessary parts ( functions ) for your model airplane ( DLL ).

- Step 6: Resolving Relocations ( if necessary )

If you encountered relocation instructions earlier, it's like having to adjust some of the parts you received from suppliers to fit your model airplane.

  1. Relocation Table: You check the relocation section in the DLL, which tells you which parts need adjustment.

    pRelocDesc = (PIMAGE_BASE_RELOCATION)(uiBaseAddress + RvaToVa((PIMAGE_NT_HEADERS)uiHeaderValue, ((PIMAGE_OPTIONAL_HEADER)uiValueD)->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress));
     
  2. Adjusting Parts: You follow the instructions in the relocation section to make the necessary adjustments to the parts (memory addresses) so they fit properly.

This ensures that everything in memory aligns correctly with the DLL.

- Step 7: Cleaning Up

Now that your model airplane is assembled and ready, you need to tidy up your workspace and ensure everything is neat and organized.

  1. Cleaning Up the Table: You clear away any leftover materials and tools you used during the assembly process, leaving your workspace clean and free from clutter.

  2. Final Steps: You perform any additional steps needed to make your model airplane ( DLL ) ready for flight. This might include setting up initial conditions, adjusting any parameters, or preparing for its first flight ( execution ).

And there you have it! Your DLL is now loaded into memory, all the parts are correctly positioned, and it's ready to be executed.

Keep in mind that this process is a simplified analogy to help you understand how DLL loading works at a low level. In reality, the Windows loader handles these steps in a more complex and efficient manner.

 

Important notes about the RDI

 

Reflective DLL Injection is like building a specialized toolkit. There’s a basic project skeleton available under the BSD license that gives you a starting point. Metasploit, a well-known tool for security testing, includes Reflective DLL Injection as a part of its payload options, allowing it to inject DLLs like a customized VNC DLL into target processes.

The ReflectiveLoader code is designed to be efficient and flexible. It’s written in C with some inline assembly, making it position-independent, which means it can run correctly no matter where it’s loaded in memory. The code is heavily commented to help you understand how it works step by step. A solid understanding of the Portable Executable (PE) file format is crucial, as this format is central to how DLLs are structured and loaded in Windows.

In Metasploit, there’s a Ruby module called Payload::Windows::ReflectiveDllInject. This module is used to load DLLs that contain the ReflectiveLoader function. The process starts by calculating the offset to the ReflectiveLoader within the DLL. Then, the module generates a small piece of shellcode (shown in Listing 1) that gets inserted into the DLL’s MZ header. This shellcode ensures that once the DLL is loaded into the target process, control is passed directly to the ReflectiveLoader .

The shellcode is very basic but crucial. It starts by adjusting the CPU’s stack to correctly point to the ReflectiveLoader. It then calls this function, which sets up the environment needed to load the DLL. After the DLL is loaded, the shellcode can also manage the connection between the injected DLL and Metasploit, such as handling the socket and the exit function.

  • Listing 1: Bootstrap Shellcode

     
    dec ebp ; M
            pop edx ; Z
            call 0 ; call next instruction
            pop ebx ; get our location (+7)
            push edx ; push edx back
            inc ebp ; restore ebp
            push ebp ; save ebp
            mov ebp, esp ; setup fresh stack frame
            add ebx, 0x???????? ; add offset to ReflectiveLoader
            call ebx ; call ReflectiveLoader
            mov ebx, eax ; save DllMain for second call
            push edi ; our socket
            push 0x4 ; signal we have attached
            push eax ; some value for hinstance
            call eax ; call DllMain( somevalue, DLL_METASPLOIT_ATTACH, socket )
            push 0x???????? ; our EXITFUNC placeholder
            push 0x5 ; signal we have detached
            push eax ; some value for hinstance
            call ebx ; call DllMain( somevalue, DLL_METASPLOIT_DETACH, exitfunk )
            ; we only return if we don't set a valid EXITFUNC
     

This bootstrap shellcode is like a handshake between your toolkit and Metasploit. It carries the payload's socket and, in the finale, the payload's exit function through calls to DllMain. This friendly exchange ensures your toolkit dances to the rhythm of the Metasploit payload system.

 

How is the ReflectiveLoader Function Called in a DLL

 

And finally, here we go with the injector -> Reflective DLL Injection - Injector,

To understand how the ReflectiveLoader function is called within a DLL, we need to explore the LoadLibraryR.c code, which is responsible for loading or injecting ReflectiveLoader DLL. The ReflectiveLoader function is a crucial part of this process. Let's break it down step by step:

Code: Reflective DLL - LoadLibraryR.c

General idea: code is designed to achieve dynamic loading and execution of a Windows Dynamic Link Library (DLL) from memory within a Windows process. This technique is commonly known as the "ReflectiveLoader" technique and is often used for various purposes, including security research, penetration testing, and in some cases, malicious activities.

How the code works! - ( Overview )

  1. RVA to File Offset Conversion ( Rva2Offset function ):

    • The code defines a function called Rva2Offset. This function is responsible for converting a Relative Virtual Address (RVA) to a file offset within a loaded DLL image.
    • RVAs are used to represent memory addresses relative to the image's base address. Converting them to file offsets allows for reading data directly from the DLL file on disk.
  2. Locating the ReflectiveLoader Function ( GetReflectiveLoaderOffset function ):

    • The GetReflectiveLoaderOffset function is used to find the offset of the ReflectiveLoader function within a DLL image. This function searches the export directory of the DLL to locate the ReflectiveLoader function by name.
  3. Loading a DLL from Memory ( LoadLibraryR function ):

    • The LoadLibraryR function is the main entry point for loading a DLL from memory using the ReflectiveLoader technique.

    • It takes as input a pointer to a buffer ( lpBuffer ) containing the DLL image in memory, the length of the buffer (dwLength), and the name of the ReflectiveLoader function ( cpReflectiveLoaderName ).

    • Inside this function:

      • It checks if the provided buffer and length are valid.

      • It calls GetReflectiveLoaderOffset to find the offset of the ReflectiveLoader function.

      • If the offset is found, it adjusts memory protection to make the memory region executable.

      • It then calls the ReflectiveLoader function, which is expected to perform initialization tasks and return the HMODULE of the loaded DLL.

  4. Loading a Remote DLL into a Host Process ( LoadRemoteLibraryR function ):

    • The LoadRemoteLibraryR function extends the functionality to load a DLL into the address space of a remote ( host ) process.

    • It takes additional parameters, including a handle to the target process ( hProcess ) and an optional parameter ( lpParameter ) that can be passed to the ReflectiveLoader function.

    • Inside this function:

      • It allocates memory within the target process for the DLL image and writes the DLL image into the target process's memory.

      • It adjusts the memory protection of the allocated memory to make it executable.

      • It creates a remote thread within the target process, with the thread's entry point set to the ReflectiveLoader function.

      • This effectively causes the ReflectiveLoader function to run within the context of the remote process.

In summary, this code enables the loading of DLLs from memory into a local or remote process. It relies on the ReflectiveLoader technique, which allows DLLs to be loaded and executed without the need for traditional loading methods such as LoadLibrary or process injection techniques like DLL injection. This approach can be used for legitimate purposes like security research or debugging but should be used responsibly, as it can also be leveraged for malicious activities.

 

Entire extensions of the Meterpreter & Code Analysis

 

And that's what we will learn at the third part :) 

 

Thanks for reading!

thanks for reading we hope you enjoyed! - Popular Opinion Polar Bear ...

Prêt à commencer? Nous sommes là pour vous aider! Demandez une démo ci-dessous: