Red Team Notes logo Red Team Notes

Modules are the libraries (dlls) that are already loaded on a process.

If Our payload needs to connect via internet, it’s a good technique to inject into a process that already has the appropiate dlls loaded. So we need to find which modules are loaded on the procceses. An example of a target dll is WS2_32.dll

CreateToolhelp32Snapshot (Classic technique) (WinAPI)

Takes a snapshot of the specified processes, as well as the heaps, modules, and threads used by these processes.

If the function succeeds, it returns an open handle to the specified snapshot.

HANDLE CreateToolhelp32Snapshot(
  [in] DWORD dwFlags,
  [in] DWORD th32ProcessID
);

dwFlags=TH32CS_SNAPMODULE -> Includes all modules of the process specified in th32ProcessID in the snapshot. To enumerate the modules, see Module32First and Module32Next. If the function fails with ERROR_BAD_LENGTH, retry the function until it succeeds.

Final code:

#include <Windows.h>
#include <stdio.h>
#include <tlhelp32.h>

int ListModules(int pid){
    HANDLE hSnapshot;
    MODULEENTRY32 lpme;
    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);

    if(INVALID_HANDLE_VALUE == hSnapshot){
        return -1;
    }

    lpme.dwSize = sizeof(MODULEENTRY32);

    // Snapshot done!
    if(!Module32First(hSnapshot, &lpme)){
        CloseHandle(hSnapshot);
        return -1;
    }
    printf("%#25s\t\t\t%#10s\t\t%#10s\n", "Module", "Base Address", "Size");
    printf("------------------------------------------------------------------------------------------------\n");
    do{
        printf("%#25s\t\t0x%#10p\t\t%#10d\n", lpme.szModule,lpme.modBaseAddr, lpme.modBaseSize);
    } while(Module32Next(hSnapshot, &lpme));

    CloseHandle(hSnapshot);
    return 1;

}

int main(int argc, char ** argv) {
    
    int pid = 3584;
    ListModules(pid);
    return 0;
}

Here we can see the output of targeting notepad.exe.

EnumProcessModulesEx (WinAPI)

EnumProcessModulesEx retrieves a handle for each module in the specified process that meets the specified filter criteria.

BOOL EnumProcessModulesEx(
  [in]  HANDLE  hProcess,
  [out] HMODULE *lphModule,
  [in]  DWORD   cb,
  [out] LPDWORD lpcbNeeded,
  [in]  DWORD   dwFilterFlag
);

Once we have a list of module handles, the name could be retrieved with GetModuleBaseNameA.

Final code:

#include <Windows.h>
#include <stdio.h>
#include <psapi.h>

#pragma comment(lib, "psapi.lib")

int ListModules(int pid){
    HANDLE hProc;
    HMODULE hModules[1024];
    DWORD cb = sizeof(hModules);
    DWORD lpcbNeeded;
    DWORD numModules;
    char lpFilename[1024];

    hProc = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, FALSE, pid);
    
    if(hProc != NULL){
        if(!EnumProcessModulesEx(hProc, hModules, cb, &lpcbNeeded, LIST_MODULES_ALL)){
            CloseHandle(hProc);
            return -1;
        }
        numModules = lpcbNeeded/sizeof(HMODULE);
        printf("[+] %d modules\n", numModules);
        printf("%#25s\t\t\t%#10s\n", "Module", "Base Address");
        printf("------------------------------------------------------------------------------\n");
        for(int i=0; i<numModules; i++){
            GetModuleBaseNameA(hProc, hModules[i], (LPSTR)lpFilename, 1024);
            printf("%#25s\t\t\t0x%#10p\n", lpFilename, hModules[i]);
        }
    }


    return 1;

}

int main(int argc, char ** argv) {
    
    int pid = 3584;
    ListModules(pid);
    return 0;
}

VirtualQueryEx (WinAPI)

VirtualQueryEx retrieves information about a range of pages within the virtual address space of a specified process.

SIZE_T VirtualQueryEx(
  [in]           HANDLE                    hProcess,
  [in, optional] LPCVOID                   lpAddress,
  [out]          PMEMORY_BASIC_INFORMATION lpBuffer,
  [in]           SIZE_T                    dwLength
);

We need to parse MEMORY_BASIC_INFORMATION in order to find the base address and the name of the module.

Final code:

#include <Windows.h>
#include <stdio.h>
#include <psapi.h>

#pragma comment(lib, "psapi.lib")   

int ListModules(int pid) {
    HANDLE hProcess;
    MEMORY_BASIC_INFORMATION mbi;
    char * base = NULL;

    hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
	if (hProcess == NULL)
        return -1;

	// Query the process memory starting from NULL
    while (VirtualQueryEx(hProcess, base, &mbi, sizeof(mbi)) == sizeof(MEMORY_BASIC_INFORMATION)) {
         char szModName[MAX_PATH];

		// Only on the base address regions
 		if ((mbi.AllocationBase == mbi.BaseAddress) && (mbi.AllocationBase != NULL)) {
			if (GetModuleFileNameEx(hProcess, (HMODULE) mbi.AllocationBase, (LPSTR) szModName, sizeof(szModName) / sizeof(TCHAR)))
				printf("%#21llx\t%s\n", mbi.AllocationBase, szModName);
        }
		// Check the next region
		base += mbi.RegionSize;
    }	
	
	CloseHandle(hProcess);
	
	return 0;
}

int main(int argc, char ** argv) {
    
    int pid = 3584;
    ListModules(pid);
    return 0;
}