mmLoader

0x01 What is mmLoader

mmLoader is a stable library for loading PE module bypassing windows PE loader. mmLoader supports x86/x64 Windows.

0x02 How to use mmLoader

Use mmLoader as source code or static library

Include all the files in src/mmLoader folder into your proejects. Or you can build the source code to static library and add reference to it in your projects.

In these two ways you can use the mmLoader APIs:

#ifndef __MMLOADER_H_INCLUDED_
#define __MMLOADER_H_INCLUDED_
#pragma once
#include <windows.h>

#ifdef __cplusplus
extern "C" {
#endif

/// <summary>
/// Error codes.
/// </summary>
#define MMEC_OK 0
#define MMEC_BAD_PE_FORMAT 1
#define MMEC_ALLOCATED_MEMORY_FAILED 2
#define MMEC_INVALID_RELOCATION_BASE 3
#define MMEC_IMPORT_MODULE_FAILED 4
#define MMEC_PROTECT_SECTION_FAILED 5
#define MMEC_INVALID_ENTRY_POINT 6
#define MMEC_INVALID_WIN32_ENV 0xff

/// <summary>
/// Enums for MemModuleHelper.
/// </summary>
typedef enum _MMHELPER_METHOD {
  MHM_BOOL_LOAD,       // Call LoadMemModule
  MHM_VOID_FREE,       // Call FreeMemModule
  MHM_FARPROC_GETPROC, // Call GetMemModuleProc
} MMHELPER_METHOD;

typedef void **HMEMMODULE;

/// <summary>
/// Helper function for using shell code.
/// </summary>
typedef LPVOID(__stdcall *Type_MemModuleHelper)(MMHELPER_METHOD, LPVOID, LPVOID, LPVOID);

/// <summary>
/// Helper function for using shell code.
/// </summary>
/// <remarks>
/// If the method == MHM_BOOL_LOAD, then the function performs the LoadMemModule function.
/// If the method == MHM_VOID_FREE, then the function performs the FreeMemModule function.
/// If the method == MHM_FARPROC_GETPROC, then the function performs the GetMemModuleProc function.
/// </remarks>
LPVOID
MemModuleHelper(_In_ MMHELPER_METHOD method, _In_ LPVOID lpArg1, _In_ LPVOID lpArg2, _In_ LPVOID lpArg3);

/// <summary>
/// Loads the memory module.
/// </summary>
/// <param name="lpPeModuleBuffer">The buffer containing the raw data of the module.</param>
/// <param name="bCallEntry">Call the module entry if true.</param>
/// <param name="pdwError">The error code.</param>
/// <returns>The handle to the memory module instance or NULL.</returns>
HMEMMODULE
LoadMemModule(_In_ LPVOID lpPeModuleBuffer, _In_ BOOL bCallEntry, _Inout_ DWORD *pdwError);

/// <summary>
/// Gets the process address of the specific function in the memory module.
/// </summary>
/// <param name="MemModuleHandle">The handle to the memory module instance.</param>
/// <param name="lpName">The function name.</param>
/// <returns>The address of the function or null.</returns>
FARPROC
GetMemModuleProc(_In_ HMEMMODULE MemModuleHandle, _In_ LPCSTR lpName);

/// <summary>
/// Frees the memory module.HMEMMODULE
/// </summary>
/// <param name="MemModuleHandle">The handle to the memory module instance.</param>
VOID
FreeMemModule(_In_ HMEMMODULE MemModuleHandle);

#ifdef __cplusplus
}
#endif

#endif // __MMLOADER_H_INCLUDED_

Use mmLoader as shell code

If you want to hide the code of mmLoader, you can choose to use mmLoader as shell code. Just build the project mmLoader-shellcode-generator and then run it you will get the single header file of mmLoaderShellCode.h:

/************************************************************************/
/* 
 * This file is generated by mmLoaderShellCode Generator (x86)
 * Target platform: 32 bit windows application
 *
 * https://github.com/tishion 
 *
/************************************************************************/
#ifndef __MMLOADERSHELLCODE_H_INCLUDED_
#define __MMLOADERSHELLCODE_H_INCLUDED_
#pragma once
#include <windows.h>

#ifdef __cplusplus
extern "C" {
#endif

/// <summary>
/// Error codes.
/// </summary>
#define MMEC_OK 0
#define MMEC_BAD_PE_FORMAT 1
#define MMEC_ALLOCATED_MEMORY_FAILED 2
#define MMEC_INVALID_RELOCATION_BASE 3
#define MMEC_IMPORT_MODULE_FAILED 4
#define MMEC_PROTECT_SECTION_FAILED 5
#define MMEC_INVALID_ENTRY_POINT 6
#define MMEC_INVALID_WIN32_ENV 0xff

/// <summary>
/// Enums for MemModuleHelper.
/// </summary>
typedef enum _MMHELPER_METHOD {
  MHM_BOOL_LOAD,       // Call LoadMemModule
  MHM_VOID_FREE,       // Call FreeMemModule
  MHM_FARPROC_GETPROC, // Call GetMemModuleProc
} MMHELPER_METHOD;

typedef void **HMEMMODULE;

/// <summary>
/// Helper function for using shell code.
/// </summary>
typedef LPVOID(*Type_MemModuleHelper)(MMHELPER_METHOD, LPVOID, LPVOID, LPVOID);

/// <summary>
/// Helper function for using shell code.
/// </summary>
/// <remarks>
/// If the method == MHM_BOOL_LOAD, then the function performs the LoadMemModule function.
/// If the method == MHM_VOID_FREE, then the function performs the FreeMemModule function.
/// If the method == MHM_FARPROC_GETPROC, then the function performs the GetMemModuleProc function.
/// </remarks>
LPVOID
MemModuleHelper(_In_ MMHELPER_METHOD method, _In_ LPVOID lpArg1, _In_ LPVOID lpArg2, _In_ LPVOID lpArg3);

/// <summary>
/// Loads the memory module.
/// </summary>
/// <param name="lpPeModuleBuffer">The buffer containing the raw data of the module.</param>
/// <param name="bCallEntry">Call the module entry if true.</param>
/// <param name="pdwError">The error code.</param>
/// <returns>The handle to the memory module instance or NULL.</returns>
HMEMMODULE
LoadMemModule(_In_ LPVOID lpPeModuleBuffer, _In_ BOOL bCallEntry, _Inout_ DWORD *pdwError);

/// <summary>
/// Gets the process address of the specific function in the memory module.
/// </summary>
/// <param name="MemModuleHandle">The handle to the memory module instance.</param>
/// <param name="lpName">The function name.</param>
/// <returns>The address of the function or null.</returns>
FARPROC
GetMemModuleProc(_In_ HMEMMODULE MemModuleHandle, _In_ LPCSTR lpName);

/// <summary>
/// Frees the memory module.HMEMMODULE
/// </summary>
/// <param name="MemModuleHandle">The handle to the memory module instance.</param>
VOID FreeMemModule(_In_ HMEMMODULE MemModuleHandle);


/// <summary>
/// The byte array of the mmLoader shell code.
/// </summary>
unsigned char mmLoaderShellCode[] = {
    ...... 
    ......
    ......
};

#endif // __MMLOADERSHELLCODE_H_INCLUDED_

Then include this header file into your project and use it like this:

int
main() {
  int iRet = -1;

  // Memory module
  HMEMMODULE hMemModule = NULL;
  DWORD dwErrorCode = 0;

  // Allocate memory buffer for shell code with EXECUTE privilege
  LPVOID lpShellCodeBase =
      ::VirtualAlloc(NULL, sizeof(mmLoaderShellCode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

  if (NULL == lpShellCodeBase) {
    ::_tprintf(_T("Failed to allocate space for ShellCode!"));
    return iRet;
  }

  // Copy shell code to the executable memory buffer
  ::RtlCopyMemory(lpShellCodeBase, mmLoaderShellCode, sizeof(mmLoaderShellCode));

  // Get the helper function
  Type_MemModuleHelper pfnMemModuleHelper = (Type_MemModuleHelper)lpShellCodeBase;

  // Here we just read the module data from disk file
  // In your real project you can download the module data from remote without witting it to disk file
#ifdef _DEBUG
  WCHAR wszDllPath[] = L"demo-moduled.dll";
#else
  WCHAR wszDllPath[] = L"demo-module.dll";
#endif
  AutoReleaseModuleBuffer moduleBuffer(wszDllPath);

  // Load the module from the buffer
  hMemModule = (HMEMMODULE)pfnMemModuleHelper(MHM_BOOL_LOAD, moduleBuffer, (LPVOID)TRUE, &dwErrorCode);

  // After the module was loaded we can release the original buffer
  moduleBuffer.Release();

  if (hMemModule) {
    _tprintf(_T("Module was load successfully. Module Base: 0x%p!\r\n"), (LPVOID)hMemModule);

    // Get address of function demoFunction
    LPVOID lpAddr = (LPVOID)pfnMemModuleHelper(MHM_FARPROC_GETPROC, hMemModule, "demoFunction", 0);
    if (lpAddr) {
      _tprintf(_T("Get address of demoFunction successfully. Address: 0x%p!\r\n"), lpAddr);

      // Function pointer type of demoFunction
      typedef BOOL(__stdcall * Type_TargetFunction)(unsigned char *, unsigned int);

      // Call the demoFunction
      Type_TargetFunction pfnFunction = (Type_TargetFunction)lpAddr;

      unsigned char buf[MAX_PATH] = {0};
      if (pfnFunction(buf, MAX_PATH)) {
        char *p = "{f56fee02-16d1-44a3-b191-4d7535f92ca5}";
        iRet = ::memcmp(buf, p, strlen(p));
        if (0 == iRet)
          _tprintf(_T("Called target function demoFunction successfully with correct return value!\r\n"));
        else
          _tprintf(_T("Called target function demoFunction successfully, but returned unexpected value!\r\n"));
      }
    } else
      _tprintf(_T("Failed to get address of MessageBoxA from memory module."));

    // Free the module
    pfnMemModuleHelper(MHM_VOID_FREE, hMemModule, 0, 0);
  } else
    _tprintf(_T("Failed to load the module.!\r\n"));

  // Free the memory buffer of the shell code
  ::VirtualFree(lpShellCodeBase, 0, MEM_RELEASE);

  return iRet;
}

0x03 Is there any limitation ?

No. Feel free to use this library. :)